lintr/0000755000176200001440000000000014600212472011400 5ustar liggesuserslintr/NAMESPACE0000644000176200001440000001156314577222573012645 0ustar liggesusers# Generated by roxygen2: do not edit by hand if (getRversion() >= "4.0.0") { importFrom(tools, R_user_dir) } else { importFrom(backports, R_user_dir) } S3method("[",lints) S3method(as.data.frame,lints) S3method(format,lint) S3method(format,lints) S3method(names,lints) S3method(print,lint) S3method(print,lints) S3method(split,lints) S3method(summary,lints) export(Lint) export(Linter) export(T_and_F_symbol_linter) export(absolute_path_linter) export(all_linters) export(all_undesirable_functions) export(all_undesirable_operators) export(any_duplicated_linter) export(any_is_na_linter) export(assignment_linter) export(available_linters) export(available_tags) export(backport_linter) export(boolean_arithmetic_linter) export(brace_linter) export(checkstyle_output) export(class_equals_linter) export(clear_cache) export(closed_curly_linter) export(commas_linter) export(commented_code_linter) export(condition_message_linter) export(conjunct_test_linter) export(consecutive_assertion_linter) export(consecutive_stopifnot_linter) export(cyclocomp_linter) export(default_linters) export(default_settings) export(default_undesirable_functions) export(default_undesirable_operators) export(duplicate_argument_linter) export(empty_assignment_linter) export(equals_na_linter) export(expect_comparison_linter) export(expect_identical_linter) export(expect_length_linter) export(expect_lint) export(expect_lint_free) export(expect_named_linter) export(expect_not_linter) export(expect_null_linter) export(expect_s3_class_linter) export(expect_s4_class_linter) export(expect_true_false_linter) export(expect_type_linter) export(extraction_operator_linter) export(fixed_regex_linter) export(for_loop_index_linter) export(function_argument_linter) export(function_left_parentheses_linter) export(function_return_linter) export(get_r_string) export(get_source_expressions) export(ids_with_token) export(if_not_else_linter) export(ifelse_censor_linter) export(implicit_assignment_linter) export(implicit_integer_linter) export(indentation_linter) export(infix_spaces_linter) export(inner_combine_linter) export(is_lint_level) export(is_numeric_linter) export(keyword_quote_linter) export(length_levels_linter) export(length_test_linter) export(lengths_linter) export(library_call_linter) export(line_length_linter) export(lint) export(lint_dir) export(lint_package) export(linters_with_defaults) export(linters_with_tags) export(literal_coercion_linter) export(make_linter_from_xpath) export(matrix_apply_linter) export(missing_argument_linter) export(missing_package_linter) export(modify_defaults) export(namespace_linter) export(nested_ifelse_linter) export(no_tab_linter) export(nonportable_path_linter) export(numeric_leading_zero_linter) export(object_length_linter) export(object_name_linter) export(object_usage_linter) export(open_curly_linter) export(outer_negation_linter) export(package_hooks_linter) export(paren_body_linter) export(paren_brace_linter) export(paste_linter) export(pipe_call_linter) export(pipe_consistency_linter) export(pipe_continuation_linter) export(quotes_linter) export(redundant_equals_linter) export(redundant_ifelse_linter) export(regex_subset_linter) export(repeat_linter) export(routine_registration_linter) export(sarif_output) export(scalar_in_linter) export(semicolon_linter) export(semicolon_terminator_linter) export(seq_linter) export(single_quotes_linter) export(sort_linter) export(spaces_inside_linter) export(spaces_left_parentheses_linter) export(sprintf_linter) export(string_boundary_linter) export(strings_as_factors_linter) export(system_file_linter) export(todo_comment_linter) export(trailing_blank_lines_linter) export(trailing_whitespace_linter) export(undesirable_function_linter) export(undesirable_operator_linter) export(unnecessary_concatenation_linter) export(unnecessary_lambda_linter) export(unnecessary_nested_if_linter) export(unnecessary_placeholder_linter) export(unneeded_concatenation_linter) export(unreachable_code_linter) export(unused_import_linter) export(use_lintr) export(vector_logic_linter) export(whitespace_linter) export(with_defaults) export(with_id) export(xml_nodes_to_lints) export(xp_call_name) export(yoda_test_linter) importFrom(cyclocomp,cyclocomp) importFrom(glue,glue) importFrom(glue,glue_collapse) importFrom(rex,character_class) importFrom(rex,re_matches) importFrom(rex,re_substitutes) importFrom(rex,regex) importFrom(rex,rex) importFrom(stats,na.omit) importFrom(utils,capture.output) importFrom(utils,getParseData) importFrom(utils,getTxtProgressBar) importFrom(utils,globalVariables) importFrom(utils,head) importFrom(utils,relist) importFrom(utils,setTxtProgressBar) importFrom(utils,tail) importFrom(utils,txtProgressBar) importFrom(xml2,as_list) importFrom(xml2,xml_attr) importFrom(xml2,xml_find_all) importFrom(xml2,xml_find_chr) importFrom(xml2,xml_find_first) importFrom(xml2,xml_find_lgl) importFrom(xml2,xml_find_num) importFrom(xml2,xml_name) importFrom(xml2,xml_text) lintr/LICENSE0000644000176200001440000000005713740716603012420 0ustar liggesusersYEAR: 2014-2016 COPYRIGHT HOLDER: James Hester lintr/README.md0000644000176200001440000000461714457657444012715 0ustar liggesusers# lintr [![R build status](https://github.com/r-lib/lintr/workflows/R-CMD-check/badge.svg)](https://github.com/r-lib/lintr/actions) [![codecov.io](https://codecov.io/gh/r-lib/lintr/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/r-lib/lintr?branch=main) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/lintr)](https://cran.r-project.org/package=lintr) [![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html) `{lintr}` provides [static code analysis for R](https://en.wikipedia.org/wiki/Static_program_analysis). It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation: ![](man/figures/demo.gif "lintr demo") `{lintr}` is complementary to [the `{styler}` package](https://github.com/r-lib/styler) which automatically restyles code, eliminating some of the problems that `{lintr}` can detect. ## Installation Install the stable version from CRAN: ```R install.packages("lintr") ``` Or the development version from GitHub: ```R # install.packages("remotes") remotes::install_github("r-lib/lintr") ``` ## Usage And then you can create a configuration file and run selected linters: ```R lintr::use_lintr(type = "tidyverse") # in a project: lintr::lint_dir() # in a package: lintr::lint_package() ``` To see a list of linters included for each configuration: ```R # tidyverse (default) names(lintr::linters_with_defaults()) # full names(lintr::all_linters()) ``` ### Setting up GitHub Actions `{usethis}` provides helper functions to generate lint workflows for GitHub Actions: ```R # in a project: usethis::use_github_action("lint-project") # in a package: usethis::use_github_action("lint") ``` You can also run lintr during continuous integration or within your IDE or text editor. See `vignette("continuous-integration")` and `vignette("editors")` for more details. Without further configuration, this will run the [default linters](https://lintr.r-lib.org/reference/default_linters.html). See `vignette("lintr")` to learn how to modify these defaults. ## Code of Conduct Please note that the lintr project is released with a [Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). By contributing to this project, you agree to abide by its terms. lintr/man/0000755000176200001440000000000014577054033012165 5ustar liggesuserslintr/man/unnecessary_concatenation_linter.Rd0000644000176200001440000000367614457657444021325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unnecessary_concatenation_linter.R \name{unnecessary_concatenation_linter} \alias{unnecessary_concatenation_linter} \title{Unneeded concatenation linter} \usage{ unnecessary_concatenation_linter(allow_single_expression = TRUE) } \arguments{ \item{allow_single_expression}{Logical, default \code{TRUE}. If \code{FALSE}, one-expression usages of \code{c()} are always linted, e.g. \code{c(x)} and \code{c(matrix(...))}. In some such cases, \code{c()} is being used for its side-effect of stripping non-name attributes; it is usually preferable to use the more readable \code{\link[=as.vector]{as.vector()}} instead. \code{\link[=as.vector]{as.vector()}} is not always preferable, for example with environments (especially, \code{R6} objects), in which case \code{list()} is the better alternative.} } \description{ Check that the \code{\link[=c]{c()}} function is not used without arguments nor with a single constant. } \examples{ # will produce lints lint( text = "x <- c()", linters = unnecessary_concatenation_linter() ) lint( text = "x <- c(TRUE)", linters = unnecessary_concatenation_linter() ) lint( text = "x <- c(1.5 + 2.5)", linters = unnecessary_concatenation_linter(allow_single_expression = FALSE) ) # okay lint( text = "x <- NULL", linters = unnecessary_concatenation_linter() ) # In case the intent here was to seed a vector of known size lint( text = "x <- integer(4L)", linters = unnecessary_concatenation_linter() ) lint( text = "x <- TRUE", linters = unnecessary_concatenation_linter() ) lint( text = "x <- c(1.5 + 2.5)", linters = unnecessary_concatenation_linter(allow_single_expression = TRUE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/length_levels_linter.Rd0000644000176200001440000000140414506330025016651 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/length_levels_linter.R \name{length_levels_linter} \alias{length_levels_linter} \title{Require usage of nlevels over length(levels(.))} \usage{ length_levels_linter() } \description{ \code{length(levels(x))} is the same as \code{nlevels(x)}, but harder to read. } \examples{ # will produce lints lint( text = "length(levels(x))", linters = length_levels_linter() ) # okay lint( text = "length(c(levels(x), levels(y)))", linters = length_levels_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability} } lintr/man/system_file_linter.Rd0000644000176200001440000000203714457657444016372 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/system_file_linter.R \name{system_file_linter} \alias{system_file_linter} \title{Block usage of \code{file.path()} with \code{system.file()}} \usage{ system_file_linter() } \description{ \code{\link[=system.file]{system.file()}} has a \code{...} argument which, internally, is passed to \code{\link[=file.path]{file.path()}}, so including it in user code is repetitive. } \examples{ # will produce lints lint( text = 'system.file(file.path("path", "to", "data"), package = "foo")', linters = system_file_linter() ) lint( text = 'file.path(system.file(package = "foo"), "path", "to", "data")', linters = system_file_linter() ) # okay lint( text = 'system.file("path", "to", "data", package = "foo")', linters = system_file_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability} } lintr/man/ids_with_token.Rd0000644000176200001440000000404414577052532015472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ids_with_token.R, R/with_id.R \name{ids_with_token} \alias{ids_with_token} \alias{with_id} \title{Get parsed IDs by token} \usage{ ids_with_token(source_expression, value, fun = `==`, source_file = NULL) with_id(source_expression, id, source_file) } \arguments{ \item{source_expression}{A list of source expressions, the result of a call to \code{\link[=get_source_expressions]{get_source_expressions()}}, for the desired filename.} \item{value}{Character. String corresponding to the token to search for. For example: \itemize{ \item "SYMBOL" \item "FUNCTION" \item "EQ_FORMALS" \item "$" \item "(" }} \item{fun}{For additional flexibility, a function to search for in the \code{token} column of \code{parsed_content}. Typically \code{==} or \code{\%in\%}.} \item{source_file}{(DEPRECATED) Same as \code{source_expression}. Will be removed.} \item{id}{Integer. The index corresponding to the desired row of \code{parsed_content}.} } \value{ \code{ids_with_token}: The indices of the \code{parsed_content} data frame entry of the list of source expressions. Indices correspond to the \emph{rows} where \code{fun} evaluates to \code{TRUE} for the \code{value} in the \emph{token} column. \code{with_id}: A data frame corresponding to the row(s) specified in \code{id}. } \description{ Gets the source IDs (row indices) corresponding to given token. } \section{Functions}{ \itemize{ \item \code{with_id()}: Return the row of the \code{parsed_content} entry of the \verb{[get_source_expressions]()} object. Typically used in conjunction with \code{ids_with_token} to iterate over rows containing desired tokens. }} \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) source_exprs <- get_source_expressions(tmp) ids_with_token(source_exprs$expressions[[1L]], value = "SYMBOL") with_id(source_exprs$expressions[[1L]], 2L) \dontshow{\}) # examplesIf} } lintr/man/nonportable_path_linter.Rd0000644000176200001440000000173714577052532017402 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/nonportable_path_linter.R \name{nonportable_path_linter} \alias{nonportable_path_linter} \title{Non-portable path linter} \usage{ nonportable_path_linter(lax = TRUE) } \arguments{ \item{lax}{Less stringent linting, leading to fewer false positives. If \code{TRUE}, only lint path strings, which \itemize{ \item contain at least two path elements, with one having at least two characters and \item contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation }} } \description{ Check that \code{\link[=file.path]{file.path()}} is used to construct safe and portable paths. } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \code{\link[=absolute_path_linter]{absolute_path_linter()}} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=robustness_linters]{robustness} } lintr/man/infix_spaces_linter.Rd0000644000176200001440000000462714510650642016510 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/infix_spaces_linter.R \name{infix_spaces_linter} \alias{infix_spaces_linter} \title{Infix spaces linter} \usage{ infix_spaces_linter(exclude_operators = NULL, allow_multiple_spaces = TRUE) } \arguments{ \item{exclude_operators}{Character vector of operators to exclude from consideration for linting. Default is to include the following "low-precedence" operators: \code{+}, \code{-}, \code{~}, \code{>}, \code{>=}, \code{<}, \code{<=}, \code{==}, \code{!=}, \code{&}, \code{&&}, \code{|}, \code{||}, \verb{<-}, \verb{:=}, \verb{<<-}, \verb{->}, \verb{->>}, \code{=}, \code{/}, \code{*}, and any infix operator (exclude infixes by passing \code{"\%\%"}). Note that \code{"="} here includes three different operators, from the parser's point of view. To lint only some of these, pass the corresponding parse tags (i.e., some of \code{"EQ_ASSIGN"}, \code{"EQ_SUB"}, and \code{"EQ_FORMALS"}; see \code{\link[utils:getParseData]{utils::getParseData()}}).} \item{allow_multiple_spaces}{Logical, default \code{TRUE}. If \code{FALSE}, usage like \code{x = 2} will also be linted; excluded by default because such usage can sometimes be used for better code alignment, as is allowed by the style guide.} } \description{ Check that infix operators are surrounded by spaces. Enforces the corresponding Tidyverse style guide rule; see \url{https://style.tidyverse.org/syntax.html#infix-operators}. } \examples{ # will produce lints lint( text = "x<-1L", linters = infix_spaces_linter() ) lint( text = "1:4 \%>\%sum()", linters = infix_spaces_linter() ) # okay lint( text = "x <- 1L", linters = infix_spaces_linter() ) lint( text = "1:4 \%>\% sum()", linters = infix_spaces_linter() ) code_lines <- " ab <- 1L abcdef <- 2L " writeLines(code_lines) lint( text = code_lines, linters = infix_spaces_linter(allow_multiple_spaces = TRUE) ) lint( text = "a||b", linters = infix_spaces_linter(exclude_operators = "||") ) lint( text = "sum(1:10, na.rm=TRUE)", linters = infix_spaces_linter(exclude_operators = "EQ_SUB") ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#infix-operators} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/missing_argument_linter.Rd0000644000176200001440000000221214457657444017415 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/missing_argument_linter.R \name{missing_argument_linter} \alias{missing_argument_linter} \title{Missing argument linter} \usage{ missing_argument_linter( except = c("alist", "quote", "switch"), allow_trailing = FALSE ) } \arguments{ \item{except}{a character vector of function names as exceptions.} \item{allow_trailing}{always allow trailing empty arguments?} } \description{ Check for missing arguments in function calls (e.g. \code{stats::median(1:10, )}). } \examples{ # will produce lints lint( text = 'tibble(x = "a", )', linters = missing_argument_linter() ) # okay lint( text = 'tibble(x = "a")', linters = missing_argument_linter() ) lint( text = 'tibble(x = "a", )', linters = missing_argument_linter(except = "tibble") ) lint( text = 'tibble(x = "a", )', linters = missing_argument_linter(allow_trailing = TRUE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=common_mistakes_linters]{common_mistakes}, \link[=configurable_linters]{configurable}, \link[=correctness_linters]{correctness} } lintr/man/common_mistakes_linters.Rd0000644000176200001440000000141214577052532017404 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{common_mistakes_linters} \alias{common_mistakes_linters} \title{Common mistake linters} \description{ Linters highlighting common mistakes, such as duplicate arguments. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'common_mistakes': \itemize{ \item{\code{\link{duplicate_argument_linter}}} \item{\code{\link{equals_na_linter}}} \item{\code{\link{length_test_linter}}} \item{\code{\link{missing_argument_linter}}} \item{\code{\link{missing_package_linter}}} \item{\code{\link{redundant_equals_linter}}} \item{\code{\link{sprintf_linter}}} \item{\code{\link{unused_import_linter}}} } } lintr/man/expect_null_linter.Rd0000644000176200001440000000255514506330025016350 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_null_linter.R \name{expect_null_linter} \alias{expect_null_linter} \title{Require usage of \code{expect_null} for checking \code{NULL}} \usage{ expect_null_linter() } \description{ Require usage of \code{expect_null(x)} over \code{expect_equal(x, NULL)} and similar usages. } \details{ \code{\link[testthat:expect_null]{testthat::expect_null()}} exists specifically for testing for \code{NULL} objects. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}}, \code{\link[testthat:equality-expectations]{testthat::expect_identical()}}, and \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = "expect_equal(x, NULL)", linters = expect_null_linter() ) lint( text = "expect_identical(x, NULL)", linters = expect_null_linter() ) lint( text = "expect_true(is.null(x))", linters = expect_null_linter() ) # okay lint( text = "expect_null(x)", linters = expect_null_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat} } lintr/man/expect_true_false_linter.Rd0000644000176200001440000000260114506330025017517 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_true_false_linter.R \name{expect_true_false_linter} \alias{expect_true_false_linter} \title{Require usage of \code{expect_true(x)} over \code{expect_equal(x, TRUE)}} \usage{ expect_true_false_linter() } \description{ \code{\link[testthat:logical-expectations]{testthat::expect_true()}} and \code{\link[testthat:logical-expectations]{testthat::expect_false()}} exist specifically for testing the \code{TRUE}/\code{FALSE} value of an object. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} and \code{\link[testthat:equality-expectations]{testthat::expect_identical()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = "expect_equal(x, TRUE)", linters = expect_true_false_linter() ) lint( text = "expect_equal(x, FALSE)", linters = expect_true_false_linter() ) # okay lint( text = "expect_true(x)", linters = expect_true_false_linter() ) lint( text = "expect_false(x)", linters = expect_true_false_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/trailing_blank_lines_linter.Rd0000644000176200001440000000165414577052532020213 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/trailing_blank_lines_linter.R \name{trailing_blank_lines_linter} \alias{trailing_blank_lines_linter} \title{Trailing blank lines linter} \usage{ trailing_blank_lines_linter() } \description{ Check that there are no trailing blank lines in source code. } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # will produce lints f <- withr::local_tempfile(lines = "x <- 1\n") readLines(f) lint( filename = f, linters = trailing_blank_lines_linter() ) # okay f <- withr::local_tempfile(lines = "x <- 1") readLines(f) lint( filename = f, linters = trailing_blank_lines_linter() ) \dontshow{\}) # examplesIf} } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=default_linters]{default}, \link[=style_linters]{style} } lintr/man/whitespace_linter.Rd0000644000176200001440000000167414457657444016211 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/whitespace_linter.R \name{whitespace_linter} \alias{whitespace_linter} \title{Whitespace linter} \usage{ whitespace_linter() } \description{ Check that the correct character is used for indentation. } \details{ Currently, only supports linting in the presence of tabs. Much ink has been spilled on this topic, and we encourage you to check out references for more information. } \examples{ # will produce lints lint( text = "\tx", linters = whitespace_linter() ) # okay lint( text = " x", linters = whitespace_linter() ) } \references{ \itemize{ \item https://www.jwz.org/doc/tabs-vs-spaces.html \item https://blog.codinghorror.com/death-to-the-space-infidels/ } } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=style_linters]{style} } lintr/man/sort_linter.Rd0000644000176200001440000000434314506330025015012 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sort_linter.R \name{sort_linter} \alias{sort_linter} \title{Check for common mistakes around sorting vectors} \usage{ sort_linter() } \description{ This linter checks for some common mistakes when using \code{\link[=order]{order()}} or \code{\link[=sort]{sort()}}. } \details{ First, it requires usage of \code{sort()} over \code{.[order(.)]}. \code{\link[=sort]{sort()}} is the dedicated option to sort a list or vector. It is more legible and around twice as fast as \code{.[order(.)]}, with the gap in performance growing with the vector size. Second, it requires usage of \code{\link[=is.unsorted]{is.unsorted()}} over equivalents using \code{sort()}. The base function \code{is.unsorted()} exists to test the sortedness of a vector. Prefer it to inefficient and less-readable equivalents like \code{x != sort(x)}. The same goes for checking \code{x == sort(x)} -- use \code{!is.unsorted(x)} instead. Moreover, use of \code{x == sort(x)} can be risky because \code{\link[=sort]{sort()}} drops missing elements by default, meaning \code{==} might end up trying to compare vectors of differing lengths. } \examples{ # will produce lints lint( text = "x[order(x)]", linters = sort_linter() ) lint( text = "x[order(x, decreasing = TRUE)]", linters = sort_linter() ) lint( text = "sort(x) == x", linters = sort_linter() ) # okay lint( text = "x[sample(order(x))]", linters = sort_linter() ) lint( text = "y[order(x)]", linters = sort_linter() ) lint( text = "sort(x, decreasing = TRUE) == x", linters = sort_linter() ) # If you are sorting several objects based on the order of one of them, such # as: x <- sample(1:26) y <- letters newx <- x[order(x)] newy <- y[order(x)] # This will be flagged by the linter. However, in this very specific case, # it would be clearer and more efficient to run order() once and assign it # to an object, rather than mix and match order() and sort() index <- order(x) newx <- x[index] newy <- y[index] } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/scalar_in_linter.Rd0000644000176200001440000000150314577052532015765 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/scalar_in_linter.R \name{scalar_in_linter} \alias{scalar_in_linter} \title{Block usage like x \%in\% "a"} \usage{ scalar_in_linter() } \description{ \code{vector \%in\% set} is appropriate for matching a vector to a set, but if that set has size 1, \code{==} is more appropriate. \verb{\%chin\%} from \code{{data.table}} is matched as well. } \details{ \code{scalar \%in\% vector} is OK, because the alternative (\code{any(vector == scalar)}) is more circuitous & potentially less clear. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/expect_lint.Rd0000644000176200001440000000414314577052532014776 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_lint.R \name{expect_lint} \alias{expect_lint} \title{Lint expectation} \usage{ expect_lint(content, checks, ..., file = NULL, language = "en") } \arguments{ \item{content}{a character vector for the file content to be linted, each vector element representing a line of text.} \item{checks}{checks to be performed: \describe{ \item{NULL}{check that no lints are returned.} \item{single string or regex object}{check that the single lint returned has a matching message.} \item{named list}{check that the single lint returned has fields that match. Accepted fields are the same as those taken by \code{\link[=Lint]{Lint()}}.} \item{list of named lists}{for each of the multiple lints returned, check that it matches the checks in the corresponding named list (as described in the point above).} } Named vectors are also accepted instead of named lists, but this is a compatibility feature that is not recommended for new code.} \item{...}{arguments passed to \code{\link[=lint]{lint()}}, e.g. the linters or cache to use.} \item{file}{if not \code{NULL}, read content from the specified file rather than from \code{content}.} \item{language}{temporarily override Rs \code{LANGUAGE} envvar, controlling localization of base R error messages. This makes testing them reproducible on all systems irrespective of their native R language setting.} } \value{ \code{NULL}, invisibly. } \description{ This is an expectation function to test that the lints produced by \code{lint} satisfy a number of checks. } \examples{ # no expected lint expect_lint("a", NULL, trailing_blank_lines_linter()) # one expected lint expect_lint("a\n", "superfluous", trailing_blank_lines_linter()) expect_lint("a\n", list(message = "superfluous", line_number = 2), trailing_blank_lines_linter()) # several expected lints expect_lint("a\n\n", list("superfluous", "superfluous"), trailing_blank_lines_linter()) expect_lint( "a\n\n", list( list(message = "superfluous", line_number = 2), list(message = "superfluous", line_number = 3) ), trailing_blank_lines_linter() ) } lintr/man/is_numeric_linter.Rd0000644000176200001440000000244514457657444016207 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/is_numeric_linter.R \name{is_numeric_linter} \alias{is_numeric_linter} \title{Redirect \code{is.numeric(x) || is.integer(x)} to just use \code{is.numeric(x)}} \usage{ is_numeric_linter() } \description{ \code{\link[=is.numeric]{is.numeric()}} returns \code{TRUE} when \code{typeof(x)} is \code{double} or \code{integer} -- testing \code{is.numeric(x) || is.integer(x)} is thus redundant. } \details{ NB: This linter plays well with \code{\link[=class_equals_linter]{class_equals_linter()}}, which can help avoid further \code{is.numeric()} equivalents like \code{any(class(x) == c("numeric", "integer"))}. } \examples{ # will produce lints lint( text = "is.numeric(y) || is.integer(y)", linters = is_numeric_linter() ) lint( text = 'class(z) \%in\% c("numeric", "integer")', linters = is_numeric_linter() ) # okay lint( text = "is.numeric(y) || is.factor(y)", linters = is_numeric_linter() ) lint( text = 'class(z) \%in\% c("numeric", "integer", "factor")', linters = is_numeric_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability} } lintr/man/expect_length_linter.Rd0000644000176200001440000000210414506330025016645 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_length_linter.R \name{expect_length_linter} \alias{expect_length_linter} \title{Require usage of \code{expect_length(x, n)} over \code{expect_equal(length(x), n)}} \usage{ expect_length_linter() } \description{ \code{\link[testthat:expect_length]{testthat::expect_length()}} exists specifically for testing the \code{\link[=length]{length()}} of an object. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = "expect_equal(length(x), 2L)", linters = expect_length_linter() ) # okay lint( text = "expect_length(x, 2L)", linters = expect_length_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/pipe_continuation_linter.Rd0000644000176200001440000000264414457657444017602 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pipe_continuation_linter.R \name{pipe_continuation_linter} \alias{pipe_continuation_linter} \title{Pipe continuation linter} \usage{ pipe_continuation_linter() } \description{ Check that each step in a pipeline is on a new line, or the entire pipe fits on one line. } \examples{ # will produce lints code_lines <- "1:3 \%>\%\n mean() \%>\% as.character()" writeLines(code_lines) lint( text = code_lines, linters = pipe_continuation_linter() ) code_lines <- "1:3 |> mean() |>\n as.character()" writeLines(code_lines) lint( text = code_lines, linters = pipe_continuation_linter() ) # okay lint( text = "1:3 \%>\% mean() \%>\% as.character()", linters = pipe_continuation_linter() ) code_lines <- "1:3 \%>\%\n mean() \%>\%\n as.character()" writeLines(code_lines) lint( text = code_lines, linters = pipe_continuation_linter() ) lint( text = "1:3 |> mean() |> as.character()", linters = pipe_continuation_linter() ) code_lines <- "1:3 |>\n mean() |>\n as.character()" writeLines(code_lines) lint( text = code_lines, linters = pipe_continuation_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/pipes.html#long-lines-2} } } \section{Tags}{ \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/extraction_operator_linter.Rd0000644000176200001440000000405714577052532020134 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/extraction_operator_linter.R \name{extraction_operator_linter} \alias{extraction_operator_linter} \title{Extraction operator linter} \usage{ extraction_operator_linter() } \description{ Check that the \code{[[} operator is used when extracting a single element from an object, not \code{[} (subsetting) nor \code{$} (interactive use). } \details{ There are three subsetting operators in R (\code{[[}, \code{[}, and \code{$}) and they interact differently with different data structures (atomic vector, list, data frame, etc.). Here are a few reasons to prefer the \code{[[} operator over \code{[} or \code{$} when you want to extract an element from a data frame or a list: \itemize{ \item Subsetting a list with \code{[} always returns a smaller list, while \code{[[} returns the list element. \item Subsetting a named atomic vector with \code{[} returns a named vector, while \code{[[} returns the vector element. \item Subsetting a data frame (but not tibble) with \code{[} is type unstable; it can return a vector or a data frame. \code{[[}, on the other hand, always returns a vector. \item For a data frame (but not tibble), \code{$} does partial matching (e.g. \code{df$a} will subset \code{df$abc}), which can be a source of bugs. \code{[[} doesn't do partial matching. } For data frames (and tibbles), irrespective of the size, the \code{[[} operator is slower than \code{$}. For lists, however, the reverse is true. } \examples{ # will produce lints lint( text = 'iris["Species"]', linters = extraction_operator_linter() ) lint( text = "iris$Species", linters = extraction_operator_linter() ) # okay lint( text = 'iris[["Species"]]', linters = extraction_operator_linter() ) } \references{ \itemize{ \item Subsetting \href{https://adv-r.hadley.nz/subsetting.html}{chapter} from \emph{Advanced R} (Wickham, 2019). } } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=style_linters]{style} } lintr/man/linters_with_tags.Rd0000644000176200001440000000433214577052532016211 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with.R \name{linters_with_tags} \alias{linters_with_tags} \title{Create a tag-based linter configuration} \usage{ linters_with_tags(tags, ..., packages = "lintr", exclude_tags = "deprecated") } \arguments{ \item{tags}{Optional character vector of tags to search. Only linters with at least one matching tag will be returned. If \code{tags} is \code{NULL}, all linters will be returned. See \code{available_tags("lintr")} to find out what tags are already used by lintr.} \item{...}{Arguments of elements to change. If unnamed, the argument is automatically named. If the named argument already exists in the list of linters, it is replaced by the new element. If it does not exist, it is added. If the value is \code{NULL}, the linter is removed.} \item{packages}{A character vector of packages to search for linters.} \item{exclude_tags}{Tags to exclude from the results. Linters with at least one matching tag will not be returned. If \code{except_tags} is \code{NULL}, no linters will be excluded. Note that \code{tags} takes priority, meaning that any tag found in both \code{tags} and \code{exclude_tags} will be included, not excluded.} } \value{ A modified list of linters. } \description{ Make a new list based on all linters provided by \code{packages} and tagged with \code{tags}. The result of this function is meant to be passed to the \code{linters} argument of \code{lint()}, or to be put in your configuration file. } \examples{ # `linters_with_defaults()` and `linters_with_tags("default")` are the same: all.equal(linters_with_defaults(), linters_with_tags("default")) # Get all linters useful for package development linters <- linters_with_tags(tags = c("package_development", "style")) names(linters) # Get all linters tagged as "default" from lintr and mypkg if (FALSE) { linters_with_tags("default", packages = c("lintr", "mypkg")) } } \seealso{ \itemize{ \item \link{linters_with_defaults} for basing off lintr's set of default linters. \item \link{all_linters} for basing off all available linters in lintr. \item \link{available_linters} to get a data frame of available linters. \item \link{linters} for a complete list of linters available in lintr. } } lintr/man/for_loop_index_linter.Rd0000644000176200001440000000177014457657444017060 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/for_loop_index_linter.R \name{for_loop_index_linter} \alias{for_loop_index_linter} \title{Block usage of for loops directly overwriting the indexing variable} \usage{ for_loop_index_linter() } \description{ \verb{for (x in x)} is a poor choice of indexing variable. This overwrites \code{x} in the calling scope and is confusing to read. } \examples{ # will produce lints lint( text = "for (x in x) { TRUE }", linters = for_loop_index_linter() ) lint( text = "for (x in foo(x, y)) { TRUE }", linters = for_loop_index_linter() ) # okay lint( text = "for (xi in x) { TRUE }", linters = for_loop_index_linter() ) lint( text = "for (col in DF$col) { TRUE }", linters = for_loop_index_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability}, \link[=robustness_linters]{robustness} } lintr/man/keyword_quote_linter.Rd0000644000176200001440000000264314510656222016732 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/keyword_quote_linter.R \name{keyword_quote_linter} \alias{keyword_quote_linter} \title{Block unnecessary quoting in calls} \usage{ keyword_quote_linter() } \description{ Any valid symbol can be used as a keyword argument to an R function call. Sometimes, it is necessary to quote (or backtick) an argument that is not an otherwise valid symbol (e.g. creating a vector whose names have spaces); besides this edge case, quoting should not be done. } \details{ The most common source of violation for this is creating named vectors, lists, or data.frame-alikes, but it can be observed in other calls as well. Similar reasoning applies to extractions with \code{$} or \code{@}. } \examples{ # will produce lints lint( text = 'data.frame("a" = 1)', linters = keyword_quote_linter() ) lint( text = "data.frame(`a` = 1)", linters = keyword_quote_linter() ) lint( text = 'my_list$"key"', linters = keyword_quote_linter() ) lint( text = 's4obj@"key"', linters = keyword_quote_linter() ) # okay lint( text = "data.frame(`a b` = 1)", linters = keyword_quote_linter() ) lint( text = "my_list$`a b`", linters = keyword_quote_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/package_development_linters.Rd0000644000176200001440000000204114250050336020174 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{package_development_linters} \alias{package_development_linters} \title{Package development linters} \description{ Linters useful to package developers, for example for writing consistent tests. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'package_development': \itemize{ \item{\code{\link{backport_linter}}} \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{expect_comparison_linter}}} \item{\code{\link{expect_identical_linter}}} \item{\code{\link{expect_length_linter}}} \item{\code{\link{expect_named_linter}}} \item{\code{\link{expect_not_linter}}} \item{\code{\link{expect_null_linter}}} \item{\code{\link{expect_s3_class_linter}}} \item{\code{\link{expect_s4_class_linter}}} \item{\code{\link{expect_true_false_linter}}} \item{\code{\link{expect_type_linter}}} \item{\code{\link{package_hooks_linter}}} \item{\code{\link{yoda_test_linter}}} } } lintr/man/strings_as_factors_linter.Rd0000644000176200001440000000315614457657444017747 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/strings_as_factors_linter.R \name{strings_as_factors_linter} \alias{strings_as_factors_linter} \title{Identify cases where \code{stringsAsFactors} should be supplied explicitly} \usage{ strings_as_factors_linter() } \description{ Designed for code bases written for versions of R before 4.0 seeking to upgrade to R >= 4.0, where one of the biggest pain points will surely be the flipping of the default value of \code{stringsAsFactors} from \code{TRUE} to \code{FALSE}. } \details{ It's not always possible to tell statically whether the change will break existing code because R is dynamically typed -- e.g. in \code{data.frame(x)} if \code{x} is a string, this code will be affected, but if \code{x} is a number, this code will be unaffected. However, in \code{data.frame(x = "a")}, the output will unambiguously be affected. We can instead supply \code{stringsAsFactors = TRUE}, which will make this code backwards-compatible. See \url{https://developer.r-project.org/Blog/public/2020/02/16/stringsasfactors/}. } \examples{ # will produce lints lint( text = 'data.frame(x = "a")', linters = strings_as_factors_linter() ) # okay lint( text = 'data.frame(x = "a", stringsAsFactors = TRUE)', linters = strings_as_factors_linter() ) lint( text = 'data.frame(x = "a", stringsAsFactors = FALSE)', linters = strings_as_factors_linter() ) lint( text = "data.frame(x = 1.2)", linters = strings_as_factors_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=robustness_linters]{robustness} } lintr/man/read_settings.Rd0000644000176200001440000000310114577052532015304 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/settings.R \name{read_settings} \alias{read_settings} \title{Read lintr settings} \usage{ read_settings(filename) } \arguments{ \item{filename}{source file to be linted} } \description{ Lintr searches for settings for a given source file in the following order: \enumerate{ \item options defined as \code{linter.setting}. \item \code{linter_file} in the same directory \item \code{linter_file} in the project directory \item \code{linter_file} in the user home directory \item \code{\link[=default_settings]{default_settings()}} } } \details{ The default linter_file name is \code{.lintr} but it can be changed with option \code{lintr.linter_file} or the environment variable \code{R_LINTR_LINTER_FILE} This file is a DCF file, see \code{\link[base:dcf]{base::read.dcf()}} for details. Experimentally, we also support keeping the config in a plain R file. By default we look for a file named '.lintr.R' (in the same directories where we search for '.lintr'). We are still deciding the future of config support in lintr, so user feedback is welcome. The advantage of R is that it maps more closely to how the configs are actually stored, whereas the DCF approach requires somewhat awkward formatting of parseable R code within valid DCF key-value pairs. The main disadvantage of the R file is it might be \emph{too} flexible, with users tempted to write configs with side effects causing hard-to-detect bugs or like YAML could work, but require new dependencies and are harder to parse both programmatically and visually. } lintr/man/expect_not_linter.Rd0000644000176200001440000000216514506330025016173 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_not_linter.R \name{expect_not_linter} \alias{expect_not_linter} \title{Require usage of \code{expect_false(x)} over \code{expect_true(!x)}} \usage{ expect_not_linter() } \description{ \code{\link[testthat:logical-expectations]{testthat::expect_false()}} exists specifically for testing that an output is \code{FALSE}. \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests by negating the output, but it is better to use the tailored function instead. The reverse is also true -- use \code{expect_false(A)} instead of \code{expect_true(!A)}. } \examples{ # will produce lints lint( text = "expect_true(!x)", linters = expect_not_linter() ) # okay lint( text = "expect_false(x)", linters = expect_not_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/unreachable_code_linter.Rd0000644000176200001440000000334614577052532017304 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unreachable_code_linter.R \name{unreachable_code_linter} \alias{unreachable_code_linter} \title{Block unreachable code and comments following return statements} \usage{ unreachable_code_linter() } \description{ Code after e.g. a \code{\link[=return]{return()}} or \code{\link[=stop]{stop()}} or in deterministically false conditional loops like \verb{if (FALSE)} can't be reached; typically this is vestigial code left after refactoring or sandboxing code, which is fine for exploration, but shouldn't ultimately be checked in. Comments meant for posterity should be placed \emph{before} the final \code{return()}. } \examples{ # will produce lints code_lines <- "f <- function() {\n return(1 + 1)\n 2 + 2\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) code_lines <- "f <- if (FALSE) {\n 2 + 2\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) code_lines <- "f <- while (FALSE) {\n 2 + 2\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) # okay code_lines <- "f <- function() {\n return(1 + 1)\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) code_lines <- "f <- if (foo) {\n 2 + 2\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) code_lines <- "f <- while (foo) {\n 2 + 2\n}" writeLines(code_lines) lint( text = code_lines, linters = unreachable_code_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability} } lintr/man/namespace_linter.Rd0000644000176200001440000000264014457657444016003 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/namespace_linter.R \name{namespace_linter} \alias{namespace_linter} \title{Namespace linter} \usage{ namespace_linter(check_exports = TRUE, check_nonexports = TRUE) } \arguments{ \item{check_exports}{Check if \code{symbol} is exported from \code{namespace} in \code{namespace::symbol} calls.} \item{check_nonexports}{Check if \code{symbol} exists in \code{namespace} in \code{namespace:::symbol} calls.} } \description{ Check for missing packages and symbols in namespace calls. Note that using \code{check_exports=TRUE} or \code{check_nonexports=TRUE} will load packages used in user code so it could potentially change the global state. } \examples{ # will produce lints lint( text = "xyzxyz::sd(c(1, 2, 3))", linters = namespace_linter() ) lint( text = "stats::ssd(c(1, 2, 3))", linters = namespace_linter() ) # okay lint( text = "stats::sd(c(1, 2, 3))", linters = namespace_linter() ) lint( text = "stats::ssd(c(1, 2, 3))", linters = namespace_linter(check_exports = FALSE) ) lint( text = "stats:::ssd(c(1, 2, 3))", linters = namespace_linter(check_nonexports = FALSE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=correctness_linters]{correctness}, \link[=executing_linters]{executing}, \link[=robustness_linters]{robustness} } lintr/man/inner_combine_linter.Rd0000644000176200001440000000226014457657444016654 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/inner_combine_linter.R \name{inner_combine_linter} \alias{inner_combine_linter} \title{Require \code{c()} to be applied before relatively expensive vectorized functions} \usage{ inner_combine_linter() } \description{ \code{as.Date(c(a, b))} is logically equivalent to \code{c(as.Date(a), as.Date(b))}. The same equivalence holds for several other vectorized functions like \code{\link[=as.POSIXct]{as.POSIXct()}} and math functions like \code{\link[=sin]{sin()}}. The former is to be preferred so that the most expensive part of the operation (\code{\link[=as.Date]{as.Date()}}) is applied only once. } \examples{ # will produce lints lint( text = "c(log10(x), log10(y), log10(z))", linters = inner_combine_linter() ) # okay lint( text = "log10(c(x, y, z))", linters = inner_combine_linter() ) lint( text = "c(log(x, base = 10), log10(x, base = 2))", linters = inner_combine_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/literal_coercion_linter.Rd0000644000176200001440000000254414457657444017367 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/literal_coercion_linter.R \name{literal_coercion_linter} \alias{literal_coercion_linter} \title{Require usage of correctly-typed literals over literal coercions} \usage{ literal_coercion_linter() } \description{ \code{as.integer(1)} (or \code{rlang::int(1)}) is the same as \code{1L} but the latter is more concise and gets typed correctly at compilation. } \details{ The same applies to missing sentinels like \code{NA} -- typically, it is not necessary to specify the storage type of \code{NA}, but when it is, prefer using the typed version (e.g. \code{NA_real_}) instead of a coercion (like \code{as.numeric(NA)}). } \examples{ # will produce lints lint( text = "int(1)", linters = literal_coercion_linter() ) lint( text = "as.character(NA)", linters = literal_coercion_linter() ) lint( text = "rlang::lgl(1L)", linters = literal_coercion_linter() ) # okay lint( text = "1L", linters = literal_coercion_linter() ) lint( text = "NA_character_", linters = literal_coercion_linter() ) lint( text = "TRUE", linters = literal_coercion_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=efficiency_linters]{efficiency} } lintr/man/duplicate_argument_linter.Rd0000644000176200001440000000270614457657444017726 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duplicate_argument_linter.R \name{duplicate_argument_linter} \alias{duplicate_argument_linter} \title{Duplicate argument linter} \usage{ duplicate_argument_linter(except = c("mutate", "transmute")) } \arguments{ \item{except}{A character vector of function names as exceptions. Defaults to functions that allow sequential updates to variables, currently \code{dplyr::mutate()} and \code{dplyr::transmute()}.} } \description{ Check for duplicate arguments in function calls. Some cases are run-time errors (e.g. \code{mean(x = 1:5, x = 2:3)}), otherwise this linter is used to discourage explicitly providing duplicate names to objects (e.g. \code{c(a = 1, a = 2)}). Duplicate-named objects are hard to work with programmatically and should typically be avoided. } \examples{ # will produce lints lint( text = "list(x = 1, x = 2)", linters = duplicate_argument_linter() ) lint( text = "fun(arg = 1, arg = 2)", linters = duplicate_argument_linter() ) # okay lint( text = "list(x = 1, x = 2)", linters = duplicate_argument_linter(except = "list") ) lint( text = "df \%>\% dplyr::mutate(x = a + b, x = x + d)", linters = duplicate_argument_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=common_mistakes_linters]{common_mistakes}, \link[=configurable_linters]{configurable}, \link[=correctness_linters]{correctness} } lintr/man/nested_ifelse_linter.Rd0000644000176200001440000000450714510656222016643 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/nested_ifelse_linter.R \name{nested_ifelse_linter} \alias{nested_ifelse_linter} \title{Block usage of nested \code{ifelse()} calls} \usage{ nested_ifelse_linter() } \description{ Calling \code{\link[=ifelse]{ifelse()}} in nested calls is problematic for two main reasons: \enumerate{ \item It can be hard to read -- mapping the code to the expected output for such code can be a messy task/require a lot of mental bandwidth, especially for code that nests more than once \item It is inefficient -- \code{ifelse()} can evaluate \emph{all} of its arguments at both yes and no (see \url{https://stackoverflow.com/q/16275149}); this issue is exacerbated for nested calls } } \details{ Users can instead rely on a more readable alternative modeled after SQL CASE WHEN statements. Let's say this is our original code: \if{html}{\out{
}}\preformatted{ifelse( x == "a", 2L, ifelse(x == "b", 3L, 1L) ) }\if{html}{\out{
}} Here are a few ways to avoid nesting and make the code more readable: \itemize{ \item Use \code{data.table::fcase()} \if{html}{\out{
}}\preformatted{data.table::fcase( x == "a", 2L, x == "b", 3L, default = 1L ) }\if{html}{\out{
}} \item Use \code{dplyr::case_match()} \if{html}{\out{
}}\preformatted{dplyr::case_match( x, "a" ~ 2L, "b" ~ 3L, .default = 1L ) }\if{html}{\out{
}} \item Use a look-up-and-merge approach (build a mapping table between values and outputs and merge this to the input) \if{html}{\out{
}}\preformatted{default <- 1L values <- data.frame( a = 2L, b = 3L ) found_value <- values[[x]] ifelse(is.null(found_value), default, found_value) }\if{html}{\out{
}} } } \examples{ # will produce lints lint( text = 'ifelse(x == "a", 1L, ifelse(x == "b", 2L, 3L))', linters = nested_ifelse_linter() ) # okay lint( text = 'dplyr::case_when(x == "a" ~ 1L, x == "b" ~ 2L, TRUE ~ 3L)', linters = nested_ifelse_linter() ) lint( text = 'data.table::fcase(x == "a", 1L, x == "b", 2L, default = 3L)', linters = nested_ifelse_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/deprecated_linters.Rd0000644000176200001440000000152414577052532016320 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{deprecated_linters} \alias{deprecated_linters} \title{Deprecated linters} \description{ Linters that are deprecated and provided for backwards compatibility only. These linters will be excluded from \code{linters_with_tags()} by default. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'deprecated': \itemize{ \item{\code{\link{closed_curly_linter}}} \item{\code{\link{consecutive_stopifnot_linter}}} \item{\code{\link{no_tab_linter}}} \item{\code{\link{open_curly_linter}}} \item{\code{\link{paren_brace_linter}}} \item{\code{\link{semicolon_terminator_linter}}} \item{\code{\link{single_quotes_linter}}} \item{\code{\link{unneeded_concatenation_linter}}} } } lintr/man/undesirable_function_linter.Rd0000644000176200001440000000430514457657444020251 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/undesirable_function_linter.R \name{undesirable_function_linter} \alias{undesirable_function_linter} \title{Undesirable function linter} \usage{ undesirable_function_linter( fun = default_undesirable_functions, symbol_is_undesirable = TRUE ) } \arguments{ \item{fun}{Named character vector. \code{names(fun)} correspond to undesirable functions, while the values give a description of why the function is undesirable. If \code{NA}, no additional information is given in the lint message. Defaults to \link{default_undesirable_functions}. To make small customizations to this list, use \code{\link[=modify_defaults]{modify_defaults()}}.} \item{symbol_is_undesirable}{Whether to consider the use of an undesirable function name as a symbol undesirable or not.} } \description{ Report the use of undesirable functions (e.g. \code{\link[base:function]{base::return()}}, \code{\link[base:options]{base::options()}}, or \code{\link[base:lapply]{base::sapply()}}) and suggest an alternative. } \examples{ # defaults for which functions are considered undesirable names(default_undesirable_functions) # will produce lints lint( text = "sapply(x, mean)", linters = undesirable_function_linter() ) lint( text = "log10(x)", linters = undesirable_function_linter(fun = c("log10" = NA)) ) lint( text = "log10(x)", linters = undesirable_function_linter(fun = c("log10" = "use log()")) ) lint( text = 'dir <- "path/to/a/directory"', linters = undesirable_function_linter(fun = c("dir" = NA)) ) # okay lint( text = "vapply(x, mean, FUN.VALUE = numeric(1))", linters = undesirable_function_linter() ) lint( text = "log(x, base = 10)", linters = undesirable_function_linter(fun = c("log10" = "use log()")) ) lint( text = 'dir <- "path/to/a/directory"', linters = undesirable_function_linter(fun = c("dir" = NA), symbol_is_undesirable = FALSE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=efficiency_linters]{efficiency}, \link[=robustness_linters]{robustness}, \link[=style_linters]{style} } lintr/man/lint-s3.Rd0000644000176200001440000000156014457657444013763 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lint.R \name{lint-s3} \alias{lint-s3} \alias{Lint} \title{Create a \code{lint} object} \usage{ Lint( filename, line_number = 1L, column_number = 1L, type = c("style", "warning", "error"), message = "", line = "", ranges = NULL, linter = "" ) } \arguments{ \item{filename}{path to the source file that was linted.} \item{line_number}{line number where the lint occurred.} \item{column_number}{column number where the lint occurred.} \item{type}{type of lint.} \item{message}{message used to describe the lint error} \item{line}{code source where the lint occurred} \item{ranges}{a list of ranges on the line that should be emphasized.} \item{linter}{deprecated. No longer used.} } \value{ an object of class \code{c("lint", "list")}. } \description{ Create a \code{lint} object } lintr/man/pipe_call_linter.Rd0000644000176200001440000000144314457657444015777 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pipe_call_linter.R \name{pipe_call_linter} \alias{pipe_call_linter} \title{Pipe call linter} \usage{ pipe_call_linter() } \description{ Force explicit calls in magrittr pipes, e.g., \code{1:3 \%>\% sum()} instead of \code{1:3 \%>\% sum}. Note that native pipe always requires a function call, i.e. \verb{1:3 |> sum} will produce an error. } \examples{ # will produce lints lint( text = "1:3 \%>\% mean \%>\% as.character", linters = pipe_call_linter() ) # okay lint( text = "1:3 \%>\% mean() \%>\% as.character()", linters = pipe_call_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/get_r_string.Rd0000644000176200001440000000433314577052532015147 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{get_r_string} \alias{get_r_string} \title{Extract text from \code{STR_CONST} nodes} \usage{ get_r_string(s, xpath = NULL) } \arguments{ \item{s}{An input string or strings. If \code{s} is an \code{xml_node} or \code{xml_nodeset} and \code{xpath} is \code{NULL}, extract its string value with \code{\link[xml2:xml_text]{xml2::xml_text()}}. If \code{s} is an \code{xml_node} or \code{xml_nodeset} and \code{xpath} is specified, it is extracted with \code{\link[xml2:xml_find_all]{xml2::xml_find_chr()}}.} \item{xpath}{An XPath, passed on to \code{\link[xml2:xml_find_all]{xml2::xml_find_chr()}} after wrapping with \code{string()}.} } \description{ Convert \code{STR_CONST} \code{text()} values into R strings. This is useful to account for arbitrary character literals valid since R 4.0, e.g. \code{R"------[hello]------"}, which is parsed in R as \code{"hello"}. It is quite cumbersome to write XPaths allowing for strings like this, so whenever your linter logic requires testing a \code{STR_CONST} node's value, use this function. NB: this is also properly vectorized on \code{s}, and accepts a variety of inputs. Empty inputs will become \code{NA} outputs, which helps ensure that \code{length(get_r_string(s)) == length(s)}. } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tmp <- withr::local_tempfile(lines = "c('a', 'b')") expr_as_xml <- get_source_expressions(tmp)$expressions[[1L]]$xml_parsed_content writeLines(as.character(expr_as_xml)) get_r_string(expr_as_xml, "expr[2]") # "a" get_r_string(expr_as_xml, "expr[3]") # "b" # more importantly, extract strings under R>=4 raw strings \dontshow{\}) # examplesIf} \dontshow{if (getRversion() >= "4.0.0") (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tmp4.0 <- withr::local_tempfile(lines = "c(R'(a\\\\b)', R'--[a\\\\\"\'\"\\\\b]--')") expr_as_xml4.0 <- get_source_expressions(tmp4.0)$expressions[[1L]]$xml_parsed_content writeLines(as.character(expr_as_xml4.0)) get_r_string(expr_as_xml4.0, "expr[2]") # "a\\b" get_r_string(expr_as_xml4.0, "expr[3]") # "a\\\"'\"\\b" \dontshow{\}) # examplesIf} } lintr/man/best_practices_linters.Rd0000644000176200001440000000525714577052532017221 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{best_practices_linters} \alias{best_practices_linters} \title{Best practices linters} \description{ Linters checking the use of coding best practices, such as explicit typing of numeric constants. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'best_practices': \itemize{ \item{\code{\link{absolute_path_linter}}} \item{\code{\link{any_duplicated_linter}}} \item{\code{\link{any_is_na_linter}}} \item{\code{\link{boolean_arithmetic_linter}}} \item{\code{\link{class_equals_linter}}} \item{\code{\link{commented_code_linter}}} \item{\code{\link{condition_message_linter}}} \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{cyclocomp_linter}}} \item{\code{\link{empty_assignment_linter}}} \item{\code{\link{expect_comparison_linter}}} \item{\code{\link{expect_length_linter}}} \item{\code{\link{expect_named_linter}}} \item{\code{\link{expect_not_linter}}} \item{\code{\link{expect_null_linter}}} \item{\code{\link{expect_s3_class_linter}}} \item{\code{\link{expect_s4_class_linter}}} \item{\code{\link{expect_true_false_linter}}} \item{\code{\link{expect_type_linter}}} \item{\code{\link{extraction_operator_linter}}} \item{\code{\link{fixed_regex_linter}}} \item{\code{\link{for_loop_index_linter}}} \item{\code{\link{function_argument_linter}}} \item{\code{\link{function_return_linter}}} \item{\code{\link{ifelse_censor_linter}}} \item{\code{\link{implicit_assignment_linter}}} \item{\code{\link{implicit_integer_linter}}} \item{\code{\link{is_numeric_linter}}} \item{\code{\link{length_levels_linter}}} \item{\code{\link{lengths_linter}}} \item{\code{\link{library_call_linter}}} \item{\code{\link{literal_coercion_linter}}} \item{\code{\link{nonportable_path_linter}}} \item{\code{\link{outer_negation_linter}}} \item{\code{\link{paste_linter}}} \item{\code{\link{redundant_equals_linter}}} \item{\code{\link{redundant_ifelse_linter}}} \item{\code{\link{regex_subset_linter}}} \item{\code{\link{routine_registration_linter}}} \item{\code{\link{scalar_in_linter}}} \item{\code{\link{seq_linter}}} \item{\code{\link{sort_linter}}} \item{\code{\link{system_file_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{undesirable_function_linter}}} \item{\code{\link{undesirable_operator_linter}}} \item{\code{\link{unnecessary_lambda_linter}}} \item{\code{\link{unnecessary_nested_if_linter}}} \item{\code{\link{unnecessary_placeholder_linter}}} \item{\code{\link{unreachable_code_linter}}} \item{\code{\link{unused_import_linter}}} \item{\code{\link{vector_logic_linter}}} \item{\code{\link{yoda_test_linter}}} } } lintr/man/lintr-package.Rd0000644000176200001440000000156414577054033015203 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lintr-package.R \docType{package} \name{lintr-package} \alias{lintr} \alias{lintr-package} \title{Lintr} \description{ Checks adherence to a given style, syntax errors, and possible semantic issues. Supports on the fly checking of R code edited with Emacs, Vim, and Sublime Text. } \seealso{ \code{\link[=lint]{lint()}}, \code{\link[=lint_package]{lint_package()}}, \code{\link[=lint_dir]{lint_dir()}}, \link{linters} } \author{ \strong{Maintainer}: Michael Chirico \email{michaelchirico4@gmail.com} Authors: \itemize{ \item Jim Hester \item Florent Angly (fangly) \item Russ Hyde \item Kun Ren \item Alexander Rosenstock (AshesITR) \item Indrajeet Patil \email{patilindrajeet.science@gmail.com} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID}) (@patilindrajeets) } } \keyword{internal} lintr/man/quotes_linter.Rd0000644000176200001440000000225314457657444015367 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quotes_linter.R \name{quotes_linter} \alias{quotes_linter} \title{Character string quote linter} \usage{ quotes_linter(delimiter = c("\\"", "'")) } \arguments{ \item{delimiter}{Which quote delimiter to accept. Defaults to the tidyverse default of \verb{"} (double-quoted strings).} } \description{ Check that the desired quote delimiter is used for string constants. } \examples{ # will produce lints lint( text = "c('a', 'b')", linters = quotes_linter() ) # okay lint( text = 'c("a", "b")', linters = quotes_linter() ) code_lines <- "paste0(x, '\"this is fine\"')" writeLines(code_lines) lint( text = code_lines, linters = quotes_linter() ) # okay lint( text = "c('a', 'b')", linters = quotes_linter(delimiter = "'") ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#character-vectors} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/vector_logic_linter.Rd0000644000176200001440000000410514577052532016512 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vector_logic_linter.R \name{vector_logic_linter} \alias{vector_logic_linter} \title{Enforce usage of scalar logical operators in conditional statements} \usage{ vector_logic_linter() } \description{ Usage of \code{&} in conditional statements is error-prone and inefficient. \code{condition} in \code{if (condition) expr} must always be of length 1, in which case \code{&&} is to be preferred. Ditto for \code{|} vs. \code{||}. } \details{ This linter covers inputs to \verb{if()} and \verb{while()} conditions and to \code{\link[testthat:logical-expectations]{testthat::expect_true()}} and \code{\link[testthat:logical-expectations]{testthat::expect_false()}}. Note that because \code{&} and \code{|} are generics, it is possible that \code{&&} / \code{||} are not perfect substitutes because \code{&} is doing method dispatch in an incompatible way. Moreover, be wary of code that may have side effects, most commonly assignments. Consider \code{if ((a <- foo(x)) | (b <- bar(y))) { ... }} vs. \code{if ((a <- foo(x)) || (b <- bar(y))) { ... }}. Because \code{||} exits early, if \code{a} is \code{TRUE}, the second condition will never be evaluated and \code{b} will not be assigned. Such usage is not allowed by the Tidyverse style guide, and the code can easily be refactored by pulling the assignment outside the condition, so using \code{||} is still preferable. } \examples{ # will produce lints lint( text = "if (TRUE & FALSE) 1", linters = vector_logic_linter() ) lint( text = "if (TRUE && (TRUE | FALSE)) 4", linters = vector_logic_linter() ) # okay lint( text = "if (TRUE && FALSE) 1", linters = vector_logic_linter() ) lint( text = "if (TRUE && (TRUE || FALSE)) 4", linters = vector_logic_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#if-statements} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=default_linters]{default}, \link[=efficiency_linters]{efficiency} } lintr/man/object_name_linter.Rd0000644000176200001440000000664114457657444016322 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object_name_linter.R \name{object_name_linter} \alias{object_name_linter} \title{Object name linter} \usage{ object_name_linter(styles = c("snake_case", "symbols"), regexes = character()) } \arguments{ \item{styles}{A subset of \Sexpr[stage=render, results=rd]{lintr:::regexes_rd}. A name should match at least one of these styles. The \code{"symbols"} style refers to names containing \emph{only} non-alphanumeric characters; e.g., defining \verb{\%+\%} from ggplot2 or \verb{\%>\%} from magrittr would not generate lint markers, whereas \verb{\%m+\%} from lubridate (containing both alphanumeric \emph{and} non-alphanumeric characters) would.} \item{regexes}{A (possibly named) character vector specifying a custom naming convention. If named, the names will be used in the lint message. Otherwise, the regexes enclosed by \code{/} will be used in the lint message. Note that specifying \code{regexes} overrides the default \code{styles}. So if you want to combine \code{regexes} and \code{styles}, both need to be explicitly specified.} } \description{ Check that object names conform to a naming style. The default naming styles are "snake_case" and "symbols". } \details{ Quotes (\verb{`"'}) and specials (\verb{\%} and trailing \verb{<-}) are not considered part of the object name. Note when used in a package, in order to ignore objects imported from other namespaces, this linter will attempt \code{\link[=getNamespaceExports]{getNamespaceExports()}} whenever an \code{import(PKG)} or \code{importFrom(PKG, ...)} statement is found in your NAMESPACE file. If \code{\link[=requireNamespace]{requireNamespace()}} fails (e.g., the package is not yet installed), the linter won't be able to ignore some usages that would otherwise be allowed. Suppose, for example, you have \code{import(upstream)} in your NAMESPACE, which makes available its exported S3 generic function \code{a_really_quite_long_function_name} that you then extend in your package by defining a corresponding method for your class \code{my_class}. Then, if \code{upstream} is not installed when this linter runs, a lint will be thrown on this object (even though you don't "own" its full name). The best way to get lintr to work correctly is to install the package so that it's available in the session where this linter is running. } \examples{ # will produce lints lint( text = "my_var <- 1L", linters = object_name_linter(styles = "CamelCase") ) lint( text = "xYz <- 1L", linters = object_name_linter(styles = c("UPPERCASE", "lowercase")) ) lint( text = "MyVar <- 1L", linters = object_name_linter(styles = "dotted.case") ) lint( text = "asd <- 1L", linters = object_name_linter(regexes = c(my_style = "F$", "f$")) ) # okay lint( text = "my_var <- 1L", linters = object_name_linter(styles = "snake_case") ) lint( text = "xyz <- 1L", linters = object_name_linter(styles = "lowercase") ) lint( text = "my.var <- 1L; myvar <- 2L", linters = object_name_linter(styles = c("dotted.case", "lowercase")) ) lint( text = "asdf <- 1L; asdF <- 1L", linters = object_name_linter(regexes = c(my_style = "F$", "f$")) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=executing_linters]{executing}, \link[=style_linters]{style} } lintr/man/paren_body_linter.Rd0000644000176200001440000000141014457657444016163 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/paren_body_linter.R \name{paren_body_linter} \alias{paren_body_linter} \title{Parenthesis before body linter} \usage{ paren_body_linter() } \description{ Check that there is a space between right parenthesis and a body expression. } \examples{ # will produce lints lint( text = "function(x)x + 1", linters = paren_body_linter() ) # okay lint( text = "function(x) x + 1", linters = paren_body_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#parentheses} } } \section{Tags}{ \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/available_linters.Rd0000644000176200001440000000534314577052532016143 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tags.R \name{available_linters} \alias{available_linters} \alias{available_tags} \title{Get Linter metadata from a package} \usage{ available_linters(packages = "lintr", tags = NULL, exclude_tags = "deprecated") available_tags(packages = "lintr") } \arguments{ \item{packages}{A character vector of packages to search for linters.} \item{tags}{Optional character vector of tags to search. Only linters with at least one matching tag will be returned. If \code{tags} is \code{NULL}, all linters will be returned. See \code{available_tags("lintr")} to find out what tags are already used by lintr.} \item{exclude_tags}{Tags to exclude from the results. Linters with at least one matching tag will not be returned. If \code{except_tags} is \code{NULL}, no linters will be excluded. Note that \code{tags} takes priority, meaning that any tag found in both \code{tags} and \code{exclude_tags} will be included, not excluded.} } \value{ \code{available_linters} returns a data frame with columns 'linter', 'package' and 'tags': \describe{ \item{linter}{A character column naming the function associated with the linter.} \item{package}{A character column containing the name of the package providing the linter.} \item{tags}{A list column containing tags associated with the linter.} } \code{available_tags} returns a character vector of linter tags used by the packages. } \description{ \code{available_linters()} obtains a tagged list of all Linters available in a package. \code{available_tags()} searches for available tags. } \section{Package Authors}{ To implement \code{available_linters()} for your package, include a file \code{inst/lintr/linters.csv} in your package. The CSV file must contain the columns 'linter' and 'tags', and be UTF-8 encoded. Additional columns will be silently ignored if present and the columns are identified by name. Each row describes a linter by \enumerate{ \item its function name (e.g. \code{"assignment_linter"}) in the column 'linter'. \item space-separated tags associated with the linter (e.g. \code{"style consistency default"}) in the column 'tags'. } Tags should be snake_case. See \code{available_tags("lintr")} to find out what tags are already used by lintr. } \examples{ lintr_linters <- available_linters() # If the package doesn't exist or isn't installed, an empty data frame will be returned available_linters("does-not-exist") lintr_linters2 <- available_linters(c("lintr", "does-not-exist")) identical(lintr_linters, lintr_linters2) available_tags() } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \code{\link[=available_tags]{available_tags()}} to retrieve the set of valid tags. } } lintr/man/absolute_path_linter.Rd0000644000176200001440000000252614457657444016704 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/absolute_path_linter.R \name{absolute_path_linter} \alias{absolute_path_linter} \title{Absolute path linter} \usage{ absolute_path_linter(lax = TRUE) } \arguments{ \item{lax}{Less stringent linting, leading to fewer false positives. If \code{TRUE}, only lint path strings, which \itemize{ \item contain at least two path elements, with one having at least two characters and \item contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation }} } \description{ Check that no absolute paths are used (e.g. "/var", "C:\\System", "~/docs"). } \examples{ \dontshow{if (getRversion() >= "4.0") (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # Following examples use raw character constant syntax introduced in R 4.0. # will produce lints lint( text = 'R"--[/blah/file.txt]--"', linters = absolute_path_linter() ) # okay lint( text = 'R"(./blah)"', linters = absolute_path_linter() ) \dontshow{\}) # examplesIf} } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \code{\link[=nonportable_path_linter]{nonportable_path_linter()}} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=robustness_linters]{robustness} } lintr/man/string_boundary_linter.Rd0000644000176200001440000000471414577052532017252 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/string_boundary_linter.R \name{string_boundary_linter} \alias{string_boundary_linter} \title{Require usage of \code{startsWith()} and \code{endsWith()} over \code{grepl()}/\code{substr()} versions} \usage{ string_boundary_linter(allow_grepl = FALSE) } \arguments{ \item{allow_grepl}{Logical, default \code{FALSE}. If \code{TRUE}, usages with \code{grepl()} are ignored. Some authors may prefer the conciseness offered by \code{grepl()} whereby \code{NA} input maps to \code{FALSE} output, which doesn't have a direct equivalent with \code{startsWith()} or \code{endsWith()}.} } \description{ \code{\link[=startsWith]{startsWith()}} is used to detect fixed initial substrings; it is more readable and more efficient than equivalents using \code{\link[=grepl]{grepl()}} or \code{\link[=substr]{substr()}}. c.f. \code{startsWith(x, "abc")}, \code{grepl("^abc", x)}, \code{substr(x, 1L, 3L) == "abc"}. } \details{ Ditto for using \code{\link[=endsWith]{endsWith()}} to detect fixed terminal substrings. Note that there is a difference in behavior between how \code{grepl()} and \code{startsWith()} (and \code{endsWith()}) handle missing values. In particular, for \code{grepl()}, \code{NA} inputs are considered \code{FALSE}, while for \code{startsWith()}, \code{NA} inputs have \code{NA} outputs. That means the strict equivalent of \code{grepl("^abc", x)} is \code{!is.na(x) & startsWith(x, "abc")}. We lint \code{grepl()} usages by default because the \code{!is.na()} version is more explicit with respect to \code{NA} handling -- though documented, the way \code{grepl()} handles missing inputs may be surprising to some users. } \examples{ # will produce lints lint( text = 'grepl("^a", x)', linters = string_boundary_linter() ) lint( text = 'grepl("z$", x)', linters = string_boundary_linter() ) # okay lint( text = 'startsWith(x, "a")', linters = string_boundary_linter() ) lint( text = 'endsWith(x, "z")', linters = string_boundary_linter() ) # If missing values are present, the suggested alternative wouldn't be strictly # equivalent, so this linter can also be turned off in such cases. lint( text = 'grepl("z$", x)', linters = string_boundary_linter(allow_grepl = TRUE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/unused_import_linter.Rd0000644000176200001440000000356414577052532016740 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unused_import_linter.R \name{unused_import_linter} \alias{unused_import_linter} \title{Check that imported packages are actually used} \usage{ unused_import_linter( allow_ns_usage = FALSE, except_packages = c("bit64", "data.table", "tidyverse"), interpret_glue = TRUE ) } \arguments{ \item{allow_ns_usage}{Suppress lints for packages only used via namespace. This is \code{FALSE} by default because \code{pkg::fun()} doesn't require \code{library(pkg)}. You can use \link[=requireNamespace]{requireNamespace("pkg")} to ensure a package is installed without loading it.} \item{except_packages}{Character vector of packages that are ignored. These are usually attached for their side effects.} \item{interpret_glue}{If \code{TRUE}, interpret \code{\link[glue:glue]{glue::glue()}} calls to avoid false positives caused by local variables which are only used in a glue expression.} } \description{ Check that imported packages are actually used } \examples{ # will produce lints code_lines <- "library(dplyr)\n1 + 1" writeLines(code_lines) lint( text = code_lines, linters = unused_import_linter() ) code_lines <- "library(dplyr)\ndplyr::tibble(a = 1)" writeLines(code_lines) lint( text = code_lines, linters = unused_import_linter() ) # okay code_lines <- "library(dplyr)\ntibble(a = 1)" writeLines(code_lines) lint( text = code_lines, linters = unused_import_linter() ) code_lines <- "library(dplyr)\ndplyr::tibble(a = 1)" writeLines(code_lines) lint( text = code_lines, linters = unused_import_linter(allow_ns_usage = TRUE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=common_mistakes_linters]{common_mistakes}, \link[=configurable_linters]{configurable}, \link[=executing_linters]{executing} } lintr/man/ifelse_censor_linter.Rd0000644000176200001440000000222614457657444016667 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ifelse_censor_linter.R \name{ifelse_censor_linter} \alias{ifelse_censor_linter} \title{Block usage of \code{ifelse()} where \code{pmin()} or \code{pmax()} is more appropriate} \usage{ ifelse_censor_linter() } \description{ \code{ifelse(x > M, M, x)} is the same as \code{pmin(x, M)}, but harder to read and requires several passes over the vector. } \details{ The same goes for other similar ways to censor a vector, e.g. \code{ifelse(x <= M, x, M)} is \code{pmin(x, M)}, \code{ifelse(x < m, m, x)} is \code{pmax(x, m)}, and \code{ifelse(x >= m, x, m)} is \code{pmax(x, m)}. } \examples{ # will produce lints lint( text = "ifelse(5:1 < pi, 5:1, pi)", linters = ifelse_censor_linter() ) lint( text = "ifelse(x > 0, x, 0)", linters = ifelse_censor_linter() ) # okay lint( text = "pmin(5:1, pi)", linters = ifelse_censor_linter() ) lint( text = "pmax(x, 0)", linters = ifelse_censor_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency} } lintr/man/executing_linters.Rd0000644000176200001440000000167414577052532016221 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{executing_linters} \alias{executing_linters} \title{Code executing linters} \description{ Linters that evaluate parts of the linted code, such as loading referenced packages. These linters should not be used with untrusted code, and may need dependencies of the linted package or project to be available in order to function correctly. For package authors, note that this includes loading the package itself, e.g. with \code{pkgload::load_all()} or installing and attaching the package. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'executing': \itemize{ \item{\code{\link{namespace_linter}}} \item{\code{\link{object_length_linter}}} \item{\code{\link{object_name_linter}}} \item{\code{\link{object_usage_linter}}} \item{\code{\link{unused_import_linter}}} } } lintr/man/is_lint_level.Rd0000644000176200001440000000246714577052532015317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/is_lint_level.R \name{is_lint_level} \alias{is_lint_level} \title{Is this an expression- or a file-level source object?} \usage{ is_lint_level(source_expression, level = c("expression", "file")) } \arguments{ \item{source_expression}{A parsed expression object, i.e., an element of the object returned by \code{\link[=get_source_expressions]{get_source_expressions()}}.} \item{level}{Which level of expression is being tested? \code{"expression"} means an individual expression, while \code{"file"} means all expressions in the current file are available.} } \description{ Helper for determining whether the current \code{source_expression} contains all expressions in the current file, or just a single expression. } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) source_exprs <- get_source_expressions(tmp) is_lint_level(source_exprs$expressions[[1L]], level = "expression") is_lint_level(source_exprs$expressions[[1L]], level = "file") is_lint_level(source_exprs$expressions[[3L]], level = "expression") is_lint_level(source_exprs$expressions[[3L]], level = "file") \dontshow{\}) # examplesIf} } lintr/man/unnecessary_lambda_linter.Rd0000644000176200001440000000252414577052532017675 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unnecessary_lambda_linter.R \name{unnecessary_lambda_linter} \alias{unnecessary_lambda_linter} \title{Block usage of anonymous functions in iteration functions when unnecessary} \usage{ unnecessary_lambda_linter() } \description{ Using an anonymous function in, e.g., \code{\link[=lapply]{lapply()}} is not always necessary, e.g. \code{lapply(DF, sum)} is the same as \code{lapply(DF, function(x) sum(x))} and the former is more readable. } \details{ Cases like \verb{lapply(x, \\(xi) grep("ptn", xi))} are excluded because, though the anonymous function \emph{can} be avoided, doing so is not always more readable. } \examples{ # will produce lints lint( text = "lapply(list(1:3, 2:4), function(xi) sum(xi))", linters = unnecessary_lambda_linter() ) # okay lint( text = "lapply(list(1:3, 2:4), sum)", linters = unnecessary_lambda_linter() ) lint( text = 'lapply(x, function(xi) grep("ptn", xi))', linters = unnecessary_lambda_linter() ) lint( text = "lapply(x, function(xi) data.frame(col = xi))", linters = unnecessary_lambda_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/expect_comparison_linter.Rd0000644000176200001440000000334014506330025017541 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_comparison_linter.R \name{expect_comparison_linter} \alias{expect_comparison_linter} \title{Require usage of \code{expect_gt(x, y)} over \code{expect_true(x > y)} (and similar)} \usage{ expect_comparison_linter() } \description{ \code{\link[testthat:comparison-expectations]{testthat::expect_gt()}}, \code{\link[testthat:comparison-expectations]{testthat::expect_gte()}}, \code{\link[testthat:comparison-expectations]{testthat::expect_lt()}}, \code{\link[testthat:comparison-expectations]{testthat::expect_lte()}}, and \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} exist specifically for testing comparisons between two objects. \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = "expect_true(x > y)", linters = expect_comparison_linter() ) lint( text = "expect_true(x <= y)", linters = expect_comparison_linter() ) lint( text = "expect_true(x == (y == 2))", linters = expect_comparison_linter() ) # okay lint( text = "expect_gt(x, y)", linters = expect_comparison_linter() ) lint( text = "expect_lte(x, y)", linters = expect_comparison_linter() ) lint( text = "expect_identical(x, y == 2)", linters = expect_comparison_linter() ) lint( text = "expect_true(x < y | x > y^2)", linters = expect_comparison_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat} } lintr/man/style_linters.Rd0000644000176200001440000000373714577052532015370 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{style_linters} \alias{style_linters} \title{Style linters} \description{ Linters highlighting code style issues. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'style': \itemize{ \item{\code{\link{assignment_linter}}} \item{\code{\link{brace_linter}}} \item{\code{\link{commas_linter}}} \item{\code{\link{commented_code_linter}}} \item{\code{\link{consecutive_assertion_linter}}} \item{\code{\link{cyclocomp_linter}}} \item{\code{\link{extraction_operator_linter}}} \item{\code{\link{function_argument_linter}}} \item{\code{\link{function_left_parentheses_linter}}} \item{\code{\link{implicit_assignment_linter}}} \item{\code{\link{implicit_integer_linter}}} \item{\code{\link{indentation_linter}}} \item{\code{\link{infix_spaces_linter}}} \item{\code{\link{keyword_quote_linter}}} \item{\code{\link{library_call_linter}}} \item{\code{\link{line_length_linter}}} \item{\code{\link{numeric_leading_zero_linter}}} \item{\code{\link{object_length_linter}}} \item{\code{\link{object_name_linter}}} \item{\code{\link{object_usage_linter}}} \item{\code{\link{package_hooks_linter}}} \item{\code{\link{paren_body_linter}}} \item{\code{\link{pipe_call_linter}}} \item{\code{\link{pipe_consistency_linter}}} \item{\code{\link{pipe_continuation_linter}}} \item{\code{\link{quotes_linter}}} \item{\code{\link{repeat_linter}}} \item{\code{\link{semicolon_linter}}} \item{\code{\link{spaces_inside_linter}}} \item{\code{\link{spaces_left_parentheses_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{todo_comment_linter}}} \item{\code{\link{trailing_blank_lines_linter}}} \item{\code{\link{trailing_whitespace_linter}}} \item{\code{\link{undesirable_function_linter}}} \item{\code{\link{undesirable_operator_linter}}} \item{\code{\link{unnecessary_concatenation_linter}}} \item{\code{\link{whitespace_linter}}} } } lintr/man/consecutive_assertion_linter.Rd0000644000176200001440000000221114457657444020457 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/consecutive_assertion_linter.R \name{consecutive_assertion_linter} \alias{consecutive_assertion_linter} \title{Force consecutive calls to assertions into just one when possible} \usage{ consecutive_assertion_linter() } \description{ \code{\link[=stopifnot]{stopifnot()}} accepts any number of tests, so sequences like \verb{stopifnot(x); stopifnot(y)} are redundant. Ditto for tests using \code{assertthat::assert_that()} without specifying \verb{msg=}. } \examples{ # will produce lints lint( text = "stopifnot(x); stopifnot(y)", linters = consecutive_assertion_linter() ) lint( text = "assert_that(x); assert_that(y)", linters = consecutive_assertion_linter() ) # okay lint( text = "stopifnot(x, y)", linters = consecutive_assertion_linter() ) lint( text = 'assert_that(x, msg = "Bad x!"); assert_that(y)', linters = consecutive_assertion_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/spaces_inside_linter.Rd0000644000176200001440000000175314457657444016664 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/spaces_inside_linter.R \name{spaces_inside_linter} \alias{spaces_inside_linter} \title{Spaces inside linter} \usage{ spaces_inside_linter() } \description{ Check that parentheses and square brackets do not have spaces directly inside them, i.e., directly following an opening delimiter or directly preceding a closing delimiter. } \examples{ # will produce lints lint( text = "c( TRUE, FALSE )", linters = spaces_inside_linter() ) lint( text = "x[ 1L ]", linters = spaces_inside_linter() ) # okay lint( text = "c(TRUE, FALSE)", linters = spaces_inside_linter() ) lint( text = "x[1L]", linters = spaces_inside_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#parentheses} } } \section{Tags}{ \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/assignment_linter.Rd0000644000176200001440000000432614506330025016174 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/assignment_linter.R \name{assignment_linter} \alias{assignment_linter} \title{Assignment linter} \usage{ assignment_linter( allow_cascading_assign = TRUE, allow_right_assign = FALSE, allow_trailing = TRUE, allow_pipe_assign = FALSE ) } \arguments{ \item{allow_cascading_assign}{Logical, default \code{TRUE}. If \code{FALSE}, \code{\link[base:assignOps]{<<-}} and \verb{->>} are not allowed.} \item{allow_right_assign}{Logical, default \code{FALSE}. If \code{TRUE}, \verb{->} and \verb{->>} are allowed.} \item{allow_trailing}{Logical, default \code{TRUE}. If \code{FALSE} then assignments aren't allowed at end of lines.} \item{allow_pipe_assign}{Logical, default \code{FALSE}. If \code{TRUE}, magrittr's \verb{\%<>\%} assignment is allowed.} } \description{ Check that \verb{<-} is always used for assignment. } \examples{ # will produce lints lint( text = "x = mean(x)", linters = assignment_linter() ) code_lines <- "1 -> x\n2 ->> y" writeLines(code_lines) lint( text = code_lines, linters = assignment_linter() ) lint( text = "x \%<>\% as.character()", linters = assignment_linter() ) # okay lint( text = "x <- mean(x)", linters = assignment_linter() ) code_lines <- "x <- 1\ny <<- 2" writeLines(code_lines) lint( text = code_lines, linters = assignment_linter() ) # customizing using arguments code_lines <- "1 -> x\n2 ->> y" writeLines(code_lines) lint( text = code_lines, linters = assignment_linter(allow_right_assign = TRUE) ) lint( text = "x <<- 1", linters = assignment_linter(allow_cascading_assign = FALSE) ) writeLines("foo(bar = \n 1)") lint( text = "foo(bar = \n 1)", linters = assignment_linter(allow_trailing = FALSE) ) lint( text = "x \%<>\% as.character()", linters = assignment_linter(allow_pipe_assign = TRUE) ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#assignment-1} \item \url{https://style.tidyverse.org/pipes.html#assignment-2} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=style_linters]{style} } lintr/man/function_return_linter.Rd0000644000176200001440000000273014457657444017273 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/function_return_linter.R \name{function_return_linter} \alias{function_return_linter} \title{Lint common mistakes/style issues cropping up from return statements} \usage{ function_return_linter() } \description{ \code{return(x <- ...)} is either distracting (because \code{x} is ignored), or confusing (because assigning to \code{x} has some side effect that is muddled by the dual-purpose expression). } \examples{ # will produce lints lint( text = "foo <- function(x) return(y <- x + 1)", linters = function_return_linter() ) lint( text = "foo <- function(x) return(x <<- x + 1)", linters = function_return_linter() ) writeLines("e <- new.env() \nfoo <- function(x) return(e$val <- x + 1)") lint( text = "e <- new.env() \nfoo <- function(x) return(e$val <- x + 1)", linters = function_return_linter() ) # okay lint( text = "foo <- function(x) return(x + 1)", linters = function_return_linter() ) code_lines <- " foo <- function(x) { x <<- x + 1 return(x) } " lint( text = code_lines, linters = function_return_linter() ) code_lines <- " e <- new.env() foo <- function(x) { e$val <- x + 1 return(e$val) } " writeLines(code_lines) lint( text = code_lines, linters = function_return_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability} } lintr/man/any_is_na_linter.Rd0000644000176200001440000000205514457657444016007 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/any_is_na_linter.R \name{any_is_na_linter} \alias{any_is_na_linter} \title{Require usage of \code{anyNA(x)} over \code{any(is.na(x))}} \usage{ any_is_na_linter() } \description{ \code{\link[=anyNA]{anyNA()}} exists as a replacement for \code{any(is.na(x))} which is more efficient for simple objects, and is at worst equally efficient. Therefore, it should be used in all situations instead of the latter. } \examples{ # will produce lints lint( text = "any(is.na(x), na.rm = TRUE)", linters = any_is_na_linter() ) lint( text = "any(is.na(foo(x)))", linters = any_is_na_linter() ) # okay lint( text = "anyNA(x)", linters = any_is_na_linter() ) lint( text = "anyNA(foo(x))", linters = any_is_na_linter() ) lint( text = "any(!is.na(x), na.rm = TRUE)", linters = any_is_na_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency} } lintr/man/unnecessary_placeholder_linter.Rd0000644000176200001440000000224114457657444020745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/unnecessary_placeholder_linter.R \name{unnecessary_placeholder_linter} \alias{unnecessary_placeholder_linter} \title{Block usage of pipeline placeholders if unnecessary} \usage{ unnecessary_placeholder_linter() } \description{ The argument placeholder \code{.} in magrittr pipelines is unnecessary if passed as the first positional argument; using it can cause confusion and impacts readability. } \details{ This is true for forward (\verb{\%>\%}), assignment (\verb{\%<>\%}), and tee (\verb{\%T>\%}) operators. } \examples{ # will produce lints lint( text = "x \%>\% sum(., na.rm = TRUE)", linters = unnecessary_placeholder_linter() ) # okay lint( text = "x \%>\% sum(na.rm = TRUE)", linters = unnecessary_placeholder_linter() ) lint( text = "x \%>\% lm(data = ., y ~ z)", linters = unnecessary_placeholder_linter() ) lint( text = "x \%>\% outer(., .)", linters = unnecessary_placeholder_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability} } lintr/man/pkg_testthat_linters.Rd0000644000176200001440000000201114506330025016675 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{pkg_testthat_linters} \alias{pkg_testthat_linters} \title{Testthat linters} \description{ Linters encouraging best practices within testthat suites. } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://testthat.r-lib.org} \item \url{https://r-pkgs.org/testing-basics.html} } } \section{Linters}{ The following linters are tagged with 'pkg_testthat': \itemize{ \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{expect_comparison_linter}}} \item{\code{\link{expect_identical_linter}}} \item{\code{\link{expect_length_linter}}} \item{\code{\link{expect_named_linter}}} \item{\code{\link{expect_not_linter}}} \item{\code{\link{expect_null_linter}}} \item{\code{\link{expect_s3_class_linter}}} \item{\code{\link{expect_s4_class_linter}}} \item{\code{\link{expect_true_false_linter}}} \item{\code{\link{expect_type_linter}}} \item{\code{\link{yoda_test_linter}}} } } lintr/man/configurable_linters.Rd0000644000176200001440000000347514577052532016667 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{configurable_linters} \alias{configurable_linters} \title{Configurable linters} \description{ Generic linters which support custom configuration to your needs. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'configurable': \itemize{ \item{\code{\link{absolute_path_linter}}} \item{\code{\link{assignment_linter}}} \item{\code{\link{backport_linter}}} \item{\code{\link{brace_linter}}} \item{\code{\link{commas_linter}}} \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{cyclocomp_linter}}} \item{\code{\link{duplicate_argument_linter}}} \item{\code{\link{fixed_regex_linter}}} \item{\code{\link{if_not_else_linter}}} \item{\code{\link{implicit_assignment_linter}}} \item{\code{\link{implicit_integer_linter}}} \item{\code{\link{indentation_linter}}} \item{\code{\link{infix_spaces_linter}}} \item{\code{\link{library_call_linter}}} \item{\code{\link{line_length_linter}}} \item{\code{\link{missing_argument_linter}}} \item{\code{\link{namespace_linter}}} \item{\code{\link{nonportable_path_linter}}} \item{\code{\link{object_length_linter}}} \item{\code{\link{object_name_linter}}} \item{\code{\link{object_usage_linter}}} \item{\code{\link{paste_linter}}} \item{\code{\link{pipe_consistency_linter}}} \item{\code{\link{quotes_linter}}} \item{\code{\link{redundant_ifelse_linter}}} \item{\code{\link{semicolon_linter}}} \item{\code{\link{string_boundary_linter}}} \item{\code{\link{todo_comment_linter}}} \item{\code{\link{trailing_whitespace_linter}}} \item{\code{\link{undesirable_function_linter}}} \item{\code{\link{undesirable_operator_linter}}} \item{\code{\link{unnecessary_concatenation_linter}}} \item{\code{\link{unused_import_linter}}} } } lintr/man/backport_linter.Rd0000644000176200001440000000225014517602346015635 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/backport_linter.R \name{backport_linter} \alias{backport_linter} \title{Backport linter} \usage{ backport_linter(r_version = getRversion(), except = character()) } \arguments{ \item{r_version}{Minimum R version to test for compatibility} \item{except}{Character vector of functions to be excluded from linting. Use this to list explicitly defined backports, e.g. those imported from the \code{{backports}} package or manually defined in your package.} } \description{ Check for usage of unavailable functions. Not reliable for testing r-devel dependencies. } \examples{ # will produce lints lint( text = "trimws(x)", linters = backport_linter("3.0.0") ) lint( text = "str2lang(x)", linters = backport_linter("3.2.0") ) # okay lint( text = "trimws(x)", linters = backport_linter("3.6.0") ) lint( text = "str2lang(x)", linters = backport_linter("4.0.0") ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=package_development_linters]{package_development}, \link[=robustness_linters]{robustness} } lintr/man/outer_negation_linter.Rd0000644000176200001440000000204114457657444017064 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/outer_negation_linter.R \name{outer_negation_linter} \alias{outer_negation_linter} \title{Require usage of \code{!any(x)} over \code{all(!x)}, \code{!all(x)} over \code{any(!x)}} \usage{ outer_negation_linter() } \description{ \code{any(!x)} is logically equivalent to \code{!any(x)}; ditto for the equivalence of \code{all(!x)} and \code{!any(x)}. Negating after aggregation only requires inverting one logical value, and is typically more readable. } \examples{ # will produce lints lint( text = "all(!x)", linters = outer_negation_linter() ) lint( text = "any(!x)", linters = outer_negation_linter() ) # okay lint( text = "!any(x)", linters = outer_negation_linter() ) lint( text = "!all(x)", linters = outer_negation_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/xp_call_name.Rd0000644000176200001440000000310214510656222015065 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xp_utils.R \name{xp_call_name} \alias{xp_call_name} \title{Get the name of the function matched by an XPath} \usage{ xp_call_name(expr, depth = 1L, condition = NULL) } \arguments{ \item{expr}{An \code{xml_node} or \code{xml_nodeset}, e.g. from \code{\link[xml2:xml_find_all]{xml2::xml_find_all()}}.} \item{depth}{Integer, default \code{1L}. How deep in the AST represented by \code{expr} should we look to find the call? By default, we assume \code{expr} is matched to an \verb{} node under which the corresponding \verb{} node is found directly. \code{depth = 0L} means \code{expr} is matched directly to the \code{SYMBOL_FUNCTION_CALL}; \code{depth > 1L} means \code{depth} total \verb{} nodes must be traversed before finding the call.} \item{condition}{An additional (XPath condition on the \code{SYMBOL_FUNCTION_CALL} required for a match. The default (\code{NULL}) is no condition. See examples.} } \description{ Often, it is more helpful to tailor the \code{message} of a lint to record which function was matched by the lint logic. This function encapsulates the logic to pull out the matched call in common situations. } \examples{ xml_from_code <- function(str) { xml2::read_xml(xmlparsedata::xml_parse_data(parse(text = str, keep.source = TRUE))) } xml <- xml_from_code("sum(1:10)") xp_call_name(xml, depth = 2L) xp_call_name(xml2::xml_find_first(xml, "expr")) xml <- xml_from_code(c("sum(1:10)", "sd(1:10)")) xp_call_name(xml, depth = 2L, condition = "text() = 'sum'") } lintr/man/all_linters.Rd0000644000176200001440000000206114577052532014765 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with.R \name{all_linters} \alias{all_linters} \title{Create a linter configuration based on all available linters} \usage{ all_linters(packages = "lintr", ...) } \arguments{ \item{packages}{A character vector of packages to search for linters.} \item{...}{Arguments of elements to change. If unnamed, the argument is automatically named. If the named argument already exists in the list of linters, it is replaced by the new element. If it does not exist, it is added. If the value is \code{NULL}, the linter is removed.} } \description{ Create a linter configuration based on all available linters } \examples{ names(all_linters()) } \seealso{ \itemize{ \item \link{linters_with_defaults} for basing off lintr's set of default linters. \item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. \item \link{available_linters} to get a data frame of available linters. \item \link{linters} for a complete list of linters available in lintr. } } lintr/man/lint.Rd0000644000176200001440000000774314577052532013437 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lint.R \name{lint} \alias{lint} \alias{lint_file} \alias{lint_dir} \alias{lint_package} \title{Lint a file, directory, or package} \usage{ lint( filename, linters = NULL, ..., cache = FALSE, parse_settings = TRUE, text = NULL ) lint_dir( path = ".", ..., relative_path = TRUE, exclusions = list("renv", "packrat"), pattern = "(?i)[.](r|rmd|qmd|rnw|rhtml|rrst|rtex|rtxt)$", parse_settings = TRUE, show_progress = NULL ) lint_package( path = ".", ..., relative_path = TRUE, exclusions = list("R/RcppExports.R"), parse_settings = TRUE, show_progress = NULL ) } \arguments{ \item{filename}{Either the filename for a file to lint, or a character string of inline R code for linting. The latter (inline data) applies whenever \code{filename} has a newline character (\\n).} \item{linters}{A named list of linter functions to apply. See \link{linters} for a full list of default and available linters.} \item{...}{Provide additional arguments to be passed to: \itemize{ \item \code{\link[=exclude]{exclude()}} (in case of \code{lint()}; e.g. \code{lints} or \code{exclusions}) \item \code{\link[=lint]{lint()}} (in case of \code{lint_dir()} and \code{lint_package()}; e.g. \code{linters} or \code{cache}) }} \item{cache}{When logical, toggle caching of lint results. I1f passed a character string, store the cache in this directory.} \item{parse_settings}{Logical, default \code{TRUE}. Whether to try and parse the settings; otherwise, the \code{\link[=default_settings]{default_settings()}} are used.} \item{text}{Optional argument for supplying a string or lines directly, e.g. if the file is already in memory or linting is being done ad hoc.} \item{path}{For the base directory of the project (for \code{lint_dir()}) or package (for \code{lint_package()}).} \item{relative_path}{if \code{TRUE}, file paths are printed using their path relative to the base directory. If \code{FALSE}, use the full absolute path.} \item{exclusions}{exclusions for \code{\link[=exclude]{exclude()}}, relative to the package path.} \item{pattern}{pattern for files, by default it will take files with any of the extensions .R, .Rmd, .qmd, .Rnw, .Rhtml, .Rrst, .Rtex, .Rtxt allowing for lowercase r (.r, ...).} \item{show_progress}{Logical controlling whether to show linting progress with a simple text progress bar \emph{via} \code{\link[utils:txtProgressBar]{utils::txtProgressBar()}}. The default behavior is to show progress in \code{\link[=interactive]{interactive()}} sessions not running a testthat suite.} } \value{ An object of class \code{c("lints", "list")}, each element of which is a \code{"list"} object. } \description{ \itemize{ \item \code{lint()} lints a single file. \item \code{lint_dir()} lints all files in a directory. \item \code{lint_package()} lints all likely locations for R files in a package, i.e. \verb{R/}, \verb{tests/}, \verb{inst/}, \verb{vignettes/}, \verb{data-raw/}, \verb{demo/}, and \verb{exec/}. } } \details{ Read \code{vignette("lintr")} to learn how to configure which linters are run by default. Note that if files contain unparseable encoding problems, only the encoding problem will be linted to avoid unintelligible error messages from other linters. } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} f <- withr::local_tempfile(lines = "a=1", fileext = "R") lint(f) # linting a file lint("a = 123\n") # linting inline-code lint(text = "a = 123") # linting inline-code \dontshow{\}) # examplesIf} if (FALSE) { lint_dir() lint_dir( linters = list(semicolon_linter()), exclusions = list( "inst/doc/creating_linters.R" = 1, "inst/example/bad.R", "renv" ) ) } if (FALSE) { lint_package() lint_package( linters = linters_with_defaults(semicolon_linter = semicolon_linter()), exclusions = list("inst/doc/creating_linters.R" = 1, "inst/example/bad.R") ) } } lintr/man/readability_linters.Rd0000644000176200001440000000523714577052532016516 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{readability_linters} \alias{readability_linters} \title{Readability linters} \description{ Linters highlighting readability issues, such as missing whitespace. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'readability': \itemize{ \item{\code{\link{boolean_arithmetic_linter}}} \item{\code{\link{brace_linter}}} \item{\code{\link{commas_linter}}} \item{\code{\link{commented_code_linter}}} \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{consecutive_assertion_linter}}} \item{\code{\link{cyclocomp_linter}}} \item{\code{\link{empty_assignment_linter}}} \item{\code{\link{expect_length_linter}}} \item{\code{\link{expect_named_linter}}} \item{\code{\link{expect_not_linter}}} \item{\code{\link{expect_true_false_linter}}} \item{\code{\link{fixed_regex_linter}}} \item{\code{\link{for_loop_index_linter}}} \item{\code{\link{function_left_parentheses_linter}}} \item{\code{\link{function_return_linter}}} \item{\code{\link{if_not_else_linter}}} \item{\code{\link{implicit_assignment_linter}}} \item{\code{\link{indentation_linter}}} \item{\code{\link{infix_spaces_linter}}} \item{\code{\link{inner_combine_linter}}} \item{\code{\link{is_numeric_linter}}} \item{\code{\link{keyword_quote_linter}}} \item{\code{\link{length_levels_linter}}} \item{\code{\link{lengths_linter}}} \item{\code{\link{library_call_linter}}} \item{\code{\link{line_length_linter}}} \item{\code{\link{matrix_apply_linter}}} \item{\code{\link{nested_ifelse_linter}}} \item{\code{\link{numeric_leading_zero_linter}}} \item{\code{\link{object_length_linter}}} \item{\code{\link{object_usage_linter}}} \item{\code{\link{outer_negation_linter}}} \item{\code{\link{paren_body_linter}}} \item{\code{\link{pipe_call_linter}}} \item{\code{\link{pipe_consistency_linter}}} \item{\code{\link{pipe_continuation_linter}}} \item{\code{\link{quotes_linter}}} \item{\code{\link{redundant_equals_linter}}} \item{\code{\link{repeat_linter}}} \item{\code{\link{scalar_in_linter}}} \item{\code{\link{semicolon_linter}}} \item{\code{\link{sort_linter}}} \item{\code{\link{spaces_inside_linter}}} \item{\code{\link{spaces_left_parentheses_linter}}} \item{\code{\link{string_boundary_linter}}} \item{\code{\link{system_file_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{unnecessary_concatenation_linter}}} \item{\code{\link{unnecessary_lambda_linter}}} \item{\code{\link{unnecessary_nested_if_linter}}} \item{\code{\link{unnecessary_placeholder_linter}}} \item{\code{\link{unreachable_code_linter}}} \item{\code{\link{yoda_test_linter}}} } } lintr/man/redundant_equals_linter.Rd0000644000176200001440000000271314457657444017406 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/redundant_equals_linter.R \name{redundant_equals_linter} \alias{redundant_equals_linter} \title{Block usage of \code{==}, \code{!=} on logical vectors} \usage{ redundant_equals_linter() } \description{ Testing \code{x == TRUE} is redundant if \code{x} is a logical vector. Wherever this is used to improve readability, the solution should instead be to improve the naming of the object to better indicate that its contents are logical. This can be done using prefixes (is, has, can, etc.). For example, \code{is_child}, \code{has_parent_supervision}, \code{can_watch_horror_movie} clarify their logical nature, while \code{child}, \code{parent_supervision}, \code{watch_horror_movie} don't. } \examples{ # will produce lints lint( text = "if (any(x == TRUE)) 1", linters = redundant_equals_linter() ) lint( text = "if (any(x != FALSE)) 0", linters = redundant_equals_linter() ) # okay lint( text = "if (any(x)) 1", linters = redundant_equals_linter() ) lint( text = "if (!all(x)) 0", linters = redundant_equals_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \code{\link[=outer_negation_linter]{outer_negation_linter()}} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=common_mistakes_linters]{common_mistakes}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/semicolon_linter.Rd0000644000176200001440000000323314457657444016036 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/semicolon_linter.R \name{semicolon_linter} \alias{semicolon_linter} \title{Semicolon linter} \usage{ semicolon_linter(allow_compound = FALSE, allow_trailing = FALSE) } \arguments{ \item{allow_compound}{Logical, default \code{FALSE}. If \code{TRUE}, "compound" semicolons (e.g. as in \verb{x; y}, i.e., on the same line of code) are allowed.} \item{allow_trailing}{Logical, default \code{FALSE}. If \code{TRUE}, "trailing" semicolons (i.e., those that terminate lines of code) are allowed.} } \description{ Check that no semicolons terminate expressions. } \examples{ # will produce lints lint( text = "a <- 1;", linters = semicolon_linter() ) lint( text = "a <- 1; b <- 1", linters = semicolon_linter() ) lint( text = "function() { a <- 1; b <- 1 }", linters = semicolon_linter() ) # okay lint( text = "a <- 1", linters = semicolon_linter() ) lint( text = "a <- 1;", linters = semicolon_linter(allow_trailing = TRUE) ) code_lines <- "a <- 1\nb <- 1" writeLines(code_lines) lint( text = code_lines, linters = semicolon_linter() ) lint( text = "a <- 1; b <- 1", linters = semicolon_linter(allow_compound = TRUE) ) code_lines <- "function() { \n a <- 1\n b <- 1\n}" writeLines(code_lines) lint( text = code_lines, linters = semicolon_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#semicolons} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/get_source_expressions.Rd0000644000176200001440000000642014577052532017261 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/get_source_expressions.R \name{get_source_expressions} \alias{get_source_expressions} \title{Parsed sourced file from a filename} \usage{ get_source_expressions(filename, lines = NULL) } \arguments{ \item{filename}{the file to be parsed.} \item{lines}{a character vector of lines. If \code{NULL}, then \code{filename} will be read.} } \value{ A \code{list} with three components: \describe{ \item{expressions}{a \code{list} of \code{n+1} objects. The first \code{n} elements correspond to each expression in \code{filename}, and consist of a list of 9 elements: \itemize{ \item{\code{filename} (\code{character})} \item{\code{line} (\code{integer}) the line in \code{filename} where this expression begins} \item{\code{column} (\code{integer}) the column in \code{filename} where this expression begins} \item{\code{lines} (named \code{character}) vector of all lines spanned by this expression, named with the line number corresponding to \code{filename}} \item{\code{parsed_content} (\code{data.frame}) as given by \code{\link[utils:getParseData]{utils::getParseData()}} for this expression} \item{\code{xml_parsed_content} (\code{xml_document}) the XML parse tree of this expression as given by \code{\link[xmlparsedata:xml_parse_data]{xmlparsedata::xml_parse_data()}}} \item{\code{content} (\code{character}) the same as \code{lines} as a single string (not split across lines)} } The final element of \code{expressions} is a list corresponding to the full file consisting of 6 elements: \itemize{ \item{\code{filename} (\code{character})} \item{\code{file_lines} (\code{character}) the \code{\link[=readLines]{readLines()}} output for this file} \item{\code{content} (\code{character}) for .R files, the same as \code{file_lines}; for .Rmd or .qmd scripts, this is the extracted R source code (as text)} \item{\code{full_parsed_content} (\code{data.frame}) as given by \code{\link[utils:getParseData]{utils::getParseData()}} for the full content} \item{\code{full_xml_parsed_content} (\code{xml_document}) the XML parse tree of all expressions as given by \code{\link[xmlparsedata:xml_parse_data]{xmlparsedata::xml_parse_data()}}} \item{\code{terminal_newline} (\code{logical}) records whether \code{filename} has a terminal newline (as determined by \code{\link[=readLines]{readLines()}} producing a corresponding warning)} } } \item{error}{A \code{Lint} object describing any parsing error.} \item{lines}{The \code{\link[=readLines]{readLines()}} output for this file.} } } \description{ This object is given as input to each linter. } \details{ The file is read using the \code{encoding} setting. This setting is found by taking the first valid result from the following locations \enumerate{ \item The \code{encoding} key from the usual lintr configuration settings. \item The \code{Encoding} field from a Package \code{DESCRIPTION} file in a parent directory. \item The \code{Encoding} field from an R Project \code{.Rproj} file in a parent directory. \item \code{"UTF-8"} as a fallback. } } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) get_source_expressions(tmp) \dontshow{\}) # examplesIf} } lintr/man/function_argument_linter.Rd0000644000176200001440000000263114506330025017550 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/function_argument_linter.R \name{function_argument_linter} \alias{function_argument_linter} \title{Function argument linter} \usage{ function_argument_linter() } \description{ Check that arguments with defaults come last in all function declarations, as per the tidyverse design guide. Changing the argument order can be a breaking change. An alternative to changing the argument order is to instead set the default for such arguments to \code{NULL}. } \examples{ # will produce lints lint( text = "function(y = 1, z = 2, x) {}", linters = function_argument_linter() ) lint( text = "function(x, y, z = 1, ..., w) {}", linters = function_argument_linter() ) # okay lint( text = "function(x, y = 1, z = 2) {}", linters = function_argument_linter() ) lint( text = "function(x, y, w, z = 1, ...) {}", linters = function_argument_linter() ) lint( text = "function(y = 1, z = 2, x = NULL) {}", linters = function_argument_linter() ) lint( text = "function(x, y, z = 1, ..., w = NULL) {}", linters = function_argument_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://design.tidyverse.org/required-no-defaults.html} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=style_linters]{style} } lintr/man/commented_code_linter.Rd0000644000176200001440000000166414577052532017007 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/comment_linters.R \name{commented_code_linter} \alias{commented_code_linter} \title{Commented code linter} \usage{ commented_code_linter() } \description{ Check that there is no commented code outside roxygen blocks. } \examples{ # will produce lints lint( text = "# x <- 1", linters = commented_code_linter() ) lint( text = "x <- f() # g()", linters = commented_code_linter() ) lint( text = "x + y # + z[1, 2]", linters = commented_code_linter() ) # okay lint( text = "x <- 1; x <- f(); x + y", linters = commented_code_linter() ) lint( text = "#' x <- 1", linters = commented_code_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/default_linters.Rd0000644000176200001440000000354614577052532015652 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zzz.R \docType{data} \name{default_linters} \alias{default_linters} \title{Default linters} \format{ An object of class \code{list} of length 25. } \usage{ default_linters } \description{ List of default linters for \code{\link[=lint]{lint()}}. Use \code{\link[=linters_with_defaults]{linters_with_defaults()}} to customize it. Most of the default linters are based on \href{https://style.tidyverse.org/}{the tidyverse style guide}. The set of default linters is as follows (any parameterized linters, e.g., \code{line_length_linter} use their default argument(s), see \verb{?} for details): } \seealso{ \link{linters} for a complete list of linters available in lintr. } \keyword{datasets} \section{Linters}{ The following linters are tagged with 'default': \itemize{ \item{\code{\link{assignment_linter}}} \item{\code{\link{brace_linter}}} \item{\code{\link{commas_linter}}} \item{\code{\link{commented_code_linter}}} \item{\code{\link{cyclocomp_linter}}} \item{\code{\link{equals_na_linter}}} \item{\code{\link{function_left_parentheses_linter}}} \item{\code{\link{indentation_linter}}} \item{\code{\link{infix_spaces_linter}}} \item{\code{\link{line_length_linter}}} \item{\code{\link{object_length_linter}}} \item{\code{\link{object_name_linter}}} \item{\code{\link{object_usage_linter}}} \item{\code{\link{paren_body_linter}}} \item{\code{\link{pipe_continuation_linter}}} \item{\code{\link{quotes_linter}}} \item{\code{\link{semicolon_linter}}} \item{\code{\link{seq_linter}}} \item{\code{\link{spaces_inside_linter}}} \item{\code{\link{spaces_left_parentheses_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{trailing_blank_lines_linter}}} \item{\code{\link{trailing_whitespace_linter}}} \item{\code{\link{vector_logic_linter}}} \item{\code{\link{whitespace_linter}}} } } lintr/man/numeric_leading_zero_linter.Rd0000644000176200001440000000166314457657444020237 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/numeric_leading_zero_linter.R \name{numeric_leading_zero_linter} \alias{numeric_leading_zero_linter} \title{Require usage of a leading zero in all fractional numerics} \usage{ numeric_leading_zero_linter() } \description{ While .1 and 0.1 mean the same thing, the latter is easier to read due to the small size of the '.' glyph. } \examples{ # will produce lints lint( text = "x <- .1", linters = numeric_leading_zero_linter() ) lint( text = "x <- -.1", linters = numeric_leading_zero_linter() ) # okay lint( text = "x <- 0.1", linters = numeric_leading_zero_linter() ) lint( text = "x <- -0.1", linters = numeric_leading_zero_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/library_call_linter.Rd0000644000176200001440000000243414577052532016475 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/library_call_linter.R \name{library_call_linter} \alias{library_call_linter} \title{Library call linter} \usage{ library_call_linter(allow_preamble = TRUE) } \arguments{ \item{allow_preamble}{Logical, default \code{TRUE}. If \code{FALSE}, no code is allowed to precede the first \code{library()} call, otherwise some setup code is allowed, but all \code{library()} calls must follow consecutively after the first one.} } \description{ Force library calls to all be at the top of the script. } \examples{ # will produce lints lint( text = " library(dplyr) print('test') library(tidyr) ", linters = library_call_linter() ) lint( text = " library(dplyr) print('test') library(tidyr) library(purrr) ", linters = library_call_linter() ) # okay lint( text = " library(dplyr) print('test') ", linters = library_call_linter() ) lint( text = " # comment library(dplyr) ", linters = library_call_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/brace_linter.Rd0000644000176200001440000000370614457657444015127 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/brace_linter.R \name{brace_linter} \alias{brace_linter} \title{Brace linter} \usage{ brace_linter(allow_single_line = FALSE) } \arguments{ \item{allow_single_line}{if \code{TRUE}, allow an open and closed curly pair on the same line.} } \description{ Perform various style checks related to placement and spacing of curly braces: } \details{ \itemize{ \item Opening curly braces are never on their own line and are always followed by a newline. \item Opening curly braces have a space before them. \item Closing curly braces are on their own line unless they are followed by an \verb{else}. \item Closing curly braces in \code{if} conditions are on the same line as the corresponding \verb{else}. \item Either both or neither branch in \code{if}/\verb{else} use curly braces, i.e., either both branches use \code{{...}} or neither does. \item Functions spanning multiple lines use curly braces. } } \examples{ # will produce lints lint( text = "f <- function() { 1 }", linters = brace_linter() ) writeLines("if (TRUE) {\n return(1) }") lint( text = "if (TRUE) {\n return(1) }", linters = brace_linter() ) # okay writeLines("f <- function() {\n 1\n}") lint( text = "f <- function() {\n 1\n}", linters = brace_linter() ) writeLines("if (TRUE) { \n return(1) \n}") lint( text = "if (TRUE) { \n return(1) \n}", linters = brace_linter() ) # customizing using arguments writeLines("if (TRUE) { return(1) }") lint( text = "if (TRUE) { return(1) }", linters = brace_linter(allow_single_line = TRUE) ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#indenting} \item \url{https://style.tidyverse.org/syntax.html#if-statements} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/efficiency_linters.Rd0000644000176200001440000000274014577052532016325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{efficiency_linters} \alias{efficiency_linters} \title{Efficiency linters} \description{ Linters highlighting code efficiency problems, such as unnecessary function calls. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'efficiency': \itemize{ \item{\code{\link{any_duplicated_linter}}} \item{\code{\link{any_is_na_linter}}} \item{\code{\link{boolean_arithmetic_linter}}} \item{\code{\link{fixed_regex_linter}}} \item{\code{\link{ifelse_censor_linter}}} \item{\code{\link{inner_combine_linter}}} \item{\code{\link{length_test_linter}}} \item{\code{\link{lengths_linter}}} \item{\code{\link{literal_coercion_linter}}} \item{\code{\link{matrix_apply_linter}}} \item{\code{\link{nested_ifelse_linter}}} \item{\code{\link{outer_negation_linter}}} \item{\code{\link{redundant_equals_linter}}} \item{\code{\link{redundant_ifelse_linter}}} \item{\code{\link{regex_subset_linter}}} \item{\code{\link{routine_registration_linter}}} \item{\code{\link{scalar_in_linter}}} \item{\code{\link{seq_linter}}} \item{\code{\link{sort_linter}}} \item{\code{\link{string_boundary_linter}}} \item{\code{\link{undesirable_function_linter}}} \item{\code{\link{undesirable_operator_linter}}} \item{\code{\link{unnecessary_concatenation_linter}}} \item{\code{\link{unnecessary_lambda_linter}}} \item{\code{\link{vector_logic_linter}}} } } lintr/man/clear_cache.Rd0000644000176200001440000000076714250050336014665 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cache.R \name{clear_cache} \alias{clear_cache} \title{Clear the lintr cache} \usage{ clear_cache(file = NULL, path = NULL) } \arguments{ \item{file}{filename whose cache to clear. If you pass \code{NULL}, it will delete all of the caches.} \item{path}{directory to store caches. Reads option 'lintr.cache_directory' as the default.} } \value{ 0 for success, 1 for failure, invisibly. } \description{ Clear the lintr cache } lintr/man/indentation_linter.Rd0000644000176200001440000000676414457657444016376 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/indentation_linter.R \name{indentation_linter} \alias{indentation_linter} \title{Check that indentation is consistent} \usage{ indentation_linter( indent = 2L, hanging_indent_style = c("tidy", "always", "never"), assignment_as_infix = TRUE ) } \arguments{ \item{indent}{Number of spaces, that a code block should be indented by relative to its parent code block. Used for multi-line code blocks (\code{{ ... }}), function calls (\code{( ... )}) and extractions (\verb{[ ... ]}, \verb{[[ ... ]]}). Defaults to 2.} \item{hanging_indent_style}{Indentation style for multi-line function calls with arguments in their first line. Defaults to tidyverse style, i.e. a block indent is used if the function call terminates with \verb{)} on a separate line and a hanging indent if not. Note that function multi-line function calls without arguments on their first line will always be expected to have block-indented arguments. If \code{hanging_indent_style} is \code{"tidy"}, multi-line function definitions are expected to be double-indented if the first line of the function definition contains no arguments and the closing parenthesis is not on its own line. \if{html}{\out{
}}\preformatted{# complies to any style map( x, f, additional_arg = 42 ) # complies to "tidy" and "never" map(x, f, additional_arg = 42 ) # complies to "always" map(x, f, additional_arg = 42 ) # complies to "tidy" and "always" map(x, f, additional_arg = 42) # complies to "never" map(x, f, additional_arg = 42) # complies to "tidy" function( a, b) \{ # body \} }\if{html}{\out{
}}} \item{assignment_as_infix}{Treat \verb{<-} as a regular (i.e. left-associative) infix operator? This means, that infix operators on the right hand side of an assignment do not trigger a second level of indentation: \if{html}{\out{
}}\preformatted{# complies to any style variable <- a \%+\% b \%+\% c # complies to assignment_as_infix = TRUE variable <- a \%+\% b \%+\% c # complies to assignment_as_infix = FALSE variable <- a \%+\% b \%+\% c }\if{html}{\out{
}}} } \description{ Check that indentation is consistent } \examples{ # will produce lints code_lines <- "if (TRUE) {\n1 + 1\n}" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter() ) code_lines <- "if (TRUE) {\n 1 + 1\n}" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter() ) code_lines <- "map(x, f,\n additional_arg = 42\n)" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter(hanging_indent_style = "always") ) code_lines <- "map(x, f,\n additional_arg = 42)" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter(hanging_indent_style = "never") ) # okay code_lines <- "map(x, f,\n additional_arg = 42\n)" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter() ) code_lines <- "if (TRUE) {\n 1 + 1\n}" writeLines(code_lines) lint( text = code_lines, linters = indentation_linter(indent = 4) ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#indenting} \item \url{https://style.tidyverse.org/functions.html#long-lines-1} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/boolean_arithmetic_linter.Rd0000644000176200001440000000177614457657444017710 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/boolean_arithmetic_linter.R \name{boolean_arithmetic_linter} \alias{boolean_arithmetic_linter} \title{Require usage of boolean operators over equivalent arithmetic} \usage{ boolean_arithmetic_linter() } \description{ \code{length(which(x == y)) == 0} is the same as \code{!any(x == y)}, but the latter is more readable and more efficient. } \examples{ # will produce lints lint( text = "length(which(x == y)) == 0L", linters = boolean_arithmetic_linter() ) lint( text = "sum(grepl(pattern, x)) == 0", linters = boolean_arithmetic_linter() ) # okay lint( text = "!any(x == y)", linters = boolean_arithmetic_linter() ) lint( text = "!any(grepl(pattern, x))", linters = boolean_arithmetic_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/conjunct_test_linter.Rd0000644000176200001440000000655714510656222016723 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/conjunct_test_linter.R \name{conjunct_test_linter} \alias{conjunct_test_linter} \title{Force \code{&&} conditions to be written separately where appropriate} \usage{ conjunct_test_linter( allow_named_stopifnot = TRUE, allow_filter = c("never", "not_dplyr", "always") ) } \arguments{ \item{allow_named_stopifnot}{Logical, \code{TRUE} by default. If \code{FALSE}, "named" calls to \code{stopifnot()}, available since R 4.0.0 to provide helpful messages for test failures, are also linted.} \item{allow_filter}{Character naming the method for linting calls to \code{filter()}. The default, \code{"never"}, means \code{filter()} and \code{dplyr::filter()} calls are linted; \code{"not_dplyr"} means only \code{dplyr::filter()} calls are linted; and \code{"always"} means no calls to \code{filter()} are linted. Calls like \code{stats::filter()} are never linted.} } \description{ For readability of test outputs, testing only one thing per call to \code{\link[testthat:logical-expectations]{testthat::expect_true()}} is preferable, i.e., \verb{expect_true(A); expect_true(B)} is better than \code{expect_true(A && B)}, and \verb{expect_false(A); expect_false(B)} is better than \code{expect_false(A || B)}. } \details{ Similar reasoning applies to \code{&&} usage inside \code{\link[=stopifnot]{stopifnot()}} and \code{assertthat::assert_that()} calls. Relatedly, \code{dplyr::filter(DF, A & B)} is the same as \code{dplyr::filter(DF, A, B)}, but the latter will be more readable / easier to format for long conditions. Note that this linter assumes usages of \code{filter()} are \code{dplyr::filter()}; if you're using another function named \code{filter()}, e.g. \code{\link[stats:filter]{stats::filter()}}, please namespace-qualify it to avoid false positives. You can omit linting \code{filter()} expressions altogether via \code{allow_filter = TRUE}. } \examples{ # will produce lints lint( text = "expect_true(x && y)", linters = conjunct_test_linter() ) lint( text = "expect_false(x || (y && z))", linters = conjunct_test_linter() ) lint( text = "stopifnot('x must be a logical scalar' = length(x) == 1 && is.logical(x) && !is.na(x))", linters = conjunct_test_linter(allow_named_stopifnot = FALSE) ) lint( text = "dplyr::filter(mtcars, mpg > 20 & vs == 0)", linters = conjunct_test_linter() ) lint( text = "filter(mtcars, mpg > 20 & vs == 0)", linters = conjunct_test_linter() ) # okay lint( text = "expect_true(x || (y && z))", linters = conjunct_test_linter() ) lint( text = 'stopifnot("x must be a logical scalar" = length(x) == 1 && is.logical(x) && !is.na(x))', linters = conjunct_test_linter(allow_named_stopifnot = TRUE) ) lint( text = "dplyr::filter(mtcars, mpg > 20 & vs == 0)", linters = conjunct_test_linter(allow_filter = "always") ) lint( text = "filter(mtcars, mpg > 20 & vs == 0)", linters = conjunct_test_linter(allow_filter = "not_dplyr") ) lint( text = "stats::filter(mtcars$cyl, mtcars$mpg > 20 & mtcars$vs == 0)", linters = conjunct_test_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/object_length_linter.Rd0000644000176200001440000000311414457657444016653 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object_length_linter.R \name{object_length_linter} \alias{object_length_linter} \title{Object length linter} \usage{ object_length_linter(length = 30L) } \arguments{ \item{length}{maximum variable name length allowed.} } \description{ Check that object names are not too long. The length of an object name is defined as the length in characters, after removing extraneous parts: } \details{ \itemize{ \item generic prefixes for implementations of S3 generics, e.g. \code{as.data.frame.my_class} has length 8. \item leading \code{.}, e.g. \code{.my_hidden_function} has length 18. \item "\%\%" for infix operators, e.g. \verb{\%my_op\%} has length 5. \item trailing \verb{<-} for assignment functions, e.g. \verb{my_attr<-} has length 7. } Note that this behavior relies in part on having packages in your Imports available; see the detailed note in \code{\link[=object_name_linter]{object_name_linter()}} for more details. } \examples{ # will produce lints lint( text = "very_very_long_variable_name <- 1L", linters = object_length_linter(length = 10L) ) # okay lint( text = "very_very_long_variable_name <- 1L", linters = object_length_linter(length = 30L) ) lint( text = "var <- 1L", linters = object_length_linter(length = 10L) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=executing_linters]{executing}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/empty_assignment_linter.Rd0000644000176200001440000000172414457657444017437 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/empty_assignment_linter.R \name{empty_assignment_linter} \alias{empty_assignment_linter} \title{Block assignment of \code{{}}} \usage{ empty_assignment_linter() } \description{ Assignment of \code{{}} is the same as assignment of \code{NULL}; use the latter for clarity. Closely related: \code{\link[=unnecessary_concatenation_linter]{unnecessary_concatenation_linter()}}. } \examples{ # will produce lints lint( text = "x <- {}", linters = empty_assignment_linter() ) writeLines("x = {\n}") lint( text = "x = {\n}", linters = empty_assignment_linter() ) # okay lint( text = "x <- { 3 + 4 }", linters = empty_assignment_linter() ) lint( text = "x <- NULL", linters = empty_assignment_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=readability_linters]{readability} } lintr/man/figures/0000755000176200001440000000000014250050336013617 5ustar liggesuserslintr/man/figures/demo.gif0000644000176200001440000101740414250050336015241 0ustar liggesusersGIF89ajͻѼibKd]FٙxoRne]K[nyk\EĺVs帤g{ԊyPXiػx{Òwdvi]KcmjֺĊekDdsLcjCdlM{Z^P;^fh8{Qf]mi%! NETSCAPE2.0!,H*\ȰC,&T1℉3jȱǏ CYq#ɓ(S\ɲ˗aʜ9c缂IHԤQ3$80ӧLiƔJիXjʵVg8`$W WA+:d0DY#E$!:KW~у j&I 2kn٫ϠCMtԩS#5N)C_FDUh,a)L!)@*̊AJ!b'Oİ`!Vly+@S)Ԅ(I a[0!ha$0  # rjVhf!dCN7l#"N|5 &ֲ53Rl(x,C#&Dؒ2@Ė 2 #6 R7CaX%:c68c7Xj&ܐ8"W`@XMiƋ zuچ*(jjh݈6΄C@Qv-)bE(""8A0J,A Te@ȧ`jDOheސ9\I31b°jÍ>Ɯ P'#n!͜OF& Fj%!$$#>7.F1L"ȅD`JZ|dTNzd#'BLLeTV򕰌e9KZr̥.w^җ 0IbL)f:˄4IjZ̦6Zn8IrzӜ%:v~ӟ@JЂMBІӡD'JъZĨF7юz }hHGJҒ(MJWҖ4/iLgJӚ8ͩNwӞ@ PJԢHMRT6PTJժZXͪSUz` XJֲhMZֶp\J׺2ū^׾ `Pkb:b +ZŬf7z hGKҚMdֺlgKͭnw pKָMr:tKZlvzx;Mz]ᅯ|_ͯ~LNp};'L [ΰ { GL(NWw.eL8αw?L"HN&;PqL*[Xβ.{`L2hN6p<:t>94-BЈN4F;ѐ'MJ[Ҙδ7N{|GMRԨNVհgMZָεw^MbNf;ЎvMjG؞n{MlNvMz~w.pO;7g#N[85{ GN(߶W0g./ǹwnr?z7.HOҗ;OԧN[X?zַ{`NhOpNx{>Ox7񐏼'O[w|7{=EOқOWֻgOkϽwo?OG;ЏO}[Ͼ}{}O?ӿ8Xx g 8Xx؁"8$X&x(*,؂.02X6x8: ȃ>@B84HFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆n!K,,  !,@l3 PCDD8EŋdLc!,.I  \Q`|[PQt1JllXת.B, aBƅ P+ 񼺚uΔL,se-Zsie`ש D5(ck«"k*W 3C`mM/ Cb(DSeT Zr$Ju` 9FZ˖1vi<,L#6 Ra*_Bkiؘ2Xc)0Fi\z"G72sZ T1f$Wv F*;tO,dI"A%ĉ"kAbFG N8(#;h@2bبD&dpP4d)@eK hѕZ:!,80@*\ȰÇ8"ʼnNxc #ICQj ATҤ͛8Ov$xщ.2M&)/(&< !ӧ#9" y#T80tW+A ۷ '0 BAUy0+.+LvQ+"FaU 'plфUP~ Eb)S紸Ɖ4d %BC3-1Xqm&`vb X|wz`"CHq8\Tx(Hg< .d9P߄KєRrX'!d)!Q)E_E,J.*e Nm$W G[|PA! n# Sh`'|fL}n `-5KC5pm!@XާRTI@ Rh#9ݨC7xS7iPj2(T`y$EBSK(c-DñXF"Hxpm#zEE.py02dG1 miN/f'PBO\oeňO( L`u `n Z_ !, Ȣ *\ÂeV`ID"Ç$lc"^z(*jP@H@J¢RA8C)+* RE+TDQͪ%: D@V(cЦZ:ٔAP&ycUJzR(0Kz:m'ejAiW(GРSD MjjO3z*V/Yr0 4a  k?Qvȝmhp9wxiN|0)T۶ms ޜy_0 !, H (B   ŋ3j8Lj |$S2tH$0cҥȏ$ҙ$-$ypBG <hӧ0)hӄ9\|r'͒89PP`XKKnA<H~T9x"YM+hiQ*Z3 jjxae]M)M  N4HfdVkx-8 .=1kz&t6IjUE(C$["X )IdsM5uOqAAd )p2 LDX2JE,%(=q9 uN83S0ELD\b/d :B d2qWP> %HB%BX!@GyC>A \P}teWɈ-#ti'syV=6HRihMQZPYIh@zijjj꩕~ꪬ*H!,*\hp,F`@(jȱG.J8C(JȲ#A$E6\@Ö8s)!(_6! Qz%UV`\!Ƃ86:8ׯV%k)GC#E`Q;hEHZ6aPR߾w̸K~QPEĒ.A~ V%Ydݸ-9 FYekI`d-HMF(Q\hFNuP bDUĹPRϐ  ״둻# }bm*%ua zaQ}`ɧ V45'҅/,A4l2LrxaL0"n4`R K]Y%G@d@ 0 {(ĝ!5EH:#G) tH9T#Lv3]f,q! 'Y/}~t)  Fyh)R&UҖ:RzSrH')` )Dzi骗ʘJU [):;HP*_eb., F+^!V!ZUFDh1J-d!-~ꬺĒ:Z7m{lNA!,H$ @`!CY0l3b/j@"Ǎ?1@'D˗_0F+ ⊙QЉgYP 1<-E_M=܊Й9.0]:. ur/y? gm1T %ΛIe,% X(N\%4I]ӞatD"QIZRP|g Pz&MRCV%8!Qfd=BJҐ&-PT/iJ) &,*e)0pӗ iʰ0@T]iH=:ӄdSNAIw0q072GJ:5hfHpLR, sAVP@ s%]H! Af y@C 1R$,C@4 q t'p) Ti5a e uo !6P/>A7Ƿ ˆFe ")P=dvCfwܦxQ {s]%8L'(hn_(2P Эt= 0%*q9.4s@`L@dF,"-A kB>TX!@!Emw8,D&$ xA2,t;e7"ݰW*:@PQ wHEVp >x^j ^e@Q9H[0An8>)T`[Po]/A:V()\:e3L I-02 g)fZ Okd@Ͷ!" Vz\ A"AҨ,I@ ns!ݸH*yy=\ ̈*ρow*0fAc\ߎK.rn⎀HEQ l:5G `lK\2(8[Whk۳~5x<>O|Bx$84Z;z `n`S>PRAgaACQ(0+ZL,6fLXPE6-czx0pgN@py{$r%,Hqb!py ka@3p Go m #1X,_q6gA! 3rfP4pp&gm!o}x`(q@X\ ׵ $@YR6)PA 􁐦 \`U&A 4Vc6_z p#6 `$8vNj&%@mVdBBn#qG;HmPlUm2:{8n01ɸH;07@n\X\%1@moDuPpqpq-sg GNgTqd.0p J2pِ%OgKyVJ.H%&ْl,96 <1:D%vF5>UEVϦ$Ff{UȒ=DEI蓇'P5DLC^GA7  8qЖem2P N(\aKȬzg@|[5_n%$+nՒ$@^V/tRbobt>ӻq. kJ:"MHbQ]"NJM )M:OFsđw si  !KV0 월>xQdu'Tgi,w.yTJ-fQַʕa|GmFj`Aw:N滺l+@!2 ,xr ˌ2BuLGL— x u O7a6)3:\'@oI){[ @dTEQ ܨO'$ P =T X Z !,x<Q|9}'_ ^2S *Fp9@X16,2b`´C,)L9C/` e?m0J<(iLa*c׈u*k`ٌPآBLGw\}pƴitlt4`J;F+yB{St ]ݓm}׍I UicERPW,YBA{ۑ04__X|,!q@AA!Y|Q ͒ " +i ` L}Y_SPJOb:q^4$r@~y.~mjOj,c.8ζz pZ@e 8@'@F|2 1`0#iX װ8m J^Uj D0%foWRSSۻw=94[kwt 4;hA}INݽڜ) aXHbq+0"+X`%R[pͼ}۝lKK=MEY~ӭLgǖ⋼ AGΙTN_0?< y"5EŸ>v_qpLA l! 8pE}mj*Q) 6=-W ;q *Vİ!yD`?Rj6x`y8 NOm9Jģ>Za=P]m^'pCJ Ĵ,a 1VG=1 KhGabj'Qn0 .J9FpgkEgfGV]dHK-=^HIuP( vPZ#|_ P ZpnP,jڞ1k f5تAXPt=ӉQ%mpfW JF[]t&G}pnrծֻ;f/Wou!lBqy@2/ +حA@23 Xf`w}/de K(mAt HB+m8ooP9{vxo-SnwnW|I?we OSuZ'V, g ' sN#ioY8~ o?߻OoW ϕ$|L/Ю+nO@BAaA x &OG S- pIt!x_slP"03p CI }5t5$AI|eW@ ޠ Z@#Q* }!"s72T,a43=L#?s8UH”%t  Nh{e^I&fY&k nx 'llB'4/>3(S Bq@+(++Bς A {Q3~-Ռ-X2b . 6T/C/ / S0b_h8`-3;3 BSe,9Q35\84IrDI!0>30S(o ~2wCE a!]XR`I<I@ˆ)2;3Z,;Z"0؉<}(: a=t1= %zdu&j">6>t/ / b?̩R (!4n@7@릵C2069Pr8@ 0D@;s% 24)>EE[,FP/p77jS pV<If6IΞtq wpR]ޑK@K pX@ KT ubL7 L>*0$T'aLҴP5 :KI~`}HE%uPA2RI`IqONQ SRJ(.I8Oܟpo[!,IW+ *@*\ȰÄnw[QAa+肩щ\q:{\*ťX5-mhtbd{KV:N(8Wap]R RQDW,p!]ͼNeMBAqW@%KЋ]B DpBR~@0j DnA { 8d5WÅ 8n9ƶȥ.qlxߍ]0*ϘN]yjNZّ lu4!ל`XgάkfxlM3\ތ˾MMBW, F1މ'MZҕ4ϧgXӚuA·jBzϧ#syeWMH$HrwG|-zύʪySgymgF&kNkHzVj&/T΋eo/;㗹[m>7CS3{5ZKoXk=lt'[׆[xRo0l (" Ħz!d. <*t|/waK4UkP AX(ѰܼBh-E?_*B"Dh@ޢJ6nt, ΢[D%w ߩ ne/iZK%)kу`8E UQPf .t<%K_&:`(!!TL  :@vS0_P= ,' bLh)gsYp(_pyZXS>g K( t^?b=\8 ;Ye~) C@HbP [K"sBy55#jtsp   J0 ~5 01.q/QN(qU76wpuE W̔ tPn6"R-P se O&g;H_%5$ *0` d@ {@A jgB[R aQ h&` z7W)CCk5;p aC 68L&\+PP Y3 ' J:<@;]#@Q9;CfsãNH VM2< l#56 X s֨[#o5Е#A7 R0wBx{9 v`j$ Kr@ {A7HTP F%5Ʒ-|҉@9xh_+p"dä} V{0HKeV+~Ò PE{Ebk GC$Qd Ոt9'OhuMULN3|9hpрrmViM38nXՋXJ;91xrQ4gj}بG!Sq-zJlڂqun. j7z(b/:Oio*l  a(6x'Y!XVSڤL/Jp*mf' (cjKQk7hJt 8OA ptvN2 b&uZD5r:g9Op#(x'J>ʨ,Kʢ'3ZfyiK($! St Zi WgQvK ugH޲~Qw*`q"_p]Q|n6)Q eİ=$٭w x[Pa(=R&y` Ǯ$WKcAڮa"qf8a MsMz@+҉QR)]j]:"=j&۲W۲ڂ<ڢ2;G70;DB6;;%(" jGw'XecӈPN!{kO׵Gg?U'Wzh0 Th XSq{vc>:۷tZ 0H0fjȸ@mj j˨VtR</'Q H Qe*ˮQ|u) gC8"<&kG"vZDzPo:Vs: {ttʂֺ}޻k"nۯ{껾۾;[{ۿ<\$v&} <L]uP >, [q 53S@ĸ9? DOBeK4|[Q 7F )yqE,pj|, yoGk|;r8(ZpFڧ i^\!,`8OMN!,`8I!,`8MN!,`8I!,`8MN!,`8I!,`8h *\ȰÇ#JHņMhxǏ Ciɓ(S\$˗0c\9!F'6gɳ'} Je΢H*u8hPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_SDwJA%6" ѫN@ 7?~j'XR Pր.Tǔs VBW}$"b8 1$1 t)UCx#ǐzBLJPP҈ehSx040hw$`HibBMEBs4F'@@$ щ#RJ-Z v +nLȁi-yi %n+LCp++(;4J*]28k*̄骷iHaH,@  JA#e,9(0^ L+Ϟ;ON0 ԣ4'ok߈kK; ij̽1P.܋XL ] <& J Y-F}RLPT$mYfrbqexC*4cv(>>\vUrmA[ %2(IIڛBE0 :Q{0:KB768;qF]$Ac,5ƤncN~YCW#p< ddyS !,`8I! ,xH@ATH†ѡŊ.RԘq#Ǐ5ɓ(S\ɒE˗#I+"IsE%{4I BSb0hRP":*Kg$9 $W.[PҀfI "dVaKnĀcʢu3a(ƾ$=qFEңEAՂM+$ ("Z+hj@  -\ArņDeC^3rF_`WCA5v2Ar`h 8'`E5€Qb0%-L,2΅n0PyhƋ1#Ё]0" 0 ?Crb rЄXSx W`#7 \P ` Wʃ3!CЄ `"t`ˌ8◈R ib^ )$ᗷ 5>D(xe ^ ]ĩO !y9C/~;"d)D̒K$ lOsq.BKL3Pe+'JπyK't, UBZ5J$3!hJ+jE/+"|a)VҞԧ:PJԢ~H$P$}PqRSEǨLŪVUE )4q@RM5bYH`2!Q TnA%e`U!R@ ,!x$0( 0=21< H<(hẑe(1 a(֔a;K)"` iUݸ6*TA-␴HH0gr †')eS8 O#յX-P;D"rh;DLb((hUK` )KIhM"Lp``N,8.; 61-ؠPEv6  "z 6PX ؅Kɚ0(!@\iw!D;9PBć+!Ɍ!,e3+g85T5| g V ٸ#TT#yp b Ѕ&,#Jkqqf *T3tbX <.K2ʹYi9]bnWA2q EH?64}Qd4&brSې{@-`ۻWBpF}3?/eM]4#r-7xDӅ:KaGՒTANg9]*эR;=QȟNW}dշa[X:̻Nv; knL;D<^dx bWH /~>w;|%&ә L٢拎 Gym@(Z>&$U{V#gEn_!ވ)m" 6dдG/;n`G9$t/R`s)@ NA ׾~O]'mX>2"c_\̅Xo@EG}#O+ Cpwc8 (dk*Cgo`n!U\^eP~hV5Y4fgi6 SgZ@o܃C*SfCX8 ȦvC!o 'r~% Zt!'&Bdž WjjP\؁kq@ L$A`"hpwgXy8dbP0&vI (ml_24Jrn DƸ $pS77~4<7`PqOypafPpʴqOgx q x!wŕב"YQ7&I+S!Y`t>7NBy+SL |s0d7 JW&_VDtcAlآ-QUI0uD9[9%&0Exfm0Yp[0Xp m dw/E!yv0J<[KFP|q:PX1(pZF "p wT G<7 d( XB?izoG0{{ȧ$$0P'?Y]Eh~pl9`_3h _k }Vq_y0)qTRxJٝHP `,U5B0p^}nj0i]X~!x0qa `n #e~ V̀d@0~i+dFb( |uinf 62(5/cq0  <1f! $8wę4 "RSN)J$zjIob-&*4qa]аwr6EJ@RЃ( zE!KQ @ JvyDX&0q8i1{lXnHE@X$ ©7@+Du %vrQ'J[X !5` @6i&^f фN_V]5jh*tJKEXReRXp-t ?& ^1}Ui گኰp4DŽXvk"pc  @04 #J4!!jpp@DQJ @ V@a^3thAMth_ e@  8 sp P е1P@kJEW G6&bب4 ;sgSPݶI5{{ `#DImm9p^fv3/NۨnQUA|dž|:f?zyV+Iȇ|76Y 5gj:yuŘ\u?m0 &`ȂGx ܃ߗʪT쫣;â+`a<\`IJJҰGq rq85S0L-l"x_ [" 5a,Ž'h\ O)r, Q쯅 š P YçSQ `>pQ ) M" 0Q[P8e~ 6"edW33Hzb$f}AB 9-b4R7 Ki[pF0qqQ08ֱZ( e@n~W I',n-m ټr[٢=ڤ]ڦÎħnڶ+23/xzf-VXgP|)g|JlvU™z:ĐE|k| < G 0 Ck^]Hp*pg1^ʀ}rp]͂H) `v1h7F ~5L*0F;탆ppA y`! i jW膵0gaB0[ɪ wH,.D*USy(v]$$kVH0L[ ٤~CW~N3S`$K(Ǒ)X` 8NtD;o0;~˻8ps craPP>^~>Trn ̹;)3\>ٓQs a<ӑ6`[ a L=^~fA%мzxY шX n)d`\?`jV9;]s4,=.] 5/x.jҾТ/Q޶@V}>_:Rwbfx0a潎n.oiQmP~kap(x9l^c J*5dq*\Y+Jɧ}MB( I\zJ:柳=+t^\_6B?f{ XeWqpN_(ނm VD׬mfr˂@!`{oT,nC ٛ}Jm 7_`)WJirïs[/8?ԏn8)vboy '}A@-ЮoTL``gv @V XA,.dC#NXE5nTG!E&\$vْK1eΤYMHh: y.\XQ7.e$Ld=9EQQYnW\ Wiծe&Dp[] W!6_&\XaW`H'c,#sd̙5sTQ͒C&풌~Sf} F[ZϦ]! Dnl,Cloȓ/GsN(^5 Qw=y__{YE O8s,/ r&c :ARIR0@btI2/RIFm?[#0gT%@C@!@!5 拆0HK Q.Y! Qy, W&D қ !4H 9H#V I UA.:1jΛl馛mB4!b <9Md@UjdNEru8M/ U:VNqN`sn@oцlA[s1Fr֙1In.|jJ:\pZ# 5]nt7m!!S.Zm$ YoV[FĔja>ZN^چ Aѥ;rA!w0IUMHuh+Ly?sI&Tnx2XZТE4Ơc,k(YST *^J@B`ƗB GoJq% iryssF"ZIe(ߕ +&l l bWH@? ŃV$eXhu;irw|`P eZa Emq4ohgKze@A7n!(2(,dx9x#"j σ9F$n8@;`5€BB'%r8#L2δ䧍nZI4G;]*t< &0@iA:h 5V@ p H`M)bň4s< @6d1̽ŕxp+'1QRq3 ]87 .E1`0 8)PAT,k!4y2P`H-t@HabPKh% c#/-J 3ۢMpS$g9͹sSdg;Nx3g=y e?Ot"@ zP ap SF0 D) C!ooɒF80=@ R%@\E(aKs^g{NdwC2PP p+rN""Dlq`o)X)R ZB&hEC8@:h՟XL#h@+9U)XPKApؽ`Jjj p X<$6`Yθ, 4˽(hfP̢@32^6Ej 70B "`%1lh>$V(`(ɤ@ -E4$XD Fv!hINm )'/M `!bϋ$|ċVK wC(߂" _@L PUsNj[h^ǃOQ.ԢEY 2C|% g88!PsG#*pT=AD53*& >/r*&: X) Jd |& "x!i>q3 $$xBL`Ld+p(P1by1G(0rDjGE*Z>06X=/989k::bI剃[$dؕ ` { E@E!"sAА/ AIaK9H١5`X ؗ;bÑDB+@3`:3<۝pd=?@`B'&AxN*?@[[ :0*Q&K@:*҉hYF@:#9 $+?H$m2S iG  Ă@ Z$EZ>&KGx8U$[E|APľ8F߉r Y$qF;`Ċ`d#cr$bV9p\'8k'kCpλ>(ȉ ꈁ=_GǏ#|CGZBsǬtL#Ly ȃ\HȥǎLe hɥ?jc Qw+INI< 2ЀWIIyQ8!HTJzEIi=*ApaZ!H8R ?8PN D G3)0l.L2S_LCbB Uu1Lg0W~Ta:S(S2"C,C-YXZXaP3r? B _H.3" 62pbX"+ \C5M8KD+J @8?lx X0]5OxM W :7E=BTx8(H[`K`Y@*a 0h8#s=SFݠSS[ ?E}}ヺNۊ48_[`Zl;ȹ<8K1K ,ub4@NJ)n91<A@#*4pJ9۵<@& X`hEȷ~,N=̷N~Mpqh\%]1'"Mc5Q]J{+ 0 7`0$[<D[?l=?H_pX[W*V e4ؿ 9p=ݍT 7M+fXԊ1^6$@HP>]7b4F88Έ-XufH)90P5UpU)$xA @LaA$"<)Vp4|L?*r2lBٙ AA,-T^(esDyN@bʀ <ijtA=@[߹AKSIMT-CC\`+(z2C=`6D5{ii ^/i.`ɀIs_D`0*fDd>hНK{ގR I@Fj?1S;#X ƃ#EhRm='`lIDklhjű@Flw<;YLpEFib#U ZZGf`TmJ.NPVefPZc㚐8w;Ҭu݂C3-`7O&l9zNNP.Tqİh&m% k +΋_Fzud|חنHJhHEEXf {IEL%wENPUg7 ?  UUwgU+&8<4:c 5.RةK2&@Q2FVPRy] 远 ˗XE'b/e̓W\X8F"s+H H?Hۡ% `+Hնʿ?[%7:LOS 9P`X[`Y@Xӂ&+{) f.萜~b2Xi+*@ " J|ʁ2h %B\XgPզhpIYI o:*ht m)qBA5!b0F<"  0H`1QQ^ e1eQtqh %i L'sF 5@L | !Ǯ+#\8nբ `upDs7LLḵM2p\HfCt K.F}5DhuαMOIL,[~ӉFs+SJҖvC-ADzCAt; -ꗯCo|[#>w>>?????(\2| !( & 3 r{Ѓ%< QHB.|a2G_ 5rp_ SsPzí/1B +L|R x;SE2.!CTD ā A!@j\\-W Hí28W#Zc!H#hA?tXbP/A" DfHEpADqW9?,a|)gW,, `Z&ҷ0MRX@vgJ y25?1U@bp )X oa͜:q0c [|A ,LE68 hh.d98ni7QT`hL7A!]bh P0rA횕W sQ2z#,+P)09$PNsC\ĀfPN"& I*#O2M"b]ȩmŖ VaZLVJ AN fd[M"1A4b`D#ZD Ƶ"ZQ| @([E*ZL+-,z$<Te@q TAĤt EJu @()е# kE*K\fA|1_(؈^r7RqVx \ڐT$zW L@2lE— B2|@]U@#2M܄OQD/X2g*Xh SX} *#"l1\f `j'3Yt>fP B@ $##24:ѷ0*XET-D }>GSU<A[.ؿ-Z 6Z6F*M@/rv9z` s"(P p +2#Dݜٵ]]RP@vHr\C)oŮ lRZkFG04 aȇD 7#H 55M)Ƞ#P<2x$ 7&Z,@=vN H"edtBDHh5`MeSJ%UU8e)=])@a$14x,Ahh8:3$pTE$&TBgF6u_;Z VTS.zb؈aA-TV+Law-ho"cIޟ6h*0AkcEVaqDri\Bu)s9QqLA(/<֨:z\a؊* z /#*MAr T<]jnJ`,+:-PkJ@0VCkUV-St¨nW,BhA`},תʪg5&GW tU< &@VtMM!A>kyBwlCY HrLyM%Ux}D=4L\𬐐Ȉꀙp"4LNM@a̛ITOY[T\税tXqœ%UX.DF*(p@ؚAM:,R8OT } XT]"(C-D *݊$1!PBdYY$Ā׉۽J@,7$W2+A! ^\5JB MT\-H"Y6RUeɾVJ6vd@OX($ xHprF.qb !v%Eu1o(\DT\aźGTY [F,P<ҍ2\[aB\ޅ)/3g-H.MXjUY/yU$т@B^4HRl00!ip8A0}@wH(t\ސlme"P֯Se ߞJoJݒ0 jQ1m0&N sU0r %"^4$.jn/Bwߦx&![lm^%y_Z &)\~!pE%@ˆx 5#@ΑA- 7knpH87<=oBUT˯l&ta%-13x!d@ʿ,!A\!!F1˯P,]t"2o&1 |ZlC$!Qpa<!HCᮅII&.jCas0`㩼p G.B618ޤpdd McM.}c<;$D$I@#\MJ6$ވ L#^D>HpE5δeuӬ/X a$X{Khi_6``A:id, JΣa`Wvn[ bdi&&h)xsOu;sPL>ts7Vwnwwwugw|+XhAD(Zڥ Cwe эh_2D,d2В:7ftf gw)Tlfϻ0C(B!4jw)E-hFIٌ灟mY1moy W.Z F_ ZB!4 'M˼Ɉ*$ nY~tV> #})c=-M,YmOҰʈ m!V$4ou?\HQL&t!R^F7P"idHOV.r#-5b6kMQ!# LE'QJs-Ce 6tItT %n Z6?xS%1e ?8Q@Q۷5k*CFEU Zx4XϊIT愠M!"NPYv" ta- F3Z`-rFUPᶔ4 qA_$"!Qm}3gU v%>xMF Oϻ&˂O7H8Hk=SA#E!s=v pPE%<VOL>F9R$S`%.}Ay {5`b4 Q/Q@UB``,dn ^֚Cl Fإ t+3"BW3S7Sd0+$tQn;`*67E~ YL T$\]erCg3XđE2(`A 1rY{mcz DjYκ,E>eyW>o5'Nzמ蚖y{{}1cwFkT엾ޱ T@>)XA ^2A~aIXB)T 5B1 iXC9yCA݄!-LD'>QXE+^T 8H3X#F2bQdCxÛ\GٕA`aȘYOSb =U$7i#Ȅ4  mP`@wL+$@CR]AF$4$>r#fLd`X < (,<̗X(/3K@Ni(ZFVH1E =RJLGf>B&0hpb&aƂ1f"zod%W&Ϥ9/iC>Ђ+I/0D-vImkqhAcDX8 &--m`V”HA4fM|uhZyKqb\$'zh`f7(Q͐W;^: ed3XIb)_5Έ /{>+LF _؝&pj?;B` E!)ML}\ J@x4ɣ:EILȗ!0 + °}B ָ\*gX)Wf@ŴÎ-?xο3d|)ʑ2xY G:|W"CsQ)s%"%aq&̿B+~^4B`,]–b e$H9Y UD>H9:PW!`0# btE ]tz)'Ax]*WYBhR\{۾& jO%D,D},*cZs^iU*ةI!'l Yj\ 3jSw(q,Qvng#B}.zF5w(  {c}HwWQQ]Q|Qq1ǑQձ(H6" Hw(C! H&Ofqa@|e m@P' {b L&藘w5, @i<NR$%Dʈ J #("` L!`H(a"-2Z P*) iti`"aL4bI;.)' *F2I p ,EH)`LJRțR^DbrR SFJ; @ *i(m.ʂKHy&$l( A<84+&n:^U4 K-q>zB`"(m\m/glKF$ ,OE*!# b|nR֦U D@V5YNvm[P%$Z@ʔMUPE`n4JΖH<PLv6d=4<Mj>Nhn $EP$a-*[ux.`?@8=‹ߐNjc f_4z,Dn6`a  j`vigNE/AIb`pfV^hs"r p)ݦNG䔢$'Z) CM p1OD$tk & WޢeaEDN8EL0"JD[P%%"sGW%@kxAb]IaS/4B @4Z|-#0$aokaf&`aNBvYzl~Wc\(PQtkR:mE4raO`|_drӑ~!e0wtubGp. ! 0g & [tua?_ G `hps$"avt~p -+`teh&|xApGXjvF tՋxC YX ƐljS0P]t^tq+ْJwHXq) 9/AA`|C%$ BwT5,ETV5@4 xz -#ObECx#49#2rH2:6[Pwj@ Ř4#* EzydT4{ CA$yH8mlA ~)ԩ02N!3 `!61*6z8$ҡ ʡ "xP xᰀdG-3$ .T/ 4d/%tdjtS!3 `" 1kir4ڤVf)/2 Gܼ!OsSPéLD+8Xf<-a0=qo|\DU|i翪"JꌄqR'Koo&Qi9TO 8pw0ܼWzNe䭔¾E*Xr-cw>TVG7{`KO? !^f|%Qϋ&$N|Vq(e`tlxzΦǭ{aiqg`A/'B@rfÊٞEg!(?݈/?SQFx`Bt,fD~lQvN9!0$L%(LYLJ{_߃x?T+;{gM_NY_wQ9qcA c9BN HPBNpĉXညY4 @LP((C̷AD1EAh@* \2D{<^#s$Dg)A0,]Q\MG MC N1rw!hK,YF[I1(J7zVyv4R؂H+C{( D"!q${L+-!% |< dNTh"3h4—D`Q("Tzf]L5ћcGj(iN-6CPniK^J,RIDN !(Bu *0K)vڒG.x3P5r0"$H')(qU(Hj0L 骊iG^瞹 ű*0_Xlq '\AÐ%ļ+P843C谁2'"L&0P BKiE -4EZ{"',"T 4B޼2 ]5C. T&r *2֒()2rE`X+j0OOČy <҅yw 4EQ#.Ua{}!f ѻ)HP7=݌=3Pо_0 >;_b,oc<[{B4p(A^ҳ P$!0~ հwNlCpp "i_TEO" jFE# RE*%'6̥nD{ ~K_Jo~+.# v(PQp{;``@: Hs/ཝp=\7;sW\_غYѸ*N>d|dIW|e,gʄbxYd\ @ F gB&Lgyq&Ld @SMЄtD+zьn HKzҔ/LkzӜ= PzԤ.uMiTzլn_ X[Gp6Li'$׽ul`'{!6 m.?{qmB[[v Gʔ}|5[HNMh e''XQ`AOAg<rsK&\&^78d>DmkI< WpqKPмgv1c>¯` 9F@?{A6c10Ir-*'qh@&HK 'IP7 `5PA^"mA*tT ࣥ;.1 `nf ^" @(G}ր rԝo=WHȄ0#t]np{bN`aF{ ]"|OAv4I]IL!bļ ¦ޣђtwYZD(,Z|q6MT>` @Kp`6{3Y x#4B#7Ba9Gm8 p70qFDVn4`а_PdHL7>@aHFBG#H-M71Ї2z4sY;H6s4c@3eH4sB؈XC?uh37V$ D'DP$EB QP CH?{c8>!8~ ?,g;pG=A o>l cH09 d< %:UZ@DP?Eo4x z~oC6iF(3tT[P3[>E)䔴P+ʓE_G@))>t c@P*LNאIP ɆG7+e>4I` -rQB In~0;m^$0(~E 32+!+wC2Ƀ1s$`?xV 8A 铏ěԋ&` G(FVqɖ?K9 iiPĘ}=W"Z}ZqP\ ÅG]?نqUS x 2)V1c5\p8aV("%* :YCR@='뢃R6VzDg\W *9rnS "# UbrZgЋVi@Z@9PRVruwB*aP:6b =6kde0cb 'Nj bWZC&d)i^"^FFijU)1KƧ5VłNg kjz`ad!ba.c52wb 6v.1`gWVDp3r' afcf_@ekڭb! :b 6*]FaKa7I Jk ժrzZj'";Z!p_n!i Wh m1phi hpI's`psd /ȢK*a vq_sAk3D ] qP)g/h@b kBaDų6ЖQAD˵, aquNpW7#! rS"w{g`x.Xzuz2z, F$ w' Q{#sZ@ P)<`-)0x$!v!w|0cg]RE-g f% %;2 Q !HGƁDh =N\)0q`PM )r.W'B "ˡ('D/@u+0680~$L \`}mpXz-2O_9ZA@2}4810I*rL+D\ Q2'p1BkL+( #tJo+ Q1`cJg)A^>F_9lTȺ709 ɢhT|Sge:2& J,94)@T5p܈i }Ihp&135|>p(ys:)]fml c; 6a]͚M)IFzEykH¬Cs3B05ISFIo RlQG'Ld\10nk0VeD A" *4@(X"=HXB] ͐D$Ԏ 1*AJ0 KiA Թ \Q040\"݁ qûpMVC)}n \d#ND PU`0,YiX0ԓIt['ЖWhD)qҒ6M}M4HBC'dѩ8/]`PwlO|Z0X *X2\V.Xs5j[[$r*n qGl%1%T\:dPQ `:JzY]%SQ0e([rY+:O|T!A܃%R#w[JGG-,N:0g2^A}Z`^*sPLEfZ^ʵf0vz 6]-d%4^H9^..Х]땩bڕ:R^ͥ3Dvcd/^!%buPS"yЫvSjVkTlӖze)jj:FO雮.qi ka`#+ ll.>.n'zq UOiqAMd^.huT$:udg"wx:-e@h\nkk'%$TK_hlXQ]`}'xVԂ_ ~ p}_A^,x`ӔTG/X)-­hv +e1~ X&2>Ï nP9>L:8ZI'71 9C+ˆ$ h.mQICE D*5Iei#“EDґ(,~iycYp 00 -Mx@ Ó*`WQf}RL&ۍ QyXEܩf:HɽIdSluV; [Q#@׹UşhYZ0Oaqک=^fq_b6^dQzALrvTvcOi*˺P1.@ ! >Q"$&^ĘQF=~RH,:DRJ-]SL5męSN<}TPEERM>UTU^ŚUkϭ]~v ŞEٴmǺW\uśW^|X &⦃?f8CY~(lfg=MnI `ԭ]gTlbƍvnޒ7[8cMAAȝ%<_Ǟ]vݽ>x͟&=zݿ_ǟ_~0!,(hhH*\ȰÇ#JHŋ3jȱO2tIɓ(S\ɲ KZʜI͛85XqIdΟ@ J!E*]ʴƝNJJիXjʵׯ`KlEfKM˶۷pj̃Zc˷_OKx)E̸U#}$yʑ`h࣠;MӒ;Aհc?,클o^{ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(VhqI\hZi ($h(,0(4h8<@)DiH&L6PF)TViXf\vu`Aژ`pk2D eQԹiܛLe%'+IR"ڒ%Z9SK5`-š ).lD@r'Fl҆j:.JIj iC@h41A Cg kVQF.Z- }ω(Dg*뜢{Yu0JPC$N:)D E Eql%uT2DII#U13*NQPda!#DA M ?#L)q6uwYN׍iMѷ`oYƵw pMP+Q֢N07iı3cT+0&2rGX JY #ԛEn0"v83DD!! 7PTJ25 p|T(si<+h j=x:~|ߦt'l-"챽>Sȷޜ!w|lQ韭hȫ+rC@TCRܩ(k$~nJx1~#CG5t}-Pe0L?@T6= &ħ9"  db~CD0DBW ]/(B  Rt^Rn.o(RR> F֧55t$IJZF@F/mlK#!Fz&`&Hc .5$n# (sua%!,bs>iHŘx֗RI[3gɚ8(D`3'_btT>SD^X1y@L\r76 O:p1:/ $TTѴ U:ќ BY>g!Zn (^n.D^9I& v-oB:8stny)bED,, KL|e;fTӦՈ&t="V::@uU5URU*c"W#<_:Xg#kRiS5%*W( E+S^% 3O `ga OmV+HjA I)#"X,l0Ǫq &hRjE@(s^E&DA@4JQ4bc @J L2XgH$i-ʳdnZ Aሔ!-kr/]aGZLfik_0ZΰIFc(է` [* M ^k^Q|7i^vXEYXO)PijZG3;H5U)_!w8C׭gpo^: ހ k]1e1{N<#7I!bU EJOל 8?"X0 ",5Op{]VsSQA Hg|zHvY)AasB `šZ70@|\j[Psֲ0@ †\X__6K>/`kkkfkF`QyQRc9{pYLGVbL,@6ACvCmGRdob95dCVAXc sdzy6V'p\tpHoxvo%V2FScnև$ pcmcF{6BCYg$SѧgdW!H^ B4D ё Bp(HѠ^q5-PHm#@5@VF h'@XC?q QG9`Y6D8YC'ov(7&؏XdXjTMcQ >vn嗁R% ]I61!)' `fcAG<jnA=iX `sA )H$ V&saw$00 0%~b(< 0a<tЕ3Kpɐs vu/w{yy{ #mGLxl[ %'Xy`$ v$29$&J$mIc5ؗה6'X"#>^g$ɇٚeAth%ɐƑB2cVm釤(SnqaU(Ys\276QSeY~ p؛ HِJ{ɜ[XӟaIk0#Jc r*M1$@}ɚYg"Cz8' UU(, Y(WN57NQ`o ГE I0n!Drx@pkipP\7 v=@#zP:prZĂ` :P1X=)%cZ@*v Wr Ч +` p.'ds<*Ġ|p\d`G}3T X_m-= @t^@ К ZD sbq0`j@dpPP  RZz$P D k[`t ޺5P"0`аPjP `= k =  ˰@8&@-53 I(@VP1 Tw3~ ω5-\y5ٚ ;Q [h `p/(p{MP`` SPC, ;k>q;` S{[p)+p 0 `wpB<F0 ЖF c Axpkp `+гSb  =yM e=k P3p 4P®n((4MKVr p-P t`@pqre\  @; ;ˮV+ ɓ(S\h( +cʜI͛ɳϟJѣH*U逥PB @)IXjUׯ=Kv%ղhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰Ƨ*무jk\Yk"lS@$lYD *S%(21H H#(­@g%0R,X." p`#pʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*.E_!W !6,emH`ÇH`bD 3jȱǏ CIɓ(?6 p"A)ereʛ8sɳϟYl)1f 4@(@JJժ43C 4)  !{۷pʍϔ@ dELD&,hV ]̸rQ `Q e8rӨ}VzR1s$CͻoLQJ#4fKMhF6)jO{DAIbJ cF}'Qu\B $ #KWl8R-f$I0l^@0 ;ZriY ʜTD% Ӂ!q‚ d2`!씵OHli4T w9 W AA09 af`@1 ۚ 5DiJ|c c2f@C(қ- SxP@-0[(J LHAc 8Ģ (@f+tWxF jt`@^ _}F5h^C~Gڀ!" 4E ϊi@&*N)$3dF*3+L ttVyGXIYTAtPfjH )UС-u`+U V,?>]Ya%R:S n4)x=$ҸpzB<@=pb1\7^-IJLBA1 -JGƅdBMvfŗ[Ӑ=2@  yZ%Q߂p!(GeF誉]F'Sd8BDj RC&I{d0"5<[ѥr%G"bB2m1)/{5.ᖁDf*([hH-ȘV{vd$+94lЋ*UL dY|:eYʠ xt&g!NL'"cHIJ3l--` WhCQA'@k a4_"bDY ^@E'gV"K<㍂08 .H1-|@&%]ty*#Us\ lyK Ne@|6Ē2zuj)V.|%%.pAƠ 0sE;c\ |8{ jx06`Ʉz+&(^WPb>lG4BҞ"ObI/b@h9q/' 8J7PWy)agb)"@ - @ٮHD t,>d@N01bXN>;)u;<vm4# :DzhqYWDtB`]Y%Qڂ4EVsw؃O;Ϗ>7MG?t⵬a\k:` -.]q WHU-VPdSL4!!W?!)6co~0z4!Q'Fme؁αcb#B$9_ CF$X ?LF@@i1s@xTİm`v2qLՃ Cz_%7b@ s.qNH0PGx&WӅ?:+opWgEi7zOgp1]1X9t[py(6,/u8g=<a';T.;'"ԉ^k&be8Xbi8Xx؋8XxȨ}hs6)AsɈHvnD&M8dWl(d(8$G)4e)cgGĎ(@ڸH(gX^9aVEԌUGIdoMGPK=G<@ vhW&0Fm!^\@xR;8Vg )  @r @`Π `@ C7k*Ct!Cw:i`I`QIYN۠  Qـ&W՘s Ljg'/bpo99 J1PB$ PA "Fh$^o[3FنbbjVk(3_[c+h(6cI@/ȅC*#eG'EN(רQjDMZY28XU4fe}I)xjcz! ͉ai]Z# *t&30לֈp$spӗ?q%Kƣk?_~ړr@*o!؄GF-PP(qkeT yv)E@C9_A/u@'hoUQWRISE'8c5i eN{X])P`YEH*&2yl;·U W),Bkly{o#2 S,r t j9\°nA-ȓsdc@TN{d#@V#$fe)T C.y0pB DRP!cA ѷ==hqou- 1e Mԇ ٵڗkkt>R Ph؋;_зQ m u;4NЯ%"072I_4 `?h?kEEa (.2dk yhˆt.(aO9e|BG}Yf5-DrT̨b8'JsB-p[ePLaa,禊 Ǿ#coLaV;Xی%rgtJcs 66w QrgGƐ.F58+gf@PzeI;W1 CVpwRI<@pL+)%j8AhA'PK$7B@A0^YpJE܇_~(6~eao VhYP@]P%t" 9*Jy:΋|Yc9`9*'!~ \6!# bWL` I@S, *C8 ) ;%3l\p ` t*ԚmBR&?HV>TMqݒ_ qЕoT9 lWP %ĝ1S/`f7bVP'A6π@Z[K\BA'p΋Ӊ ʡɜ"5j^Bif^R^ .:5zQ .B'_ IK{7o b)&4p>2JpApD&@;0HA&Ԑ:mC e7CSp0*QE 02'5< @K±H$I(ˈBzB(nB$Iʃ fCTAl,< #h7=ZR21 1,dkP>8L;Q`H>AE9!bFlH+-_Ba )ҲX-"9V:089)780ȲmݳS8WEe6C>y#AݫuZZ!F\|\l.Ft|tELAwArz_x`=`Vxava#xb+b3V;yD&dM6e[v-4>efs>vgzh6hVziviIA _80?nviLV0 (0pqEj>^0*i_0q5 ިmhZ4Mlq(HL|28${)FF꤃(o ea?9w RQ>)CLEgo<%hNQ"! EHT(u5Ke_{cw!O!v$ l#i}] -::x#X uDItr c&(T- )Zjq[fRZ5o1d!,X8 ŹЪ̱ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻxǶF-4c~S \߾C^aeƒ133MӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ {o 6F(Vhfv ($bt`(,0(4h82@ !,X8haȿ̊ԖKˌLJّ!,HH*\XÇb @ȱACIɓ(;< ƌW@8sɓPj^$XhƞH*]ʔ'Ԩ(AX4L$̦`ÊKv7X{hmh5,K.$h.Fڞt(0斷ݴ˸1ǵp@`NL)j>0s9! &S.C1PRQM+r@s.6l+(O ⱞOȘ<)N5=z7WgsH[?}h Twta҄z ~9&DRq!Nuh(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix瞜aL^vYs꧒%*(B_驨 ڵ]G g$hP(i!XhX$avQ0VP1GA]q V[Bs@tЉ@*P"I !IbއE#*nI|DQE#*x/xI`FLUF׽y Eta!rd̰ 5 ,s4kKZXli5첦V9FrH,#9 '"ԤdLt P( ,DA@3i]&JGq Ӊ({ !̤sCVdʸQ)[,A qg76$vHH3N IkM|X |J7OJ o=nߥ 2x% #~pJIl'y"߾1N?*p2I I1E%X< VFGZe $ B[ *Ai?, NBaqb! 崰4Atք%@_qwJwSH,z` H2.fLh#1x  0RQF#uGpfsc2 }:E֯(^ IJZ̤&7Nz (GIRL|,Qo7y%=Fe_2@/H">K"!"H%0@EcQxڠCIF F^NLv1F7OM懛)@乃1`B (4۔E! Q*xF(!P(#t@<.IWfK=Fo8 \$;' No &B@,ծz` XJVf]*Zҧu/n}+ѐڻ5,]3_kC no8DS8)P H~h`uKAr#l 0*#bdՆDC8&p7tk x .7q!!YEa#P @_34E1@žXJ#B U[yyI.)=:dN Г$} )Oze%i"2oabBQ+i%x~w l/hrX ̢-1Q _d@ PK g3@'qHiAV"ň7A~gJ:1ΕeYB¶V$2 vYnI@JEd~QfJThH |m%`emҍ_\48hR<6)0 hp cH`OU{[T8WhH8w!1iƑ[!I~\r ,gB@ZB!H߂ MLH 8ӕ#iH j Ő-A@2Ьł%=Bh;s!!$Jm1+6!Hyۘ*" 3A*TJO &Т`E &pKbQȑM !] d cD 3(J yfs%Qa4B`emϪ]|;2PIT EDhf$#';ڿ3Ͽ8X0su&\);+a0 4" gp@Z^Ju 8@ MA j!\hw8Q ]2$Nu@ 4]c?;Yuՠ_ d9CWy0 (>R!Bq%_&t4sy~6'76Y"3X3SwR qu\k_@**yC  Ià~)qs b91\Uq"A !f *!`~I:C[S{bck`Am^αxba x~̧§8$?WƘ F`F%ɉoaJ55$j5VL443a&R.Wl;q 9 AL6aV[("&c:a4 qQ0օ#'Ȁ7Vr.s B}5xQID 7 `є3EIА$tY#0TfXo͑f(Ue;,7{ o 7Zz5ހ7BMxp bfd&8 ~@tPX}@%0e!pB$,~8*7\P#$@{F$ O U7?c}ADu:7l"+1ԇW`!9T2RbdPUD1K^y:Zz蚮꺮ڮ:Zzگ*V*[q!`(~=eg!'K 1ƉlnE}Pq j  hNZI@ye I`o0/We5IPuqrxv GH_juJbkq>s@3೺\4ɧBx:OKmka60;VJˮn; X sJ )ɯh8eK Fڸ;[{۹;[{$WVh6C"X4!h ¯ӯD.8Ѐ|s0KP 謊x[(Ji 4A|ъ;[I(?1 b1yf+ǒ|@D8ᲗRE+{ BlD ]LaI2pq'!qWM:dxHLg'Q dI;4;nÄPI)<>@Bz|*z pwK :lػ`ɲy뭁b5Pòˣɋ; ̳, Pz,̓ Hl fHj!,H*\ȰÇ#>TDŋ3jȱ#F% xIɓ( ˗0cI̛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3CsK͠CNTM"Xk꩗zi`'F`܏4!@sQŀ#L⛅碏# sQҧS>evۭ})΂oЏ(L W0 gH8̡w4N4 B0/NGLY^k x(F8x?9J46 Q`fv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlI5y]ؐtr2xpڹVs-AQ0(G8(0!bJ63:0L+@ &<)*z5`*Je"O8-ݬ"uQp@(}lIUY] :@-B_PuD' bŷ"ǵёXR Swgk{k`0~ @JЂM($b(éХ Da,a$C'M8  p2PPc$*C$af\ljS*9èT%CDcpPY6\Fw$,fmLT Qh0xܵ7z9s0!(5Qt183찏QǣbDr<n6$I# ;@Tp%F FUE3Hp[\udH m`jQ1BV!%D)4P%RD(pW4ynF@ n=nd 5p-Et[ZFshbତnOb+` &0GxF*B.щ^~QX᭼jA㔈& a\#&Sh'0PeGt]LKgN8Ϲw@Ї>"_taETAB!:ɅTlP#W]r`c{ț()@LQ0j@ TWb T+dCW~Vp+&aTGC ٥+AR?Rb/."}QrI:*&vZX(~UN'8~r]I#N[G.nеa;!I.FL`f(g[F Jr lW#A r@gBY A T4 rP'n6 W=0'_^_D'yG/mb >&8@ U>pF%R AI|%4w61 ׀&j5 rB \a-Q 2р3i,Kg ̐PvGnJ+ d < ЦiXO"&L_2R"\0RL \3uw%&[W|\% q|RWR[fMa"sSهU&xF2Ow0g8]XGX$ vqg큊nǐCqGե$fX`=qEbP!t&WGuSA6"*K2 +@ 0 B*lk'Ui҆p.}^nP +Qs/8 !citlVf_$exoUeQ_'XЂ A  . Ze$FŗQi]xf"R%v*{$9 Y|q.f(ˇW7bd@f " q|+i)B0"(Ӈ@ 鏲IQS,eDם9Yy虞깞ٞF( Y[IOٟ:.xz:Ǡz%r[I1yvUaƏE4z[wW8` Xv$JH%z0,MؠaAF^'W )D$P3,fn6n(''?f^X}cWqJ4dhJ,677M1P '* @ +tn:3@hzBڨ:ZzکĨU4՝) H! P t}1 t.7OI`Us T]k"AMdg ( k[YuDqıcD'cԬZET11;70 .L` B]xBoe#BXJA0.r;V[PV~p Qa4͐ Ek T?)e S1 x0Y (40 4@k̋.p9 M;KZnf A\#R ۤ9X0PR @ZlјezW\DWFHewid< p% cnʫ3jÓZ!, 8?X H*L JHŋ3jȱǏ +>1bȓ(S\ɲK%MI͛8g$@ ϟ@ ) @̡H*]̦PJz(XjZU`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(NnUIraZ !ZMR][U1@_}E<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨅg z>*꫰*무j뭸뮼+k&l_;.F+Vkfv_6 k覫+k,l' CN' qLTl7NPzP1I@(CE$rd e+t30*Pd DnjC&"$ 9G"0&pxۻ8V.O-s!,WH*\ȰÆmN83jܸrIɓ(S%GYz p@.(ϟ@TM*T9(ѧPJ:0)8zB iRKٳcMeK<9U!`PR(h%ۨޒJY|Z˭P*^X/ws<˘G9s)~j)kםsvU2X 1Aݢy #$="x BصDVZ )VawSvw͓W(˟?}'?'-aR=LFbQqՠfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihl`j ޛ `z(C >Tmzkh8I]{Z8%HP1dGE:q4g< !pP n"tC `!P4_qO RlGvJgHMG P̵+=2EѱCx3:>.% aor'gbkl'D/V* 3n\YZ_ŒaF,1|Moj~ @Jl9 ‰  [HR$/FT, H}X,~u&l#QT48eEJBT lDR" D Q_.~z+ 'ca`!% \Ax? VW<)p5BoQ oXi]!y_wӡBe cU0`S) Đe@ '_Ki3laǕG Gw_mR&b@Y++UI7Wԑ:@-b0 }$gaSȊ0 p5IY@QIշ%tqpaW8s{",6p9  rUUigӸI}ӗrv.x)xְ~XX*ux([c~яdyFrsy;ZDVl8 eE:.~!`׊eQda!"q4vNfzL("?J]X}\^) ExeHFiau]X n` >C &!{X чa@B sEOPdx|r eQT7u1_ 7!y7 heQwwʗ"EmY(\d.JaEI2B+اA"X[“<a cZZ prjb@ F9J)Vz(A5ǠA:Zzګ: N?WMPAڪ5tq-Qsb1y!t!C"!=oD߇D8pDiH EŃFEܑ1|ڬLm*rIįA_g7iR|@׸sSD0Q@fD!zJD{-!xpffA!#[A'TPu#d+[z$qJP73B;D[F{HJL۴NPR;T[VkwА *aQd8%Ao>Z<Ѱ%B^T5B0a[7vXAJw}2}zNW8C1 :WY/Y( yt;1[{|4%Ktr`4* @QpWpHG1I  sy  I3 EըkkSԥhuc VWzR2[!! ~gkP8nvYS0].Xǿzxp o0l[E{LX`Qz04xK1@ G < qD ;t]LfogvbP $RX \J}'{.]wGE~rRz:w<ܦv(=q3Cepe E.4MAuK@ HGBog]͠cVV!B$28#MG,0@*Tu5.q#݁D$E DFoHqXR0cIY.y[dI !,W H,*\ȰÆ @3jܸp68Iɓ(S\ *Z4,ؑϟ@  L)YTʴӧP1sJjBI`bU`ÊKSHrTPRݻP'-"աspZ}9gfL*Ș3 ag3ZHn Sc.AIh6GMSr)Fd$(Dp%02bbb8<3l$ų aDQqpߒdUEoU%f*@ Ӯ c3.-ee[H8 M V8X ]>PP RbjH8E+H vҚ-i \0 n| UHפrˋ[Ǩ4'^V ۘ߬O`~E[Y1*3B8#~>߼D$1ݓ!-1G H.S `HR^$NTClqFnL (\ -s@8 3 f'4%A"[ BAP-B ǒ u4vJZM8:dh io59iG6EbakX`+A *`& r !ǗU!Kvt D|]FGp D/3P-p 2`0up eр~`RPlb AwQh&2 )/A w=w@m5uؒ^5/3t"q`h B90~A=Z#:w[!Bt{r^'axTkfCȈ~DeuRHPCiz2gLZ1ao=Y#fBMGьbdqQ $ By`{ 0vG"J0I)a`icaVe0e Jp&Q [b 11 N `LWpd0 Y'\XW1h['x s)AxC{,:o7Zz ڠ:ZzRt繡 ":$Z&z1'u3iyvZM;'/(’c#1c`>:Bg1fw A'&yX H ) IY-E 3HE9E#5h2p C,IY`%j7 91jkV4JY'S9*&"v!4+H%*P 􂢾W G,* ( ]X$j&zjdZ#uJZzȚʺڬ:ZzغPYNu7m&'^b ɨ`Gv1VucoF dxE 6C E8Ť") E 0-6j5[[ME-ÔW"\dh BOIFʀcja4}:RFi]t %cxB   #؁kNU3 zAV;#Z,Ùhjl۶npr;t[v{xz|;[ @cw`L렍TIV`^'!" v`L#Z@"1F5 Jv7gHOasۭ CfW38PZvx@&= NWHb  pmX 2e3 v1 i)@ P8pPr1r)t4PmbQ@ a@X e 1*͠ qtQ@'r@ DE\Aj)V0z PG 4KyV'J[2H)oNܾƼP' 2fz)J aR`_X07@ƃ`hG/ 9>P52IhdW0TI |Ţԫ3k,PÁɍz1i d|/Ǜɫ9X&ʙ1{|RuZwZi 1hS\[Q \!,8_h H*\ȰaA>ŋ3jȱǏ C9I(I\ɲ˗0cLr̛8sɳA> JQ<ʴӧP &]իXgʵׯ KٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfvm$ևt@b'%U)5-T#xVET"6 yV=iH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰ʞ)*뮼+k&6F+Vkf+iv+k覫Vk,l' 7G,Wlgw4)E#V4h/ dbV&أ*xU/ rJ=n-X;s!#JقW8^4)β+` M-X.H,OL] tBW;wcF!dM I-xA R]'aex,޸J#hAjȅcH! E7rn" VIt/HiQ XB"˜{SYr PB)I 8b ]^=㶷|F=J/j蟢\@@P@!`w) !,X8hiMO̮щҚKPLJP!,X8hg,mԘ؟&Ӊxyߑ!,8Xh H*\Ȱ #J!ŋ3 @Ǐ9 IFxeƕ+]rM1oɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KLːElR0 .!֘c˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhf qj0ZXlDЈtƕ1(FGIbi/D#Ae))AX48GA&)D,3hW  Q i#LSV<U+t#C Csy,IZhVՠY&ӡ;Xbfx1R"bJKhp"dǨBNb]@ *À*G&IW#maJPI ,' Q-벫8@ WAz JssJ,Wf;-V`jh >[> AJK* |K!,_ H*\ȰÇ#JH3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6x g8(!j`4fv ($h(,0(4h8<@)$b imL6PF)TViXf\v`Tdd鑒flp)tix|矀*蠄j衈&袌6裐F*餔Vj饘.J yAAj:**И' 4*NF:  " 6b H jk.TH&T! ,@tCi0@AM C  Pе!P+2o`P#=A춥R-X ԋrp0,l!0p,IRli #4p@@<\@|H?7 ,h@ǂ3G r+] "aqx$"@8YzhɅ4'w :=~ksB9P\ .jk9p>~P"[4Q+,y_yۦ>Œ; -HzNXѹpK-(7#P@{NC*1JK~@=KSY 'tB 2R!zD/ѐЯ?bA0 Cy±nt{ hAPISN6 d@$B2< J uc ^[{Q ͵΂{anT,pLH27 ZPUZB,1u)PvL]H uW┬hE !,X8,Ь˦ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx~kKob~Q- I.jIlI+Iid$  6HRٲ9N!, 8Hh H*Lg,Hb'Q JȱE H$ M\) bȕ0IfG6sRt @ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KLyL/r (!R*Օc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6>ivM PgTD-D)-!UL6 !@ "B~ 35"K F)KjP@MBA#]0 #d !E@a$%lE)1O,DipBPpfTAJ|D0EO0邟,$ $O9$T0G $!%D̘3BQ4#2*5C"w\vL>OÛ]ejjs/UUqE!,X8hk|MOͣЈҡKPLJPМ!, ?H*\ȰÇ#J8 3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿuQ 6F(Vhfv ($h(,0(4ӂ6樣My@)DiH&L6PF)eD0Xf\v`)dihlp)tix|矀*蠄jaሤ U@hUde4ji40駔**ꨓj;Qei @ᩭ+CCAlziPX D}4DPZ*QACL"Ҋ@4s!\ )zBp@) EcˆHb) R(rAQ-qD!U: Af@|TP0oɎJb@N@UnQAŖ!*,3 @:@"Q *D L0r ZJ@"D`U@  *tF?ͷQ$! K% 4|;2biAZ [n&lj^יGȧ+=yʮjᲯn*8``1HJW$rfr(u%M:"bw<딓9C,zv#2\|ς,\@I}32Z R99AqщjC bQJ .T c#wvcL8)=o 񃝍"tDDi~:d#5v@"!UъWTQ@!,X8 ¾,̭ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKn6 Ewg~C ,0gy ;P&{ OɆJ,ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OSn{h& 6F(Vhfvᇗ A(,0(42K !,088h H|‡#LҰċ ȱc =Ñ(4@.[ŒI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߬%>=ɔᣇ#X]b8P6̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ[PWU ;3` U* H.dR-A H(Q @54@EO\YPӅ J X$|6Tȋ`qPt!HP HA"RC@>R A!A4dP88ы>VX.]deCrq.ǐw!.\$ԣ~zhxbä6FZ(aYQRuXA4⦗!,088h H @#Jؤʼn3G ?PŇ#S ,yRK!_ʌ)͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿\rTچ*Mii`)À3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ}PȐ-o@ƅgA ,pc4PP4s\q!$ 0HJJPaaA]P "FYDLq b|QbJ B|d* ( 9 XIt(eMxқ aLi!+.9hB&eT  DZ&ؗ+B' ᠊ ~#.rТIa䲡DZ yx0.jjO&N+PF-KD'A ҮCC&aꏃ*A- 0AXJ$-#z'`YfGOy T1'嫭ƌ \RYRlr* (b-kɅY{5DP9\S@!,@8(h HXÇ0\3B@ǁ)~ȓ\i %˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷(Хωw,igʿzu.BM&J˸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKN=9 (dR QLu{"&A8p  ߩ>{Q_Bh!E"AQH_(1$(8 ߅Jh (pƇ~ /5D(RG/f7~2lR XE'CL#LD Ł' HJZӈP~d""3UfF  0.H [gキ4XhX|)K!,.1 H*\ȰÇ#JDċ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟Oo 0\_N, S& 6F(Vhfv ($h(,0j@1h8<@)DiH&PF)TViXf\v`)dihlp)tix|TBhQBX(C4h32:Ф"T鈕)3*ꨟi )x trD}jP̨4ʀ:8h`R^t)1%Q2"%/*'+5^r)C]0 # PpkK@A0Y:{I1"w3j ;'EVθ&]ކ Zr[nz bۮnǛeXkϸD"'Bp._(G$qġ`퇷20i$M@x D:1BA~ CkO(ġ!@T1?pT A }-C_LaeK@G M *` ѐ 6QJ&+k@~jW1Tж}/ ͐ oH `<ƒ  bQj(J&7v!ZwtHc f|#^\kĕ+!,L H*\ȰÇ~ŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_ϾgޣqA>JϿ(h& 6F(Vhfv+A(,0(4h8@)DiH&L6PF)TViXf\v`)dih&={_=4m|iP @'AWQ I g 0DA*@)+ nP.&b<#r4* :@Fp7`$Bg-b y O.%D1GH# kH@${Pf(F'ڟ 3 <^+w~`Ҳk@<3 n"2σ cÌ CICM\ɲHIL =ɳgƔ> *p!ѣH]ӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μ nEf3w@q7 +H\˟OϿ(h& 6F(Vhfv ($h(,0(4h8h u:)DiH&L6PF)TViDF]`Zv`)dihlp)tix|矀*蠄j衈&袌6RoV!,P8\H7(`(O.t0D+^dF5 1ɒO\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵkB,M  @La-N@Rۑm.1b˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞YKc e@!, HA*\ȰÇ# 0E/!;Iɓ(S\ɲ˗0cʜI2!͛/5Nԉs 2= JѣH*](ρIJJիXjJD`ÊKٳh zHFXB1fڻxwƾ |' ǐ#KN?!*K'CM2j/ժ=o!%53۸sM!Y4ȓ+_q*`w3سkM8cO<-ѼW i]W_h^C$F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihE f pKi'yd$)(Hx&EM4aQ**)P4Y.馜OHFJEꪬ꫰*무jX|8T~+rO|ŔZHgǚE T촨UfXY`]6.+ko|wtnl' 7|4Ixq%HV ,$l(2 -,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.nx|/8[Wn^lw砇.褗n騧ꬷǾrOJ{흲;.iܮۻ#J|6]r<.G/Wogw/-m/'p>[K?"oGb@ $`z@*Ѣ'H Z̠7z GH(L W0 gH8̡w@& (8.0*;%:PFPCW"h(-b`,H2hL6pH:x̣> IBL"F:򑐌$'IIO",HK&3< [ qߍl" =٤X(U"Y+̲R}Bn@SA#$ y+2 HX@(TEAXZxO.B4\A*#΁S_H AF #` k`P\/vqwvE#`ydiTӗ,z :Җ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZnTR>hExd"bL _|4*On1t(Ÿ @PW[ kEX܌*J"JJxu f3ˠlA)hgZ%gSֺlgKͭnw pKMr\REhBB~@9YMKa/9bHR5  6'#ܼ鹜g$mp*ҽe[3*ݿtt xX.tGi)@ T`yKs1B`Bp W-5%Q.Wl0q D!VnP$rI†ȬmZШ 88oJ ֲZ&e^VeֶWmsMY@y ' IIjݚ1'fBZ%BT`抅# D „ yC<ɷ!5,#AdžTs蹥h c K4`s{Lه K"'-gZҞz u`2_ZR $l[ œ܇Q$A 6A.vMh v^Y…64` E,vDГ) }g ~'W` 4?p /x'5 n@0@j[i4;q@ h ̗\w@*QE 0h P ^0P\֣0.**( wp Z L eʪAu   Хh uN``~ G2/( &@ W/ Y&v?y9@+`v[`LGAVlP Gpm@#b&PsˣH*90)D:pyy= 0Q (C' kyzKb'T`=e9`ʥ?wȱCYܲf)>Zf *zq` Syйa 0 H׻>㢻_Kjkqλ r g *t[b0p p_kx 0 =Јzi+>b'8n'lU)G1[\6u5vq,7jx pus*_g)|ex{v,r;z0•w+\ k=LoEJL^Y \8rШ[kġiz ,xP, 3`wLQ{Uu&Ld789lL4a7v}\ plU,<~<kl+$<1fg<uɉ$Gӏbζ vp Ѳ P @m,@'gjUϝEe`pMH2-2kqԗfW  piV &PRh Z l&[@ J қGvmzln j(c`D ϵ 0(d-4-  Iز-Ã)?4&HbỀ&ߜ Ag&jbaPߐ cuI@=W-yro7tRp(ft `a{+.Qq9qy挙Gt-Ar} sH, v+R R6RDƧMp] FZ_ga;Ft1s Tkpz0GM0.aСaBeVV})QĔ>JY-rl].?l9{Ɖ7"?畁\[ twA/0Q4ߐhuN wiH~_/L)eix&gIhQ/N-6oiSNF~/>UYٗydF .{lv ~F=W,0)o\f/(N"͎N |xfe-# d}Wtč7gdfM&gw &-?d$X A cCp880B Dp'2%ҐP]hBIDԑR!Ȩ)FZwFAHfF:tHK"<$df/~HX2˓g2%*JJJ$KCȑ%$BVNDڻi! t,%tx5@@L&LƎWD\Yd#$Hb#*lL*Vq! L u.ٹ钣%T$ᒧW72@ WP3IS`;}'?)f!K4S+bm0!#\>C@+"4PAPE"/$K( qp1DH<*#jP4K;(%B80Km@CH "b XIȄYKq҇aF9dJ!:&yqڠil@xdmi !qjy  ' "m4>a&:RA%f=F$P`#DK*X RfBA *"@yjt4">x'2gg[ѱZH[G%x'Q꽷ԁGS V 8VuT(`8y%(=>P2@֝*\dg'jfD[^1fp#f Y*^Ȗ7vh2n.o9FViUd EZ:8PN9ywh)DV`Bj"dSwT=7XekRKꌾ%$i|I - P*ġh0|}Ҟr^Z[nc&vvn3.-! aM0+, )U(CX=pIH ֺuD\I˸DDArGP+Q4B@tqQ D8PD" >IP º"5YJuؒ}DA*%$IO vpp}&S7{ LҔQ' ׂVQhD,H!ENwZJ5c疩E(X(d\ q p \u5q[@+1u.jzu_D$yuB-mk "@9}0gO rm/(?‰5JY10&dBe ˆ_䰓=q$qk->s\։?Pa)MD@ 5+ QP)a JW_G_Ɇ{ȃ/zuXTCA zhB Ba)T\ [@ܙ(ĥM$ \a-bSznA;p^&[$ȷ4 ^ U"`7E Y$sx*PV)Jڎ́``vS ش:t_U my+1-\{-\CMPl[H8煇# FqjQ=hegLљp($^B]ꃧ8qi;/B!UZE /7n2;+̟PY.sAdE;aӒ6!rpGOH? ܱkBHs")-83A!XB1(L Tγi?$4 ,A+0-+R#1/|? ¹z,P@e{aLDDCC@@?S0B,+,-|e+;0.4OD1?;#*8A^د=z!d R )|3?8L(KC>G՚Ě8d2L=>gA1PѾfʳtdIMAq8ULo#i1@5rqk0QHHHrȄHWS~=D\!TI6ɾbI<(Hh7ʑ+qRJ"mR+؄PT+qȢ|+ 1`|ȅCٸPʷG7\sH0ʣ*\:$Jʻʵdʕ{L1x72KI|\R +I\Hȼ\p'ȻLJ|LZJ`IT?S>%A5(E>=N2BuTT0=/UST S"TP UQUR-US=UVZ=-U5 QTemPT[!YUVUEQ`ecd%!SU=0 eVkVl _V/%UCa Ujl-W`UkeO5QeQ_e\VyTj=G,.PxW Xf=p ];5U1Xt]bWMr}W\=W^T؈-2 )YX-YEX[EUW<؞T֖M؊=VWؓUvVMYֆMמgX^mV0Y]VD=הm.@VteX2X5ZO٭ٌعbZz XœڃV] @ӧ[۞X{Zc}Y[KMREV-Z-Wu[oXXDqXB]\rm׾mZZX\\ [=%dM[E[ۼU[PCd\])=Z%%ٲUWUӜ=]ЅYmYd@]^^^^5ٸ!0`Н^=_M_]_m_?Hu____Zԑ_``.`SN`^`n`y}` ` ` >_0m```߫d.98Na^anaWh8]aahBmy(b!b"Z0-b&nb'~]$pЅb+b,bR-b/&T0c2.5c5^c6V`nc8c9_M:VNc>vV&m>.-;c!.B5b:]G+6UWK*f) ZdPWD Fnҩ/SkG䟐Ԃ4x Ѐ4[2MI(X<6Vfd0]ueXnP^e;{]Ue1MeŀeeD)%TuVETX>g%@M1wcB;P7SLiEF4g UмEX$ .'`#̲ 2]vC}>2ԕ>ɇx)` S13 62A-iMb>ޯm$gLVW6uFiggh+eåBXvh .pP"H&|~-vFԷVзx  IiI>J; V؉tl~e(gPh AV6hy67`dEH8R Htx lx$iHϓ8@va湜syu,Cp>Y fwt!wqd' fWsU.yؙ!,c=>=)qx7(yIXYDu&n/؂:a:ꕍ?8z02~s7IxqYsq (@hqsʚ/?(X'Iy(Nȥ;@ ˈt{&| I⫋؃ HI$ ˇ)#*6\@"RbzIQ &ݙB)(d qXP"Ł2f#7aʁ* (^؄{/+xq$p&I(+:ȒQ?eBQKAl%~3LbdBˇwjS'≰C+"L.a h >tĉ''Q Y Ì%!na d`1 Pv8Ij(i$*n-Z* $Y$mٻ vĉ `,6 Үpc4*8#'ߩw\U 8t=]E%\t6ܺwwo HYU 25q j;EO"x _,,`BMU &Q^5jwE1?WU"%hK3pE=8BNAg&dǁZ sł$FSRR !a˝ A@1לWʍA4?Tb܅dŹ)KE9AIE.$ܴQ(dJ9E"Pd ]B"a\БQD`QQitM.h *ҽhUqIwhH@fd\efZ qVQV@,'yeTXF8y 4't y( >:b`@&`mxZ()Š &D|0 +w}@m$HfJ-yuG;HZ?vB޹O<789 N`0ɖPIDPN/B#: Ɔ*d:x ZT4]ʔ R" ]ZTT\$ WDsh( C+@~ܩD`I;dnbɩ?@C:|*@bvZ z(_gN5jE(EqTe)QN߾ۑRvMY#I4Ƈs"vQi(p@`8!-r}-l@*9g܁ ĵ<;3a$r pX@x+NFe:S-N|w.A!(9DY`h GL(DpC@ v/5f'' ؀2hG<%xH H  aT@V %"'T\H@ " -Z0"3yE` /y`#^`;!D,'*a+f_B_TT&T֌r q"K(&Ґ_w Rٰ}e"L*:"hE;b0 !#Jd0z(h,c@ F'A::qf@f"%G!vq 8@7;%dTvI"d`e.ڎ<ʔ`_RRP s2/Jd2ȦKaDZ3yhK:OՕ<4,T!f"d#{] J3 @(5 #k\6_\H-|6[#kXB4n+,؃mmKvhdֳag)@49Driwr_ q_8]~t}"g{ /cS|1vG:zyȃF,"&CeLhP$ntÀ`C }H@~SgշE А|M( WtZ~a?{no^"?Wٟ=5 -D 2$B4)A#ZAB `,l@'A#J+@- rL)2%](  *D2L ՈA_9 v~!!,b!-T BAmdT F*.|ª@,B!H! A4Th]Ѐ"aab DaNA"@$ܧ9!.."//"n%@BT(t ``L1B%PaVW4DD'D 4X6bd 2$d@x(~D48*⩥L4"0A$B&B.dm 20fc"@6>,(/a @'tA@ *IDX Dc$-X$ À-w!',B Zd2c 螸 2$UVU^%Vf[-A!JE1_" TB& $(Z2\¹@%"(Hy\ e& 4 _%X@? b'%PEh%gvg~&hkAXahjXX&YqVsj9F1؆i2ٛMm:!}a^h'qq'J=-.%-"u^'vfvs[wi.A&mz'{gz'zp{~'!g"~'h@.}g'V^(.vM (A]{r$3.>: h6/̳ 88 \8xK>胸XH ÃA A@FEs9(pwV<5fABВɦfE(f\@SPnGhK$uqR7CGH08qd^lPoX7'0E@4d8rI T EDMd e\+T& lE:Nݏ#{WEZl;AP3DtdKR\(ԅ;@ވ0 @OuP8w\vtB6t>=|:3H7D77bxwx7 BuQ 0 ؉hHۍd, 0WA4YNIX Cn|N#t:] 4zBɅ3\H 4Gԋ'BSndI Lu(Bll'Ս9J<;poCTɕd qo),NO-W$̼K:63:t>z3mw:3As͢ @PobP8H$,Pڟ xQc Fy|}(MO-C˟ 0B$0߄@ qEyTlv[E@ dtvP!thug$Tޏ΢D y?C ](tТhTG|qѠRq8 WDR+1| *S0#bHR@C6p#6F>B=ЅsvFm2MLab Rj'Li˜OJm @L)6`$KE¾r ?`0@ \6JAzJSH "0ZC&fg\$_ q)75ߜsE?;?U_]v{ڷwРT%,?_~sl̘\]m$Ivu<ЀRP" 2p*( DXAD2.ixhL"7@E+l@#=!5 M@6Mon  J, a2%,IVyN< ˠvt&?c D: @ !&G6 F o@9YR)=Q# .+-h%`"-j!ʓ$0FQNIBCbP:FT4.(I$G@jr,RVq«6œ0lq\_8 bBZ IW+F(DD&xaX8? GP%F @ RtA.-ҺV @0hGW=a)HCD <@VuI5LM |ٻrNpM uf]I{rt[^/]v-=q Π .~#/{/7StU W?xc |6p]<9a[6\by_✁Ssdb f1yc.q0 AsQrĴrPxW)23bfN09XF\f, =s:rlʁM H%4C~H!%PQMEs:ӟƴ/jb|`h¢iaj fÞήGh[%%tt{io _+I~_g>_]mkbB ,8'we5]nsOE>:mޝ]=y oNxvFٶ qcVb{Jߟ^q2.IH}OrMzy{<#\/o1# O j@,y]m y7x=f\hOבtb`fTyA@G_z=9fdo^>zKH*̢i`Bݤ>e4y`t7uxrmn,Tt g'X 4&M6@w1ut0É^Waqz.=g@% Qg"="y!qg'g ор#}:?jz`?`THtAb5B *OF>r4 F@*ZaxHX I!b-$ZT0OV@t p" R Dt e"| P Nx1H  t "p#(atP@ @ DB £ =OM S@ j!(p@,֡a   Շ΂Ԁ"@& @}xn B?Fep0mP)0e4E10$I)t` r@P,ʀ& d q!&`nQq o!NI%q0'@(IHpơp    @Dj`$XD 0m+ F3s42ŀ0(0[S01w86 0QܲA~ !36h.#@#<2T1GJ 0 -}0>2-3=pll@.!2?2$肐St3# Σ23MS=8šj* O@'SB;;'t|˺L,D"e3' RRj@ AJ:`4V"a/FI0+jTA]FB !(@!)8@dVBM2rKB!`+sBTDAxIE7 x n7֠x -*.?Ld8 EM[73є|$ F3 `6m÷J`$t"AF#! >5S4QStE3,TNt9΀Udf5]J L3$T Ub):0P߁W4p2t;bM?g* ށ9t:T L耑2P5%y@EYO\&T / u`ŵ$ aDՋ3fCfkR$ #B!TOQ anihh` ^e)&*BN"$(” Y-ca[ :>W;x n u 6>ޠE [o5ڑ"w`H@G\0 r$w( , glK_*aNvKiAiG2 By{oaN@  @/`nnXpR 4~'Xf8U3>eCwNx.8P @!X3#w~JA1X\ae XZ@~8G┋0Ř<, (OC}Xm7g&`w,uW󔭠3A#Du@Huk@}a"3"BC?Cb  oz*b"x,x"ACnQ@*CȩOڜ+y2czj4:QFn\SvXdIY ԄF_ Kt4`MdLMH˔Md4$d$$O,3WFZfQ&@>g1ທWEaP-_J@`pNPO&r۾{ O[Y|eYR{Rl[cRzq[@ \S܏StO2\N`BV\IZ\R~$RYhRZ|j{TFzO>gҭ)FmF`VA,;NEorܟCk/Ʀ:.=nDRUCJ>UnĈ\wW.ìapM,|y =H[MzPқNtj]$OX8}\a>&<撽г诼^h}}}Օp^]]hP!h6tbҟHbX 6 <Ȉ@~AKnlұ&>ȆH虢 ±msuu uX|$mLbfgZI`A3 :\Pp JK ;i^ ӱ塗In_H&P_ ReiƢiz;Hv x0Fkf6x‰TJ@ɠVӝT^7 p`0b6l!Ÿ]](Ƶ7R51c I:ڐ`"[8q EbUHpU812,SСD=4ҥL ԩԩTZ3kF, ) eU] .l"`1\pRْGU e,_VZ'&N^~%A8_euG iv/|蒱uP$EIp奥PGJj*Y@#W*z$ej*@ lJcVX6;lN#Pк l8bkI鱺 UKobk4ﹿ諿N]ڋp /l*3 / l`IqRhYj2w[[ s2'jqt6L{оK.LtF+[rT"oo[/ytzZw vb#41a-qpA 5Ҁ R۰g xd-`*(%Vuj.+8xwMSk#ZNkܯή3yE_~֪K=nm{^q}Yj/-X;>qvT=Hk<ߋrk|lO,Z+?ԉSۗU.rS(~!heҝ(HzT_;i֪i0wO@f(i&`PX:QSU6oQz!GD(,"%iș?,)Ѐ1\E$=aFDeHa4P |)k./b2LZiYibV4ÍcuiFl$):1h8B u44eEP-]*V@$ 8q^@ېRU@1HS8@bd.V%{Q'b]c 8% &,bdusR &O)IwAL<4b(Ž Py C AD$⋧P(qL"`E0qB `$$IjR ytP8(i ʹHȈ1D~F+N/|s"v[J"@JX@A$8A1R |&_cV0D _=E!p@ *|a 9ZW174 j8"aDT ,Z'3WPX Ff1(ÉF;-z@cњ2(160(i\{dB: 2!6i+cպ>\.h khQWLB+"@W7[XC j1 \4&$qLdM%j S* '8SC d(  `G<ڀ~D7 8aJ|cPGR8h4"3zDZDB՜A| To0_P* fX@j7 kRH9(*\ @5`L֠8J#ȞD*|jHaq!BCtt L"@@0PC NN#Xxo2q/ t u!X k05sY@6ybBFIsxld8H`xSaXtvh2#ڳ=n @ D  QzqE#P0P}X@A)#zJЀ,{1SY1uCF1f:'jPsa DsC[,8h$\0`>< ji衐 0Ld`@)屇QN! ',w@c0&ђ3lP+5|%OyhA'`~tCC 0V) R`5N@pRm'c^"FHq a tcPp3``0Њ8@nw%(tѐ *LP r}V0 в׏ PPw(=>p @8f#NI3+mExw  s pfICi 3^T6RԖPgZ .i OIi5AyM=xIzEe14L2ICG  &$ ֖8) EJI I y݁`]y P E0unspiPSPP(` Z"`){P` GlpaH @.FT p@ Кx@20/0w poy| v3w` /?j%I^WK`( ɆdfOY!r v1 :j&ldvQI/ty6(9-UǟSxr) Ѣ jx 3*+j6>:Nz7Q9 rp0lEcv``_/Y.*qqZ$@%o~"'h2p>1q5}E ҍj) 9D{"_"(: 8 HTbtzys(&O`%gO '%`2~&Z%Zy!rwz S(>!:Sr&>rP`NګpPI(\z&&Qb&*z(NJ1%r'7. =l&zLb@(m &Z+[ANpb2I#E0T'>*;,;b'3 -k! T+u+{);1˲>\d@;2KO;=5J=?R=&0-Z;CKJX-bHCӣAt4P˶-S 0̵=^KB˵SEcFeT+0SZGv"K!4 T&3A?CӶ+q!,IQ2 ]8ٺ,[  (bs@UPeP"tYQE0`COOϫSraJBIy 8Qz LRA()YeUF`5 a<}'˾Ia (PpE<g] q [ +K 4p-LT%a'Ay41E'i e XDIa _P U0\# TA4E]!Q"G Nl&[+2~Qvq0 ߋ0 !QJ S0t 6O.(h@m̳(J&N@pb Dp'Qȭ \ `M`Qq!>9p֔o` p3ri& AdRLT'|YLD[ջ^q4! |K0l"Zw>R]@= P JgR[tΕ'EvF P  a!\ pE !"|UB&7((sti#8R  K0 *'uFؚм3k;?q:[ (Ҏ I`HX!@ PYUpf00 ap 2@ yqi55jR FQa !$: WP␑\"VPya/I7qP,ѐ&da ֦P  B B\^Sg۹N-%r W𽈱`T"h}϶!& aҞ@` bP 0W~ՅD% 0-L ̐p% P \  8 @84x5Kq֭9 5KAP  ^(ޥhԼ`B7-I>1-m"1 r +LZ`F' Kc A, l.О*#uޤ&P \~`#胰+q9|`Rj>B ٫䧎!:gbo?Haغ$аsM?%:%bҚQ܌-,zv) @^1O&Ԭժj$A{QJUS;DקP䷅+D~k Fa++ \^}2O~BW<@/຺G;<J /C34$@(*+S#/[=X37GB>@W7?3o22ݣD+?GlN_^q`QV{d)nYq(d> T305U;4Q#e~]>r#D @ j577s66W7/m?sbD٭uoHOyl=+/$?:jD)?4͟2B"ʏoݏۼoS+' U@On'q"auZ *> u5SCz>0;@0zob Mpu0PA?H[E!,`AT8a!†*$@'V8"LjCZI$H(SP˗aʜI͛8sg42 c2s.LDO:,c+. #hI<KٳhӪ]˶[84(":SfI8$+D\t"r*hHјdzrD1oCMgiЧSmJ¢c,iƔm捞 hE-cG-v05 )D4t<'@0@ϜЌ-y1 04D(R B4R!!/#k?)DiV#R- 4 $rH]Eb-Džt5ԆS'r /,WBD%ABѣ|I*h3TAK%%tB=c8& 9 %X"JH@"АElŸBVQ 8譸뮼N4C[AP-x:A\21%hSb!]qѠ %9:tFBkk:G r ,!v 8Q i 2\& ) 9"TI {-[ܩX@4l00M +y)0'\:",N8!K.@ 4^@[Tvdm>*E-L. -tmF$ݫk럄n c{%xGS'иQxԸ AP3L̤dn_0X˘/w@+$('Q\bJZP%H4|Is0E aY\:Ji_ J Pid&?A f )l)( Z7\"lA y 2x m? g6B"81*Ĉ@M\ 05a}XQ"PV@E2 -Q  Nj$ H hc <@g "$#H-PBX &IɶLPtA5& G2p@@RAA LcY<? z`(y]8D01`UG<Ҁ~G (8 |C QyU @3@)Z %CD%W 3tB$Ʊd) L>`laO g t/djANp"c '^A(.0Owt`@jsij1@9J]Vq30@> b4%dh9A'k|"xQPs!0E_';F `x",0Q2Ù,v dC q 5P+j0#>I\i8(! @Tb(\#x.Q6(D †|`hZP ` X&3|.|`*@uԪ@Mzg!e@@4jp"4xe^ͧX!h=<0`m*`@0-ٌ^0 ptWm}ń:mG%^;ءRa2 1 tPаQ(![M * S\.s= d4q&Ё\̕*a+WQMRSD|Iu( 2 PK@C rg!{85&{cBSy XAE NT(pׄH]gK@|p2 :TpZ*4CMC5֍`a a9hE`XT}҆~(,= *}칪n3$= Q9Hj=P~.)2AQ@/ Ui{ ^0A{#gJ JD~ 6 4t$ \:>X1CvvaE܌nnik,j&x1dv*d@5'1ܕ c3gV.2LɫAjUaQ,㥙Za JHk_jw8@#SO`+ ؃$ g- |HC~P#p5Q{@ V 5vB4wB3w |!7{"8=:'1:AW`!X8 q @) ɕ^&2 5)sAa@ 10 &UP0 A,β N@ (P*a fyt%*8@ b\ٕiZ !r0 ),UBP  \ҍPF `YQ!UR2! " Bƹ<& &i." #R󛝐9 ) ~v@ S  G"1=*9HQvU$g|V6G1D7{UsOBAc4I'Iw!%!zF`#B*j&7CLz7X8' M:T*ScUZ[Jڥ`bS ?cj:r@趦pa!xwr0_*zz$ -62᥎J\:zکR*Z#yq`Qzs:Z>ZhZe1 <1%0m 9!jeX/8ZPxìQАӺ ત2@*1qW<Ь"ȃ&QdȬnGv9J0!Z4Z 0#@v,0TxdXc{ I= H@CMXcwQf01z*N;\̓'ZF?u1 BS8N8f Zzc|M@/` $ʶ#m/p 1 < $16Iu{êo; 0@Pb0 QECTēPp;80ԝBX034epC8RjV`PNEPW@;Hy`[~w/Zppi` 5'qaw0HR8pYF En+ttoC4N5p( whp:C)M˱ ZHh4^eͣ \?DUn-C5r]@T-gP T~  . ]bNՎeNozۍ2G`0А$Gy N'w\~@ e"[hmQ-(KDN]"@Ұ5^Os`^^bZm0A ͞^/ Pߜ 5?Ж) ٠#K9 Vu\PH@V,a0d| L`f9z<&gAz6E/_DeiV $dSk1OB@-Zq_l tv[v1evoXd=rp=f?g0j`gI0T/>g _f?r%z%=Z=6gub+6yg78&b.V6_e_e`uֱn3 DEbܹ]S93jP4aD.cL 8Ћ f,Of0? O?aOK$@:+a:cG_"& L`ޅ I'iA^cs֔!N&gqՁUq`q^5uDJ 3gP(+$`u |&Ԓ1$c|Y Gqb]","_hYY2yyi?'L,YТ  yyh* P`%kjkŅUœ0v)5F{%UTyvtulؑj*B Zly)S $[7꩚ ^Q[[H a %Uvy<6 CX:2d*l?& EE?1)!T+Ųv\j?J,AQl-veAMtKWpHEWL^^!4Xx$:0S/4u@ic_ z|L\ F u Q H==(%*`JaWMHf?8Y S(xMډC 0lC^GCLB8 Z`i Xz(њ%Y+8`~'Gn6N $C%`.DqaPĨ@48)b*d({!"&s ga^lNrD/uy*@Npd/P-sb֑3Ba)PaRF&ǔYo"HC:T(\~Ö'-@M?ﭠ p!P6$ylMLh&`TG(6 "9L9z7@eL[ yo%g04~= 0|+NPh"lT4Y hx(&VXnnL%24! hh 9-z#} 0ن]ҘI` k1?1 `AtiXӚg61=\:b<eEL.A)R`HD1p\'Ħ"PM'? u#hIQ5OةWj".GTG҈# Uڑt!@+xE>rqtyKzTtx aPYNiКbȂc pp B}J (V=sIږѣV{܉&W].S(뵕\*s- 5ZW 厊e1榗etƘZx\W~x*J"`27{ ^Gp2 @." m_+fjK 20[|cs]qQl\XKLYKfrka'YS֋|ecqhYc&s|f4W[ٜf8ar 7ٽl<́&t }hD'Z-2HYK9giz5)]W"Vfu]jXZwZlֻ{k`>.(1b'[6lV+Ц}mlg;v&͙dfwn4!h}o|[-龭o|'x '\ gxp,Cx-~q!]=q<"d\+/x$M\3G8]] hưy}sgwZzэ~t' Ek757kGH纒>^S{vjzۻMrw;Kir@{w[5oz / 3 (8|5 ._F! 4PzJnJ10@),pUW  ]:߳ Y{Fk}11cM B)4_gϷ 6 PF;.Jh2H?Hؐ@-kY06 =6Ki 1Ѕ3U4 D?9 1$ /b`)t9:`<^@s@#*"R೮d {)B*ľe Q+@ ?6(U;#؋td+?\ 3IXH@ c4` ~ȃ<2Eؗ8@H+@4ȃxRi+ >E &%Xɍ Z@CIY0`BXysMHIĂXLZ$Fkl7t+6i=VC7ЄPЂȩFO IPuTF[W @D`,Fȍ;eThnl`HbG~d t}T ]S(PjxgY;ӪNmC-0q@=0S.TIUM6XSeQ?Jbh'xP) [-ȢtGHH8v@U& 8Պ]9sڃHP9j xBHx9A8=H& 5f'EZ%h @x%BjIJ؂' yp֘CǑ X7z 0 # Y @VeOg؈25sde] َ sS8UYKWY+P+? w`UX݅%!֑Hi4 % GZX95(@]) lAل_ɝ h܉YdL3.Qٖb֝`ݑH&]ܪ(ݣ]n; 0M[p95 X -3`w_X`y^T.3K- c iUL 70 DQٓ 6^@Q"^FHSC_^?: #Y (u,B3lJ1`ڕM\XE,pTɍМp$F\5 aP 6BN*I9*=BU@b^PY!,{aޑXE* @ 0e KK^ơ`XTB_#ʨ/ؘJy1OB p jNkcN@c> _jt|B{_|Ʒ8 fЪo|ʯ|3\8E" {}ѷ6\ b,PV=,JEhkDF%(KH_ׄH0[EZIW43=>}~_VDx[]k 1 MH]HXN؅ -t A T)N&cYciɕ >!2P6I[CQ dCH8dMh@@ $P%]/nxoʗ#aEE&,YE7Rܽka;t0s X+PFJ CJ Ŷ%-P~K#~@IfA}'sz!!8"%X $ZL*+A(QR ]b A/bЍM" t]%# v.7@ v JRId^^&&IMw'}iU "E H#h^&\C B)2xJBQ>A$2CTD0}|~۟{,*S \ A#M h@lemR(Ǩ Gb&"`;Uز묾IL|0 +TʙgA]0K;4CI1`JAزh@a\2`{N \vYkJcߝNjObԁ,87[( q" 1,-k(El~wm~%W\U 3-;^K2m7mBmtAI#&vȇ|>=CMfn{=G/>?Ƀ>? ?') vl~+0P A gh ((aG8@Ɋ p*3Q4AXw7a X"0$D0‹;\" IE'YCՀR$7ІPKRLQ(Oq%&m< Z2EncSP厈0wQc.,x&+GE bx%lLaZ0IA?YtrV8愙a U r4Fo:@,A` -ЉRphtVq&^xK&,x'DpH/XlB,@x х/ ?Jyaall#l&$䮠9@A 1JHv:1҉&Q:Dx͑QŢPPO۸bBʃ4$"ީ"De%5Zԛ  ,#hC$h `%\5@ F*-955/( G!ߠ:%c@O\'&YX'f Yo"(D tb @5&,30@-@V+.;79 0B;DPɉW?ρ@W - N[έ8kBaYϼa.qs6B0s~Nq "u'q˶@ %, M> !&l[+hApF.Πr qF *e t_֢ [23nBnp(ց\x4xSY o}83A_tvMp`@u-o7p Np@_LOeF0\@z& 2 ػ0 04'NpзC쎭\k`DՇ칃\TՁ4VZQZY5S\ Yv0X(L3 qkՉ B[_ )! FP]Y5,. G] *`-A*y]5p| F`2*D!XA;TAgðVq(:hC:tHyM4/uM/0MDu1'SG\0:dښe ^}I7 D d=_uy4p>Oh-Pɕ r p!A +iQ[!|('TpD!齔))N>t)/viћfP4h Li#h#DˣKiQjDˢișD)j $Ó ^i$Hj~PR麔Kj긔10l(a lI*rj˗>Aۥ+C.T(*@mIppB \M*Ғ&rE"r(f=Ă,]ڐdB+F -6E:,P㝨KfKƮl,P% l,,\1B0K'͏# &mS$2@ Ag&H"擓DKa.(FR56҂dEayL@'(A`]쑆Xą'Z*<]Ff) 0,CX*Ej IBI=N B/7SԂGu؇ -~^RM*IlR,~GOG\IGdItXvH-at\1E#IB("Aw̓@0ԁ@B'v@6 X~tA*X^&oIʯUK.KH}XI@G&^ȊDt_AS Ct " $@1pÅ"@'L0 GrЩJ.@ H#B""21'qf.LYnǽBjA  +2wO 6)8;.YЖ;VEԳ:3<:^!#-?4AA4B'@+4C5DOhD%EgF'LT@߼ @9MXٔD@ؒG}d0~M*t܄M5LelP+ AX+@N餁=S{RSh<5(|P B5A $Q4 [CN\p3G5_cSLY`o^2bQ,-c?6d;6sbGe/egfUf^I6e{m0^ ,4 ry6(k?3H cPLjPopKkԂ.qg t66a`@r+l7f/&y\k62P!(_oHP}qr}~I| B\2dRXՉ% /GŁR'10MS~9k'OC~h'Tfĺs EV;,hx"'aa@!23ѣ,CHb5} @7NT : 6y;`YeJ5\!Sl,f(OmVg'- #%ucaA˭-gPUEMUQTWVGP;*>C6Oyfh/w:Y "B5Ce:ZSXwzzD"`3[_ynJKA>tpa 2P<|g5`َ~CݓZN`:6B1,p̡T`^ ,P!0D,KȄPDĠTADD JI(юJ<+NI 9d6>a5itm$f[AG 5L!2!mcN8Bv(VJ ^ZyKL)"E=l9lGb* *\rFK1+fXHNȁs $i`3(FCH!bŎ()KR UHā萇&IĠ G2a [E,:bBc@<AT@YB,f+٪RHA-(GQIRi j,j Z,r'@Yޒl,p0ֹtJWT = 6[.,i[|fJ_ &/*v 6+ySiO`?N( >!VauFfe*}Qbp-\bi &n!!*(saC§Eb h,"7N2Ilbt`nUc\*Ol؀+9`M|jdٌ69ql5Mg?Ёi1 hx&F?ґ"j7̀"*ma9TϝFL@iSKОYU40 ĐZ8&ix@@0"u Ȁㅑ5-(m&U9y,# ޓK4"Og #ޝK\|Ё Z/I`PT!⺊]3 TrJx~wwՅxLiϟWweJR$nzȪ=q "ZU"M tSkfG 3,2>Rwpn|,\bVB:C@ O@el NBylL5:`&D`G26Tvc2`:A"Ǣa #Buz@2 , C^FgtEPzGhxҡ[_ar LV@I).b0eBd%-fީa4r6V[UxL)cvcLiEgBtx@ 6Fb$DAI GUAĚ86WȾުgG V!Ram?K+Ia*^'A[#ta|8w&sr5@4fc8%(2 (A 2!O~P'!^``~A6/xD A"+ jwzP`5J"pU.?:xfZ@ yd? `&!AV\dFx|,@TZ@CqYB`": 6(66L I Ɓ9*8nx("t,=A7Eob`CmA3)dh8B,vYz)bZTP7X0#7AjG*.A?UZRr_pEk2 H`) "c$“8ELAfwaᲠ@*6!pPde;Yb_jATj1Q-foD¡t >LB H@0W^1` g3(6]R'~N8ni#rV#me@3 6ۃx F!d6jD3%[r aβFeG3Z$?)%Zh)&j-VO+q2!d xt(44Vnd 0| r2{h h@McMҀ& "6K|Y84&YH- xg>`}L@H/ S^andZBkΡsB ĠypaoAyoJ&UN *`'xg,gpp`K&^p w`INQh "`<_E=%!@B\)&j!z< GKP  4!Ei=)4vQP_9<'N?.6)&f*Wt@3RaXA@;4XAV bhy =4I@_ U$pP"dM AIƠȔ$v*ɟHJ) z TtD);I6W)5fʺR)ri9, +]^9 @0E<ҡxyA$[0/ѫd8W$I6 S "$"+W>a]AGa&=ڕە]}+y5+˻` c}+zʿ l,읫:D=DˈæJ=CG"bJ"6f :Lt׸QFp*k$(ދj qJaui|+>a}45^(7_k](`ڕƔ')^~ֱlپ>^ݞ~1Sn Mc0S^Mo"D;0/K r8Rc! P`9xS-\! :`~'??v6 LEMf"ʠFFb ِ\!7"r5W0V4Q qXt/ `A4 |-.XF&&R(Y@3k bV-x$ Hn:ř$rHP3*!a"a)!cفT/N04֢\dkWcɚXljӺ}۶ ܹaڽ7o]| xo`btpckB#ZSiK2]: 85ȐаLaY Aތ"%c /M SVkP ⬲ DQ`n6J]<N%:edI|2+̚ Q r12I 3HBbD4Dh' ࠒ?'? Ph }eIICb@ Gi v"+aXZm`XrHlbUi04QV:J`0&+ŀji N:ͼ60$Hn[8lvYϮ/f-N(:z13`퐅~"h/A R^V~Ҩc]AAfx 2PT"H'd_"wvIHyG"c/iſC)]Wy ɫ" M0X;ZFw HXG -LItB82 ;fPr`@+HQrR@!h/4KJ'4@!q `n2 Ff*ܟKID .$3C W F`υ(3--u- I"P>>VHGD-jEp&+,0 : R u.@T̠<ؚXl k,7+q-jPwEL\3^ ,,E&5"r 5` ldBը."d:gL'p8tP#;B<+@.n8 1A>n]p pOI7ҟ#7x1m96W 8ǓA]@@bΠC G*/a ɹ樣; TP)pd&`CnPP Ce Q _&I/0ڰ eZjq!A6 : p s` Y> xP^p^a4j05+x(-հ -4z9r~ I ,N* 7*G*ULlJq R"x@ Bwy 1 |" I&x&-p %#`7p!!Ұ```6@pa\( 0yv `I}H `i1T*yЭg!!+OF]'w7-ˮzp{xٔzpG)P0 [vpWT ^z Yche!HT~%ɂ̺ PpA `X /0a9zyGBQ]}Fˊ>J!E` 6 '+=F 3BH9 FP B5".FoDN0Pȋ1ʼlλҫ #`{TJEW$y(F7jKD+J$$HcFF[{ûEb{\{1LklaDvI@ VrGq*phԺcP $[`®…#F< 'Eu[H8ë!:_B@/S{şucVDSz;E"I`S[!כ0Z0fk am!k}Ii uCs,ȁlȇTL@Ubb<#xaY\Ȝ![xșVb kAw e+3".bQH T>$- B@L D=+?|[A'Aѥ|]tA&#@ul!36+d !iC3c 7 T O^THqE|ľ^!a = #^kʔg֝Dä"8RE 0D wyt~TJ$Q1aOD E!_W\0SʀĪbwD\ReVK4mob;r_KdQbUV8hP h :D4\TSLPHe@M=}TPETDfly(F8$ cjȆ"q)*@@,0>U Qatb6(/^ rۍir[?Рpj@%&& cn)7\zP7R)W#b-Ro+ 0<bl)%pH X 0"l/wDuAJ,( ⊖V¹5?4PAc٬X9vH2jP1 Z2 BH $LTdu^..b&c&XEiBx&~ĉG gqVpæyȠ#.!BL1K̕F*hޠ՗pd4Wi Jf}%p{PZ$hǐ9 uRpqH 9 XL= "aȗV귚9! )B9#Ebfx.bzD 27…Gyx Z xu!+7lj+ /:w2dB;oM5IbG}@Q6/YGd$g+Ə(t>(VP~QEERo[ y= ɽ!@‘8C7+8C p0ר V.WAwqxP8jPMrA%BKMx :=^|U1P q?#|@a|. tpĉR(ܟ(3,fvfGB!ҰD7*W"FL"p@pb+_pO!KGљgdD&?&aM.X4-h5XJqC?eD :\ "4CA74"\a~52r}$PYyX%BNSr /г04B@!2h$H4Q@(  DCi@Ц؂I]Lwȡ*&PsB?J/ID Ii: 2D%:QVOAlA(5I6zqH!64g@TQ Hb.1)C< ˪D'|CԝF-Yc!5^"< dJxre-:*Da"h:WDqlqnP_)$w!i0 r?c`BaC/BbV݂ [8;ܡ.-9ׄ|e,[Z!0U[J}@Ұ-[2@#`|WkWRH|K[`U@-+v׻U8I`D 6Y) rȈ|<%U"$񈝤"@G%TkGTp@&$X?BeVZe b@x]T ^6* +r302e`U;d0ͱ"P0H2e/2gb E}R%l `/T#($tchsp΍)KPnr!ݕW ׆ qG.ycw' WZ'ﮀaī̼q Wyɱl|vcemZz>tB' WBy9:҉ob[leuw_yޱ(9{Ml.uf:>wCmiBU&x>x^/LD9>A3„0d$ |/xw`g<`aG} RSVIAJ)J !b,?轸ĖA" nj~|?AYHDb7ZpH4B0?C$b܉%*lgn XX>0(P+c Ю0aߛ!@x &_p?@< ;(H=0$7Fԣ&t' SЅ8[FZ @@ #f@?9=ɸ 88UXb-a&h8BVЂ</2<×(4\#"[PگY4 ?: B\J ($ % XP90' >T4ąna9Dh;_ 4 d`ejkL;0́^P*CPA> p 8腺9X.-H0b^9"Z7E"XUX8sDsƑ T@KJ0Z8 )JV [ #@HYdex?)T7|Rjp H7I&ƫJ(RƃQ Vqli QU8PEー0IQGZx?H[% " :IJȁ'87HH(XKIpKQ8TԈUh̚Z',-RH`ɨEɇh##D- (0XKLj`x>r΄8넾RhIȂr·\]KX7v)0 (!$DI`PL 8pQ3{L)jςH?H MpJ0LP(j0$R̂dEPKY<0'0jOD`FB7$1 iJ(+p P8ЃfH(OD:,ž=,u/ >BS`1]K0bȅ-9EV(D PAAP2н:0bD=Ȁ"lG&J EBWG,Sti sDhIR?`FM)_J1`Hx7#0ac[98 'pU[ˑkk5TpEj5f\ԃ6(rrVpDs `8M( "`EX(MXЫFE> -hP\Riк@Bl!pP؅ُ YB+ٽ9Y OX'ّٜٝٛʞڠY%ڢ5ڣ 8EڥeڦP. ZZ]WKӼڮگeXP]ڃ۳-[UL˵۸*jǥۻ5[(۾[ܿ%5E]K[u\ ȝܾ[ ܺ J\- fϵ=Hٯ\6̅GئbTe ULOKS 7[ xA }.puX"]+]1ȓ= :]@ 95 )^b⇰awMd ] ` QB4E :_ؒP^".+_Yxa!5yp)Nz&`L考 k^/yx`4Ph ˖L y7酀(SE.@~ XIlB^hdpB0( 1)ȗq@sh(4ff3@XJh"%Nxf?!-8Supi~p+h>i\}h!醖Wd&h|3hX[ +Cfꎞuઙ Qa6Rj"5 ^ WPpƒ赌&r+e z6P#hh١fxH1p hfz2AY.7^F c ɡ! "o8o:QhPx8O &3؃UcW;YhnY:űɀj8pp)Xo.f>bep)"!< ^ zJVo~!.;bwiƈo#gq2X?> (o9΂WN"ikl ^'1#*铒׃1 5D>\h #$`DZ$fGj L҃t)XҠWUkb*2:6JWk8gIٖT_2#x#r#%$B3r w~_tgJ.V L0ahvK)x2(u"P*n_X4qqs^J%}Vj/.o:Ű9wt'&%Z}npjw P`SO}92xOXGfp`ƚ")ư@ N@`0|Sv@Ο '*ˮB:<t_xEwؚ,h*8v(EPG+J-2z钭7h+3بr'.K8g oղ.7ځp,x*AFO/-`$IɒQ}3"O'_fy%Q+JK0~[D'7"Vp|p %vHWY9i2x{}OqQZ1}VA?`|'H"Cj=CE'<-"Fv/0D %#e! (;d2KJAa #o>u >~9rgzDʋOHͪ Dh׎1BA)S$d!Id%Nkx;b ɪĆvm2@ i{,b=\<(,Dͯ'ĸN \Q[3X0F,6VF,N4z6XXQ-=`9\DaOPhsy-8v|E5cyo voɢPkq!Zw\ƙfu1 x@Q/3X7☣;D70w̐YcH@q|"PF'1qeZ%?L3DcNCHc$D w61%@!% Arx{~%7%$z)2>y$nڠU.zgUi壁E*鋰楨w.D΅լA^ *1ƭ0tH J𫷔i}jivbi夾Ik08N*slsˌK%1ic fߕ4핋j\4ikb3&7o3$&!s3QP!'=)0Dg=n!R) Ѓ"4c JQBV]<7яVTEIjғ4*5Jі42)MkY2 .өxR4HdȧA҈i@)yZ850LL2PA^$C 2=@ ?eH@谆5 5O8% 2 K;{,gִ0E7QjJBb0;@ @",<%-B0+LB0YKo5; 3P/_,06P@8w8 | gm3P <W))@haHR%1dێh@ H_e9ѥbd9u䋆 6w 8C !F`㵖#%-WQXA^*0N"dY{ZLA2j'ϪrL9<9 a+P AZp4T15HJđM:RL%EUfh4WQg!LT#jT `8G@TcѸMۛ$9 HY6mTz^ `%9oXu݊8;2آ0 H50B;8H`Mڢ籆3C$IbɤQq ^ L1 Hw:sFрWh!{'Юx$sSZ2Ȑ3%XQPw260Fswp'<rH\J@;H pNdep8 \LfAsVr{2 Q8:B WbM Tq\L J 9 gC^'A"Y14LBd L\!谅p} Cy )&Yr V]ۡAxy>."l< "F`,΀WxpF>-&LB޳~5J'A;Xp@qɁ|4,kE,D ei8KM@/C< KUL$XߐE/23(F,A#|NG F)GGbܢ/ G8@l¹:@@6b; ņ:F/E3gA @f,DX$2 IP  O \ sjM9K꽘8*)r+bZ$ivkF+IRQ빢k&&kk*F +X˽Ff6bʫ1 _U eM#XX¾Wh@Lw,sBŐOZ l̓h,~k܆\Fb tQD@A3k h1dBcȓ(^FDCfmBz|M"l\z"Q/ӎ̯u[ٺ 4a[-cQ,ʊlØ׈|͖ɜ$Xt-0 q6&"h!|U L,BLu$ jW"DUC\!8+֍-VId-0>og2@\"¦D A>bMkQ @F$Bŏ4t+L*&jBh@A9&t"l  DAsD."V $3/ B1`B"O |i"X`mGYB$ېy248ڢA&YZ\ O$C$ JIُp`ˍ=ZcVt8\G12-o4) 2,W/AZ;(-3EX@ A0@AâMsuIHU 0½!٤@( |M](+/;$33(AP0!!C0Z ; V()[\ЕLO8C:>( 29hCO R"/0(5wT>8<_щ< 4مueD Kw, (V[uHT@:^*V H:,=2#$:s;Wej[}v3`@'Vvg50{L]4\AؑYdbB1(B5 BE8$z.$80ݢ8 wm!@JovSD Չu\"u޽' tafah <]hC7>7,78!;>|8XWSu@d5Wk]YΏ<[\,C\M碀X/Č7ZL#Hݚ!d^B2<>dU ,{V/RCmW\r*"[bJF++Ebc "jL j/DA\FPtx,Gho*yRg7v7j]C{n<< >:t{44~6[ d̹"Ё,w5zl 2@ 0q *$8Bg (Үa#w$TeK/a$ fM7qԹg'.\I, $'(3䔀5k$pB#H `Z}oU+-S5RBڈmYMZVAap0"+{ц$CX6+i@&`1fJ1~*D4c0}=[,7dV*8agE]}%s T]"n|mhyȨŤUrc kIl!P ɬ )$h.6d+{:P'241E73ś@qH0T\FfÍY RH,%'-#̩18[,J0ijJ2LS/\I6tS9;̳M=)A=4@-CDTDmA]*)qYR tXK? UQI-TF5b PeUZ|04B0 uWs&5eRWab #.mTo]1gLB ƖA>BUhQ$@dUh {kC?rRasF & Xxe+TBp9Nso;Y rE$ a!IP^$+CN5U dzJ4T12:aSd1D XHI[l)AdkQ#TV A2hEZ>KH[0VB cUP[9H bi_OBp(A䲜E\ 8Vr Z驯KaD7^+dFGbC{ql PA~ $gnH$G8b \* ~?D+F zSxǀ4AB%4 QB-_Bΐ5 qCPHEēG#.MD4WRZx E0tN4ɤ H4Q#HWHHARH#a͈dѹ&7@T$!1I0L &APrXc")Q T^rd%_JYΒ-qX撗-{)]sXЂILcip 354/尐),BP"c1 /@ 9`,x cS%;y` -'$(A(tΔvA:]ĦW B!$!A!@rs $/!fD8`v̔e ѵ_S%hJ'K 2P)SQt*'b(E;:P *)$/&A w>@P5ŽhRkLh (?*Q9O@!X4jLQX~%48ǒ dB!QNTcH P 2"dz *XA DbkWR VN!<,XAA ZЬ (?ɒ9$bŹ1]A"@J Ю&^pM(0r`%4^%`&3 o:Rp+bXP~]8M;S" 5C\q*,@bm: ^0cƊڊb' =;$X5YR+ݖ ЄಅHqV1yX傐dcKdࢎ3TZ A}A|h$?LKI%Lt9(MÂIw ĜaV , Y: '@.ɨ_2ZL!0%p@,&؜2)T @ =:@7lUϸ=`nR@ Ԯڐ/o7A da&6K$p0 B $& "b8fb o`* A4`0v A%LAAB."&8!.`G|dYQf =/h%"K@Ba tQ"\tL-E/O&aA0h`.B/p#AP,DNd, U".cltqY ^e2Ԥ g \)MpŐl q(uR%GRr%p}F$`Ba uM)K&wj8AP+QF%{R%|D q 8 r %] $)WBP:`Ǵ3SBl0@ ,b2Md1@16awQlZʠtkv t*+ " ᶎ %!С& DF*@B#1p@r!ɲ'9o` <_l@VJj״B A . DCAl3 |C`,7{Jda1JH!D/Ԋ tHTs *FT \ $&I&L!i93F6jF` Lr#rA F h kGxz4 .!z,E 69YT/Mab hb:@` f-8a` bځ(O<++OML `bG8P!5jCoMDr<@a/# $ADlF#E0DsHA\`@B U4!B9'V!Χ8"JpcuTRYO"%3+!Ӳ\IIYdFش%|d˕b 8YMaY<VS2A`f`ҫve5꬘aH\"BTj`0>nbaa6+t#0e *@ ATA#-^-"/F@m-:$%D (Աdb)|G:$ "h154C15c#,W6+" #;'6~>$5TB~`|7| DvtWD}k|Ki6`zc?X8`!8( TPL`´ "A`V˼Opl )$\NO1,d !A;~nƅ\:h@ !O !r6,Of!<%<4FӮ#(<oϠރfk~*(&W9dB_nI @xn"+`/ա֮*a bZQ,1 @($`Baց[]7+6/\ ֲ%` i(ڠD}5@-Fv x: SPqp,aaMV.Ȝ ͛8sdA`'O@ JѣH*]ʴӧP)  Xb;ufsGS QTSP+”  Hܝr7ش4g<!k֫PTiˆH3c\f@[ 1 v$Cʪ͹vȸ ҏPAv5A@B#SvW][noq$S'`e};_Orr^ HN%r5bqy$NM#Yف4 YTv (h"j|^Va yDIN/2-#bPI3R )ZZ[ئ$T;i! O^<>s4 d`E# L!!) @!x3R:T*4oVV#ڈ:| 3 0'V%D>P@!O ,Co!4k-3Qk#1(H0z(qXH >+@8_ /su`8+;10a 1@ 4k0 7#qH+0PwCP h$0260Q20r^H P k iS7zRwk p1ӏuS= SܡȒ&P(  ."푌6Q2(5pw}FH]axl` F@ LpG"R p&iP @=D(rq5$%!0]#vXP1,3 I0yf"Zy,uș -g 1  ! q9!IÙI>cwᨚuhq203 4QIP p=  jwTI02PqZI%}s5ȐY'0uP b 5{4Q'^ EV֑H,H'hA /c@b+s3PBA P4s k  / tGZbCTV@u :q!¡a Q v Rq:!sC,JB V$(w Ap#s($v:b¨a‚1;ʧS" -.p!:':ILawP 11Mu$JIP1@%eu0 Z:X|$q!*&Apvhw5"+PY+=QLh_8+V =2xw 7WF;tea ?g@$HP@(@ D0IsptbdD:=8B̼`=/rcUc64/BE7&E/FcYB6B\dG;Dc{4c0 EmcIt=Fd`/C(48O(Ӿ T(B?B=[45f'GO=&D̳˻Hy& qQOq!WjBuv NZ=KrkR=XGG1sP+'t%JWvtuf؉׍؃aPapbUmV7M`רmh `] a'ZRK0_km\rg67OpLڎ؄ 'xKfaE>'P E4:28 NP֋gFW@I2Q \$J@ Н4Xkn 7$ ;-[PF6 =:3^?Z5;?%)@)5S h V]M|' 4 U6} NF`2 &= R ʐ=.ׅA? xr5!Yqi3AsE G\7KZ*JUg2-nn&qN/A[p `:{iװep~FW t.EZc$x e%]0n ° ȯ_9nU ]ư t ^jVa- 6 P `C & _\7D PgžneP>b2ۅ̱0 Nb(:8e3~iA`Z3}c /FA 7e/o`]Pf( 6/(EL?-PR?ToUoX!sk`Xme I3[e(-$RmVO-Abd9iLpw tA;Q9!4q%9^63T{HvKnp(pW0{y_w2׮x`EG ˠ#|bkWxUzmyqOq W p: +/GJ򱕓A(X{tuG@PRHryU ‡; 0J"ċh O"H< M%5TY12Q%:pE9uOA%ZQI\ʔESQ>Z*TQ@Lwt҈Pm C&hbvDfcx*lAos~5(.- YGzp3r -],QTi: }f2t%nՏdh]'ce]U%Yw/YNd- U&BQq1&R9[A# +B 3pÝ"DQLJ$hDA`3(,*;ĮklcFIHizt @c.ej Tgaxz, ˝F :xV.8#ioq-0$hAa:eE\J@9h+slNHGWR R4+ J`,(UTj f.`t*vXbXd5hv0 ae 8|UWg~HNYKqu꧁{ `мzp7 6`vk)! j U~P2u-{- f` :@*ȡБC4Ee<yTgxdzjثuD/NZvl0HfbHC9 R@'9J`|B#4'p{AS&'=.BJɿ|h ZeP.ep^)Ba `Dױ0DhNQneOO\f%<'Oˠ4=hf}awu-q8*/@X P EFC0#D c#CQ /2 zpI. =!xHly5 \,r AOHݷr`(;!D `t[Pii;!M yvs"k ,CPY8F7`3Fc#vŽN($Z5YDc }@PKe)My'D{f#>m)ɉPX#\e^2'3?iaIDBh h4t|>*'@ȅ'N| 0{Nm @#`p[zS`Yu&dh.OFT(DGyQrsUbBʌ=h Ҋ4*eiK]j!sT@CPL}:\ cKzT&UKNTFUS-%P9Aj!>jNP08VSVƕr-L Bh]1 !EĠ L(AI%.a]ԥHNvm>Ξ QU%mi1 %,"$("dЅ%z*@%@ ^"0 e $0gErvޖZT%<RI`O0VhM{޹W*fš%A;b E2@ŽZp ONP& @Vb2@X6[PDQ{bXƚ0W9qٲ_C}K9r@ D\AscX[r}$%LJ`Y #fdC2@0Id7cNX(=Ŕ"IhHkqc2742#xs"k `ȃ 80H9@U{Co 0*d&mF#2.1\PAl]`gVXٶ,1 P@& 8[7\PB5.2(d،#xQ^oG\PS-r J]txǑH\@=~r\+gy]r|2oyX?:s4O6}` {l~t'H,"7KZxńwa  9/Εu{ D " ApH\! {Pذ]{V-aEzt']PL5K3^ْ:O\y&=yЇ^'}MzԧM' u>򰏨]A ̀vbMs/1,AK5,'_'{,-C3?[B)/ (A bV\P?'R\ЀK3?E0F 8 )k1$@Ⱥ)Z'Wx Ȳ?خ bx;q)vY@H8|›«(^rԉ?@d9 q='@B "08:^ WPyA=Ѽ7҅&AFaC#b B @ $\@I$p<@^P!.X4bxʃ ~ʃ>@2.PJd/;Z-,D 0ȃ 8t x bp4+ ,ڨ+ȃPp YKq| f̃j$D҉[+,PI( HHH\GsԈ+pJbHJL`Ax>dC^v\>ItH5*BQbFI@$ʫe;Gs^쿇  .(.˜3X00hVQILmӔx؃)=،΀h"/yp% @CXIh `9!" Ȋ80}pKi2M))PA OK8ys :qN^hClxN\80QNF x7RO !] aỎ /" 洠aWh *J@O5 朅]΁ /"&N4P)xR\BxPPҌҕRVQ2(OЅ(U^؂T 1ԎeQ hXQeEDq %0I`LtY P@jZgHF(ִa `#R,4LacjICmV(7(cBUjAA#k(œ Г1S)XM8=W x`x1Q(GAlu@91[9YT@!UnY1B(U"Z* hPU W~ŋ品}SppXSԉYEQeMϬHX̂=# Ce}Ӂ] L`ȋ0XZ 0ڠ@!\8 ZYՕYQƒΑ)}#gH $Uo%Y@!y7Ky0DŔ}8 x>WXClwŕNxAUό(ViY N9;k|`Bd%?Zw!*'|f8/ i!:  o)ӞY@w 9BD;5y"0ٙh^9+XgpC_C"ApqV؁PFq n(ݍE L"BtP_;-V`+ʍ`meaf_ԑh R)>b9^H) ΒBu=Aub"㝠MbʽaAf@,j#vauɄf^91w5bC0Sî-(afĴYv&cP?HFSLIDiƙZ` vٜŜKQ=gn뱠 vuN P`=y;v'U~geeW透)gxEJQtF@g[,_Ag9\p,a`]pp婢u=QAivv `c[`bb&8_7lO0\]Nkjv&g"(jSQ&dhsf!i+eN陁;&HS-*rLRjMiĞ iRZ:t͖HCx,!$*@ z1k R:"yY68jzf" JἹYi8ˍqIi#A ՂY進 1(=R{.  xlSNmZ8j ydpnzoh,ڢ8WoV0 2p!$a% H1jJf`KZp1$~p*@_?,E5;@oҕrB /$jd=MBzo$pa s)FȢ;"® ;z0p;78vH6q^ )Gpkq4;`$qP)2qS$==#[C`%ȩ_%^zڧ$7ïbb2&d*&4i&:i_`$) 'm٧d (mY'4!+P~t%vz|  X28uvw_kצRu/*wv3VQ,uv\x/'O_i\kwoڠmC&{gx"~{/58ymGxo׉owv2 o'\ pBxzgyHwoh_OgmzWwzW_!$H4)Xt{ {@_8{j{m'|+wyqx {ȏͿ|8ͧؓ- %}үq?}՗9@67:Bꀼ?}?~|e}//}7-(YC[T/<_hD8].;뇀i[@,h 2l0Ĉ'Rh"Ƅ7r#Ȑ"G,i$ʔiQ6hpB%Μ:w'PA-j(buA0Rj*֬ZrUh`>T~m-ܸrҭk&Dޮ.zXao `"13"+<$abdIIL Ѥ1=Fѐ(V)cAEqԑ tHB<&􉑠ԵAHdXءȠNJq-Jښ*JP 0"q3Zإ\>HijBkz ӋCkNqCJqlh1H&NdDNLA%V4G ,Z[JKmÀmQBѰ)A"+LK-Bs0`A/1:d@,c_@(!&nr8qP @ $I:4CE%x-PD:πW"A(ABDA@s"_$s8'2l+d+pGLp+؇K/1x+t[ P*@-8fCxxKM JA 9.,C8 ֘Hcs(EL"8d* jP1eGy0uQC~~W,&M r`թR[~dև]&nY p02`;nuyV8%NӲ=:x@|n-KuO}g۝wsxPWX N3<d|"\`ɀUh(%u9rP0)m_,9ifyoA$'^I@A=ߦ.$`+D}&D2 8Oeq^H!R^XP 4_U4y+5m_*BE߹ AJqL [ktm<N$Ő4?]@(#XA6L ,_8|PN!յ8y@,FUM1|C% "hAPA/2E!LeZ%+3-4³s@ P_ԱCaCb>Ԃ}YErJ5@kY8N@6E*b5DkED@!B!,-R,DNyAB.Fr$D03>>QM q0dcZ^VY,L:A#$$"?Dl#VbLmM3Lc9WB5CPfK2^CJe3V4 8 *L ա/q\}T܍dA: bD-dXB^ͥAZP!)d#Ā5Q #C,R]JIP"0!g@̂].y׭=CB`RwXC1`'>t)[ \聑 m]I:@^$@t@r)J!All#fТ", `l4M|,ւ&Z08D:!ɺPl#+HmD+ąmD,nB*mF,ٶmN-"Y'FoJF)6l8(44:9p6}D9춄 B`9{rcT" j=& (LG$ԧ4ERA>2}?[*ܼh#DO}:e+ >6oy6'E)Te3hwh @9Ⱥ>d;.rӺeJ-pt .p0AX(A'80I `Ě͘=*ѧ'Q&$fL3iִygN;yKA5ziҡ$6u )ʥJRUZG(FZuן\ ְbu sֵ{o޵xmԾPrh2_~~i> <B7|p<%B p24K>MSśkWF Bfjܑ}쯰~E"<$\"t(g! *ܒK#刣ZK2<M34rR11rN:9s<ܰNc(b]pp> DC%HEH%TOQ <ϔN=PE51QMTUEtOVW}V䌕VWk\u-^}X>EsbYV5m>0X!& vgQYn; Er)v[tMZQ\d-tz B -6{7nPDk&uw^-==\l?[[e6~8]nkeffq#8K) !,$ HÅ#6HPŋ3jȱǏ C8ɓ(S\ɲ˗+X*pP@j4V`e0Yϟh ӧ,JJdիXjj)W9dAYTO](Ac8"(HxahL Id0`!^JL˘3ռC0P"Na0ŒSELmAq@ Ѕ\o,#x$%F1YFc.弙ËOoP:XH'LEO K7mXg!^ >8@ ̷KhV A 7d{8ag*~x(DV5 };Y c1/ ,g KbJ=iL0X})a7rQU"K{Mo;՟5M=Db'i?YqAzHj@N0ѤP:TZUzXZaR \K:"qc "1ǤkR|N/a4pR}c ttqq  Q706~Z |8tjʨbN8Saf@0%5(!Zpꓦ]C99g WP4CqxsOe|)`pz>u1ʤӫ Nsvh#Ca&ģ*Bep Fwx1@ 岤Ӻj;g譿ڰ:2hZґ Z:6>5p8@Bc=8$=Z2f)4df>=7 =Kт?c=̀dQ@Qdz T 33QE) J*AFe2ڡ ~CCmqFEhpE@>`2/F1EFf<F=@B~k2Np j=ۃqk 3sm[=48a u+CsB@'di˸E] 0`ۨ_rB|a 1/@IښE0BP zhB1+h`CtD+`>50@}^PQE:V|cg (ҀNw@ Z4P, @H0 +@pBX c[& %|()_BE#C@͊kAK[Pkd? XF Tp49r}t ys\dLUŏ+&ir  U/JJJRKDKnJTWF`U=F D ZUo a>LlNZ/FP&BS MELOF|G|i3w3K,Qưƞ8KK`Fb8PR#8ڣP  ,\ huPvN=s'FP QԹQx L0` qF@ɺDv-ʲhXr>@ht K \N=l>&;kμ,ǯltGB lr *FOCΟM4NP ZW@ѽ@T}Fd@f>Ɖ&]e"pĜ.}PyS P٣uqL,5 ,5 o%*ש Sc+xcF#qF6Ie^UF  P͗K0O[ Vj"yoFUUNը0  &-P haˀ,|=O$0 V0VeE{RW-)gpVqXpk5-P"`&kVV?Me0W w9zuH%|Iy9R55b,Gk݊~X!AR5>r.1+~5`V8{ $4vi{uckqRnU `P$ kM`FXV }Zf# 08[#>%d4mAKoF=`a^'\#Vm sXcG]z(Bhed>&GWI^$/ 5YVV_[ضl֨5*8pXbw%gvkDb O2ʐ pNVN <bC`c^%u j`t2[ԅV;NN{Ke?ֻ^W~> ~ XeKj z-d ~ fE8>kV+f ^^UPV[P >d^Bm2@0+@G+hahGT:x P ıZ8s98!Zj 0أQ-3-"UFPrh KFQȠvYzJ0=c+Wd LرraGaIμ>T7:xнeˁ64C |Ek$Xy699TAPu y!RvԯgRAFV ??QgH3hd \;q&I`; =qG3|.>X+1(yL9Lb 3pf&ka;a4HՀjLܸRBq˘3(pVS B?8*d`xv F#RV i6TQx#&@_r9|q PQ|'R@‘?>Ti dNqN (uRrH@;P',H:/(EO4R E56H Y2 ][bcdX!˳ ;\wn Ѡ !X;gCcM$V7̈=tKRaUb^6RxL!PxE $%T\fB[(䥂=^l+_72 (p:z  kFiQ8 %%P`4_.pDAqْ.=RwiRdsf8 9Q fX@Xs=x;cW(IMѓT*(KS*PA!@{KQ+3jӜUCσP`YtDjN|ZUnԘX(WU400U4-YVs)]zuiͫ^W`QzUV+8P Fl-dOWƵgf=ʂְmi.L[[ Wմ}-kkKٞuml]ٮ^qW+W@|enXmk\BW .nC;]6.v\h+]Wmz݋^W($1_Wo_%:C'X]pڥԂU K:J |Q|P \ WPPEhx=1ۉ ZX8J;< P H =@ Hk,(0(38!躍p=Xl H8c͐5TX9RH (S ד5^ЄKt$Bhځd6CCAAT@CH d<%bP;D8T0<@Cs =#+,B?`5l?Fb8>@o,*[,AP|R$9=TC9]4)-\)(C9|B5ѻ#B : *p,PdXz=y`LIRSa *03y̔.Z!UA`qsy-[( بF 2F/HX Id VHR90 W C:(O 8`K(I!(p hG` ȁ1DaԀ h7hE>;k𝈢7@RN\p@ӉRXQ p! 04>pϞP %IS: v8 ݈͝,@$^hxl# FǸDpD{q:7~>mooYk DhqUD hɎwXN؅ P"`x-x3y`'i9ǮZ`x x[-x:pRnxgH?taZ_ .ӈa(*`;%xN SK$LDK}Ë-ЁM( M QzJ(E`K Z0HS (]a50]}8]X2 I>)`E+&(/7z([(]{0W#o^d0PNhǥ ZPPh hd.vuQ@P'Ul!C. aO8NV1 GRd?~P"ѤJ2mtӨRRj*֬,r+Xaǒ-땃A\TSAdaXLQ u@"\= G",bֶ#F(ZVv34APL,jgӮmrۺw;a\pe" 8& :Q$(XǴ70xmX8#`= j6 vK 8 x * U(A~ J8!Zx!!,!%x")"-ⶡ184x#g0f =#9dA@I#M:$QjEq _1%]z㗽33x+.P̩Ei*;"ʸ '3Qh.%aRj)ja E'pW!qj a**"k9:r몱ꮗnz,*1" ʣEiaמflܞGVa7"{/` <0Z|[0 ;pW[/ W|1.Jlq{s<2 |2jDMi@xqVhSns77<`, 6؉l `-Emc)E^!M:CGQlM`s*X$.ohH^d̔)j( V.)ޠCD /Q2C% b!Q Ұs!` *d j"2^-fWJi: j_ "uȪ:ץrU4 S Py H05)'[X;JTp j(Q:_,Z}q @dYLե([hBX|@ `FH!C20N%8)(̼U+t؇>Б P /Cْ! p<ErQ`"Cc nkTy < 䑐B(8C F@Eh1'j3 &HivuHɰx9d\%@(Z,Nb -WXqE 8Cb'p"p#$`n_Kb04a, l:݌vN`VJv &+5.'J\hCT^3 O92Ht|fE[@ n-f0_`B,QtHˍ5%2DHg-gսҁq03`CGvD}06In AL``<ϡ;K?e/\;hD9dBfK8f胻/dN&)/$>#i#`™JuQ$vwJX%P|%RDp؃/Ԑ崈D3UMVPγN )LUd'2in(l"ǔӊ~E)f  ))\iE. )l ~ n|i^H@$4e&`Ԉ @ iL^, 6Ai$@ ,& 4J:@ϔ*&`@RB$imH h2(CD-08E:+00B3x*tƒ,e'l(H-"B+H@"TkTNOX _6-?6F,#n˸*Nll,2\ Ђ"B(Ti*8yb!8MV@2,&% BZ]C,KX*ªd#BR,؆U%0e@2D[le0Dc+2DVF' 5!e@|)(AO3H@<EDk>@Xp\E?HHSHbE*HAq\Ntd!/Tod$Xu#WR^53Wp*D@dFH8V!SS,d%` gظ4@&dNWTuΈEbLUo;)_ J5J `_GLn4A͐SL0\CPJ,Hh(% `IeOv(y`kē5،j*ёd۠*A 1DE!,Iqo0)oQHx;*Du'@ +I`Z|_R!G]f P ~PYOn~O,@R8yG42P<Evө.)6&)avQm&'yR'ᴋS7w҅&)X@+TKC$L8@֐SԂ`OhLeV`(pv#]D6;M<9?/B 3T~#E2A5hl BQFFm"A NyX qHVA>5c9NoG!OB X+]Xd䂾*-eAKEl n CX5lL5T_%BTN0ĕ?!oURvF{Wêg[* +FЁDHzUsoth!ĕm2A|QNUUy@PBK@VO]W!8#*s'QQId)a("fÖAPP_Y-xGM;4 IpZW[Ki5hO˘ @lP>nٗPXP[fi*PS Kj鉛~ꨥ.880%F(bA9(KHWF B! ;저LxG*P֭䉄vz';mpi Rl /i'hCX!ᅐ= LxH.%qchZXk3i/vw.<j^5+*L. +#; =k\A8Ck_0{Ġ #Z&^0Tmw:QH}I@AZ ilLVU YZ*VvAgEk+C _08 6V%jGj׾]ztrM,EH%]l^X3ʔe ~uhJ@!a" !4˻]Iv hyCB M7 / nԭ.w9ax l-P aC%` 1'd .4\7x Qb[ B+*6+ŮCT65&1ab,ؘȷЕ![Y!mfʹ=66 EiHPP m,$pE:[DB:N@!F.-Q+'I<8VhtLЉ9i=CYŋl)5 0f` HHf"@){FT;b3=Ab,S@43@'(P$5N肌$rZ zٸA+!E H~dMV/ _V`hv)u^41EDT:"V@qD|]$sNýe?;3s`'$)/py `G+M/PQr"䝰+W_Pz²C,8+v߻ӟir?{O"sї¹Dѭ{q?њW~ )_ @6PP`[ev  Rꔁ#PoͩlxGR :-*O!"+g0ko{-P P  /t 7 Lh 0 @ΪռP :^a b6"reVOڍ4 I,l ã0F 7$" \9`A[Ov 5 B Ā֥uN"!>c*m*CI c МL`< %(^OPrHޡ*UJ|c# IAD2O e/`"q`,'Ѯ*7Q < A  FB&!#G^,`$-fa!$á@Gt`v@H!rP!Y + B|1!K#nr :~M4t^! 42Q @$!y p@ou4@ R r(A ֥"` 2Z R ,̼ |-DΫ9*P"KjF~ `0L8j@n.4A>hV g&! 6cZ# J DZaAu J."Z5f n"[̃YGh0 m cb D՘h9hV*Ѭ/eOS1ADcAVC+Ju!mz@r0a RlFYa2 8@"B>fkݥ v5'Z0,h " zCE-Q<: ʠwUCBaŒBv76A:Ö R_*/# h# @BbB76v-#gؖ"\CF{#l y5tD#BPZ`bXe# |]΂eA W2@C0-m}X}xBU7-C<:x $)y%:W(Iq B\s? ea;&F}ddK 0 A@EDE=R< DaA_#AxD]ja(`D^ڧn^Z\U 4`-\#Ud)Jc@}D:P~s ,@ @9%xSQsP *aW!! ր* 7:9 xa>:Qd[c3T&@U^\%C7kUOoUPi({伞+@C! d`s3l(|l4QBn:լ[~ ;ٴk۞Ã,JKY(RB3% 8ydHth `'h"YZT60 ʟ8z\ )zTS֪Q!PqDY4Y΁RJ5d%̅0oX`B'T $ ) A|7!"L\( ]v EdF*YN> eRnq0  q)C]D 2pa -$LQBU4Ku@ȊDHZ2TA0 r+"F(q@"zf V%@hTAtJQP\ᆑf %&Jd2Wg{Zb"0MIll.lE]t= ls tN 5@پ#F@r ± r(Aw"9e/Ջ4wLQA f`T6I.rK%r*r.ۦHI3[&GY&/s> tз5 !v4bT+tN? uR?Զ}Yku^ b=6fvjO9"D wXMweqā~ xoEoԸ4;VsyzޑHpHh@%A8|z.꬇{z-R e?}҃MwO}orEs7e} |^}~?>??3>A 05AAP@558C/Ě)b(t5%]\rWb#'3>pP4XHBBO!V1 ^$)RPPLR5c"s!P@ N!9 )1p":7PD +ChW@A0D`|'ɑt4J`! :BZ!L ua||)RCn %nA3h],І(H$FA3j!)ЦJW03Q"|"YI^D Jl+d`r{'(ЃjDdԐ r8 TA8D.`T!CL<$Y[Ng{n t<@po`@An`J`Ix н3|}Dۢ_2!Qzݱ CWp) *YEz ` "84.h [$NX_) M `o8P'(*aya o;RdN FᒛBx6 0moE 0F 6$ 2!֫LE4Kt5"+lI@ u/LIb`|g|YaFhm9f'"(|JdfJ@謁n, آqaTNjF121/ե] kj(B:б}$4w> lN`x7xvh0 "Ѐ!i5P|<:a(4Ӑb8( MيC%,q(U0 r5'c B Ƞ:CP/-"4@h%CqjQ4 F ^d8np~3l<]j} Qp8Pl=_ eiH0a5g PWN;ʝN(}:-5tQ@qy ;pVDU쥱Wڬ`h9apaƚm y{0%u{Lx3m`6 g[uk#% `mN{[c2"g^XKo'p@p lB(n (IX :X!ģ:o!v!LEP~7$20ztMp}@0u3@KQ~8fRYup-^}SPjyt $yna@D pXm3BzY ǁ6,Ifd`t@erL}exq_g ;P>0uy`q0(e `tMSa'4+Յ  { Mm@xg ƶnpp s V ou xE P$w`[w ~ 0,jȘ_ bLZ^V8YcJ獖 ?@90 _$@z5T0P+1 +,AG8|0 jg y0PD0 ' 0 0wpC0/$tw" 1Ej ?P fEiUӅSYqp02nT@f55 ShYc!%[DTD`!pI7p! l!0{ G%`6 ƇܰIڰ_&)2 ؕT ɀjTDF$uT}b%%! E_G{9V痬izgGsob([ 0nSE`FXFp:- 1PTzwTafz[ 02 Y Suj 詞  z`PI:CY9V;B=2D0{zH\xtc@.0 r0S=k kq)k'Fӌ0kϖ[ U{m_`Ϧ^JF[]7-n`- s`- nDB/QC/%m@rC s-0ZcSAz1Fz0J/A-yj-/ e@1:-DZ02 .z :;zAzXCآ:IBzPʪأ*v1D" j@ģ;bî£HS:J$Z⺥JʯQ-к2{$ [{q*@=4*4k(qt+32 !D*2ƀdþb Lk]`tQh@eq:A;7mrf0KGy0‹Ѡ nbGp?4qVoc _-0 uSP^a%b1p%P@` C1R $ J@tȸc)0$GLS%|  $3Q1{Ql0 @԰',fgbf<'(˼Ppp `a$Q \>1K +0q0 p +}K; >!Q @c䱫#,/*Q ȰE©щl>8P PS^1$ 0"-t +U12#!$a s .zY @"$с$L3 2%~@D-f2 n+%+p( "9^n9` K~k=à# $2Ov^s:vnOm.3ް7QNn>꨾^뱾c?.%>4NwADO ` @ u!AsQŖCT>2{ebxg v` {BX `1Z bIEN? yDU'uU6 KW([ׄ pVj0 @Vu;(@y2J`R~O} USՐKu;O`OgV@pVyWVXpNrOUZ5 y"eO,%FQDBUQ -dXn @T);Lo??E%KEU~kwMja5VeXl9%?@Y^`ktYv5Pe$0qg ׀ vo$asM 0 *( { @!o @pyC{f]D@Epf$0F`oP_ AN/L5PA21 hdpbpR[uM : ƹ`aKA*6EF tWkbv4eMod8GqPi:F+KNA@G ޤ8Fy@ѮZ( Һ d&YEXbƍ?9dʕ-_fYfΝ7$@jt)@Q'G=0},$+.0{r$ 1,R|_3.p (lū @I(Ӧ%n!_Ǡ &+"bNj@混X @ C B詆 Xs9 iP@"% @V(hDyªZD[aGa|@(JH%*4AyP͈)dh$rw Ib$1=4"bMPd$t3C3;4QEeQG-9DPCb!9+^8%‰:H WAYk1'j\MS^B2p*"H-RR#DX5X8Nu^Kharb EȰd~I J\s┏2S+bdpRjz>x pDBLl!2:2Fu`/[OZ cb j x| ~'c`B!hY&QyȨp;k I^)BF*Hlw%DeG VlC<Hzz]t&CMɡ\sg gO^KlUkc^iAV7F !kwY^R;E,0)BKKdZVpQ Z e8CN7ZpAC*T0^P"gA `D>!0Pb#1*Xx)X*!<]\\8 1kÍp=p vrRpHA$g;2@08d D9h؄U؎vCr b j !"R@18qBL<iKa !2"8J-4Ɂڨ  GEXY p* %3i2:CYqF m 1CsJ1;HVB4t8hD%:Qj Qrb@HBى8ib0rmHGѐB>J=@Q;Adq3 jG.)bEYD\Q 0#'WH`Q `E?j [E,cM hK@СL[؜곢}mle;[ΐ\Ó 𳭵~+RƝ~mNGw2W#-pk;]V׺5r'ѢSms*]+v^׽ [2FÅo~_׿481/}g ,t\VXpg a @UM`1TƷc(Xt^\"ȢJ+dP p< gmX@!P2 `Q 'X sbv0FW,D" Hbn$HeIdtH :pE w%# fu&+& )xpWd(aB2„<ȈE3(DLPXpHygFN @D!H0HTw}\aqZ$Ȱ`N0Ep ? $0bU\ʈC+L -0=O*x5oktŸD%.ђ-,= "Y@}Z@ ^T1IJA(-4 "v72p<47*_$F 0 >վ({`0L BU77aLeKh-`2pC-h fA Y *pC$wˆ`<"& 8dҌÐ< H4xd3 '7 Y:J!Dw@v0!jl#jĔԈZ v-E6XiāFIۺ"#UX!-(+S1,@Zx?x["0j q@:)#(@QB@^PАTFHhX\"@ " H!?ZZ1$XK7I0B`K@"VI Z@Sx 6:;y2:é)p])KAĸCXh'.ۅEQ b(TMxUD t\RElDxأA)CA=JHBE!eD#8B^=XBxB&Tx1J5BE؂ 3%XLD\097jH"T9xG.P2JCAػAwB\H FEMV-Fư$ZvVxS(R`Shè I(#b(D/ɌI=ps3*=_H}teZzz$"ZXǰ˱l,bTSƱj.ʌ j>w[&,BJI aK=K 'EČFz̈r=-(4,ʼn+ Xz\ x$ׄMꂲ ,,\M0@аM(B sSIߌtN* 䔲#HRMAN'Ӯ|4DTdtO-poXY%dT Ue౅Ol F5X5 C%P QO5ѰDQ9Lu}Q֔ QCaRq!C#ER 6k [1~рI(;4(&!-4 .Mת:B TɒlN$*^x7N*8J ,*uԀj=(QĚR:mS4 wH*YӬ1NӀDH6C5^F @I8 S+*Л7 CW(3 4 $~(T2  V d;,+@S @+2#DVJPCİAb8<;/[mwZ([%WzMZU4|SJX@XWWl8TwA2Kf|- ]4v=6M) 9+W}8 XułЁٌU(#[ <҃}X ؎%ZH٩Wp-%yE,0ői;،ZYEYX_Xӡy~5[Wе舏d(Q VQ" ^S"tHڍȀ-8aɎ4$Hi$ZJxP*Ԓ8]|I4xCw8'p  xߖ!) -ݕ ȅ-^ꥠT^pފJߴp ^r9 M "CF"I % V@ !Ѓ *U Ȅfp߶THAśT7 &؈ %xВ(`2.F"ڋEG5hC X,(X@yR, Ep H9#``P( =@ @籏N3`^"Y1$hY؈1X!`H3I?hԀ{c~e>5yV!c:l0 q0!et)'h2I\Fm,Uf\ysuۈ] HF: Qge鄶T2Sy >2ZmݵGh#6$#z9:$p?#@;jeR2~Nr-7iZ4ӾW'=_ڶKξw|8Ư ?Ο }:֯{C䙷cH +cO~=']]AEY\"m\Ҝ^Lb! 2ؠ`F!`*d)CDhx @hQ"Gt!Ѕ7☣E'ݎ= X 3щ 0 .1`B' "H3.qԓAe \gkC")@ ( OB]JHCYsbf"l"2h)&"d"P^2%|x$F)Bߗ` f!v.(#$r*vɣKHU-,BMELH3 ~& qD&$V<Ed 2^cNkc⛬b7 R24;CR$9>KcfqQҜJ1 2*W,nDI M+ܳ?CKG#]tjOCNGMr[s4aa 'w]gCHGh܍֒P\8{cPݷ^#3޸d?fN^]:w6^vsˑyIqsp;h g+yW(A὞绳J Yd!A 5Ox(h A@-fP%4(<N#Q!8D9iba@ \A >D$b!j@50 !jX@; $#P)!BġM!Z/aA*j qL"-!]BP<* aNHu 8Tbo"t!:B+Kfd0e5p%{GRkw^yQweIgS ("qhD\)A)ʣVg/<+(>8@'F҂" @P! OihAPd+>:PAB [c)` hc`XQR ȠR@R~:$;6A9RAY@3..|@MTmaU\Xgg!HPpcCdQ0E^HRo ۹B"TCxP@Њ UiZ ozb*>A;8a@l7w }gm"\OѭҏăC0T3g  Bh-9 c7xOe)x!*0-UPY4vhG+f^ &W>6 ,zG`?b ]BqEr<"Gr#b2HN`!yq]*>D7` p \zʳ~eykwOrbJϣܮw`S8筃D!*/QO|H| HKbYN;d8]r @i ` a|f#(Ve8\ Hғ{$ c`H3Ä!C '@%eEF:uL5Y01WU$Y&"LZ0 QmSěE j^` A U R @$3 | .hO T8ĭNP4PE Z!bπ# }u ,TP yF,-C ,RZ`!$,M(u!4RWB )ENh*Ӝ$ajb(Q *@4D'Xa2ATx'@ Éu&bٻBĸ #Ѕ.WHH(h B'8D#T 4fa KB1 K.ЂLlpc3Vb-R;Z৐ lBx<\+Gs !th<"4K_WNȟf͊fbBbdFڡFrdGzGdHHdI0IJAtA-JpD 3Bh@*LIOPLe5!(ȑp(@IB"HQr% >! @("@!Yv%.Z2NCZe\^\ҥR\aLʥ;֥^f`^ `%l$ah^a2[ fdcJ`y +N]`hא*c!e@![i6[P0 G` sd&(bre&f F$@ Q)Oɉ hÑМUL&$ӔGO>P/˜ aeC yeoZ&fRF|pچ3'hg"`S*!!14PLA'Xl(8eP $% @ @@p@Dꀉ8hE5'hRG @,C)0U4AIPc%[U @<: Cx3>L@7*%x C@@z@k)P 6hyX%rnTR T@>Aó}ɔ 4+g$ATO*9ÑMX$=/A BD CGAA*ᓌ$LP1OٴB2,C  ՀXYE <DX:nh:d%:>,4/l ++L >+8ÊR2@х<n{5Rh[g9b] @1xP+/'(U\+?q/vXA YJlL$<;XB@(+LjtFD<%"YVB0*V'*nCeВIF,e  (0gaP 3T|NipAj 0V-  Xk%bЧ@ؑEQ^=I]hC7>778:@7\@X/ڀB8R/LpZq5őDs,Q@›ȱX tCB(-*Yt?Y8TR$@u,#MATQ`FF I WA3{W& HT |A.Ё",z*r Yaג}l0cT7g4U_xmDy4p!DeQ2MO HYR D`)Īĺ6+:thC>p3%$7l+3 l$f­%TmAg(T5ZG #Uh䃰A[Sn B; 5Rl-B[ %A238CB-̒- P3C!L8!&\;)|Om7=75,0Uv@aR ն+|xȷYCp ܁ l=UW^xch/FTKDw>,P}7ɔe\ b %tAB%ᙝg%8]ybGG\zLjxDvFDXpA,D8//8bo:: 6`œޟ^̸D|) Dk_ tBDR$#@لDF0A`^8;C5lL {DELEHDH:[L+d+> LߩEEG<_c>zfg@oShu:(y<^ u &缼† ZbU}3L~*}^D}S|Q D4 hbHWn0BP'iQhȑ`@ 1A@B+x PhoJA vT㋽s bq5At9&4YL*,2x[)s I0@#SBC~G~+)(;"xb-T@0B/%Cwŝ1"mS/,Czʂ%$ /XT%tɦxDތB-B?3@ CDі$&CI t"ªڪ! ƄB$H'QTeK/aƔ9fM%mԹgO?:hЅ\T"AAN 8y2aZid"hPE €m8C'%0Z+(Y1ǔp! lPg`؇f=TxK/Π`qĐ%=iDQj {S@ eG /@RFELf\7E@Հ =`@t!q0 @Fᨎ Tb AU@A5z*h;Vk o#@h `u::tv]1{J^4+{]rHdKPv q A=í- @ HRM&q  $|fcFXE 2k`\0Sz2x$(i8 .xrPNf˃! g` kەhNSy BnoNad?񉹁dEб(`/8 N<8 <ۙ+6IO'ڦ]9p g8J52"PzQW2$*|2d` A:1 eB9 `/:(9@3P"|7ּgu=ʩsmOfuWM-k9@s*k`q|n ,d$W6;o6B2g]s&4 P9ZH @@kSm@0&9@$`]u@`hZ f`ޜYZ M41~S vB( 6-$ʛ1;(mtc7 g0߰Ti1hqn YQښ$(0!.d G΂fK@ Z'`nVep ס8ق .B EiuQg3ڲQFk5P{ap{BrSE&.\gM+ 9a:]ّ+!/pS99B.kt?ڞ &ܩ'ׄ R@ %D1K;M"վ?Pc2ĸMe4<D :@l @hlNfjNFq음! 0! z@L ` a"A  >d>@\ n^!*ur @!\p2@@ P N PO!䮷@PE*j&> m  a։k!nvP0N tpB! @f^ཚa\((Έ[|@pAm0 $fA̢rA߰옏g5` $ o(N&hvgd(<@fKi(L4Ȝ6b!0H z@̾9h lLn^lک`LѠC`~٠-J>B2 2ۡ6#΀L#1 uJ$ER0ڡ# ڴ @vj(CJ)1rR%g4چAd^ t$4 T! Q)7"&(c2V2r'0rf6 Z -R 'r`.4<A v1af,r z&7a+2r( @==. 7" F8 :P!#,E=4l6P;PzǼ;I<=@F@X|l$=`:-@85``>b7s*?pS7>c:$ 8'`@7? (4A aA( 7 >Bɣ<@Yf9M>/‚?y-I'>T!^!ra4@lTJ=n  c#l4=F*Т=^Bt)Ik?S(nfmE%V=ăKyB 55&?NAP4T!;GKD!5RM+u5T_uM_%TTq5WeUWyX V{GG5XGD txmz%`X5[Mj:A \N B!ʠ$/RhbL]$ĠP`E]\`z#@ Z[)X6X`f4!l!lJ+qJ&*LST^@„`A.#d`fa h d)agg!d38@7.!P h | x@naY4/hSpV!N 3dMADgcWynnVl \I3ʔ bޠ+1G$Ad`pj RoF"A" # e .oIt b2 b" S,t>q/-mʃ#b`/ `~uX6DVhMWGzC52asD.oQ0!p|{7E25bf67Vck6zqwUMaގVKV<:IhӬa#L x;2h4)" yuJjv&:|*hښbzOl96 :&LvG ?X!@4vR % 7b+ 3t۪rƀ*-PRa,X!aߘ.:KzCפʞzfkozvۂl`*ݐ ɗ^3dsژ2k@经wbOrNa&m \!"ʠTpZ{vq/t)!oDo/flGnGq/htqj j!ٯ '<@|nb7ka `/,owT p`& +o&O#%/NNZò!d4S}O tV#4hhQ&qB[@ ʛg L;Abp@.-e B]ׇm  ]0@N}`c:w#m͗~0P-9Ǘe;QpGap^SNN,RKA !z*c^̿:N8{5VU4?~NC=m,~ >m,R=d=xiD2] @%jjꪪP#]`Æ` xˮ@XVyi)0+ECFU@QΑ *ۨ"nP"ьGR 42KEq SKbd" `*0/+CA3 xMzK|$lNPw}xr 0C@RAqE$!q2y<ʠ}R :&!@rE,dmҊI&tB$V"}4 L &ĝe0 '2h;bϼG5YYR%(L WBT0 gH8̡w p =@L:9t!`Hœy`P::B2IQH~f'w5"3>1 YHBL F:#$'IJZlXMQwq=b[HIOyr+@4cEK")ŒOJ-Ԥ^2'OBf ̷8`.Js +" ye3*\]|TL1H `D;8=@^ /c`W ]șK&]- d¡h*N]"z,: k`T`07<HV|]J29D:jw襢е\Rp` 8e/ @*#KLxNE:K!uD1@$I${#)ǜud;8K lp$,o T3[ܩ5d&"T'dx 盹qEȒBjDX a[ֽgKs3Gӆ`9Z  r-ڷÖz>4| qԍZy@ Jta>(=(g@Cj3W݁ LYAaD`:Lhnƣx4P{!%4j\f5D$=H](sIBzdA+pُG.QBtÞ30 FPfE$ (=@J1,E'%a>.)Qo@Ӝ @2[@eKFI/ OiޗW4 'g7}3pwDsD  g0}~DZ}+V`|'z!~ ?P~s0{{ln`N cr0,(L05d2guAu)j@ @fhvWA/` ehm0 P `ih 2# @T80f5@g[G$Z` Qo]mc1'>,G]ԋqz ;҆1PePg H^_! q)ĸ1:7H5Zbr'"&y5R@TT8 QY*PVg\wgZ ܁8x 0#xa.[!  n > .Qopz[Pf° ?Em`p>bp@)Cqj)'s1p/iyi PטJZ`|ye f)ĩm5qH\R|iB5pNP~+L)'17sy zImQ,4sR*I oi8 u "V'JX~PAC cY;r/bgupvs#J}>|I l`GP BvlH`@ QAQTp/ҐSU7| 1R]RgW̱C*AMT e]UKMaU>Yw}Z{T:}} N ~R pw"́ : 7oZn4jhFq̥ ȥ, 1 ^ק! g>Sѥ: HJ&1TPh1!kT` /0e.RxOo3FF0v wx7})1cӚ\z mJך 0qz*1 14wU {wae+q<1y12'& A|!!`1 A)2 , A{qJvA‘i8\AB'BD@} B{[!|8")E3Q!p;őW)lD11QA||O&qO+ O0!b3#5D'/SZSWx&ffr ("kN;fuNB %mqA%62'ȻE¬[2yJ6;TË uH.!Z@\t˻IbYUw[M] kBDTdL`ai )Q d D}x4 bC0k),#6$]AlC _X2:B,-pVrOBS1aa` $Xe _}@ ]'AU]! Ÿ*l"0ܾ3nx01@:.1)@m0S ( d`8`Abu"40Kb#Ǣ,5Se1ӑʋ 1A205c()Rnf<ƌƍ,&rZw|ʌ@${ʡE qz@ P B=6_q tctڸMaq#"#K ) KbL͚ߤھ}ݾ ylO`PZOuA3S(f9PFT%ffU$BNCrrSUUx. 2 @pk МJ 5iWЩy.:WX59Xm-ݴX V wP Ed3m0%0`%_lr 4U(v +@0-I[%Bc);X .I~ac5@>ne`&f@/U p.&`-p 5ZCR$3ॸ+0 0߿P50hFiNh\ ᰍ` `e.ߦ$0 g0I|$ފ~`k{~\~qt\6Qp6x hq`iuoN 'tp @qq,~ Qvwzy0g`UwQP ft$07 x0ytG𻧻 #2._ W[x|-{d|  A}P@ :d Xj45 S v:z 82XfpkLLS9ǣ }Xhޡ uP' *Xara_/ i  @VP~T0ߺpɨ^hM<îYHח^e 2)Qk @UcYpcQM-pMQVoҀP+V)\Ր;PPZ0@w. X@IU#uv ß(.uVy| 3pbj+@T+ԫVz3` .:f񠆕I1D8\xtɳÈSWaŎ%[Yiծe[qΥ[]- *(iZ1ďPH@`Ftस˄ PCDa;xS#G l-/mH= ,U8SP!-/; `oTeڎ d ukԈ -czrJӂ2#HHi'";"MzLqUV[uUXcu;TSM['\uX@bMXb bv,hZlv[nZVo]mUk5<:lnw^z^|w_~_b"B& $i,#9$vA3اncCydYUŒ }C+ Y,&ub'K U0ɨE{ jO!Kv駣zyOQb (p9Y R"+f(WȆlag0`f&uZY{ %)`ZIR#Wr)r`rF:ex!bH!bdԇ5 2|!TZ " (XMUb֑B`qե-qar̳׾aC8-JL0?1(>T SaK&3?OxB0ay% I{ߣ`-xAB|P_wD #3% C2@>%( ‚+DY- 6TD(H2BPHq821%x}X -jAyÌn<4|&P'1+0/!a41$d! $b@0躂@ a  H+)1.+ =P 9&q ;d-myK\N?%HM6ьxp.âB-qyMlfSO hW/&yNtzOҹwg=yO|Sg?\ %hAI0R1hCPrNaxʐ*t-F#%iIE*آxn@ j@ ,AbP4&iO5"*Z#\iaMJ=)PT%OzUfU[jW1Uc%kYJVUkekS}.F*)XV-cIC-E̎BƠ,Q|A%2,"u.+xl:.23 nv._Xv@)`۰TkXҺ%^^)gVj:pp*Q" a_ф}⁥5 HjA" ^`?Ġ@&V[7.)n{@wŀ!4f"H:ƴ2+n9_ߛ þ»ٝlcsa"3'6X4 T"ןI}ﭐOK 5l޴41,gb'aӭ rnhG_Ih@`\I2j5mhXr xHĠLb" 8~A^)7By82><38ĿPktu v4# ep8ԝ%0L@߃o< JصH03`@$zItX勆oA>2 ޻#LȽ@ [# @=?`䡁yhN XP??Q=caU`@X`@s@;>@?Ma" wP.U0?W(ҋJA1КZACBX>?|+<܈2@2d]P(@B/؈ӽ= ܂kH<þ;nS+xj@ ?x;8B.I$C߻qPCU}9o2<PQ?DuАP<_T=UI(+0k`p؃xyHՋdHxx=r+K=ۈ"$8P;db/Ѩ`X@X 'H@3D!\xW  `X|Z4[$HV;fGߋEHI[ALu ]qH@jQ"YBulX(8H<:w$Kz|00 ˔ *H IlڃGM)KK5 D|0 ̦K逊ˀBT5IpIzIl3P"[fjw,4@ C\y@Ku`8 ($Ghx O$M̊t$$z˿TK~i h$ h;`)+x|Pʃp^Pnژ^20jD \0_p.t,0ᄌ: "L |yXj:=] @*T Z,Y"$pJUp<ҐP,KRS:pO,ĎFлF#/̄J?2Q 1K(rH!-C- HD2EPK@TB /40T HŅ,ӣB xP <ӌ1tUU/6- Ԗ9P`UxʀS͂@]M-͌@du\T՗x /]T?دW:KxdPW(LH.(ׯ`i[Z6|,Ѝؕځ=pP PPЂ!=vWX 6W`@hЂ8@@; &qw+ZkLIPJV| x"he K` 0Z(}Wdk h\[NJ QYx݉큓pTUԧ0R?>\|L+VƯj0\Ԃ̥Ĺ[>̉Q ;Yp RGՃd mωXE9/c(3a<]*ٵAȵ{Ot\ئ5moE.]]剸R`ah\sWP܏ *"8 MqK86PCx# P蹼oYTPz< v =| ߸ 6@_Haƍ+H vh@slv0 e%xv9h`"" ڃ.Ψ`XH bd!7?N[8.h` |(bP26.n=FR5x5F԰!]gWA`5vcq,> 1&!v =`` 9c#Q=`5nU ^>5by[,ccPJҌ7VTjUތ10e:~xYV7fc?~Ic{fd%\ Rghcw78hFaMUOHfjFOa $1QT3.Ȓ1_0(G\i49AK_Q 9XM8SbiGIsqH i< ̓@JiH cjk"bq:⯘B9hWͯ (FAjE-.hvjZ8bKYiE޸! (렞iS7i,Jlklk闎aCQTk^fj)9i| >XlϦv_%hfhی #k&$n 9_|7+uoJ$|joo++Nrz(/:|op xu pצeHY?H-:lxqY)! B <ȃ^0HMʹϕ 'tq (q̙+r009肞e8l1s7snAxXisyJp8_/@A74{V)|x5(\|@32ȀIXI7 35;(.t13(Dm̓;+2U^h* D"S99ȌY(DjKMDW+S{C7Yy[^%%"„0(ATyB-LfDrV$z%@F f;C fDpJ@H&qʅy͌iqdМW. QC$Jd@L,@'ЕF/bZ+Q,J :$HJH̠_2 j=PĊLl N `C2PևB +.2`Cn!BR<@ *pMApXqOћ:Td+AFe0;#P !^xEVC2VB[b|3\P$AMlt/ E@e]B%$24-\ BZh=1Ёc7GJ``7xE:F@Md eƒ,яIɣ&P\(yX~@Mp9 58'ʼn3REPɐIW5F BPHyp^#'8I,\HmQ d/Y" vcda0 Y@D1P\CQWȀ '%uiy:C5GԅS!h=!`L1XEAPAi>E/&!U%"(BV ^:97De8@2 :CʰCB1]@KNKjzmY@`B!*U phUIg(z[.A3Jh>찣zPtxkX1T} hW3lBb)@m]*L)BB ءgHݸXq[%;U#i`*SaĪS ۝etE+4D[sJP;:k=*:]FG+=n_Y?P!eEV^=Z= qLvJ//D+%ʺGӅ8PX '͂0lhtha Sf`&rF @Ҹ >") I:F,TF+*pʓSf!7VNL ,` U͠@ 8EizSATjx֊eWnaZS<4*>hD#jAg@ ]I l3mdvd /o].8i$,((' !M \R4E F:BBFA.ց U f4VhEhQuhU4MՓo 5$ p`-8po9XG/ q]|`ߪNBݢ^({<ru޷eo9vTbl)/4I XF5r JNX2 2AP &a_ VJD Ŷ#{}7ގAD.|4l8B@B|G>aCxRg*  cV.ЊNwYrd2( }X;?fNαy_@_T%} "+* WA%՗]d78'S81ȇR7 (`` qܛ@/LHlMPudDE`VuGױ X#\buqXEAy " `XBqP@h^', P(`|Ut`i  Ł  fT,%aܩ! `zVcA<#H P"dpXxbB lbd%O'*݈"? -N!\@-/A-"Bܢ)BDb,pbhOM4*4dYYd,#%& cY%*B>(Bkc9**N%r" ^ᣴ>4:)&l$5M"4 =8`"( d?BJ.lTdPEcTcAdudAY@J"*5DM8:̣d9^$B&e8^qJ$EdPJc&$:T#U*:AGd=~3{%L6dNBeZUVcO3c.;n%f)AeAb&#Nd4Y,M$&fٍbec6XЎcNcW>f?m_ &fӾ0Sghbe7fif&c&j*kl֦m>$m&mfogkq*o6!\m:>\rRu%2F<5&]w~'xRtgxZx'zgu'{{'|g|vҧ}'~bf ''y(g(&qC(ȧFN(V^('&sf臆hlvA艮ƨ'B樎(~gB)&)*(>)Fh2^)fni{Z ~)g^)芪bw)֩))n꩟)*( *&*2AtFNJ] f(nFgBjB2DFw**j{.B५*檮**++&.@2kB"A86fުƩhj\81C N+Bi^BU( UA%4` \*تh)P4˳>_b tLQ~DBa-4Q$LtAX@c- X@D݁qԄ O G lӪ~@χ {F @[FF!FAg,QtmA0V|mDbm~PIĪ^p#`$^ISA"YK@!xG0j]Vlnj(X8HY8ޥe~H*ٴIŠnP87@):B73@Fo)6܀"9|.L6t>EC l $@"ԁɖt@H| 0Lیﹴ !D* :0/»ə(/Y@I5`;Ul.(I(d$I o"UCx)>l DB 79/|v| d7/9hC݀lP @O2 ! \qx+RBQ+͐(0ωp(hq%@*#TEaBDqF9֞$@?Ff0#)QQmU)@ŢMSJN BJEYbj7h6<h1p92Bx@7hCFM#xPSYKD" D l{d N4y]Դ 9f1ye,lPJ4s#3Bȟy}0i2|VH@ cr @@#@4hBS:l܀4܀+*7G 2+WE!BBľmYxh  QՍ–ܞAþy3meЀc9Gq@653Z/}B*A #_߅ 2HH 髋]6lc4B2,bo>29<ۍ`#P8< '͟_yځ98!6B( 6`Q5 r6h@h&`J.0BgWZW&AY#&"B H04-v$(#,'B)38/wX)doF}O}rk# \#_dYe?A%D2?ƀ$@ƥv^?fAbeuw'Kq#8$HOuxBdZ(n.8i'I8fvBขr6v߸' 4/#GOs둻xg9^9zr{g4(0$vņ9o* Ag*_g9 hh:y:'/:7;:GO:Ws_:go:w::::::ǺϺӺ:纮::'/;7;;GO;Wgo;w;{@!, 08 H\ȰÇ#JHŋ3jȱǏ CIb’(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJՔ'jʵכ hJٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺl DilXݽ oI sk?Natճk/p}ËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dԀ m&g4pœxBz矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨGZI6OʧR*kX$P@J-ı%T+apPH7fv+k覫+kjvFFsKPE b \?kċE7V\w ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx|߀.n'7G.V'QfEPsfR'̐,3ĺD{U/o'7G/Wogw/o觯/o HL:'H Z̠7H7v |kc$Z cH8̡w@ "oB O*vWTh sQ|9^! Ro5 WRVޱSL> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0Ib(/,^y] /L<4IjZ̦}prK$ùL<r&&fGu7]4wJ̧>4~#8/X[BP/ik.p`"L 3 !@ 7`ĄV(T ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌s@IQ v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l)",0c2Θ8-hhkAXPl(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhRIAl@(|߀.n'7G.w]Ad^P)L !,Q8pH,rl:ШtJZجvzU$xL.z&w{N^s}Ip bR H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ J(4-؃K x`ҪXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ}sÆbh+p`"L 3 !@ 7`ĄV(T ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌s@IQ v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l)",0c2Θ8-hhkAXPl(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhRIAl@(|߀.n'7G.w]Ad^P)L !,Q8pH,rl:ШtJZجvzU$xL.z&w{N^s}Ip bR H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ J(4-؃K x`ҪXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ}sÆbh+p`"L 3 !@ 7`ĄV(T ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌s@IQ v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l)",0c2Θ8-hhkAXPl(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhRIAl@(|߀.n'7G.w]Ad^P)L !,Q8pH,rl:ШtJZجvzU$xL.z&w{N^s}Ip bR H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ J(4-؃K x`ҪXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ}sÆbh+p`"L 3 !@ 7`ĄV(T ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌s@IQ v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l)",0c2Θ8-hhkAXPl(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhRIAl@(|߀.n'7G.w]Ad^P)L !,Q8pH,rl:ШtJZجvzU$xL.z&w{N^s}Ip bR H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ J(4-؃K x`ҪXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ}sÆbh+p`"L 3 !@ 7`ĄV(T ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌s@IQ v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫+k,l)",0c2Θ8-hhkAXPl(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhRIAl@(|߀.n'7G.w]Ad^P)L !,Q8pH,rl:ШtJZجvzU$xL.z&w{N^s}Ip bR H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ J(4-؃K x`ҪXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ}sÆbh+>} assign outside the current environment in a way that can be hard to reason about. Prefer fully-encapsulated functions wherever possible, or, if necessary, assign to a specific environment with \code{\link[=assign]{assign()}}. Recall that you can create an environment at the desired scope with \code{\link[=new.env]{new.env()}}. } } \keyword{datasets} lintr/man/length_test_linter.Rd0000644000176200001440000000162714510650642016352 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/length_test_linter.R \name{length_test_linter} \alias{length_test_linter} \title{Check for a common mistake where length is applied in the wrong place} \usage{ length_test_linter() } \description{ Usage like \code{length(x == 0)} is a mistake. If you intended to check \code{x} is empty, use \code{length(x) == 0}. Other mistakes are possible, but running \code{length()} on the outcome of a logical comparison is never the best choice. } \examples{ # will produce lints lint( text = "length(x == 0)", linters = length_test_linter() ) # okay lint( text = "length(x) > 0", linters = length_test_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=robustness_linters]{robustness} } lintr/man/linters_with_defaults.Rd0000644000176200001440000000430114577052532017056 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with.R \name{linters_with_defaults} \alias{linters_with_defaults} \alias{with_defaults} \title{Create a linter configuration based on defaults} \usage{ linters_with_defaults(..., defaults = default_linters) with_defaults(..., default = default_linters) } \arguments{ \item{...}{Arguments of elements to change. If unnamed, the argument is automatically named. If the named argument already exists in the list of linters, it is replaced by the new element. If it does not exist, it is added. If the value is \code{NULL}, the linter is removed.} \item{defaults, default}{Default list of linters to modify. Must be named.} } \description{ Make a new list based on \pkg{lintr}'s default linters. The result of this function is meant to be passed to the \code{linters} argument of \code{lint()}, or to be put in your configuration file. } \examples{ \dontshow{if (requireNamespace("withr", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # When using interactively you will usually pass the result onto `lint` or `lint_package()` f <- withr::local_tempfile(lines = "my_slightly_long_variable_name <- 2.3", fileext = "R") lint(f, linters = linters_with_defaults(line_length_linter = line_length_linter(120))) # the default linter list with a different line length cutoff my_linters <- linters_with_defaults(line_length_linter = line_length_linter(120)) # omit the argument name if you are just using different arguments my_linters <- linters_with_defaults(defaults = my_linters, object_name_linter("camelCase")) # remove assignment checks (with NULL), add absolute path checks my_linters <- linters_with_defaults( defaults = my_linters, assignment_linter = NULL, absolute_path_linter() ) # checking the included linters names(my_linters) \dontshow{\}) # examplesIf} } \seealso{ \itemize{ \item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. \item \link{all_linters} for basing off all available linters in lintr. \item \link{available_linters} to get a data frame of available linters. \item \link{linters} for a complete list of linters available in lintr. } } lintr/man/robustness_linters.Rd0000644000176200001440000000202014577052532016417 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{robustness_linters} \alias{robustness_linters} \title{Robustness linters} \description{ Linters highlighting code robustness issues, such as possibly wrong edge case behavior. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'robustness': \itemize{ \item{\code{\link{absolute_path_linter}}} \item{\code{\link{backport_linter}}} \item{\code{\link{class_equals_linter}}} \item{\code{\link{equals_na_linter}}} \item{\code{\link{for_loop_index_linter}}} \item{\code{\link{missing_package_linter}}} \item{\code{\link{namespace_linter}}} \item{\code{\link{nonportable_path_linter}}} \item{\code{\link{routine_registration_linter}}} \item{\code{\link{seq_linter}}} \item{\code{\link{strings_as_factors_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{undesirable_function_linter}}} \item{\code{\link{undesirable_operator_linter}}} } } lintr/man/trailing_whitespace_linter.Rd0000644000176200001440000000251014457657444020070 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/trailing_whitespace_linter.R \name{trailing_whitespace_linter} \alias{trailing_whitespace_linter} \title{Trailing whitespace linter} \usage{ trailing_whitespace_linter(allow_empty_lines = FALSE, allow_in_strings = TRUE) } \arguments{ \item{allow_empty_lines}{Suppress lints for lines that contain only whitespace.} \item{allow_in_strings}{Suppress lints for trailing whitespace in string constants.} } \description{ Check that there are no space characters at the end of source lines. } \examples{ # will produce lints lint( text = "x <- 1.2 ", linters = trailing_whitespace_linter() ) code_lines <- "a <- TRUE\n \nb <- FALSE" writeLines(code_lines) lint( text = code_lines, linters = trailing_whitespace_linter() ) # okay lint( text = "x <- 1.2", linters = trailing_whitespace_linter() ) lint( text = "x <- 1.2 # comment about this assignment", linters = trailing_whitespace_linter() ) code_lines <- "a <- TRUE\n \nb <- FALSE" writeLines(code_lines) lint( text = code_lines, linters = trailing_whitespace_linter(allow_empty_lines = TRUE) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=style_linters]{style} } lintr/man/T_and_F_symbol_linter.Rd0000644000176200001440000000211114577052532016705 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/T_and_F_symbol_linter.R \name{T_and_F_symbol_linter} \alias{T_and_F_symbol_linter} \title{\code{T} and \code{F} symbol linter} \usage{ T_and_F_symbol_linter() } \description{ Avoid the symbols \code{T} and \code{F}, and use \code{TRUE} and \code{FALSE} instead. } \examples{ # will produce lints lint( text = "x <- T; y <- F", linters = T_and_F_symbol_linter() ) lint( text = "T = 1.2; F = 2.4", linters = T_and_F_symbol_linter() ) # okay lint( text = "x <- c(TRUE, FALSE)", linters = T_and_F_symbol_linter() ) lint( text = "t = 1.2; f = 2.4", linters = T_and_F_symbol_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#logical-vectors} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=robustness_linters]{robustness}, \link[=style_linters]{style} } lintr/man/yoda_test_linter.Rd0000644000176200001440000000225014506330025016011 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/yoda_test_linter.R \name{yoda_test_linter} \alias{yoda_test_linter} \title{Block obvious "yoda tests"} \usage{ yoda_test_linter() } \description{ Yoda tests use \verb{(expected, actual)} instead of the more common \verb{(actual, expected)}. This is not always possible to detect statically; this linter focuses on the simple case of testing an expression against a literal value, e.g. \verb{(1L, foo(x))} should be \verb{(foo(x), 1L)}. } \examples{ # will produce lints lint( text = "expect_equal(2, x)", linters = yoda_test_linter() ) lint( text = 'expect_identical("a", x)', linters = yoda_test_linter() ) # okay lint( text = "expect_equal(x, 2)", linters = yoda_test_linter() ) lint( text = 'expect_identical(x, "a")', linters = yoda_test_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. \url{https://en.wikipedia.org/wiki/Yoda_conditions} } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/consistency_linters.Rd0000644000176200001440000000255614577052532016567 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/linter_tag_docs.R \name{consistency_linters} \alias{consistency_linters} \title{Consistency linters} \description{ Linters checking enforcing a consistent alternative if there are multiple syntactically valid ways to write something. } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'consistency': \itemize{ \item{\code{\link{assignment_linter}}} \item{\code{\link{class_equals_linter}}} \item{\code{\link{condition_message_linter}}} \item{\code{\link{consecutive_assertion_linter}}} \item{\code{\link{function_argument_linter}}} \item{\code{\link{if_not_else_linter}}} \item{\code{\link{implicit_integer_linter}}} \item{\code{\link{inner_combine_linter}}} \item{\code{\link{is_numeric_linter}}} \item{\code{\link{keyword_quote_linter}}} \item{\code{\link{length_levels_linter}}} \item{\code{\link{literal_coercion_linter}}} \item{\code{\link{numeric_leading_zero_linter}}} \item{\code{\link{object_name_linter}}} \item{\code{\link{paste_linter}}} \item{\code{\link{quotes_linter}}} \item{\code{\link{redundant_ifelse_linter}}} \item{\code{\link{scalar_in_linter}}} \item{\code{\link{seq_linter}}} \item{\code{\link{system_file_linter}}} \item{\code{\link{T_and_F_symbol_linter}}} \item{\code{\link{whitespace_linter}}} } } lintr/man/expect_lint_free.Rd0000644000176200001440000000077114250050336015766 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_lint.R \name{expect_lint_free} \alias{expect_lint_free} \title{Test that the package is lint free} \usage{ expect_lint_free(...) } \arguments{ \item{...}{arguments passed to \code{\link[=lint_package]{lint_package()}}} } \description{ This function is a thin wrapper around lint_package that simply tests there are no lints in the package. It can be used to ensure that your tests fail if the package contains lints. } lintr/man/line_length_linter.Rd0000644000176200001440000000165414457657444016343 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/line_length_linter.R \name{line_length_linter} \alias{line_length_linter} \title{Line length linter} \usage{ line_length_linter(length = 80L) } \arguments{ \item{length}{maximum line length allowed. Default is 80L (Hollerith limit).} } \description{ Check that the line length of both comments and code is less than \code{length}. } \examples{ # will produce lints lint( text = strrep("x", 23L), linters = line_length_linter(length = 20L) ) # okay lint( text = strrep("x", 21L), linters = line_length_linter(length = 40L) ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#long-lines} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/package_hooks_linter.Rd0000644000176200001440000000442014457657444016643 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/package_hooks_linter.R \name{package_hooks_linter} \alias{package_hooks_linter} \title{Package hooks linter} \usage{ package_hooks_linter() } \description{ Check various common "gotchas" in \code{\link[=.onLoad]{.onLoad()}}, \code{\link[=.onAttach]{.onAttach()}}, \code{\link[=.Last.lib]{.Last.lib()}}, and \code{\link[=.onDetach]{.onDetach()}} namespace hooks that will cause \verb{R CMD check} issues. See Writing R Extensions for details. } \details{ \enumerate{ \item \code{.onLoad()} shouldn't call \code{\link[=cat]{cat()}}, \code{\link[=message]{message()}}, \code{\link[=print]{print()}}, \code{\link[=writeLines]{writeLines()}}, \code{\link[=packageStartupMessage]{packageStartupMessage()}}, \code{\link[=require]{require()}}, \code{\link[=library]{library()}}, or \code{\link[=installed.packages]{installed.packages()}}. \item \code{.onAttach()} shouldn't call \code{cat()}, \code{message()}, \code{print()}, \code{writeLines()}, \code{\link[=library.dynam]{library.dynam()}}, \code{require()}, \code{library()}, or \code{installed.packages()}. \item \code{.Last.lib()} and \code{.onDetach()} shouldn't call \code{\link[=library.dynam.unload]{library.dynam.unload()}}. \item \code{.onLoad()} and \code{.onAttach()} should take two arguments, with names matching \verb{^lib} and \verb{^pkg}; \code{.Last.lib()} and \code{.onDetach()} should take one argument with name matching \verb{^lib}. } } \examples{ # will produce lints lint( text = ".onLoad <- function(lib, ...) { }", linters = package_hooks_linter() ) lint( text = ".onAttach <- function(lib, pkg) { require(foo) }", linters = package_hooks_linter() ) lint( text = ".onDetach <- function(pkg) { }", linters = package_hooks_linter() ) # okay lint( text = ".onLoad <- function(lib, pkg) { }", linters = package_hooks_linter() ) lint( text = '.onAttach <- function(lib, pkg) { loadNamespace("foo") }', linters = package_hooks_linter() ) lint( text = ".onDetach <- function(lib) { }", linters = package_hooks_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=correctness_linters]{correctness}, \link[=package_development_linters]{package_development}, \link[=style_linters]{style} } lintr/man/object_usage_linter.Rd0000644000176200001440000000364314457657444016505 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object_usage_linter.R \name{object_usage_linter} \alias{object_usage_linter} \title{Object usage linter} \usage{ object_usage_linter(interpret_glue = TRUE, skip_with = TRUE) } \arguments{ \item{interpret_glue}{If \code{TRUE}, interpret \code{\link[glue:glue]{glue::glue()}} calls to avoid false positives caused by local variables which are only used in a glue expression.} \item{skip_with}{A logical. If \code{TRUE} (default), code in \code{with()} expressions will be skipped. This argument will be passed to \code{skipWith} argument of \code{codetools::checkUsage()}.} } \description{ Check that closures have the proper usage using \code{\link[codetools:checkUsage]{codetools::checkUsage()}}. Note that this runs \code{\link[base:eval]{base::eval()}} on the code, so \strong{do not use with untrusted code}. } \examples{ # will produce lints lint( text = "foo <- function() { x <- 1 }", linters = object_usage_linter() ) # okay lint( text = "foo <- function(x) { x <- 1 }", linters = object_usage_linter() ) lint( text = "foo <- function() { x <- 1; return(x) }", linters = object_usage_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Linters}{ The following linters are tagged with 'package_development': \itemize{ \item{\code{\link{backport_linter}}} \item{\code{\link{conjunct_test_linter}}} \item{\code{\link{expect_comparison_linter}}} \item{\code{\link{expect_identical_linter}}} \item{\code{\link{expect_length_linter}}} \item{\code{\link{expect_named_linter}}} \item{\code{\link{expect_not_linter}}} \item{\code{\link{expect_null_linter}}} \item{\code{\link{expect_s3_class_linter}}} \item{\code{\link{expect_s4_class_linter}}} \item{\code{\link{expect_true_false_linter}}} \item{\code{\link{expect_type_linter}}} \item{\code{\link{package_hooks_linter}}} \item{\code{\link{yoda_test_linter}}} } } lintr/man/modify_defaults.Rd0000644000176200001440000000347514457657444015657 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with.R \name{modify_defaults} \alias{modify_defaults} \title{Modify lintr defaults} \usage{ modify_defaults(defaults, ...) } \arguments{ \item{defaults}{named list of elements to modify.} \item{...}{arguments of elements to change. If unnamed, the argument is automatically named. If the named argument already exists in \code{defaults}, it is replaced by the new element. If it does not exist, it is added. If the value is \code{NULL}, the element is removed.} } \value{ A modified list of elements, sorted by name. To achieve this sort in a platform-independent way, two transformations are applied to the names: (1) replace \verb{_} with \code{0} and (2) convert \code{\link[=tolower]{tolower()}}. } \description{ Modify a list of defaults by name, allowing for replacement, deletion and addition of new elements. } \examples{ # custom list of undesirable functions: # remove `sapply` (using `NULL`) # add `cat` (with an accompanying message), # add `print` (unnamed, i.e. with no accompanying message) # add `source` (as taken from `all_undesirable_functions`) my_undesirable_functions <- modify_defaults( defaults = default_undesirable_functions, sapply = NULL, "cat" = "No cat allowed", "print", all_undesirable_functions[["source"]] ) # list names of functions specified as undesirable names(my_undesirable_functions) } \seealso{ \itemize{ \item \link{linters_with_defaults} for basing off lintr's set of default linters. \item \link{all_linters} for basing off all available linters in lintr. \item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. \item \link{available_linters} to get a data frame of available linters. \item \link{linters} for a complete list of linters available in lintr. } } lintr/man/use_lintr.Rd0000644000176200001440000000232314577052532014462 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/use_lintr.R \name{use_lintr} \alias{use_lintr} \title{Use lintr in your project} \usage{ use_lintr(path = ".", type = c("tidyverse", "full")) } \arguments{ \item{path}{Path to project root, where a \code{.lintr} file should be created. If the \code{.lintr} file already exists, an error will be thrown.} \item{type}{What kind of configuration to create? \itemize{ \item \code{tidyverse} creates a minimal lintr config, based on the default linters (\code{\link[=linters_with_defaults]{linters_with_defaults()}}). These are suitable for following \href{https://style.tidyverse.org/}{the tidyverse style guide}. \item \code{full} creates a lintr config using all available linters via \code{\link[=linters_with_tags]{linters_with_tags()}}. }} } \value{ Path to the generated configuration, invisibly. } \description{ Create a minimal lintr config file as a starting point for customization } \examples{ if (FALSE) { # use the default set of linters lintr::use_lintr() # or try all linters lintr::use_lintr(type = "full") # then lintr::lint_dir() } } \seealso{ \code{vignette("lintr")} for detailed introduction to using and configuring lintr. } lintr/man/commas_linter.Rd0000644000176200001440000000267214506330025015305 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/commas_linter.R \name{commas_linter} \alias{commas_linter} \title{Commas linter} \usage{ commas_linter(allow_trailing = FALSE) } \arguments{ \item{allow_trailing}{If \code{TRUE}, the linter allows a comma to be followed directly by a closing bracket without a space.} } \description{ Check that all commas are followed by spaces, but do not have spaces before them. } \examples{ # will produce lints lint( text = "switch(op , x = foo, y = bar)", linters = commas_linter() ) lint( text = "mean(x,trim = 0.2,na.rm = TRUE)", linters = commas_linter() ) lint( text = "x[ ,, drop=TRUE]", linters = commas_linter() ) lint( text = "x[1,]", linters = commas_linter() ) # okay lint( text = "switch(op, x = foo, y = bar)", linters = commas_linter() ) lint( text = "switch(op, x = , y = bar)", linters = commas_linter() ) lint( text = "mean(x, trim = 0.2, na.rm = TRUE)", linters = commas_linter() ) lint( text = "a[1, , 2, , 3]", linters = commas_linter() ) lint( text = "x[1,]", linters = commas_linter(allow_trailing = TRUE) ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#commas} } } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/if_not_else_linter.Rd0000644000176200001440000000373514510656222016322 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/if_not_else_linter.R \name{if_not_else_linter} \alias{if_not_else_linter} \title{Block statements like if (!A) x else y} \usage{ if_not_else_linter(exceptions = c("is.null", "is.na", "missing")) } \arguments{ \item{exceptions}{Character vector of calls to exclude from linting. By default, \code{\link[=is.null]{is.null()}}, \code{\link[=is.na]{is.na()}}, and \code{\link[=missing]{missing()}} are excluded given the common idiom \code{!is.na(x)} as "x is present".} } \description{ \code{if (!A) x else y} is the same as \code{if (A) y else x}, but the latter is easier to reason about in the \verb{else} case. The former requires double negation that can be avoided by switching the statement order. } \details{ This only applies in the simple \verb{if/else} case. Statements like \code{if (!A) x else if (B) y else z} don't always have a simpler or more readable form. It also applies to \code{\link[=ifelse]{ifelse()}} and the package equivalents \code{dplyr::if_else()} and \code{data.table::fifelse()}. } \examples{ # will produce lints lint( text = "if (!A) x else y", linters = if_not_else_linter() ) lint( text = "if (!A) x else if (!B) y else z", linters = if_not_else_linter() ) lint( text = "ifelse(!is_treatment, x, y)", linters = if_not_else_linter() ) lint( text = "if (!is.null(x)) x else 2", linters = if_not_else_linter(exceptions = character()) ) # okay lint( text = "if (A) x else y", linters = if_not_else_linter() ) lint( text = "if (!A) x else if (B) z else y", linters = if_not_else_linter() ) lint( text = "ifelse(is_treatment, y, x)", linters = if_not_else_linter() ) lint( text = "if (!is.null(x)) x else 2", linters = if_not_else_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency}, \link[=readability_linters]{readability} } lintr/man/regex_subset_linter.Rd0000644000176200001440000000353214577052532016535 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/regex_subset_linter.R \name{regex_subset_linter} \alias{regex_subset_linter} \title{Require usage of direct methods for subsetting strings via regex} \usage{ regex_subset_linter() } \description{ Using \code{value = TRUE} in \code{\link[=grep]{grep()}} returns the subset of the input that matches the pattern, e.g. \code{grep("[a-m]", letters, value = TRUE)} will return the first 13 elements (\code{a} through \code{m}). } \details{ \code{letters[grep("[a-m]", letters)]} and \code{letters[grepl("[a-m]", letters)]} both return the same thing, but more circuitously and more verbosely. The \code{stringr} package also provides an even more readable alternative, namely \code{str_subset()}, which should be preferred to versions using \code{str_detect()} and \code{str_which()}. } \section{Exceptions}{ Note that \code{x[grep(pattern, x)]} and \code{grep(pattern, x, value = TRUE)} are not \emph{completely} interchangeable when \code{x} is not character (most commonly, when \code{x} is a factor), because the output of the latter will be a character vector while the former remains a factor. It still may be preferable to refactor such code, as it may be faster to match the pattern on \code{levels(x)} and use that to subset instead. } \examples{ # will produce lints lint( text = "x[grep(pattern, x)]", linters = regex_subset_linter() ) lint( text = "x[stringr::str_which(x, pattern)]", linters = regex_subset_linter() ) # okay lint( text = "grep(pattern, x, value = TRUE)", linters = regex_subset_linter() ) lint( text = "stringr::str_subset(x, pattern)", linters = regex_subset_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency} } lintr/man/checkstyle_output.Rd0000644000176200001440000000073314262410705016226 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lint.R \name{checkstyle_output} \alias{checkstyle_output} \title{Checkstyle Report for lint results} \usage{ checkstyle_output(lints, filename = "lintr_results.xml") } \arguments{ \item{lints}{the linting results.} \item{filename}{the name of the output report} } \description{ Generate a report of the linting results using the \href{https://checkstyle.sourceforge.io}{Checkstyle} XML format. } lintr/man/expect_named_linter.Rd0000644000176200001440000000234414506330025016456 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_named_linter.R \name{expect_named_linter} \alias{expect_named_linter} \title{Require usage of \code{expect_named(x, n)} over \code{expect_equal(names(x), n)}} \usage{ expect_named_linter() } \description{ \code{\link[testthat:expect_named]{testthat::expect_named()}} exists specifically for testing the \code{\link[=names]{names()}} of an object. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = 'expect_equal(names(x), "a")', linters = expect_named_linter() ) # okay lint( text = 'expect_named(x, "a")', linters = expect_named_linter() ) lint( text = 'expect_equal(colnames(x), "a")', linters = expect_named_linter() ) lint( text = 'expect_equal(dimnames(x), "a")', linters = expect_named_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat}, \link[=readability_linters]{readability} } lintr/man/expect_s3_class_linter.Rd0000644000176200001440000000264314506330025017106 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_s3_class_linter.R \name{expect_s3_class_linter} \alias{expect_s3_class_linter} \title{Require usage of \code{expect_s3_class()}} \usage{ expect_s3_class_linter() } \description{ \code{\link[testthat:inheritance-expectations]{testthat::expect_s3_class()}} exists specifically for testing the class of S3 objects. \code{\link[testthat:equality-expectations]{testthat::expect_equal()}}, \code{\link[testthat:equality-expectations]{testthat::expect_identical()}}, and \code{\link[testthat:logical-expectations]{testthat::expect_true()}} can also be used for such tests, but it is better to use the tailored function instead. } \examples{ # will produce lints lint( text = 'expect_equal(class(x), "data.frame")', linters = expect_s3_class_linter() ) lint( text = 'expect_equal(class(x), "numeric")', linters = expect_s3_class_linter() ) # okay lint( text = 'expect_s3_class(x, "data.frame")', linters = expect_s3_class_linter() ) lint( text = 'expect_type(x, "double")', linters = expect_s3_class_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \code{\link[=expect_s4_class_linter]{expect_s4_class_linter()}} } } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat} } lintr/man/any_duplicated_linter.Rd0000644000176200001440000000232414457657444017033 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/any_duplicated_linter.R \name{any_duplicated_linter} \alias{any_duplicated_linter} \title{Require usage of \code{anyDuplicated(x) > 0} over \code{any(duplicated(x))}} \usage{ any_duplicated_linter() } \description{ \code{\link[=anyDuplicated]{anyDuplicated()}} exists as a replacement for \code{any(duplicated(.))}, which is more efficient for simple objects, and is at worst equally efficient. Therefore, it should be used in all situations instead of the latter. } \details{ Also match usage like \code{length(unique(x$col)) == nrow(x)}, which can be replaced by \code{anyDuplicated(x$col) == 0L}. } \examples{ # will produce lints lint( text = "any(duplicated(x), na.rm = TRUE)", linters = any_duplicated_linter() ) lint( text = "length(unique(x)) == length(x)", linters = any_duplicated_linter() ) # okay lint( text = "anyDuplicated(x)", linters = any_duplicated_linter() ) lint( text = "anyDuplicated(x) == 0L", linters = any_duplicated_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency} } lintr/man/normalize_exclusions.Rd0000644000176200001440000000277414250050336016730 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/exclude.R \name{normalize_exclusions} \alias{normalize_exclusions} \title{Normalize lint exclusions} \usage{ normalize_exclusions(x, normalize_path = TRUE, root = getwd(), pattern = NULL) } \arguments{ \item{x}{Exclusion specification \itemize{ \item A character vector of filenames or directories relative to \code{root} \item A named list of integers specifying lines to be excluded per file \item A named list of named lists specifying linters and lines to be excluded for the linters per file. }} \item{normalize_path}{Should the names of the returned exclusion list be normalized paths? If no, they will be relative to \code{root}.} \item{root}{Base directory for relative filename resolution.} \item{pattern}{If non-NULL, only exclude files in excluded directories if they match \code{pattern}. Passed to \link[base:list.files]{list.files} if a directory is excluded.} } \value{ A named list of file exclusions. The names of the list specify the filenames to be excluded. Each file exclusion is a possibly named list containing line numbers to exclude, or the sentinel \code{Inf} for completely excluded files. If the an entry is named, the exclusions only take effect for the linter with the same name. If \code{normalize_path} is \code{TRUE}, file names will be normalized relative to \code{root}. Otherwise the paths are left as provided (relative to \code{root} or absolute). } \description{ Normalize lint exclusions } \keyword{internal} lintr/man/cyclocomp_linter.Rd0000644000176200001440000000205114457657444016033 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cyclocomp_linter.R \name{cyclocomp_linter} \alias{cyclocomp_linter} \title{Cyclomatic complexity linter} \usage{ cyclocomp_linter(complexity_limit = 15L) } \arguments{ \item{complexity_limit}{Maximum cyclomatic complexity, default 15. Expressions more complex than this are linted. See \code{\link[cyclocomp:cyclocomp]{cyclocomp::cyclocomp()}}.} } \description{ Check for overly complicated expressions. See \code{\link[cyclocomp:cyclocomp]{cyclocomp::cyclocomp()}}. } \examples{ # will produce lints lint( text = "if (TRUE) 1 else 2", linters = cyclocomp_linter(complexity_limit = 1L) ) # okay lint( text = "if (TRUE) 1 else 2", linters = cyclocomp_linter(complexity_limit = 2L) ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/expect_identical_linter.Rd0000644000176200001440000000430514506330025017325 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expect_identical_linter.R \name{expect_identical_linter} \alias{expect_identical_linter} \title{Require usage of \code{expect_identical(x, y)} where appropriate} \usage{ expect_identical_linter() } \description{ This linter enforces the usage of \code{\link[testthat:equality-expectations]{testthat::expect_identical()}} as the default expectation for comparisons in a testthat suite. \code{expect_true(identical(x, y))} is an equivalent but unadvised method of the same test. Further, \code{\link[testthat:equality-expectations]{testthat::expect_equal()}} should only be used when \code{expect_identical()} is inappropriate, i.e., when \code{x} and \code{y} need only be \emph{numerically equivalent} instead of fully identical (in which case, provide the \verb{tolerance=} argument to \code{expect_equal()} explicitly). This also applies when it's inconvenient to check full equality (e.g., names can be ignored, in which case \code{ignore_attr = "names"} should be supplied to \code{expect_equal()} (or, for 2nd edition, \code{check.attributes = FALSE}). } \section{Exceptions}{ The linter allows \code{expect_equal()} in three circumstances: \enumerate{ \item A named argument is set (e.g. \code{ignore_attr} or \code{tolerance}) \item Comparison is made to an explicit decimal, e.g. \code{expect_equal(x, 1.0)} (implicitly setting \code{tolerance}) \item \code{...} is passed (wrapper functions which might set arguments such as \code{ignore_attr} or \code{tolerance}) } } \examples{ # will produce lints lint( text = "expect_equal(x, y)", linters = expect_identical_linter() ) lint( text = "expect_true(identical(x, y))", linters = expect_identical_linter() ) # okay lint( text = "expect_identical(x, y)", linters = expect_identical_linter() ) lint( text = "expect_equal(x, y, check.attributes = FALSE)", linters = expect_identical_linter() ) lint( text = "expect_equal(x, y, tolerance = 1e-6)", linters = expect_identical_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=package_development_linters]{package_development}, \link[=pkg_testthat_linters]{pkg_testthat} } lintr/man/todo_comment_linter.Rd0000644000176200001440000000205314577052532016522 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/comment_linters.R \name{todo_comment_linter} \alias{todo_comment_linter} \title{TODO comment linter} \usage{ todo_comment_linter(todo = c("todo", "fixme")) } \arguments{ \item{todo}{Vector of strings that identify TODO comments.} } \description{ Check that the source contains no TODO comments (case-insensitive). } \examples{ # will produce lints lint( text = "x + y # TODO", linters = todo_comment_linter() ) lint( text = "pi <- 1.0 # FIXME", linters = todo_comment_linter() ) lint( text = "x <- TRUE # hack", linters = todo_comment_linter(todo = c("todo", "fixme", "hack")) ) # okay lint( text = "x + y # my informative comment", linters = todo_comment_linter() ) lint( text = "pi <- 3.14", linters = todo_comment_linter() ) lint( text = "x <- TRUE", linters = todo_comment_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=style_linters]{style} } lintr/man/paste_linter.Rd0000644000176200001440000000777514577052532015167 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/paste_linter.R \name{paste_linter} \alias{paste_linter} \title{Raise lints for several common poor usages of \code{paste()}} \usage{ paste_linter( allow_empty_sep = FALSE, allow_to_string = FALSE, allow_file_path = c("double_slash", "always", "never") ) } \arguments{ \item{allow_empty_sep}{Logical, default \code{FALSE}. If \code{TRUE}, usage of \code{paste()} with \code{sep = ""} is not linted.} \item{allow_to_string}{Logical, default \code{FALSE}. If \code{TRUE}, usage of \code{paste()} and \code{paste0()} with \code{collapse = ", "} is not linted.} \item{allow_file_path}{String, one of \code{"never"}, \code{"double_slash"}, or \code{"always"}; \code{"double_slash"} by default. If \code{"never"}, usage of \code{paste()} and \code{paste0()} to construct file paths is not linted. If \code{"double_slash"}, strings containing consecutive forward slashes will not lint. The main use case here is for URLs -- "paths" like \code{"https://"} will not induce lints, since constructing them with \code{file.path()} might be deemed unnatural. Lastly, if \code{"always"}, strings with consecutive forward slashes will also lint. Note that \code{"//"} is never linted when it comes at the beginning or end of the input, to avoid requiring empty inputs like \code{file.path("", ...)} or \code{file.path(..., "")}.} } \description{ The following issues are linted by default by this linter (see arguments for which can be de-activated optionally): } \details{ \enumerate{ \item Block usage of \code{\link[=paste]{paste()}} with \code{sep = ""}. \code{\link[=paste0]{paste0()}} is a faster, more concise alternative. \item Block usage of \code{paste()} or \code{paste0()} with \code{collapse = ", "}. \code{\link[=toString]{toString()}} is a direct wrapper for this, and alternatives like \code{\link[glue:glue_collapse]{glue::glue_collapse()}} might give better messages for humans. \item Block usage of \code{paste0()} that supplies \verb{sep=} -- this is not a formal argument to \code{paste0}, and is likely to be a mistake. \item Block usage of \code{paste()} / \code{paste0()} combined with \code{\link[=rep]{rep()}} that could be replaced by \code{\link[=strrep]{strrep()}}. \code{strrep()} can handle the task of building a block of repeated strings (e.g. often used to build "horizontal lines" for messages). This is both more readable and skips the (likely small) overhead of putting two strings into the global string cache when only one is needed. Only target scalar usages -- \code{strrep} can handle more complicated cases (e.g. \code{strrep(letters, 26:1)}, but those aren't as easily translated from a \code{paste(collapse=)} call. } } \examples{ # will produce lints lint( text = 'paste("a", "b", sep = "")', linters = paste_linter() ) lint( text = 'paste(c("a", "b"), collapse = ", ")', linters = paste_linter() ) lint( text = 'paste0(c("a", "b"), sep = " ")', linters = paste_linter() ) lint( text = 'paste0(rep("*", 10L), collapse = "")', linters = paste_linter() ) lint( text = 'paste0("http://site.com/", path)', linters = paste_linter(allow_file_path = "never") ) # okay lint( text = 'paste0("a", "b")', linters = paste_linter() ) lint( text = 'paste("a", "b", sep = "")', linters = paste_linter(allow_empty_sep = TRUE) ) lint( text = 'toString(c("a", "b"))', linters = paste_linter() ) lint( text = 'paste(c("a", "b"), collapse = ", ")', linters = paste_linter(allow_to_string = TRUE) ) lint( text = 'paste(c("a", "b"))', linters = paste_linter() ) lint( text = 'strrep("*", 10L)', linters = paste_linter() ) lint( text = 'paste0(year, "/", month, "/", day)', linters = paste_linter(allow_file_path = "always") ) lint( text = 'paste0("http://site.com/", path)', linters = paste_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=configurable_linters]{configurable}, \link[=consistency_linters]{consistency} } lintr/man/class_equals_linter.Rd0000644000176200001440000000244714457657444016533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/class_equals_linter.R \name{class_equals_linter} \alias{class_equals_linter} \title{Block comparison of class with \code{==}} \usage{ class_equals_linter() } \description{ Usage like \code{class(x) == "character"} is prone to error since class in R is in general a vector. The correct version for S3 classes is \code{\link[=inherits]{inherits()}}: \code{inherits(x, "character")}. Often, class \code{k} will have an \code{is.} equivalent, for example \code{\link[=is.character]{is.character()}} or \code{\link[=is.data.frame]{is.data.frame()}}. } \details{ Similar reasoning applies for \code{class(x) \%in\% "character"}. } \examples{ # will produce lints lint( text = 'is_lm <- class(x) == "lm"', linters = class_equals_linter() ) lint( text = 'if ("lm" \%in\% class(x)) is_lm <- TRUE', linters = class_equals_linter() ) # okay lint( text = 'is_lm <- inherits(x, "lm")', linters = class_equals_linter() ) lint( text = 'if (inherits(x, "lm")) is_lm <- TRUE', linters = class_equals_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=consistency_linters]{consistency}, \link[=robustness_linters]{robustness} } lintr/man/make_linter_from_xpath.Rd0000644000176200001440000000226014577052532017177 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/make_linter_from_xpath.R \name{make_linter_from_xpath} \alias{make_linter_from_xpath} \title{Create a linter from an XPath} \usage{ make_linter_from_xpath( xpath, lint_message, type = c("warning", "style", "error"), level = c("expression", "file") ) } \arguments{ \item{xpath}{Character string, an XPath identifying R code to lint. See \code{\link[xmlparsedata:xml_parse_data]{xmlparsedata::xml_parse_data()}} and \code{\link[=get_source_expressions]{get_source_expressions()}}.} \item{lint_message}{The message to be included as the \code{message} to the \code{Lint} object. If \code{lint_message} is a character vector the same length as \code{xml}, the \code{i}-th lint will be given the \code{i}-th message.} \item{type}{type of lint.} \item{level}{Which level of expression is being tested? \code{"expression"} means an individual expression, while \code{"file"} means all expressions in the current file are available.} } \description{ Create a linter from an XPath } \examples{ number_linter <- make_linter_from_xpath("//NUM_CONST", "This is a number.") lint(text = "1 + 2", linters = number_linter()) } lintr/man/exclude.Rd0000644000176200001440000000371014517602346014106 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/exclude.R \name{exclude} \alias{exclude} \title{Exclude lines or files from linting} \usage{ exclude(lints, exclusions = settings$exclusions, linter_names = NULL, ...) } \arguments{ \item{lints}{that need to be filtered.} \item{exclusions}{manually specified exclusions} \item{linter_names}{character vector of names of the active linters, used for parsing inline exclusions.} \item{...}{additional arguments passed to \code{\link[=parse_exclusions]{parse_exclusions()}}} } \description{ Exclude lines or files from linting } \details{ Exclusions can be specified in three different ways. \enumerate{ \item Single line in the source file. default: \verb{# nolint}, possibly followed by a listing of linters to exclude. If the listing is missing, all linters are excluded on that line. The default listing format is \verb{# nolint: linter_name, linter2_name.}. There may not be anything between the colon and the line exclusion tag and the listing must be terminated with a full stop (\code{.}) for the linter list to be respected. \item Line range in the source file. default: \verb{# nolint start}, \verb{# nolint end}. \verb{# nolint start} accepts linter lists in the same form as \verb{# nolint}. \item Exclusions parameter, a list with named and/or unnamed entries. Outer elements have the following characteristics: \enumerate{ \item Unnamed elements specify filenames or directories. \item Named elements are a vector or list of line numbers, with \code{Inf} indicating 'all lines'. The name gives a path relative to the config. \enumerate{ \item Unnamed elements denote exclusion of all linters in the given path or directory. \item Named elements, where the name specifies a linter, denote exclusion for that linter. For convenience, a vector can be used in place of a list whenever it would not introduce ambiguity, e.g. a character vector of files to exclude or a vector of lines to exclude. } } } } lintr/man/lengths_linter.Rd0000644000176200001440000000175314457657444015517 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lengths_linter.R \name{lengths_linter} \alias{lengths_linter} \title{Require usage of \code{lengths()} where possible} \usage{ lengths_linter() } \description{ \code{\link[=lengths]{lengths()}} is a function that was added to base R in version 3.2.0 to get the length of each element of a list. It is equivalent to \code{sapply(x, length)}, but faster and more readable. } \examples{ # will produce lints lint( text = "sapply(x, length)", linters = lengths_linter() ) lint( text = "vapply(x, length, integer(1L))", linters = lengths_linter() ) lint( text = "purrr::map_int(x, length)", linters = lengths_linter() ) # okay lint( text = "lengths(x)", linters = lengths_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=best_practices_linters]{best_practices}, \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/man/sarif_output.Rd0000644000176200001440000000070214263437277015206 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lint.R \name{sarif_output} \alias{sarif_output} \title{SARIF Report for lint results} \usage{ sarif_output(lints, filename = "lintr_results.sarif") } \arguments{ \item{lints}{the linting results.} \item{filename}{the name of the output report} } \description{ Generate a report of the linting results using the \href{https://sarifweb.azurewebsites.net/}{SARIF} format. } lintr/man/function_left_parentheses_linter.Rd0000644000176200001440000000260114457657444021304 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/function_left_parentheses_linter.R \name{function_left_parentheses_linter} \alias{function_left_parentheses_linter} \title{Function left parentheses linter} \usage{ function_left_parentheses_linter() } \description{ Check that all left parentheses in a function call do not have spaces before them (e.g. \code{mean (1:3)}). Although this is syntactically valid, it makes the code difficult to read. } \details{ Exceptions are made for control flow functions (\code{if}, \code{for}, etc.). } \examples{ # will produce lints lint( text = "mean (x)", linters = function_left_parentheses_linter() ) lint( text = "stats::sd(c (x, y, z))", linters = function_left_parentheses_linter() ) # okay lint( text = "mean(x)", linters = function_left_parentheses_linter() ) lint( text = "stats::sd(c(x, y, z))", linters = function_left_parentheses_linter() ) lint( text = "foo <- function(x) (x + 1)", linters = function_left_parentheses_linter() ) } \seealso{ \itemize{ \item \link{linters} for a complete list of linters available in lintr. \item \url{https://style.tidyverse.org/syntax.html#parentheses} \item \code{\link[=spaces_left_parentheses_linter]{spaces_left_parentheses_linter()}} } } \section{Tags}{ \link[=default_linters]{default}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/xml_nodes_to_lints.Rd0000644000176200001440000000502014250050336016342 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xml_nodes_to_lints.R \name{xml_nodes_to_lints} \alias{xml_nodes_to_lints} \title{Convert an XML node or nodeset into a Lint} \usage{ xml_nodes_to_lints( xml, source_expression, lint_message, type = c("style", "warning", "error"), column_number_xpath = range_start_xpath, range_start_xpath = "number(./@col1)", range_end_xpath = "number(./@col2)" ) } \arguments{ \item{xml}{An \code{xml_node} object (to generate one \code{Lint}) or an \code{xml_nodeset} object (to generate several \code{Lint}s), e.g. as returned by \code{\link[xml2:xml_find_all]{xml2::xml_find_all()}} or \code{\link[xml2:xml_find_all]{xml2::xml_find_first()}} or a list of \code{xml_node} objects.} \item{source_expression}{A source expression object, e.g. as returned typically by \code{\link[=lint]{lint()}}, or more generally by \code{\link[=get_source_expressions]{get_source_expressions()}}.} \item{lint_message}{The message to be included as the \code{message} to the \code{Lint} object. If \code{lint_message} is a character vector the same length as \code{xml}, the \code{i}-th lint will be given the \code{i}-th message.} \item{type}{type of lint.} \item{column_number_xpath}{XPath expression to return the column number location of the lint. Defaults to the start of the range matched by \code{range_start_xpath}. See details for more information.} \item{range_start_xpath}{XPath expression to return the range start location of the lint. Defaults to the start of the expression matched by \code{xml}. See details for more information.} \item{range_end_xpath}{XPath expression to return the range end location of the lint. Defaults to the end of the expression matched by \code{xml}. See details for more information.} } \value{ For \code{xml_node}s, a \code{lint}. For \code{xml_nodeset}s, \code{lints} (a list of \code{lint}s). } \description{ Convenience function for converting nodes matched by XPath-based linter logic into a \code{\link[=Lint]{Lint()}} object to return. } \details{ The location XPaths, \code{column_number_xpath}, \code{range_start_xpath} and \code{range_end_xpath} are evaluated using \code{\link[xml2:xml_find_all]{xml2::xml_find_num()}} and will usually be of the form \code{"number(./relative/xpath)"}. Note that the location line number cannot be changed and lints spanning multiple lines will ignore \code{range_end_xpath}. \code{column_number_xpath} and \code{range_start_xpath} are assumed to always refer to locations on the starting line of the \code{xml} node. } lintr/man/pipe_consistency_linter.Rd0000644000176200001440000000233014510656222017400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pipe_consistency_linter.R \name{pipe_consistency_linter} \alias{pipe_consistency_linter} \title{Pipe consistency linter} \usage{ pipe_consistency_linter(pipe = c("auto", "\%>\%", "|>")) } \arguments{ \item{pipe}{Which pipe operator is valid (either \code{"\%>\%"} or \code{"|>"}). By default (\code{"auto"}), the linter has no preference but will check that each file uses only one type of pipe operator.} } \description{ Check that pipe operators are used consistently by file, or optionally specify one valid pipe operator. } \examples{ # will produce lints lint( text = "1:3 |> mean() \%>\% as.character()", linters = pipe_consistency_linter() ) lint( text = "1:3 \%>\% mean() \%>\% as.character()", linters = pipe_consistency_linter("|>") ) # okay lint( text = "1:3 \%>\% mean() \%>\% as.character()", linters = pipe_consistency_linter() ) lint( text = "1:3 |> mean() |> as.character()", linters = pipe_consistency_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=configurable_linters]{configurable}, \link[=readability_linters]{readability}, \link[=style_linters]{style} } lintr/man/parse_exclusions.Rd0000644000176200001440000000266514506330025016041 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/exclude.R \name{parse_exclusions} \alias{parse_exclusions} \title{read a source file and parse all the excluded lines from it} \usage{ parse_exclusions( file, exclude = settings$exclude, exclude_next = settings$exclude_next, exclude_start = settings$exclude_start, exclude_end = settings$exclude_end, exclude_linter = settings$exclude_linter, exclude_linter_sep = settings$exclude_linter_sep, lines = NULL, linter_names = NULL ) } \arguments{ \item{file}{R source file} \item{exclude}{Regular expression used to mark lines to exclude.} \item{exclude_next}{Regular expression used to mark lines immediately preceding excluded lines.} \item{exclude_start}{Regular expression used to mark the start of an excluded range.} \item{exclude_end}{Regular expression used to mark the end of an excluded range.} \item{exclude_linter}{Regular expression used to capture a list of to-be-excluded linters immediately following a \code{exclude} or \code{exclude_start} marker.} \item{exclude_linter_sep}{Regular expression used to split a linter list into individual linter names for exclusion.} \item{lines}{A character vector of the content lines of \code{file}.} \item{linter_names}{Names of active linters.} } \value{ A possibly named list of excluded lines, possibly for specific linters. } \description{ read a source file and parse all the excluded lines from it } lintr/man/missing_package_linter.Rd0000644000176200001440000000135214457657444017172 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/missing_package_linter.R \name{missing_package_linter} \alias{missing_package_linter} \title{Missing package linter} \usage{ missing_package_linter() } \description{ Check for missing packages in \code{library()}, \code{require()}, \code{loadNamespace()}, and \code{requireNamespace()} calls. } \examples{ # will produce lints lint( text = "library(xyzxyz)", linters = missing_package_linter() ) # okay lint( text = "library(stats)", linters = missing_package_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=common_mistakes_linters]{common_mistakes}, \link[=robustness_linters]{robustness} } lintr/man/default_settings.Rd0000644000176200001440000000337514517602346016030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zzz.R \docType{data} \name{default_settings} \alias{default_settings} \alias{settings} \alias{config} \alias{lintr-config} \alias{lintr-settings} \alias{.lintr} \title{Default lintr settings} \format{ An object of class \code{list} of length 13. } \usage{ default_settings } \description{ The default settings consist of \itemize{ \item \code{linters}: a list of default linters (see \code{\link[=default_linters]{default_linters()}}) \item \code{encoding}: the character encoding assumed for the file \item \code{exclude}: pattern used to exclude a line of code \item \code{exclude_start}, \code{exclude_end}: patterns used to mark start and end of the code block to exclude \item \code{exclude_linter}, \code{exclude_linter_sep}: patterns used to exclude linters \item \code{exclusions}: a list of exclusions, see \code{\link[=exclude]{exclude()}} for a complete description of valid values. \item \code{cache_directory}: location of cache directory \item \code{comment_token}: a GitHub token character \item \code{comment_bot}: decides if lintr comment bot on GitHub can comment on commits \item \code{error_on_lint}: decides if error should be produced when any lints are found } There are no settings without defaults, i.e., this list describes every valid setting. } \examples{ # available settings names(default_settings) # linters included by default names(default_settings$linters) # default values for a few of the other settings default_settings[c( "encoding", "exclude", "exclude_start", "exclude_end", "exclude_linter", "exclude_linter_sep", "exclusions", "error_on_lint" )] } \seealso{ \code{\link[=read_settings]{read_settings()}}, \link{default_linters} } \keyword{datasets} lintr/man/matrix_apply_linter.Rd0000644000176200001440000000201214457657444016551 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/matrix_apply_linter.R \name{matrix_apply_linter} \alias{matrix_apply_linter} \title{Require usage of \code{colSums(x)} or \code{rowSums(x)} over \code{apply(x, ., sum)}} \usage{ matrix_apply_linter() } \description{ \code{\link[=colSums]{colSums()}} and \code{\link[=rowSums]{rowSums()}} are clearer and more performant alternatives to \code{apply(x, 2, sum)} and \code{apply(x, 1, sum)} respectively in the case of 2D arrays, or matrices } \examples{ # will produce lints lint( text = "apply(x, 1, sum)", linters = matrix_apply_linter() ) lint( text = "apply(x, 2, sum)", linters = matrix_apply_linter() ) lint( text = "apply(x, 2, sum, na.rm = TRUE)", linters = matrix_apply_linter() ) lint( text = "apply(x, 2:4, sum)", linters = matrix_apply_linter() ) } \seealso{ \link{linters} for a complete list of linters available in lintr. } \section{Tags}{ \link[=efficiency_linters]{efficiency}, \link[=readability_linters]{readability} } lintr/DESCRIPTION0000644000176200001440000001235714600212472013116 0ustar liggesusersPackage: lintr Title: A 'Linter' for R Code Version: 3.1.2 Authors@R: c( person("Jim", "Hester", , role = "aut"), person("Florent", "Angly", role = "aut", comment = "fangly"), person("Russ", "Hyde", role = "aut"), person("Michael", "Chirico", email = "michaelchirico4@gmail.com", role = c("aut", "cre")), person("Kun", "Ren", role = "aut"), person("Alexander", "Rosenstock", role = "aut", comment = "AshesITR"), person("Indrajeet", "Patil", , "patilindrajeet.science@gmail.com", role = "aut", comment = c(ORCID = "0000-0003-1995-6531", Twitter = "@patilindrajeets")) ) Description: Checks adherence to a given style, syntax errors and possible semantic issues. Supports on the fly checking of R code edited with 'RStudio IDE', 'Emacs', 'Vim', 'Sublime Text', 'Atom' and 'Visual Studio Code'. License: MIT + file LICENSE URL: https://github.com/r-lib/lintr, https://lintr.r-lib.org BugReports: https://github.com/r-lib/lintr/issues Depends: R (>= 3.5) Imports: backports (>= 1.1.7), codetools, cyclocomp, digest, glue, knitr, rex, stats, utils, xml2 (>= 1.0.0), xmlparsedata (>= 1.0.5) Suggests: bookdown, crayon, httr (>= 1.2.1), jsonlite, mockery, patrick, rlang, rmarkdown, rstudioapi (>= 0.2), testthat (>= 3.1.5), tibble, tufte, withr (>= 2.5.0) Enhances: data.table VignetteBuilder: knitr Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 RoxygenNote: 7.3.1 Collate: 'make_linter_from_xpath.R' 'xp_utils.R' 'utils.R' 'AAA.R' 'T_and_F_symbol_linter.R' 'absolute_path_linter.R' 'actions.R' 'addins.R' 'any_duplicated_linter.R' 'any_is_na_linter.R' 'assignment_linter.R' 'backport_linter.R' 'boolean_arithmetic_linter.R' 'brace_linter.R' 'cache.R' 'class_equals_linter.R' 'commas_linter.R' 'comment_linters.R' 'comments.R' 'condition_message_linter.R' 'conjunct_test_linter.R' 'consecutive_assertion_linter.R' 'cyclocomp_linter.R' 'declared_functions.R' 'deprecated.R' 'duplicate_argument_linter.R' 'empty_assignment_linter.R' 'equals_na_linter.R' 'exclude.R' 'expect_comparison_linter.R' 'expect_identical_linter.R' 'expect_length_linter.R' 'expect_lint.R' 'expect_named_linter.R' 'expect_not_linter.R' 'expect_null_linter.R' 'expect_s3_class_linter.R' 'expect_s4_class_linter.R' 'expect_true_false_linter.R' 'expect_type_linter.R' 'extract.R' 'extraction_operator_linter.R' 'fixed_regex_linter.R' 'for_loop_index_linter.R' 'function_argument_linter.R' 'function_left_parentheses_linter.R' 'function_return_linter.R' 'get_source_expressions.R' 'ids_with_token.R' 'if_not_else_linter.R' 'ifelse_censor_linter.R' 'implicit_assignment_linter.R' 'implicit_integer_linter.R' 'indentation_linter.R' 'infix_spaces_linter.R' 'inner_combine_linter.R' 'is_lint_level.R' 'is_numeric_linter.R' 'keyword_quote_linter.R' 'length_levels_linter.R' 'length_test_linter.R' 'lengths_linter.R' 'library_call_linter.R' 'line_length_linter.R' 'lint.R' 'linter_tag_docs.R' 'linter_tags.R' 'lintr-deprecated.R' 'lintr-package.R' 'literal_coercion_linter.R' 'make_linter_from_regex.R' 'matrix_apply_linter.R' 'methods.R' 'missing_argument_linter.R' 'missing_package_linter.R' 'namespace.R' 'namespace_linter.R' 'nested_ifelse_linter.R' 'nonportable_path_linter.R' 'numeric_leading_zero_linter.R' 'object_length_linter.R' 'object_name_linter.R' 'object_usage_linter.R' 'outer_negation_linter.R' 'package_hooks_linter.R' 'paren_body_linter.R' 'paste_linter.R' 'path_utils.R' 'pipe_call_linter.R' 'pipe_consistency_linter.R' 'pipe_continuation_linter.R' 'quotes_linter.R' 'redundant_equals_linter.R' 'redundant_ifelse_linter.R' 'regex_subset_linter.R' 'repeat_linter.R' 'routine_registration_linter.R' 'scalar_in_linter.R' 'semicolon_linter.R' 'seq_linter.R' 'settings.R' 'settings_utils.R' 'shared_constants.R' 'sort_linter.R' 'spaces_inside_linter.R' 'spaces_left_parentheses_linter.R' 'sprintf_linter.R' 'string_boundary_linter.R' 'strings_as_factors_linter.R' 'system_file_linter.R' 'trailing_blank_lines_linter.R' 'trailing_whitespace_linter.R' 'tree_utils.R' 'undesirable_function_linter.R' 'undesirable_operator_linter.R' 'unnecessary_concatenation_linter.R' 'unnecessary_lambda_linter.R' 'unnecessary_nested_if_linter.R' 'unnecessary_placeholder_linter.R' 'unreachable_code_linter.R' 'unused_import_linter.R' 'use_lintr.R' 'vector_logic_linter.R' 'whitespace_linter.R' 'with.R' 'with_id.R' 'xml_nodes_to_lints.R' 'yoda_test_linter.R' 'zzz.R' Language: en-US NeedsCompilation: no Packaged: 2024-03-24 22:09:44 UTC; michael Author: Jim Hester [aut], Florent Angly [aut] (fangly), Russ Hyde [aut], Michael Chirico [aut, cre], Kun Ren [aut], Alexander Rosenstock [aut] (AshesITR), Indrajeet Patil [aut] (, @patilindrajeets) Maintainer: Michael Chirico Repository: CRAN Date/Publication: 2024-03-25 06:10:02 UTC lintr/build/0000755000176200001440000000000014600122247012477 5ustar liggesuserslintr/build/vignette.rds0000644000176200001440000000045114600122247015036 0ustar liggesusersQK0dzN['S}_bs/PMB{@$5v2]0 B 9NS8"|b\or%͝6[d r\Pײ3h3ȝB!IqN=)qjkDyɍ?[i|w?lintr/build/lintr.pdf0000644000176200001440000151444714600122243014336 0ustar liggesusers%PDF-1.5 % 2 0 obj << /Type /ObjStm /N 100 /First 831 /Length 1663 /Filter /FlateDecode >> stream xڽYM8W\&URsa!15ma02HV%( 'A IBB HGF$ba gИ5"i CNI, I'alPJCa c Fh$$bQbGe ]P )x@ƓILpI00^)0.'B%#08[H:YLUxaGMȐt ct/n |)~c x/p ~ELD.BE2D(sC$V4 `ɢgmj B<EM( <yqxO I(hFN8p% L@XM$NC #<ċЊ8`Y`E3dBt2fVDh`C)'OxbuV""#6 D/)p йKDГB(z[C{'5̈́! ф6,1r @ $6HPNQlն $C!ԓ0 DB"pe#X`W_&_O?6S5G[ǦǛʝ[7k)=iR鵚C [_pu b}H 1x2¬/_*#-:e4v:}_}"Ud2O"o~Hʯ50MUf`>*_¤UfJܑEj(?8ʁI >+42^5%R_DʵڤmaxSEYŜh1y]=S!~>%GT3ox i{,턽];"֩&) {#HPO {fX3\{{\aZPAgEVW!յeNg {U6|Xt.X 5̇|G)B'r`q9PvwG}Y9V\4s9o^T3iT{I9JmQ/gڢxޏ R].%ٟ|S/; C0ns>TQ:ߚt^WN'47/Wuî^_X&?fZm}E>q`=߉[\Oy->j.~zmѿi|()ʋjT` g?g6ꮔ?vU endstream endobj 203 0 obj << /Type /ObjStm /N 100 /First 884 /Length 1693 /Filter /FlateDecode >> stream xڵA(,ż*(9=]93n!Em_i0$&] ǽp (8 Q@"Exϐ13"a`c))%K>SWw_e۪J_lZ}j~ߓOV2_=ۚ52Ij$IJ[g]{mi[i[ 6^w9!wiLhwnOmK{w]:]VVZ5Y?AQ[ZImsϫI*WMT_sg5.Ӕx;y+Lu_5`RR̸N;iJ}^qխ3ϲmʓ-sʢ47v31*`"T!U334u }JfBcwR/\#?$;U<~pܬ((78 ; 7RI!̈Ҕ[Б:Q)Ƚ tM;iRUnu.ϽѸCNrޟ@v2H#L6Kr(8Hy~9+dLKoVtddxj"Cku[re{M,oDʽZrژCjY]y޶?M.G/-z3^ϳi.7ɠٹOgo!irU?ܑZ/UR6U+*+ʮ?d,t3\ƒ 2x\T;I>߸8񙇝(k4gy 5> stream xڕVn6}W6j$u/[ ۻ/m#Dߗ؛'I̙3C `|~u75 A8X=86 ʓ`U>bK*:YWyRgq0 @DD88Hr'81q(IAGB6jS5zg8)NN M3X؟^ҫ\d ^ '^ѴώIKvg}#jUwjZ|$A֢Հz%okx mx5MT;+֤; J'dL{]u1Ҍ Q l#[kzU;AЍAln 5%W$cD Y׿/LGjRcuI>5)Iv|i+}q<@:`u/9| endstream endobj 572 0 obj << /Length 736 /Filter /FlateDecode >> stream xڝWMs0Wpt2t42z4BH4vM>1 o})|}fxu&Nuh8i8 :?#tՀyMb@ 6T.u짱~UI v0w !9 d!RQ *lQM{=-y1|5*ӡi<у,C%IRUy c{0F=yK>kP0LR:bI)ԏq#CQ٥^Dڮ =ɼ(ȫ 0+oQ}M2M4SM?E񙼯qJ&Pedp}cQ endstream endobj 576 0 obj << /Length 603 /Filter /FlateDecode >> stream xڝˮ y "ȹl+CKOҧ/NJG1tKo~f~(QJeޣ}߬6pDu-r+t`{. B*vumǠr=:I-WkNK,ΊoXVUkb!k0'2pgq{V@>ÏX*A^?Q:z)}iha$Zd8,WlzB$`/2AϱWYD4UvɯoSi_-gLcD_1%B$@ᬦ3vF!LQ3n K (b} I s~DMS&#ccM>jytp:uj~5(ؓb3Zul1yZ87x/4 0> stream xϯ8+8jl~R[]YqCi7߁ }zJLT%\B~|f v>X}LK gzE$ɼ>π?f.Y&ST endstream endobj 404 0 obj << /Type /ObjStm /N 100 /First 884 /Length 2106 /Filter /FlateDecode >> stream xڽZ]8}ϯcÂmFzfT-J^iw 54ʿcB $NRPsm(IDRCDc H -'{E(#pJ bH.8 %TD ȣ^1BAP10=J$@ǰ44HC,TWlS*H6d RAr c4%y|Z΅H- AB+˕ 7 xBGYt4V Fl!9 "f+a:!8ekx,#Ff<+# C7c"ܔ(SM'gۤ-Us  y}(8 :& FuBCj >Ն뉂O-'GcSL03bWpVш mD4*ݣo(M,.7ɰ~Xz!"M8 O#hiH:#d7~6W)*MEg񫒻ZƉZW̝{ٹ<Uw;R`|ce\~vn<G]# ?jYlZe M3(2u}(|zoYUL8Òifýkou= j]n}UC4}ex8QŬh@%bJgMUݯDeYzY|l4DZ wN`N ֮)7WWc!uR>7/7GbZdP<*lx?:*`7-GasUZ6]<9c[Wp=5=}Va_o˪vhr=M]"O9O" /'x1oK/}+n־y[B{O1e\d3W-ma r箼$ūGKy$íb Xѳqjqp;Ot+C&Xa|*]Pr!*>/'Go6uс;xxb$P߇9,79g-xᎂ[m5ܯuѮUkiZ!`_5dLoᇻFC!?|zYOieX-KaSe=@@+nh-(ʧˑ1)" 83DHP1V4ҷ2\VU;(wC?D졉1"mU=( VUrSԺM D7pŬH>9= ?z?n3Ȍ3K2hao.cVZV V̈H,p-p-p-k[޶me۪mkڶg[yQ+/jEH(m.&l:`rA1P@xN/ʴu,LŀRX=<cnY?!%pzkfO@` t`S1hA@S1pp{ ''0c`40f, 4?Am16 endstream endobj 672 0 obj << /Length 1186 /Filter /FlateDecode >> stream xKo6Z ei.M BhҐTgKJm>n>i/,=)#E MUÉK_0]iyx#v܀'AR)@.-EUw:u6m 6\VOP#)veI ؟`_$:h}dٵdAK}#RY #Uu xc? X kU޶|)z>{Az`_ 2pmŀDΞs_мfx6J{a gWnf\VQR2rg'&F!Wmgkl y^;~КEi,X$#COҦY; _:6+6-6϶NS\AlhmÖ.V/ZӲ_Pܻ"DŸR~V3YqBˎt\Ʒj6/@BЀ;!/" EAGnF R >NVvu_;V(ai(u4E . 5Gw4no5",u 쨸-V봏BtnX^Ӿx-c/3}=o顝 kȪ<~_Vyh-BktFy F?L)3,0[!ܨmtj>NT3Zpg1S#fj$Lr#Qa;D?: w]τ31\ǏnBƫǸhMGwD~Fa},;\X+BN͠| ^nb(!펤P\x9cAU=͊km ն_f_c n&@^Ŝq.RW\EFΞ8mf_`eqrӎ¼6$b׻#08-(]~Q.5KS>ykUo~G#b|}Y-]Aq ڳD՝n=L#Ks1:1|~+7Yy0r8q%X]گWS52tfuFؗ`U}tuJ>/F|y?މ(-nUYU.tit~8t, endstream endobj 775 0 obj << /Length 1169 /Filter /FlateDecode >> stream xMo6:J{)RT =-r-юYTHKNv8AX3oޙ(E(rͧyUEtR$+@趉Eϟ>A5A~Gj]PC[ds77 :߲UNqioJ鋺1{oJV9⵽ KV9sȾ5{{&eyj_V)^y $+RKxB2eI*,XP/g*OaHn%M2eły$E1;㢳t♟iOK4ۏutwBխ쟫O1ULK8e[%qFOR XGƇ;Lm*G1W0̭mc\%)w.wi? d}f/; 5ຯnkai*-Cy" V |/U$5yG/ kbv|&iRs5T!gpa^o ֬q/ %drTy0f fϜ]e ҇uqnp̰DnEIǻ$/i=ĂNy<fʹ$Wt)X/MQN{=8 Oڌ+ѳl6Ry&@9+Fm ?( =yg@޶8\>_AwTn$ rXQ}BnMk3g `{$ԋie-M1 A ;f6HE-"E W~ڪ߅(i&yg8~17Xvۋ \hƾaad3t3^,TvV i{\#=5LkQUXz ͱNЇuJnQ^h=+IKƝ;ZD۰ 2 wh8w^(j zw\wطzxoְYZ> stream xZ] WhnD%p[؋3(3=ҿuyn;oxCQ:-qSmq$-jH5< upv'*R+JҡxMIIdmF00l4R-̩Ւ*5WJCMU*+ SV5^ݸ:ԍ-qn \K1SF0Fzlca̒<&9c斤[)nܓH $T `B'(S p &S=A {s mZ05z&VGcOI=ABY}=A +d Hҙxg 6<ɺ'0xN`kWOY z٬$uCQ p†zehqPѩA tR,bq'1Ri{bx2TPY*| ԖY|i1H񞡾DQ`"âwDOjLF23'w1(7:"CEu56BI6a'ON/u:}}ss{wuzwͿNܾݫ[^|Wtۻ E(<;\sb2I:H?ݾM?uWWC<Ɔ.3 }=,y^ XQmd,ce=y`}Ġ0z ܲhO0d'>~g DY#Zcx G{w Jt᧟^y6fY_?;xk.x;\}y/wS\E뛻"_ zro?_yJq} \=.`ܛq?ރ3H19x~l1Zᯆ?8q?$I'OŸ? $IOß? 4iᯅZkᯅbiXI~̈́@VJ6zxJ$7OmAc@'ߪB27Q08N0z h~z{rL0o0a0Nj>v04˸L0o`(aPpcnb}G.yȬCs@=[b=O17 ega=O*.%ޙ0ഀcӬyRG :zT\Jؿy"bLe;+8z7je\Jz Zq)A sY>"ZqnYՊۉ8KʬyRz18zZ8f[s4ZjQsլwzܔm׻Ezn}js8WY- !pzD_8f[ }v[YNj빚q(rn]ܚ0fn]Gz CcY-s5Kpzf'd/۽:ݺ8Ϣj n&CgXQup/\5ݺj'?LjsTm#]ﮆs՘Ԣ稪17 ,*8Kj*UxVPq(}Q}ֻsT-=[2Y-G;aVAZb`f9ˬwb 8SLjaqO]&pz!,^myaY;aV?fYmֻp0kW¬of7u"n*(Uv|#(1j-F~BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFQ-dT BFX 2=_,y.m {|y˫<y #0\*v}|yǗW=*aȫ}"zzW8EyuCUg(:D^v:!j-ȫ_^L@^=Clr:!:yUyu,(-T(y߮TcR@qJ@qJE@qJE@qJ@Rj+eV*Ŋ+Ds%:\u헨Dź+G/% endstream endobj 849 0 obj << /Length 1337 /Filter /FlateDecode >> stream xKs8 WNP5(zزU{Nfj/;!dFkZ&mI-\@%Sw%`o5O<1b!bQh0w}91:_WjbgEѷH4Sm? Oa0U`x[ }4F0b}nȨvhq0$h"YTm(>Ǔ &*i*+}| IG­<$I #ֵ\zDP44dR|0§ 8n3Hx,\)> stream xWKo6Wam`ňIԡ,Z, 4=%A@K-,'×,9k(Hgf >N~Y,!Ipp!ʒ M"S\Քn.:=O M11戨۪l;}x@V诅$bS9MfA9Qfe2 (7Uziҿ`VNI+vvD+r-SVv=J966_ EH[ j+6V]h: G(1z:#mNJ;.J4#ʉ;{bC.8 ëy%VHݫ0<9"Ga^-?Č">dgļkV.4 #XǡoX? Zc Q$`3w.'SE `ʃ|=6eD3u0F)0T?O#uGGpcD6)%(#{;T YGbVC_GYnBMm7Щl&wļ#yx"7@fj͏!k̀=c+y-63Mf|Qgc{\ F$C7K'iA~# y'@{ޮB>ԒZgZ:&ԫuPDNKtΜkL/t\5n,7[TcN6__q`UFN2 rG.])SYW KE$ۺkY(SK'MlU !,uFa+c IKRj7zSTDŃݖek/CN;egEp[I-Vn*ۏ.8ZمS#b7X4s?Z( D1cN\&}_@R(6Q %ۈ`J}M4dt ĐJ\7 : M% endstream endobj 923 0 obj << /Length 1808 /Filter /FlateDecode >> stream xXoF_[ޚkOm8JVpZre8׿3; gCee̬lyyu; K"/rnr.Bsn2JmWe)+mUi[U~:LNnD{j[.\7/8Oy>Io߻N_;({u3@_ ίF_]`bWguy- #%1w6x 9qzrٕzʲ\pliqmneQlk+}3/~y9FAD7.(DOE/EӂZ<mr]%n0z7lsО> ,v?_>enCk΅eQJsC~̡Su!﹡ybV^G e1l<09x) h D%8Фf)ZT!FOTj31ޖaZW,~wYZbOk+J xfM7ZnVwΊxQ5Kl ƅߛ)κfK5m',f͆K7ܪلy@Ө9K/1Hcx91N~vIl0B+'E6:dnݵZ-F'6.mÖ\AHva.#[z 2v i^?LJj"}e04呢lt& F Adι}d ѿw]c<b}*NYJwN$B e*v]V9(0djZc ;,v >`gͮʌ;egDOssO w]bk_jy0d qE2XWy*Op5$w up$ZgmlNӚ;䡀7_ǰM~C0[0B,N"!,Р{R0_/!B-zcɄJ]SXU4ʾhjmZaVPҎP2 vWM<+>eb(ؗM\e3iZҝ0@]躲;'QբKM:{JUH}v?֙>Qlf#XBcf{<(f!bԛx:6$MW3mRôq9;Ө M@Y٘dܔ.m"ު|N>ʣ*s#`tR2Jflzu?oF&#Jfa@@3|FIBx oj/+W_"6·NSMJL6Ę?nx`w_;:^nz`g3ǮQvI<ñ1<> stream xZYo~ϯ[etRh}Zm5jIPhf(Y0|]7yuNjŁ ^|W.ů.NFgǰ,QNYpa_1Z/bE+ξLwK;NӇF7 A/v?\Ȩݡt01a ?2L{W{,&AO'+hh17k*+̳5i&\9L,k1Pp{{K# Hb@Y6J!Dطj!|ޙ YS|1Pij5Z//cS=]F85j|=.:͚S11lz#? yOZ)AIr*VЪk`!00˶rci| (>s)P@5kǬbpR- A+.T 溓֞ PL8^:9< m~WT =RZR@>"nݴ5!ѠͮxzֺU2pYp$YzwLI'@q ƛ0Vy%Y/;FxD刏YqDe|d)1a(+hms\d3Ytl%3߾!H2J"EN<W ZO˽nnprڇYT7Y~v e?& q{{I!PIXe!pYS =A!z̀|‚Vw Z"0@=;#f#)iʺꑄf >˞`@vF&&Q׍Գ 뗡aɥC9:'ġQmF~lng ml*mX?:^FwI6NCBHt 5;Ji4;1 /M>%y~p#+B?iڕGV77%^CDo~/R>Z<]׽ylC%XnG~uzU$)I57RKpJ}Qh[l*+X 㻁 dS35=8dT=E=nGrAqr^"&%DwlYtB2bESgd?d ],Ozز-cBf rd=;ey?>F8~}a bvNCQJ1 )ֿca"eda61< ދ4u ؄sc"L1O3K tp,ڭ AN03DSo°nbrPd^ UL>/fR)' ʛ endstream endobj 807 0 obj << /Type /ObjStm /N 100 /First 901 /Length 2402 /Filter /FlateDecode >> stream xZQs~c]d&mgC[D9*JRM Jxc" qoawwEW]$F+.6; mqBmu S\.\L!J& \LV\MX b K!AR*.%\BhpT'ţ5(QA8"(:@Ie`JёUJ-Aq*l1 "R+:aL[b0<crba~hQaJ@#V?$XY* U8, < :wETpTGUr%mwiVv(0,S#AŇ1FSvl$`bJyV+lN1|f1H Xj,*bbGQN/a"jw9*"LpT= 1)A/!&5".lD`15WHl=,i#rg,FQ j㦏>n㦏;m}&*u\ [PɄJA¼ ;\Ǝ&q!2O谷j'sH!(E)>L:ւT̙=Oo-AFl' {})[p .ƓK$0aV1g=Tpr<mQB!s"=< 9pB!iIQ1:>ECf<(ⱝN萴xNN!k )ct쐵B*`S) 8I+"Y$%R5ocxugο7 pu}x=!R!KvNkoHw^]^,ή} Ȱc}f}vݭ^NNVc2{q΁$Rt)#GYW7n﹛zQLl a梧SD A#z$RJ(X! ?RmSk ޱ"~Ek6~ GD>r=}$'B=S\PsD8NV4x(ar$ N<|{Rɿ"Jw_~؈NlD'5ZuJ#>҈4#H#RK/M_nrӗ/7}t,MЖ4}[-4. endstream endobj 1016 0 obj << /Length 1001 /Filter /FlateDecode >> stream xVn8+,$ fIȢEIUZ؜ʒ#ѵsIJӴ(Zta=k1{:JŃC0F~9qQSg;.+=h%[>-޼|'`X+DfuC?VxNcgTndh,U"&FUT`wd1}, #&l?JD>삗kC/㞫Ϭ*`AϛYAOOkBt֯@ S]W/Vm5,y{0쌲t"cVJי8`ۉ)E:O~kseb(qjjw+fj)νN?x4t?bL3L=$szϹ)>a?,13D㌴6ydvWpنQFUS3L[(ϲ=~{d~hƢO8AcobDQծ}͋KSsᘦ:#)]4&LRPpe$Jꭥp^\] AwNg함n"#x0$$'o E[ٳX>>-~.f3o0ҳ&mgG-u8a]8g\4հQ !ieOhEic܋c wwS8N{5tm@6/RUVEJfG T_W9"tԏQ%jZ줨o4# OwHNpG@5` 3-R S(B1 s= 'Pdfʓh#V57;6Phitտ<{L 3DMD-ԑX8y|F"obkq[fSܬWT&LArԡh:FrtCn:&¤璉?I\N+bxPO*g endstream endobj 1030 0 obj << /Length 673 /Filter /FlateDecode >> stream xUn0)lK*MӤ5*"* 蒼lLkNs?;,WƻixAzLMXcE0,^b㴜%"-\ T6H[CmW4$-ۡ&EG UO$-K5vieh"C7I\n$(,[h PKjH{sRuWNю+ř`L|$6"/\-"dnE&Q_[IecY&M_E՚be+5[=\t)W|Kr;뛑pqyzBD1Ky} PET meM=H]_MdKi˧5 ߻4(4j=-s&$``[\cD01Zj ŀH[, OS080`@ns^>':G-eU-Y{!.\lJ-~Gw:rK- ܹv]KLӁ.%- Z6xna^)dt{4LaQYyImg/">r(,mcs\}1`Kٗ6cl#6Y['74Pjo endstream endobj 1045 0 obj << /Length 979 /Filter /FlateDecode >> stream xXn8}W)J@DKHEZS2- %C{~y,ɲӴmE̜!mtޝMnIAș!ȉ#HR΋ZJCP'-J0/i mWښ@êWJÔvUvVz'\$d,mVŊ*Y4o !W!.ˤ`Ԇ˫cLe24g|<'!Ne_݄(%/.͏]%o"+TVW|пJ}p>j;$/`U!nav{Iٲt]DwUҺϾ1&#\ (H|`2urU2vyAfk(C&Y$&ԪFtShcXl7C¨}-Xדb,I2P7o+A7`!}RFe7GUɘ dw74=ihQi+bt@=eiz3qCYSMMCBmxcM%ڿ:0;%( "DHҖ{зA-OdZawvt{>Q|v0 ]_?c_-,-hC! VKwAF(ѽQFwSEw lE,V_]t~9vL@ endstream endobj 1057 0 obj << /Length 1259 /Filter /FlateDecode >> stream xڵW]o8}ϯ@Fi6`0Rwv:U5ݦOQ'A%i\@pJl%\{񱃜ϓߦ+9Y%aLF(I(ts盋cf¬ 20/D%,*V~:Af(u(HbÏBa*Px{빊zXFH>FA|^'!؇x,>mּ&/ +zw˕o~1# 80ɄΞ |ټc]]^INϮ٢s+b"ޭo^=8 _plXrs$USn O`i:grpoE1'߾#g/ie٩k'8Hڥs3ç;p$N@O{X[K;́ Z}k.؜ {Ƌ[u nX.yKOP mt# 3DySlDQWVҖbP%OD#uudiaY[=ZK>h L*!.Cؒop1( /t*)8 ZΚ| Eݜ(CDV|Y)Տ F}B5z&'4'Z^_w)/s͖b7 ϙs1Ot]6jcms'i{ ,W.ooa\tdOViOz/X" KΪ,oU-ӈFd҈:uVgϏP~g=2r+bB?KƇuؚ|'hꠍq"׍]!m"3G.M瀄c1b R1u@S('X 0jvSNQdw8f1_) $ {^+ *N!yPXƨw+&f{7͟ugv6?{KqOgP8崺|ެGILbl)oO$pxn4ݖiD:=CKSAz=CSJJr%]>[bL!ʆ#&4$F5I@)\·Ț d-VXM0ܥ4KK-K!5H" lZ~@`Υꖺ_ʳPñj_##eD;tzCjn;A #, (|i Tr6A$}@ſG!Ft.Q uWR5bp7 endstream endobj 1066 0 obj << /Length 1595 /Filter /FlateDecode >> stream xڝXK6W@e=I6qrI+ѶYrEz7CMX%I 2zᒕoN#< [>eՙv6PIL3XYŌiBh\Z ]fkّc)z7hܮn_xZԕl/nlHZmCAhziT=sX{=c\l&DLbn+\r&Gc q56I#|򦖬VwqlvWV m4r⩌jֶZu _ƆVϰExM3j5b{@lk 5i!1M: ke>t2C+z46a uk~P(f%/k!PEr0a:SEU FF56~nU`w(=a[vSOcHzF~eH=7F/C^M ȜEQ" RZ U7L]QVg}ݴ S"DM,P5e[Jt7T"d5]}ÜgGSظ򞏳sv y0 PjEp~)\ y1~vF7c=L% 7s8찫GSr83 3t\Yc {nhbkcpS1Ni*VP/X+΁ Z!/UgY-D/6;v䜻hp/T*^ocM|h2L #CbDcGМ+KFyQ %g)8(,k$M\*o,) 㮥E>9x`Imj9+.K5J)1D#&5)El5Z(Tq# `3$uZo;<!U:"mKyeNi2X큀L):Qϴeq % e&ܴ%Gt3'жWehx1^cjq {Q SGD*?yyHVr~{~駲] ?ZDě.nW$L71"U$Ncê/;js$/uđ PWݛ (N ܞJQX4ݠ2fb> CcY#gnZs%^oWJa}nBw x 2&KGPR9V/1.YRp oJdiW,GSGԲuzuʊh M;UIu^s1Ͽnj5ٹ^t lGH˘U9XfeK.dwmQW x̉׀ü\I }/n m:[򭙷~a+'9Z݃'U>=Z endstream endobj 980 0 obj << /Type /ObjStm /N 100 /First 941 /Length 2230 /Filter /FlateDecode >> stream xZn}W4!=U}k8Y Ed"\R!9PȞY, =Úu=]dQmR#޳T&]V>&Qz[<yZNY~W(( *dRElY0}0LH)+1pB+J$w@ǣI11fY1GV9@&Zz$1Al8ycTl74زYЃL'`2|&1)=k7GYE=;WnEr8)FкOШ|`##L"t*!?C'[FAq3,=V<@Q`[˄tĠ>`v*x#2{' aip,sPQV(urd.+d]H b X? *qbv%Y J6'JT9pO{AqD\̘bQ>bQ>28uJE_X.${fkej]Q]Y7W}yc([ ž/⫡#x`z{]d>_4l}ޔLU?.AD߫>r1NI>XΚ0g8l ]tF3YȝcUo UQN/zqLk#}?NF-Mg-^?p;-`!hčISr{]apCe=<at"kF^`HLZ3QPB L{̦v8HLYmO^g=@&Ԟ`YaaF:BI۸mWu᠒Q<@Fl=jۡpx5z6E~&@״ ϸ|,C˷H+ԩh2Ƅ$S9ϴg+ z~cY? b CCX;/@ UJH,YL(YRl1ztc)E#0l<@BöUg(M YtǤRXߗ|cx$d! =Ν>[1gѳavpMeI8 {a 蝻^;J{AӋfitNH҃2^WUqf%l *xe^ֽ#"$(w\ʾ]̛Ňym(w{丱N%o^ha(z\Lj@U޼UK>>4U=~²YI{dun$7eVXUny;v@eW ^p5f.Wzlȗz)\{B!ZZ՗ɴOnµ՚qEI Ap癶0($tL`3ud R'փ, iq7iIH;TgDje஻ +JɃ"sz/2\W|]W(}o]Rh""WCs_p<%@; 3O0>2e] "yа%jqh;-5^CC9E)iz>!ye8?~$rz^gP啴eCF(.pda;`q]ԗ:|)aPv0:4.7s٧V6 2rHvy_Sin7RQ٫|U[U>Y^>5ꨪ6j7@UZΛ}Mw䝿99I7-0tR?QGӹT‡S5X> stream xWKo6Wae fHa@@랒!˴MUZ(;)Erm7R~pf8ϓ}z)Jc{G0FA{(b=$~Yrw'ɀF($ 2+\󜩥f2&iUCY0siqSmSRw|HcS/~.S Ck1Jڙ/y_$P^;NrYT+.>Q93(MvvY_]f@y@G4ҭƌiJY*mq̱C.5[۝M- {!/h:C#zǕ7x+F[ s-૔k1-R\eS%kn!6 &b# #oE /d>H> stream x͘A0> Jݕz26U/jzȲbd3IYRRoc.M|_HCe?>nhls%u?f]RVi2٤b'%6+y,;b.~@ܼ|UJK='.=BoCRPN( 'Lsxh021-ʓ-ȣ[|E$ҿG[gje`βEp<=huG_>> stream xV͏:ȃK"mvc<(]TZrq.IiE *xoz1< o(/M0Q-J'Yi6PF@LϲRJrV/Y+m;Q,;ޚ3#0MQYaH.e^kZQr-$?1'%X\.Z)kѲ8gNPS%yf~)Y*ea9$z˂HV%> UK@&jUyE_ljs^=h > G<0AQNb!Sј!K`HwY*dv)TH9RRj}رܝAH}&Q-꯼m@zo3ң&#aL}^ o(ϼǞbBPG@K F!Cxg }#I!07+uz?DD ioiu0#^5Ν`._N{3N"3i_SىT(L"k\h̼כn{)~܊'U77v16oqr`AjNUb^APnni<>\fH)]wCc cZw mtcBWgКRMc9rR>jN֍q]gZ4ۦe~ @v\.-LYBE t7o<1Zxnf ,M^מ^s(TӪrWpՀ9̄|';Y7vyqeɆ݋)Z~χڤy_zW 7M+{4a䑍F endstream endobj 1219 0 obj << /Length 1220 /Filter /FlateDecode >> stream xڵWo6~_/6$%G=k2`-q"e*KE1;R,ɲ6+uwH ;K;>N[?t"w `\w#Rg:w3c&reE H ]brNmnj1 ~4f|}oL0`Z/6t^UFyNy=G]ZCȢfZJbZ2=ꗰ %("[,ؤz dWgY nànȜx=x9<ȍBggL׎G@( ԹUkkd ipqP~Y64x-gT(G u4 ) g}m@ YE,J@f 1BO!\,G8$DeDNKr%M XL< lY> m+}94(>-w`?|)zYnWúYtX-^~S$F[k.:Y}1:[e |'H%*!bAPxzg+LE0FWGcWt#N4/*q4ቴ>C >@i.`chPV BxIgD.gֺŹ蠀 Cm|ĢT4/5jUr3C%bkП]gGŤ/(WS\Rm QUJ[$ҡ[23ʁmfxOk~d22VZڡa[y~xYnY&l?aZoSl҆ Y=vqP9IM -#Ɔxb]ˎ]RjS|LLQJg)P.b)VjO9\OZ/ju1p|s~ϯ%8 endstream endobj 1076 0 obj << /Type /ObjStm /N 100 /First 1009 /Length 2896 /Filter /FlateDecode >> stream x[ߏ~b$]qgF'-.OHEo\6uJ5`݊Z?|3VrF =0! g5ϩD SJ( P|6g`|g<[dzLB[,c"_‡yO-L2VuI)] `Joį iHJ#yw:t0@i¤Wn1 _m9 TibD ^:Pglҁ"k\bz#30"R¢ I3\tiet;f5G3TB1ezCHVU,;JKC ZI6βE+ÏPB*YcTI.],)|HcB̲ i°=J[Hc1 dx$*pA|AOn{"pmί=Z(vy߿?PN%5ђ;lo.~wxL^8n{a%` -N_^`b`"*}~by?s_P6WƁ1=j~ {Hlx(m4%C|{aQ]C1A }5IZ:\~oV?Q o# T\_ qy@y>Bd*KVYʒUd%,Ye*KVY2eɔ%SLY2eɔ%SLY2eɔ%,Ygx®B aWpINFGn "6u0v|w׌(Dd{T/ ve[qPorlq3R) 93$Ni9ҷiǶ%3b-898:n=ѽƲA ǽ;ʢRyXS rdtۍKhor|AKt2:E2eҼ5VK!֟L%Mw[ZkC˜^Q_K'biEgCp}Y?pk4g@b%wH*{ AO;ՊAc 9#JMTP>z[)s q" (A٘fEG;4%XGRL1[aEsN^)Akb4Z&7ςrӾ}޾mޗF#3:`,o8AmJCTO{re|l*/o=-]!\t_pߌ}Yn97;wH{dn ܺHV;~M 6{陠j2)DD_chC|ʻ pNű7]?g#̝Q> /u㽡 _S)(%lB*g~JuaG|V ESfn 'Bd5F9-^]x, ?'rpɀe2ytm=) "ZiAtx5e U ӊ*+]v fs6‰B<5M>L߃`[ GjOD-ÙD:Д ")%r1|4C5|MN9)K Nes/>w L.Iu&_W&SKt$xK"/xPe?.Ee0n 6sG΅ ?B\@4i*d9! YN8X,/#n:hMbzPb9sz:);19~[`a>;I_Ee' QYpŏ ]g]ч ICx!OF5kz[]o$%L~DRY{1®r1)\KB!>OΤ8wzݼ뷛©pn^;ef%x*rK8!/n9~~\;G wo -Xte֟au˕Y66>T^Ʀ gzJXQ ˦ղK'ح$C $]M/IkN8X= cS_v/glV^=fHun/S[Nή*3۰*Tp^F֧>շE{dWM~u}&?R솔] 0KGr/^:ʽt{h=Hduj'[tg|n O47a[G1?NT %>Ϫ.5Vx@GBYhP'WB  ngp,a控^eٳ^'ͻq Y&%7kd] XƻퟸۍmwCMe 3"k\7zl>8|8ܘ{Q|46mt^ endstream endobj 1235 0 obj << /Length 745 /Filter /FlateDecode >> stream xV[O0~ϯV"Ɨ4i{ MaHUYpKD.e}5$َ;}sa#'>FR0CFOG~ؒ4DJ"#\XB8:^ںZ%iL|7R48H7#wpf-xyU',ڍ4b#ɁVShփ`+aL3`fƷgu,anc;65;InlJAj!S0sDu^n2^vo7}vacM^c22ۧx$3vNMJy3-G]5Әӯг~dUӅz6[:$[} cam@x˙5[+#i { tp\bck:' y,iʺ ֍X.ΪxζozY#%mԞ-1iƊ8ޓS \Y&7FLMw/ssg u!21אtˍfop;17$/o endstream endobj 1247 0 obj << /Length 1107 /Filter /FlateDecode >> stream xW[o6~ 2!)QaE}OY(m%WR𢛭zvnC\2A+D:N2f1/%QDL0?`Uu>?YuV5X$`מalj><0$vNÑb3D#,~@$bnPDh8C?Z{OI(P"A",it0ŵ6 mrz)#t8 f*r^,q/^;',l^^ F1%ÂS0XĦ"0s7u_fzrU[L_S0B.& p P,9e 'ȟ/+4L҄hVnC7aS\9'\fx~a`UkxHaJЖO`FMլW7I@3U6B!}ea $j6,sP~PujŞ#NCHt,c) -AIq0 LƖ qn:24IzHSZds(OB>ueQG`]v} H)о" (҈ GNթΪӕVotj*@+ϲ6Ӫ(1r!i9N%%c'T ]U(8q@~k 5 ͚WXsU窪vY{ koYUS8Bb1TEcʟfXS:[P$.+S͘8jӅW%َ~|6y1qY7`j#y'Cf CkL\R&ܶ[$>8ksK<6g#N Ivz|60׈ҺkhGi] M]#]:Sơξ91ۇ{*Wcdႌ]}?O#}>w8c>D0>C,Nww=ٯ{" endstream endobj 1265 0 obj << /Length 1014 /Filter /FlateDecode >> stream xڽVn8+P%RR=dEP`g/Im +K^lKvH9!973oސ9"`|S&3&R}|_f(f$NZ-B=ye\࿁KJ1e1JepQD( `];_DCE$Ĝ`!Et!;WQر86O7 5EwφHy`ΓԖ%>QbRuyUv֭lF`Ff!5~CX?wJIǀU:(ad2|~o+Gg`ر|2E#KN#L-,z<̵-ELuboܿfv[b%v1Fmr,)k/sEaW,vDYz1ejDy9cPuLuUoRbv#Eb#fy\csŪz!fr؉u)%Aڡ?^B!kkCEwgFNS;b]˶SixCg*P?R؋sT9QI pMd7!&.do/{ݕ(ZbϪ<ʆٝ WGpͣy9HYJWH'8ܓzrsw*7%r+&άSgX,ܰaUT IA Yh87p7^T-Ϋ+ĨQ=GlߥUٌ6  摘{'5^dt|Q ;y |pMMZn^݉%vj?1\ul~m M Qwve}tDZ7GrU WxlumU=m77/FwqD endstream endobj 1279 0 obj << /Length 567 /Filter /FlateDecode >> stream xV0csKۤRom)" ԰ 1BE5[Ҟ<瑭GzYQCQ(!e> |=AQ, Q"]'2<[MV OPpܢWHW Ξöh̛ϭ `C.`3?dyns"Fsh@*?Gژ hj,w!ȽqM =IcK4⪛.-%C\˳qʹVY1L7}Ԛe7nB,tW=."Û_;a)b m0.guWo#syX*E<~ZD_T>ZJ!-b7-  vY_ΪVڀ@86ЇϠbNis"36^|]X'Ʃzs Ֆ7֨K1si飨L:7&#Pq[Buʬ\WYQ9nDi6LvmI endstream endobj 1292 0 obj << /Length 706 /Filter /FlateDecode >> stream xV[o0~WXÈ\ܵaV5{ʪ:!dmw֮j5M;>2DЉnf1q͖]/@a@24Kfl8:+]bӬIժX/V[ו(U"$8{x&zڣc7ҏbBVHH!^EYd&1yF#T֟GoQ:ZlJTy"0;5Ch0ةX[Y=bw)xsWGVN?g@W=bl}+Ut^ -qY" FАW,#a9asϥٕo֖߷C5MGr` & 8X'ľʥԽMYD菚WcJp kT{3WJ\WnkݼvtKc:s NcH/[ͧGMWL^fדcTٿ:0è仧 ۑj0@cOYBsxuR^ѻ.4Y0:^]?w@Xg69m F]%+k~FP ,jG)=Z_Foʦj}x`,q V$Hp# yStG=ƵӐa_hוY4PQvA7U=Ys7]مY9RdMn72O>rpLdg:$ A1 endstream endobj 1316 0 obj << /Length 1154 /Filter /FlateDecode >> stream xWMo6W撔DJunE{h㞲 hGVPlI'=$)r{{{>nV>KQ(6;`ˆyaޭ5u^袩TJ,j-Oq:axƇ46KVعS_kxM[~,پ/bq^kcהe,꽝*B-{c]T PZ<Ri#!bpƬ/t.%^g~_&OHo"ZnE+Y Oѱք#N^ ֋RmkHJYVKbV^ 䆂qiԊa~C߈N} ƥ[3ޞ̷E6Ydc#v!AaZ#7iϥjNsE -OȯwTsim)Vi;jv$7h(JqW]E}\qշ`CIGIe+rx {VV^DD!Kf F&eov>`"E1FJғeorV<(M [Kf p1 Kp\<>ꄜV"UeHa`%ʚr/M&~,cٟSRqȮ2t8nvz )t~|q^n Դ)Xw蔥١@NPr%YNcq sHJ8?7zLQr x#py'3?55~Xܡ1Wz6pqlDwNGz H+2]~whvhݜ,mK%j`"%Kvu/ Ćb_~GAӥ XW5|LYV]Cud[JT(E D41Lq]s3:$RESقS2vGYT$z -)bGEQkf$<3<>W_Z<L<]WE>T(>geM\x Dcl Rꋽz>שǝl;в"vbnR:3}kGݗ@3yI endstream endobj 1228 0 obj << /Type /ObjStm /N 100 /First 961 /Length 2189 /Filter /FlateDecode >> stream xZKo#7WKraQ,`6@`̡-madI+v>_QjHvkrQ.]sVesX>N>IY&sE:OʧœU0I%ѝ/"@D+*%S(sƂ 2V윽5P`%eilL qU ci33Vf)gT8!`Ĥ0EP,6Fd1 QrlE0<ƲT7X!)o7r06S`Vyތ9#VTBr"cGP'P̜Yрʲi 3lI@$#P/ZЂ/j\IJCdEB`FN ;S= Ҋ"qTv\ y9U7 )3J6e*Mf"djlM|F90/f "PbJJH,31/*TvUL Uʪ9+c^E`. DgX% .@p SeQJ*T(PL e2:bgO2/U=Íbz:x/sSZ'' #v.jN?fq>㾘Zu~ (vv0Ħ/`n)؎-)YQ`|tմꃪ~BUϭmamsVmYdYu٬YmB\Or N#XDjX& ?^Ϊq,?Qppv0OH[8 8Rj}bꟓ٧y١z7j'YuUg].VU}ZFoy3_6z2S]Es1zMiy>e &lԏۓA%HjtL}ʠ :d i?ajk5Sx5 ߷ԍnw[Uŀ-_:@ױЏu~F|T꾹YNշNRX@}jc tJe=ju*eI/qfY7CcmԈVA1h)Nh<Yr= F[M' & (J9'*|aaAAxW[ί׫v֬V-BI\KM0.%Y22 6F27GS׏;d>憓R_ {sc q7*ٽ-z^MJ!uD%7RS&Azams3]/klD-:rڿli;P04Yn+:PtZ6L'PQ:HNL`{zȦtƬtJN}d{mqmTSh;=dFsRfmYߵӿꏽ ӫ00d}`Y=bH:yۓP%v(PmYt9䶌Hiްw1+c\T1V1s&D/73yW\9 ۤoJj%N5:"tPٰ\R==]^i*aim|6Omׁu|אƴ^v'ވn=="=e~=fF}OP `gw'|Og/⬷tTgV2udZɼ%)jKA:;"vDneۭlm' q!_faɨn7^ޮ!'Q3B;͡)OM0zI;zXdtJ# 5nۻ`ؼ)ْ=`TKcVퟨV @ e E=p0(c ;#x=[=FIȇb $7bN tH.1ֳH|9m cO"H99'xt ('\<Ɨnk&%ʏSgEᘺW)2Dh¡Hr! sBɑ6p12/|bpEb׀7]E&G(z?r/䫶^/Q._屃׽> stream xV]o }ϯ@C1V-oQ83Dm..R#Q.{2Dtty%X,F0 !C9t˫8Z vMZ7lݨBϊ1n߈tQBWÝj/BG-T`AlpB𽺩TJ67qC8iu1z/u ;LьbISM n&{!'*rLe?)OWN&®0S>8ɿ?R8/^W!GH֕jx760f

nt"߯BD4LhګP~LB16= ҧ8XC(0qZk豊s-!k]&O]1~pӥu]Wt yH@V}Wj=7اP98,1w;;ON]@UQTAݕT-*6gCz|JW}n'Pp8EjKݚX[=]vy @B=8Yҥ=>[~uwHHJSQ~YYmN :CZTM}|(u]䩂묚l}hљ?>|13Ӆ{c  endstream endobj 1382 0 obj << /Length 1101 /Filter /FlateDecode >> stream xXMo8W96%RR{hM޶uOi!mIߡ(9k 5y8d`ɛ͂` 0DqB8XTTNآr=x'{0BiM8u"3Տ%1\ H*.^ƑYmy8JtPllYac8.ʡ\%z:8Ffgzhf`8<~VW ),䃬2E巋ɷIԢZ'(NA߃SqVt$buqFN΃$ p ǏO)MJ0ʳ?Y)ӒG(NPF^ /2TQB^UpǪU~\vtg0ny}6ɳѳ06Z#S(/o9&?cbyԢqNNFeYŖP/Hhꎯ[fgla%$W%9~'i7 e}svWGnV琄W;&G=MTYQt|۪M@^CJa:v<;G{PD<9u e#kH|B5Y76spy̮O 1߮R@# |z ܽBA H3 ?>TJyDİ`hI)J/9OavC!Ծ}cV+gO?,>|z.Xu%ؓ3x#won h->> stream xXmo6_y@+1CQ^Rt@5ÆbRcӎVYCx,9l0HQ|CjG?<{={vz!Q@rrmOEd弘YkUeo~yzyG2.$RiZ/*$, < X 0"X,ld ~v[Գ)<&"t@СK Ri&S 3f.|uB&1=@iI*"j,kKwY]2vDpv"T]J憸9.ݜ2z2;T3NU)7E>qs,lnuJk]jKzZ Mf.tyBMTZZ' L[d.I:T٥zs 5!S7R^huu/n Nj7ԌP7Sb$X5u"8ӷ9~AÌƅZ$"D=ƣ6*-V*d.bn-WlN ^X!Pp1+thddȌ- e>P1Pl\o/BĂ#F8 a~zj[,~t<}#H2>Z^hɝ7?#x/رAH;p63y{@1%PppH1s>Yq[)*٤>J:AKtϟ =".UMD9ÏEL{,9{4c ZV|)UUQ l+ Qw~%c\QK*c8t8SoTb2'LLF`\^)zs,b\#Ǯ C yay:׀O6ZU$^lLs<X}H$`/':Cޭz-+*n PQkxG MEreֱcmRF%8an@,,n;|B%6m+1EA?w,=L eHpM w^t̮1j JEEJ衄*0apkzU6TgY ԾSX&%stzd}T`@Svdw/[WsU3|ެ[SqDl z1G_){v/?u endstream endobj 1428 0 obj << /Length 1108 /Filter /FlateDecode >> stream xWmo6_yC'5f M [[SVL;ji8.{)riC;{+{FFgQ(h͖ Qo,jOhNH9WJT:/ -_gaz+ jԈ3}vb/(0S0#<9o<G3gܔ`m CN[ /,\W0/]>{fg#!DzFua8F1%(%@!Jc:"c 7$n/W+у=@AZM,]YrKd3.퇂ƭ+V@+}܄gy> Ά 9}GO Qu0"NHKYn_żCwrp$Ȁ||KZgR-{YP2ޒ6` 3o.rwjq6P {5Qku?g~vn|)'/vR.fO#vG~@-4=WKoۈ@ LmSӦiBM9hGFCy;,:aQ&Nx9!8jb?:LC|hm@B)ԥaCSͪ,(jS*_K0, ,PDn/ʪ|cZyͫ{7IPL@k2e%QG,mKSfٙ/yQ.-5k #WSS-L82鋮9ԅ$a/ {]o%q AC˭Ģ.2m,%.khS\lOp}~nsqx Niנ)k |G(eݥŐvXҰBN0%,JP1(&'c+Nx ;ypJ+С$>_G>gdY4Cf調Je;)_71x+\CK{:ߐ!_o+z#vnȼv6{$a~A\rS\]EV0/1N %D_Y\JT6]|Tq?a*gG '{@[ XӨ endstream endobj 1458 0 obj << /Length 872 /Filter /FlateDecode >> stream xVKo8WpClbHJc^)Poi!-(G㸿~IQrl#n :| ~n>`#(I j8[Rba%EІbtK o7A.. @!$q maSٹH#YݼwfPoi[%697~hj^݇d#;Xqs$ vp; bH-?[]S[-袞\}E6b0R &#/ɿr>wĊk&Ga`5eǝTH u{zfMiQ?F1\Xxs]SK/CIL%/S:jѴELYfPoẄ[y c_aP+?20`Rn[Ƥ(3m{u[Ԕz%Ve}8H #ZxFQxvInU/x#ҏΏ?h,Kz^0mՐroLRd_@!Laq)e5liT)WsYG`O :Xcj=Tr\M+) arGGL(eiMM1se[sͷ-ucF`2\[t2iSۿ14]pVs7}j}GC+F-/תd_Ғ endstream endobj 1336 0 obj << /Type /ObjStm /N 100 /First 990 /Length 2641 /Filter /FlateDecode >> stream xZے6}cҸ\ʥUU)kHE!y!GIOhFi(КA: ڢaA c" F@C"7[-]@"$~&B (5cYX#rW(9rFi%|v"k0Vqxp;$~KCγDx3iF%|xꈿܡ5(F䕰chRz#XHlKf` ܳ<,%I0),ɭV+1^Qnu͘a(oCw*FO؞hL@ _FRR?8CvP@t" B^p ت!͓H[T !jX10sb~#ʘ^cXKdSO~acr.ʣc,p̤Rvl '?"y1!o82̃>kB"3.Y1<27)`Yײ%[QCP01,B"Dk{@6ӱ~ e]ּ4O ֪")C$)GNj例G 3(ekm(JN['/#&DmXD_tm/?X^k/ xM?~43">y-_սx#?B,^zqݺFCuS_,~İuo9~bvͲ#Z~~>7%' $-cz :m߷mN5z *.)-aoO>'xx6d,~|eS2"!.M=IJ,ֽ׭.jn%U. |˓p^t6h9 9əS8nmn[Lʼ 9R&Fi$(j~mfƤ%b!y=TzVD:ٌLiFaѲB wvI=u.ygyT&D#cL;aX;n`?dH$Eu[e5Djs}9jyp&3H̸ᮛUFob&ru4n:oڎ-ҜglJ98@RX$@[e@E@NV+׎{>'yE|YįgTfAYJ+M_H/ t }9C9C9C9C9CP]+" rkxE _Us=( IT8adL7fVgA* etڌl`ov^\H,cPdt'E Ȁky9^b&[5`f iYVbsA{&C:ۛևQ>4ny:5]/4Ao6/(쏨}cUriR]s1H/8 ,WC`Qw^lM$1l-ɼ0fOB~m?3"N%ݸzU߶vy7/@$OϕP;et&m3*JVe6C4j^UCm'ʕ3 \ An7nm},6yp:u%qO.;f^G#B8'6VY6rhƍofUwMI=o'Z"/6CRM9MQ*e ^N'y Ēw,xӂú }{`]{K%J_G H̹J(fA@OT|j5IS(a:bA`a8N]Lާ'3/xWϞօ]7|VͭQfwLgGf2G׼/uU{֟бst&m$v:GE}u[BqGUu tÒ$s.>''{ʏ<퍑֟ s^Q{NMH4aw&~|gJ3%ߙLwƝd`dq@Ai un|I-ߘXCPKES!s.cAE@z /ql >wsa4C)Ƞ< }|&J^sh[8M6ErTm{ޜje&ůN?Z}}hLL(\ãr 5<*\ãr 5<*J1ɡE=d8;ՙL~aPf_銇q(82(7q-{ endstream endobj 1484 0 obj << /Length 964 /Filter /FlateDecode >> stream xVKs6WjBIqf2zr2$|U[ I8RH@`o> q,@5"Y P(|EҲTZʘ$E*3ǽOp#o5uO`!h`hAC{D)Pe+ { #>LV.ZGN‚pL*O:S 2*L!)wVd:O5T`JQr{rҒSY$€Knjn).Su"Ӧvr8Q{)MjιiPcAݔ(gן}(G9@3qGzUgB|,x`F:0@qDszSiKy0k,m,SuL8L*VzWxGGt7 .6ngWU妒y~|C5]ܖ٧ۖ}7j֎.t1Dgr^YVZGC"WZn6{]vq01sU~T*f~j23I!_ D_;| mʛȍzyǀOn-mYޚ`~Ua~ffg1 ֶC=ٛ\:AׇA>i/ "8O9ۭJoCJs!гmSî.4kc D0|q>|81Q܉ 1L?m%;lc|88bl_uFRM3nd:q7=P)܇4oA /gE̻Y!s#ojwJ|?M^6(m[. QbDa-EL!T1 endstream endobj 1513 0 obj << /Length 1115 /Filter /FlateDecode >> stream xW[o6~J͐ԍ*vA%a-*7rEw(RȎh>X4w2vԢRZh)ӗ3Cd%Pq?ĜJ4gΌ0bf4.ۊLP/|ڃ1팆M4U|Y(ҕ_BHz0 :^&^ѷ*BڱWee,r|-DmaRfWP~hrY4 jn兡LkǪo^\@YtLj1btB{Yfl Cj̀(uq!ni{-j'+R7sbQ(k6ݧE3a{Yk7yJõ ׍a~6)A a0|yBF"1>s#v2(0羹vBP/?ViCPQ@ Ow )Jii7#oMvwAT̘b#c*$ŢJnkYnF[QqP\F}n?4qgðe[]!HJ!(.?to80P]p`q/O uu wH ݩ\)k 0'aځ tINa+KDfǁ1u qif#.Gf46|''u!k>!=B{p0,8x,%9v]EQuo/F77k6hxƮ@967#^ŢmL~r(jEvfg?Tځ$izA8T,2b-!/? 8Ҙ"J.a7ތMzD:7Yq3ž=5tj0[)y\H"}PHx)E {q5c4Jz&yͼN5<[nfva~3s N;Jg,N2 ^=Q1e o^Kk-2rP/P)( O endstream endobj 1542 0 obj << /Length 1240 /Filter /FlateDecode >> stream xX[o8~ϯ11}n[;B[Slc)&ʹUU q\g˭:|;]QwEg9ӌf؝&UcŵZ;/~eQGs1 B(5[&n{t4#3'l6?Niw@CVUj!8+zWq. Q6 3٘".ICC\dVGvIa˜ b7'#T;7"1M;^^ډSY@M$ß*!ߊT`ş.)mig$"oc]ne":^Ih\^6򤞡E2Zgs2;)1 >m||6'̝+9H38D@NP FxTi^e%ȷuR>3̦-bNڏ@(WcRh{(`}(u̚"FAk21ۤ|4pp_,xk#bfQ[jq ^MA}309b>;r׭}<0BǢ:9G2|64ʠ`zj)snlKY7+:0)e }z 4SI.c`͞&Ǚ(Qu('9]cuyC-\b-ynyGʝWKߏbR|x[KO)lS )VZ <šiRKlM~&4o:hTMލ6mJUW/JPnv/ A8Lv^csKoэ?&VmnphhOG2Ls endstream endobj 1462 0 obj << /Type /ObjStm /N 100 /First 995 /Length 2677 /Filter /FlateDecode >> stream xZmo6_50 rWp4]V}I{+zH6GpH<3Z+s,8AXB QDI"SXwG>7΋M9LJ.٭!DŗFɑ0:0.g!H޲„} L2(H6 2[ JU%-ȹ,A!9JGέv- B ƈJ:"^:F&wa[f1BJy 6%CIB 1Åa}n#Ljl}#?Nkrny0}. }>x| 7AiǒA{Ƣd0l,xëIC{n_?P l:kپNH)Gxw{DECtSDd4H"t*m, D*}8|iH!0 ϒR!7A !wm| [% #ONOO7׍v]>^m_m_Λ7V~~yIMIZ17Ʉ$lTTDu/gN+rY,/n,~:(jU2bRD3 ]sgL7@v贕.QtX_n!5jRt:&ɛ8'~IQFѵQ/2Y,gmM 0ٯ'H^mނky]dfO QW_b胴Fg&]ʒ#TrQlj] "TىQDyN4&v+8G!-vsl1=C/M+RTo*,CbvT'k#HJShIGjÇk?u6sQ_4'/tS $r֬D*f?D^.9i@"vId^GKk3=eX-+"""APPZۖ]h9&0"1VzQ/0<3scewp(5N9V7pWyֵh.3ZI~܏hnKW7WgbZL#[B .Ga~X]׳w ^ INY'H N|aΐLwf8Zf1aK"jڧc(f;-8Z(J$Ӂ k+.ݴ͸4fD"n<\/|JOFd* H BѰŒ+Xl'Onp]e(~4R4@f_oB)s*~KcY'vHmmMHmr^Fg$'2jcp8m˕ңi/WJ7=r>KQ(N(N(N,N,N*ʩ(A &A0E"""oVuӝ˰\B&f88kVېxgvWOEx8oowfY! 8.dW͛zXO{ (_ly}.4H$ljK.ɅOG!:#@'ɇ>Ƒ'T08-Ft3*ї(c*c3R9LJayCΨW^?\WOj6_^Tクݼq%ꮑ]taӹf_"č ΖS`iUfWG^l8ͯ▤.Ń0o|8$O}9IM$TR91f|bOgt,Em">VWFGjsEO'6HH϶5mV}+E(^Fo 7țBM!4hJ4HA*)Le*-SiJTZ4əI2qƂ qurNχd"YM|4f90|[aqEw4.ƇOۣ .&>qA _{Ԧ)'Fh endstream endobj 1572 0 obj << /Length 974 /Filter /FlateDecode >> stream xV[o6~%QuX3`V) Zmmt^X]] DZ ~2ޑ2(QY0_8PfAE$q0io@2I Gر25zVQof2:{7ʳA{GgoElvy{䇨.XVtB1VQ3Ssd*i;(YL\*+8Us"L@o01Jq6 &9kqg,1+ygCEЛ;uzOD_#!^ޢt8ڔ^S[el"Dێ3u}q`/Knl g$8təXe2 1F%.F.`֬v{z#?y-HV;۹OOScm%M A)c7 i/0Q_pL^%uGšjP,RXS7Vdv[擯l{ gz(IP(%BIY;+)(O8ϳ-bx(,AB^{a)Db?0ɧ΀|֊q L`FT7Lc$E` 1Pw~˿2dvMh^-iǾ+vT7ˆ7$32EЍLO Ow ,W"B\ ̠xȪ~Y]7 Mk∵2G8ٯ<3v}_L^ur%9` BP;-! RKo8&GJXť=mw 3gd ~=40XͣkVB,!oOz=N:fXboObmSI;p6S` p'i+civ)̄pDb endstream endobj 1595 0 obj << /Length 1194 /Filter /FlateDecode >> stream xWn6}W.Ј+-^}k>h[.^R IْxE ? 3g iXaj}Zb?Vkz JbDo .]UwL847}͛* Q62z$)v,kWAFsčp[.sQmfYt/?oyAi~8q=H-´Aj&W+iΪJǍľik>H%%#}vh" 7Iq$Hj>;,wPr,*QY uLuG E(TGr/%!v-@#T˨K^Vk^'-ۉeQ6-e4hXma\/$ !5'z5iz/`ď1vȉ +: -!r8 I4#qgK"΄S+g=rR] O)挛Phk_5?~q݁wI($ow˪3 >z~czP '$֓uHLMJեz`Hbrg<~S|hM4BxKASWw.#4yo;37eu$w֓W?2ޞ2ؙ,>NpY[ endstream endobj 1632 0 obj << /Length 781 /Filter /FlateDecode >> stream xXn0+x Kh 襍oI %H;Z/P`0<șfA׳Ue\BD(}- t7#aysq%#i쁟n +/q6\ZM;{FW~p{o$tY̅Bn &Zuq` t;:ȀKp(C];^oq#/qw,7٧f,{?K4>Ĵi2R'3˕,Q $D4+YPi3Pah*5OӬ ?5P*nHx::S'B RTvc HSB6p)<~18e'$JW˻j\WVSzU]5-\ڱnzг;_olx-Ui`#VR./s0Q&_8Wp endstream endobj 1644 0 obj << /Length 766 /Filter /FlateDecode >> stream xV]O0}ﯰKcDZ6@BhFyTtiaP!mPU{O"0F9 ]$!C&l(gj1J|&jy%Kbts@yc{0bm. m45:EA^<z֔@5hHt^76$jY6+(luNH =?h+Ok@å>i.,`@¿}HgW2Wi803gsD~m4xBD!>#SMS^iʆG9y(nǃ o`KIj[zug.4]QI*'Rګa7SvFƴr!)fLVn,U}c}EJumQVmwn^XpEAw|62brH4@nR;VcGJU [w6~ml4k}3j#暹V88ͥ!f瘄Vs0{wŵXކІ)k}q endstream endobj 1658 0 obj << /Length 796 /Filter /FlateDecode >> stream xVO0~_aۉn$6'@(nHQ9Mc 4M;MAgϣ)Hb3&]GO0wѕ ft~tIXHjp3 }I*<s2< U0 E Acy̖>:C8\c'rmyA;JQ{ǣ0,25.^lqbE6ֽlFx`],`l 3P3f>p)tǏE4Wv.^S/&*3Ϸ&YQ,g7 I,p&AtI|Y0oFz` % *\QU_/JN5ؔX - TxդI> stream xZM1d,$@,fd0V z${12ivg]JSU(JhgQ$Ih!8܍(AU|+ZDn HFhyDM䄶GPg ̚0F֚gd`"$GZG Q5|ۮ݊7o-K*? 2_1`ExSF7N?}ފŇVƍꮾYiv;0?xWݮ_H2?uS}KN"6Z-cmY0Ǒ_V׽@E0EEpEEE-_gY-ߛכ] Z}Zuŏu﹂p@\J2iK)hBԾ^y/>t^ӻVuv9m f_Q R9/-hu}[6یj5A$ &h #刼TLZm-O ck2+vWm[=r=40+JO7)6 NF= 2mUPb"c9I`8 {ɭp^M&{Df0(Zժ),fm7my1£iY%#XVihϻn[*N0!rM$M۵m] tUmM_px w؞Z鐩]1Ky0BZqIFdh epq2y/q 8>jfݯK+S^9 V9&}3aIZ2}e:qaz"-“:ɕuKvJTJ\|R/ޞhMwjZUyA:}4\p0!2 ŤS,{ְ[Lg4ZC4LMG_+;VAJA}d5?M5ffdQ_a>yxErR9jO\@/T)V'x&AvnlwLl|R]Ÿ@Dw]cv׮By B]/= O$^s&$';騚KW<KH7r]~@teb.>]iyu?ǹ LÀO/KWqVOͧH ίX͓%kbѾ}y 9v؏Qm Ц mNdkEއoKfs͖>;!JCܕs:1hEBO7A՗Cp۔g أ㜵lO8&!< :9 lo( L2`~͌qT-wǿGA\pp endstream endobj 1669 0 obj << /Length 1739 /Filter /FlateDecode >> stream xڝXݏ6߿JY @>JRR<%Q [`{3rI.=ߌǀo~on^,I."ĉRA `_Ce~A gz Lu~o9j ׀Q`΂b LҏGo^\D9=^Ӱ՛iۙQ툇n=9VYFkIJ./U_tyfu, v#"!W]ȸ bNF҇^ժdגν$e3~Sj=hߛ_Ы%j0yqPč؅Nv.:h . "#,?eiشc@[L\uFG;hvE:%zlFXt$a j[*[Q.7:RrtROHeasi'Bލegxfm̊N?{s+@6,e;_NF$7/#aY6HȊY%)A3_+%Q  3`ΨW{lԢM;klТ"'`bf3L?l K?٭]CFw_ w0l#"6QdV5~K4/Ś-g>^;84,k2gh[c< ;45X; '<$%)}pK0{ ) ʹnh$I=`>HP,j0'Cԥ!\[-/VƧx_T.Q%;31?fs'ϧ?mymOf@BZ;l{7 endstream endobj 1700 0 obj << /Length 1338 /Filter /FlateDecode >> stream xW[o6~[e fH%vKb/k0dȒ+RM{Hٱ"^Ģs|^QZ&^B0c.B/ )<+ty:4&ۣL;EVowiSZEeT'}>2a; 8heg>%wmz1 4solS%`^DC"%w>5E;ב8$f+5{NLfԷ,G A^x$Va YSLQWG,;wXBXBsƌo6UvJ+eeCuDYB熕%f XmR'U<$6jUe]ݥd:Oۨ]3pV"AoK6VfcWmeV8urc+S - sz' љv6CɌYajؒ$Vqxj6E.KG.*BI?n˃-DPO!#6Diԍƨ1^ttM{])I3i ^ UpEr2 Ov})rxu,oѣ\O"¸OvfJ8_r\f=}ɿ!*}8m6´Dt.3DRޟ'o.Ɨ7ޛ  ̏2 I'fg$b Y"_ xhBEim̽Rz/vRjr3&( A?3o(ߵGyYBp/Ed/ @q7O$K=/[;p/:Wb!ډ0ʋ&KRJWXz8Btr ax' ^[ø2΄4 4o,\\BD&+[wK+wYG_ֻk>I__C| endstream endobj 1718 0 obj << /Length 1261 /Filter /FlateDecode >> stream xX[o6~I@"R ذ~@,~/R,G`оǼCo?W7Id(Kh1bq#hZBзj-+-Ue}\ruó1&Hߞٲ"=5T9w!Kَ΍W7L)Nb?"2h1UY:i뮐UKQcfw̱]3x]˃v*p!>9xp?SrΒlڑ2wQm]ybYN5,JPhἾ' f8|m";\~,&j( :~zݟn8+&oV p@HS, GaA ]^1 /~mRB:G 0ؓ~'@\0r;DL@^Rî#ÍSk#Yߺi{i֫xS?bFZ~ /b̧2pxc1ԲzW.d\1EqrZV]},c/ Nv"tDEhl(S,>픖ha$8c`_CƳ1"٠NyrI|%D ]D!m]T+=PCH<`'^GKܲm t6%0#%=bۺ+{;vgdt5uqϗK-PLN*@#S3|gȊ7A7 )@ZOibPrQ2\E% FiD Jd8dc0V>䥡sY6UUe[w}7r/mJy *ͯɏ[UlzkadJ(uk bpQץlpy46 ]OYrFg?3$'t%Tҥ\ྺl6ZaR{S$(ϫ `J`ew[Y|r5hcQMPm,EVPI–1ZTu#v'wf˴'ک؜\Uk7\XSeT%GY䵦}=]RhĦeYZ9I  2N"zt_߿QDv#Axgs endstream endobj 1731 0 obj << /Length 889 /Filter /FlateDecode >> stream xVO0~篈K*Qc;q~LiO{bSeRZM,v;N Mٱ}w\>X(ho=1 ȋ#X@һC:>t>ɀ‰$" /,h v._eLil-Ws10;4ς~@hf5 @u\gֲ*h<3k=_ɔ2Ls^Մ2NYQb? 6Ҵ3Wp3dۍҼ̄z;`."Q[ c•|#L3!o7a"p &pr4–!Hz)#[ƍku˚(#LmJRfҊߝ, ;jl?L\Bݳb6L{=vV{m!tLƃ)ӳtx}#~ xӑv%hW".ZC!|K|\nV"[#f 4]||}yƿ_2}$`_{H endstream endobj 1748 0 obj << /Length 1006 /Filter /FlateDecode >> stream xڽWKo8Wً Iz衋m ۺ0hQ$WP$eqj'h¡H Z">e8Y& D Q,"&stqnZj٭eQu M>^]l='')0H_+d}D$$1iP~?F?"8Rz8<DG ޜ(7s±5S>W 0I}M%1#lZR,?xWq8y|%<^kA 6Լ?$ŜD}>8x@ `1.Rۓ%J?8KFYifKySxV)jȑ>< y L2jv޼y]=0ScvӠh6ӌ-+rY[mQ7bji7+ecKp" #ZVfg|5ŌċN5x)k4dֆ)nZ57TLM|e(Gkq: ̦pku:ͣzl?!ny*g8Um*ޱYi`1K dX}'cf,EC@{n$%W>_8 mŵ9ӹ t ^߫{ Aem!3~V.4XoMx#_%ץ% C,7_ +A-grxmB!sQjNW̝4{m!, n&Wf1 ܤs?{orqkFo..=}r(7/;}Nlg)j3:ZLv7z#JPCq`"Khk!FXaDq\'t븻i^\ԥuLN,Dmt=22!E6562VoN֫~~Jwr2[dkȕygE|͍=+eḫ.X{-f endstream endobj 1665 0 obj << /Type /ObjStm /N 100 /First 968 /Length 1940 /Filter /FlateDecode >> stream xZM7W\(X"# l!䙎,#p}^Q802YUd}'&\8J.M G)ifc&d'"\ӀA" \hBt jJ&$WfQ 0F@ F xKl*~e5p&@6_GQ+t&[#(68&58m95jFJVK.I[LGJ89#D *[rl΂_%l  U8L6 ù lFCT`&. z*)tBda!Å٬KR[C͐@$v-6 j[C /6 N&%H@&i? pLR36kfa#7.7[`Yz1KMvgƙqi]$r*bk5զi60v@ھ`[B-*m[y@1yn*G`hUڀ )xGG&8?uӟ_J oω0Xt_qh^ƣ8͒OV˭{M J=A|!r/j^^]?Mቛ>n;UU7~rUmd۬vnd[we\8pK}}5& ^L^nדwMn:‹?~ۃѺƂR9L=GYLc'J=nF|X=_98᫧.lwo||mZR[.Sy 4#VLcс<#`C JN8t*-ތDGƢ[@e$:e/e4P}c5yX .4@-PV B2j8u3a:\qe'BzQu* Dٛns;_ DgbE[IH>i9j-_m,;dKs"jz_쫦1|7Jx;nuYr}E-ǣvݦionWe!f[OZ1fiB{uOcsvځLAjqmvl7|cF=#R*>@ 6#Ey ř \}FcsCvfáGCkܚxif3JcEh(>4vIk3գ^\#t]o(QDsEgiMnO2o! twE!I@|?ׯN=}!Fa,vg:IZ] ]l/ΈUHTy$;YAucф*qXÑN v|?M'Ov!:=cC%td ۲u2vUi b~s09m/%|V-3Xcv::|~.[&v{4!\-9ɃF |4JzF$*: }CK2 $deNsh}M:gI B;`tNy,,6PC)c@3A#Ƚ̃c H|ZUяcJBBi/za@< D^]B2z@c;NA's=\xQA !/IA\/.H nˬJ_L;-@rhw%Y\LI nnf/&`h2$l/ O)+ttžhCXƢa0z*zGz]85EA\6V[c J endstream endobj 1764 0 obj << /Length 1763 /Filter /FlateDecode >> stream xڥX[o6~ϯ0YQw Cņ@t/m2kٯ9)k9˲1fnkw9`1X=bXX Wf5qx`+ͨ]*y T5;g#lh<~ |b>_l!pg!/h*⯳wO—B.ȑD,XGb W9諒uZًПttjs=zu~4> stream xVMo8WC%fH{b eQ#K$ɿɎ"@HK7o8"hz7k6)XH5 y]:<謝j"/[]_f/.lg$vfɄx4X;(1ܖң]Ƌ8@4BR5L׵֟ 'c=bGs7uHIڮu6GCeuz)M{iIQMnV>&~ܣƘ lUب5Bt*&&ƣDb֓/-{eۥk(0/Чɇ"Yu FhOl^F1"vm:0,J`(F>2]lǒ0zݿr(Ca!z+wLrs0xplq`b-Tsb-uͫ@%`luӶ7}zW=s 0\)oLPI 2UuUA˕qO/( Tdg9Ъtc gǧG`Di'G> /5U4-5d4f x4ަ0խwcEkMMwY?&*/ۖT6VK|%^Y(@gqWYM@LWG섮|[`aă;SB6Hռl!jyp{U8>i7ƭ?K[]QWnȪЭgQf3rբН!qgW9)?/ \?)'$mjʹaxY5-@'Siw7ݷ.ğce)W0x!nz1C42Si8XJnTw4;U2 endstream endobj 1810 0 obj << /Length 1255 /Filter /FlateDecode >> stream xڽW[OH~ϯv_ sTVJUҖ *8&x1qj;qR!1'se"0AFRL1b\Jb$ "rmLꭓČ>L2&"|X%qu\gU.Fv:} q@6@Z ]|>~ w. Yp>_5Lm[C0 k 5̫!%phX%N1S` hLpڣDapeer~HW Q ϭ{o=h}U?Z.F"i^8z p|HʸHWU/; oVC:(AC~DLF|gUuUQl۔ {U98ʲGwR=-պKpڸ?QrdjNښgӼQ O!4aTD 'JFT@瞚%~]S)KSVҒ2=}1atnjn#x8SHIlW6q*ou4ˋV2n".*ȞN"S[7vhXKJeS0 l@-2PzpjW GvB&*LDk-=]NYؾ&S<FEU[ vVE']@b1(`[!嫻dY U?qxfiV?yR_woT'U\~UT6Ϫ yͲ&_X~Y#[3[{=?q#2O|UE CQ2lo~ʳ/wG;L`2BjXFu;ap0EɷZ-{zٶZU6c[n&}w(eۗS_ӫR.6yꞚ6x`@5koQj3j4 8N$q䕳PF0GC2ZlYS`o*lr%‡ $#GD\#vw I?Ang  Lɣ 0H> stream xVYo8~ /`39;Os$LYWy,%Xp+tnJ|Qօ֋ 9@HYi+ݡYuEm_Mw^7}6=Ǒ8 HCY<f{Q,sZAWS&WL)JG 8$Qptݓx6y9|PWhO88 Vp`.s]E ^pkt1ys0]ӂ4#M(T`n\:ذe"KF0 ˜[ЍBVm̳(N1%S}sldTF\"{0& ٣sĈ}ݜ ebe[mMlCƔzgϟtF ʍ[ &$=! JO}\cU\mZymMv{H~+#3ڏ4߹ڗ~cBM%)TXҡAdTO.qfЪ7G0b&fWeO; !yT1$ ZytW^'~ϑ<]ځ #K[ôT'f!]`9FF۔.`*VǙ0s0O,s8!L6W(_F endstream endobj 1850 0 obj << /Length 1008 /Filter /FlateDecode >> stream xW]o6} I@S aÚ{[=%ȌE\QJKRr, 6)QCC_fK g KQB0 J%gh@בPo36&oVR[;/ucj7wB:nȴ2e{l]O$ϥ(zNhJ3LEl0&G&+Ta(Dl`rW!(ZmJtmBSFǘH-Y:2C֒`Yk( H\)!ORi'PGbR`0j`*Le0C3(բMM1!8inǜt8ޘ&)8;1ar J#VΩuzo7 h|WyӃ:rKW}W7eTo< kMe~0h ` e{xݮL]pT)2qz y`pܰsMNJ'±S1O#W ZcJ׽"2~va'σpB{ sV0^LƬ[ϪwXn̠ˮN3Pg$/HGGr(Y\Wc4E> ^!glp/m{ݼ{}f wy{8XrGhF" BX9:ubMxס딠kD|'g{2"1[RZ $2!םNfm'L[kP6=|mIߢomoq0L}$qUoȶ1z6ѱy=̨T(8c^ڳX>$^cnրMnp8$7:| n!覬6+ngKݿt㧯vWD endstream endobj 1770 0 obj << /Type /ObjStm /N 100 /First 978 /Length 2045 /Filter /FlateDecode >> stream xZMo7W^(pi @AJeH}PKIh%RCq8ܥqRT ñX؈HV!)B2 I1 6wfNؐc'OJ{V ƅ E:aBR1b\ ůuYvh :x+d5'HvȆ\3`j$c$]J%Z ( Ȏ/*y#AI1%`8 lC*:yH:BV0-ظ&5:QPpJV0+jX=!Pҭ(柠xjiknZ/ |VeZ[0mɰ`aȦpm1iiwtFCN0rq"H>3ӗէnYdyds$'&HBbRrJ/d7f51=x)!h}_z{t9f'o`"kR a.VfǏ0Pw>{۝oƃbo@dE| b<4?\@ɴNY>f5S :6U, Fȉiѳ 㛿+χQ'J[vÁ6vu 6(Be=A)d Z2Pů+o7iGH#h!C*Vy@mf  Cm"-6SqE(> Bs߉]dr =uݕ]%JqҸDi\\"֨^&&Ș|()Zy[82V/q ,Ȁ[÷Z^v){4ϥSWVW*lu+{t}e݆JlEZiպt|Bkoʿrw0HyC{cvVHjB5L@m;=N_$ cpmCpaAJCw`A.q;Y57YYޫ$06'z 1rc,MhݹuM|Vp SEW>^5^ WE dE[A /jdžPj#*={gjTr}he qt',uPQ>d/!/և 6ăIu$%mJk [ r`p}c7 Ԗ`[aDGB jBpon; kr۵4&I#iH4&IbK~m ҽ Gs1-W%Z_S =/P`>":+S{xש=M}~}$ޫGS}JB$R1mRq)Rre%e2!7^BuC9Z a2Pčyb" {YFXu_a endstream endobj 1869 0 obj << /Length 1029 /Filter /FlateDecode >> stream xWMo6W3$%=ld{h=e-ӎJr"Rb)"I̛7B}:]\qM D"$ Mv<ǻJYUllQ2YfYT}~q㖜P,"PR beDZ9'LwN>l2UvW@E(5~&4lSm̃V& 酯zMKJ*#À->~?"mPjiwFgY Qߣ2;ZymԄG}UQ:+Yɲ4顨Yj1x^>|1Lr:> $6#7c?Џz (cagf`mUqٳ^he:4J m_۴|i8oK+JW³4Apeڮ60kZţt c\$i\/8E$<1x_b-2)MZ 8B pp XV՝>|cUcC@nXzaw"L:( vKK(4&_ a̲'nf}S=@H׎)Lw. ֯>q'nN39P\97=zꔈN ^_@>wAޛXwGo칦`Hj]ɑ8}Ѥl∁MUøK뼩:jafuXIML#+m=+󐺱5ŝ4ˋFvyl$Jqt^`)JJ 'P1o\'Ṕ٦N2{w@0-ΝG]To "~5s!xU,^lyQ6j>@~r]k5 I{joev9 Ui&ra8υt}͂^pVŴ> stream xڵVn:+tQ >D,E/nSn%QX$w(Rر 3g<8-A&L'WB!U"4] Ja`f::8)ih qf̕D?ڣ(x0/Ч=c=t=QF1Qcpp%ަN|#+6@`ĭ(Zg\W= cr ( ~GSLF #H#FQU.H`DŽSql^=y%,vuZ&/W#lGa%?<wP7mҼyCyoԧ!5G UīAX&Edee'FiZŭvWJna`D_leP;L}}V`!|kϐ^5yz"p[4A(30AbL;(h< pA+Lysmw6 37魭L8TqmA.>JEލSlzvun㶛$yQVY-6#+iU7:ɰD L0xxb|MOx(CuvA՘yɲ>}Sl]%KrxG\ @pޞJ!w춵 {1h:id() Gp%1SN '=tkGksT#3[?@ .y:+B6PdwI7~V c endstream endobj 1901 0 obj << /Length 1786 /Filter /FlateDecode >> stream xڭXo6_!lj1+RDmC&e:*K$7/b(xw{ˋ7בD"> ȋ#@x˵w7s.g_:ڼ*?V{]mU,k>g *Z^sa{|'Bvw|o =XJ`0q^;~xYr/cS:K{sQ%{ |JN@BD !4s fFDJ0@`\E2`V}t7mq=='p!Vv8J`)4*]@v[:I|ސC4%,i. Ln+N-ljG[R+Z^UA(pB 1em˗~sX5]VoD _("i ;9Qj f{TbP ! 9i:f) X#Gk'9N2=[S<}\"2nӼh˭nnkm( kh9/yC4',#9nǥCda#YhL/a!H&XH=5 !s;T7H Z#[2U8=MM[Pe.haV}IMkMmzƃ .R"-)h:mmpg=BY񹴗4큆 &]iAFLu$3@$D$O-#6mՁ\sӎde_a$O,!lB:jh=ك4a<a tern!}v;>S42>E/ "G`"-k /fO 7Ϩ9óPN"yQbS`I@% I*nG|f8NvƣmޟG1SJqrkgFtf+`m%ͶhvϜuKމۼJ*uyQ`bh"t&Ըm:P'$\m)d4W:([L0xZ[T+zK?gF–А L]eƑFTp۰MC -u ^Mp/nMrwX"A>Daʴ{&>[=|Cb6_H6 klEB)]֒qgzyWf/O- {ޜft~ܛ^TD'BKc><ÍiÁٺH{{aP41KP00Tkp;]M'NW#=KK"~##pgaG>u}w 98w#-ԋ|j|t o/ ]-/E endstream endobj 1916 0 obj << /Length 1502 /Filter /FlateDecode >> stream xڽXmo6_!x&1KR^C%CbSMB#;7OnG4u<=wim<>y4⑷X{RG{w >]*JzAeP3&|S 6Z*aBݾK@UUqԛG<Գirz' %)`^z ԨlYUyWJ9PAì'k_6%8jSV݈ھgX 3J Ifr{;2gl휱(_\.&OX@=faD9Ib-'7[Ki=[G/dġ1')v.RBDD[y"qp4a n\͂ؗ9S3Srfݢ)6j o>_Ppp ~MCk_d 3Ni (}+Ȅ¯v* X ,PAE*dd}DMw&b2dNHJ˓zl#S!ff9k/&/%5>+#dN=EʩYfZ:I驷NZnqjH2'Qŋۧgujz1uVKS ٹG$:Q[vR N*v!m!g 2nqb8PqP!L|Y! p;Qx8!903-~Pa~o@`m<&iQFo}9KPVZ=u(SDXhwD:b=c fR8DC1vfx7ΚKiTYq1KmNu#6^3#Ƚr9mPXUi2pi _qb$KQ2[ey/ T*2eVLа{ݶ]1&m̩=?.]c9]ʬ1 h~)K]GJt_#WĈ8:{mo>.x[o7 vLRbF_/Z~4:nɉ:g0~ n/q? ca endstream endobj 1926 0 obj << /Length 713 /Filter /FlateDecode >> stream xWO0_au ^`uOqKDhXώF-PV|:gX  &#)@ 9%`Sw4ˮ,Vr&R1[PZzɩ!qJ1 дZD~r86L'8ߍF}3#C#jys/,[(4 Y,Ś1 \]F[/:;V6#] H0{HzCЯ|1 =IT0WSUoLk7^Q vE1Jl1T1U1I(Xjeg@),> stream xW[o6~=H@̒aݚm0h('ʍ}:*J -P£C3v^/~[-^\Ĺ<ቷzRF&!VwQaNj$ %4[=EtM2wuT^Pw&8Y8KZ%hN6 ӱՙdI`G􄢪qM^0o9c0 'p컸պӞ=GՃw#O|.~ݏ|{~#xY7+al!ay/5B+ͦcaGH"( `X4qg 1v&aڸ?:Iqf6C4YI'O}Ceq >P4HXoh[Q*6`c%Qq݃a۽#؁hUTJv B -q?#FꡘG,lwT$L>窨w= ;t+[N endstream endobj 1947 0 obj << /Length 711 /Filter /FlateDecode >> stream xV]o0}ϯD*v|L&IS5i-}Jh!)i}7ƶj6!MAo7}Cg>%`2O/L|Rr**Q 4t=mx Eea3ÂOTDݞl+ & ҷSCEp 2}'bv<QY?Ji5⽨2j#ƅsӱ :{TUZIO TrjB8*uVGfGK2w pzv}R\EIŎ1f6τ4L3HGxӣL#øY|46t8q{eZ6<Ղ%b«'boNTyMDNr,d,.II3m""Yvt]wKb>U})fx4 G22"YlZEFpDŽh4w%0thΐG^gp󤮱K =X =  QHA j4@@;n`oG` 3 ~| endstream endobj 1876 0 obj << /Type /ObjStm /N 100 /First 978 /Length 2101 /Filter /FlateDecode >> stream xZMo9W&߁1@f0 ,Ivh$[uR'խ"H߫B ]:E60J8mhF}h{m#xxGu V>* f"7Iv D$[ZDD #A[ $OV^DGy41_ؗæ;lP"97!js~tt4_^Ԣc|^_^ ~2Nf^M;Kt6yM|PxᒓPTӧZҍbN?tԁWutWŠbbbbl|ӍYFջi-iXէկ/t~MZA G c$Ǫ I<7HTDފ'g M~_.Џ W 'x6<(|){67Mgw z)fT&?g8ëfΧ+x䓴v7 X%_O,a@R iq* HCFjN_λcà3䥦uمnU|^ho7Bƣq@rڂ-:Q?E' ]2}?=謕ayC%0ފ$R MĪC{zCW=~ޔmczc4S:MxUq\4'$Sjt^ZNՈ$oEU{Es蟜[[[Ē[Ē[Ē[Ē[Ē[Dw 4^KNLDf`N89PLx.x9 vY=͢Es$,4"ɮh 2aX2Zu[RgRx%؅R% W9K`art.U!Y6&PJj{z۠[{KoQX-NJ┞ũ##@gJLpt">i'!i/u/D (:DC4<i'Pr屙TS(P,ջ_'ߋm{Ѽ뫕|̖5OWLT-(j+/O:x~6BYi@X ž׸(CvZvQ7Al"Gp92Cqc+8Gz۽Vo u[< uJM& :Z#[hφg ݥ=SSN1!!'HOPDq؉B3kXT| Xkk#WRKIIhz%qj2> |!+r:V_٪Z,_Mz6w2X?o:fm>w&/ endstream endobj 1962 0 obj << /Length 865 /Filter /FlateDecode >> stream xUMs0W0,oO{Hۤ3=)d6 $'v}%$Mi:>ڧ}o+h-,h}}~l p`3 A\/<.m?p~οMσGD08͞lS&%ҁ]PCuպlmJn"Wd3+裓pXߩiG]GE̯0 jGXH-Wr]i3mWYQP:!䦠ݩ֎۠2\BP8 E# ^XKl66MLmXWQO&|XIv endstream endobj 1975 0 obj << /Length 902 /Filter /FlateDecode >> stream xV]o6} =HhĐ=lX3V)-YRbc?d(`"/Ͻ6ADo_V8Eb?F QBp( JtTc[ \w=ƾy;z}e'CBq%*eAlw_ !AXqQT4RGڠh@\?x&$d(qHQB$KdH7I8qRz06)DӈVűp9_\J&尫RLw]ۍY;3Vf1y SG kX;H쬜qcFN-[48qņL=rMOA'ZȈDd l"̈1Y:c$4fJ0ut\} GY3;{\MF$ߴgj@!j|:)'r)KM{H\ci y1/$AU &L}.R4F\?r*V ymf,˚aۧ^7{DFζ0)Sg8:eaDWW~bV`+7|#>8E?UO87.,V0բ,/5;S1х*m{r3lm2HTeyPykލ"_rﶹ*-/7U `qx aE8M fT_ @ysxBO'3,+!L>5oE`=}*wޘ˫zŸA "^r? bɦZYp@דh_!_6iͧߟ2 ė|^ 9 Q2rKz/s5Od{?s&oQ#/+^& endstream endobj 1986 0 obj << /Length 1887 /Filter /FlateDecode >> stream xڭXmo6_!x*c5nK E.0 EmaQrlWGNuQx<>ܑXqbZ! =Y(%,ߣZk f7_񂞤qChjYV*6(=/ `హo͹4*ܥ~e}WI-*[ԥc85GQ2#) Y`|%QgfnFDe2y 24h:χcqНփG>EPŝy|mzgv*[j7PO\Xg8ΐM0B6S;Y*)p4$; AU0ڟYRU#qG<4R:-a~3ӌÎ{ M<6d6wX`/I#X?\Slv34|UeW&Խy(HepϘC $ijZ2x!Z1{߽}1@2+Jv(t=DW6ٳ]lyv||Gg6tH?Yȵ PF9?!|km鋺6+ ʻo11Zao;Uf>a^yTC {}оFJLjaOR?gAqd(XV)DXqVn.?"PuHVc1(JرRV#܀P& }>UZ4[P ŎZ4=4;!iRRրؗTne['XQXjc5ͫBlyA~q~sѺ}_'jo9{`S469S'4.Ȳ4np0'kg;#?.АB~K +NRmuD! Gs58w0 ]E"iyʪ]14#d]NRk]ԉs7wp"ux[Ou"p ZFt bDT8Wۀ<48s e]Q\ݎJ X.QF%Tv>u__77쵌s8FdwBQI3} jk%ToX]WML+/KT1ӱ.W=`+rUEY:a[e0ֱy+ǴTtֳ G׆0( Fpwq@)&pxF`t랭h>nm^-o_Jf>Q)\xEx2 ҟr1 endstream endobj 1999 0 obj << /Length 1931 /Filter /FlateDecode >> stream xYmo6_!dfc+^öCRw"ӎVY$iHղ' EǻsiZY/Bz̳K:gCg|a]OW-m[9beSȲL};\?jq1 ~}+cmpl3)ERɋ$#$}TDq% |mT~Y.q?. !!hh]ܖUdݙfp ^'J|n(wN^('ځ8J>sK܀V|;m)n`GZG00еR7Ŧɟ͖ *f@ǸžK-ObL?^:=1OS1FŌ:R.f63OqX*u{Y\1-Ȣ<"v'Xxd&2.Mo @YfR#JU2cbRfd)6[3SFnI4bJL wͰQPF+913ADɝ_7ԏw..L߫.e]ˬ*}XZA hYս w}d;&6&S={l"Bdfse:+`A M&Ae6"c%AV5iߡqpLVQE\]T>TB%x5bn25 o`nj`EL~;6[h eO=G4(+$JEmRMͧeЦ.g(2O<08vt=RBⅾBIa]ԟ:;Pl9=ZX46lm@i2%3tj[(kL-Ҿ/>#5(ʞmSCeZf͎OK.A\Ȫ‡Z)`d%u{ xCɘ>i=Y 'J;Q!4AAk2ݟƜ_Z4)#KhTuRaYG PNeF8 ' ۢkp7 hNSNXyH iFU:H֔M&u@~ L 8|T"׬TzK=*cԃ ԁ$UgX*`fGl|UEu5~ 6zwP'""p@pڀSG_]it760æwMAh0Gըž®tQww7Crxi!#?fP"E)'C75`} u<^b SWC8,o&w1q۱Vb_?HِU}[;zv!WZ6M[}SYT^c]_=!|> endstream endobj 2019 0 obj << /Length 1822 /Filter /FlateDecode >> stream xڽXmo6_!+`c5#R^C$E-q mh-WiK;+ -{}sΫ39|;+.Kǜq|3?\ p'KmVL!urg&3vyxΈQ̘h>j*5E!ĈrB+$f],Kmo%|,#&A}#vGBDbyxH)Gִӕ& ,hZspUҳϮrQ6 }Sʦ#Zx!mU{,Ԃz, =j1gj_6U N>#RCU7|4/̗*)y QU)x2'rHV*J =TŢL]IJcpƆHs'޴9n<㲒d3D^!i\Ihx<q!(3 Zz{H 0 Eѵߣ'.Fcu@֜!< +ֺ`1JD{ ES (\Osb)Yq8DUYBiX[]+b"V"8ё$ 7 0s|2Z=XY}HG s%EV=`Vv0Z7qC8փ vղe }0O[\ӽ@J.=/tdkClpϞԮbE3wjY 7p7ɲ栄Uq #kA/#`/-4I%Kyg]vU!d+nIjsF)9>c`#rp>RNy'O ;6TQ"0:̀FoYOt3K_GmCJh:Ia/ [up@@UsyaTݜ:Nwxidq䕬m$`W^T:~$H[aO/ˡQbDZ,KSvqB{S&*F"pUgQc_ݣGڠP57n{/d3ZL`)^VahA\}4 ˚o?]CKTtY~!U\s;Wh _ư&cA0),\q`eW÷XMTwY' 5BХW4 bUYd4i3V< k?Ydw&;إa}WP6[˺C,KmW2.e _^p6*VA~Go^;^s᳏o__{=pśG^UV S"vR4j4M3jZpSO[J<@,d.È-pY P̦ك bj[8t W-O|1mP~zt6nPa#}dD°~Hejv> stream xڭXmo6_! c1Q/E;aCa;`HCG,8~w"%K6 #GSoQ0< )7_z~~ *uU/lT=LkHȄZU ;33Y4~Zlh~ -ŔD"F..onMUμY1~3!vjʥ$iyVo.),V?|z㲓δqm-`Kr @ p;ewbw&"NxsUv]c¨-.ccX5!W`8H1y2S-hE, StJ3z3\-D^ afޏɅǂ$ؙHݖYdOH Dsy@mcz A;`#hC`MȀT;,qŁyA21(FBׁeǵ! y_+, 7-Ԯ|T:BuuEe )D~giQ C2o5"Go_l~a2$Q#] K*ǔK|Tl?D޴&K@K󺫌 x f7z#ư!=Ir$ybϋ hzs 0ʼ4 v ϝ¤Ja"]ܸCCSۍ3r@O..ػK끡gywg Inɛ˒z{\\Ro A'Iݵk/dERd}um7.< J|i&0Ng)Wv ՛"W-qcØIFJÆc*"yDnyX`v 0 'kƶofcMwmNiC6+H?1+XįJguiyG>;CuswCFG6} TzB<:ݧFBzۘ^"ZK3Je7m $l{n٤Z+\i'1tM`vѬ8IS,ӕzXZqp`, D/ja;}wJZUmat06AUHPzo܁̥1n@[C@Fq./>lrB!`=V.z2y#0qĪ?'*c9RO b2x#> stream xWK6W(2抔Ǣ-mE{k㞶+6Hr!ْeiCa$sV4_Lnh$( I,<>qdqC}F,yOR ԝg%Dhx߱|#yݻyn}#'a^8Fa^W!m2ۦ,Z c%}MmQO)Qư ST6;wV1ظiIpOoΉUn?UR\4& %mP~0gVŗ._ & 3jt.D'JO/&&x$IG`TA~;h(a._ϖ,#=;@ I+( z,Jbbٽ,ܗٜPϭ+k)^fsY޼~fLD_t1F4i%6(QTRh(n$"a 6ku%*4B8:j/E؍BѨtdYxt®dJ_0ghiMihƞ˖(N-Q!Jj;#:)9}Ȼں,\+ڊ >]ͨthbf̒y+E3i]&]bղ1˲@1J]d ~eIPac5[{lJK.>̥jo*jTl޴֬[墮ةJ>gJjkU l~zEr6~\:qiSu4cʤTeuR͸!,273JP3TlnCSvۉď!9C>9T"D=X F¤'JAi"^"ulm~]e~XO–J/ltZyi"Q7vGp4y5Y1vp"OCA ojb1d|!!_(qf#djԞQP_c rCgp;9aJf|8t]l/4ǿ HH,صxΤUm*azvev3eN2u'P2UXfp?7&o-BuuuͅoϖzqJմa\ƒ?&[?ybȒeP&E4aC5 5wwSOzckF?Ab\,ݴ/Nc}> stream xZo#7~_!^v_4ERv@({>xI8ؓ"Q&N&f93HGR/g|p2>Bx.Ƈ9DWNhcT?).@`㠯0Q (uUNRAG$D'Q}Y) G̒ ^U2D$ XWEI2j'*בP"Q _eaQKLYtOJ ^Pr0!@~4s(& ]!*KP)̻XX d(T)կ%&kMxa2"O D`H*]lRjU fSAa:u*Il8v KP>袺 Đ`_*Ɣu>,4Fj1_*ET#P$]"+ȤtU'bP"PcR1g]YX|&IԯTL* "8@`y:OT=>"!ؙ`# mv;PI Ɖ.=>^G pTaX@;-2Rdj,&ǺMNYF9A&DiDg꒳玒F0n%r 39B'VÎKWZOx͆u1?v4?23>\0hGκb Qs.Wv›dڜXGfosy&QZn6cғ`Z0D ߨZg5>w]Tܧ_t XbẑUD "nc,6gۻLbog=_M;{|5 g|ۺῷJEly2t7{RU"mxQew3mP(\e)Y-; *S ,9!DK+z1u-ͻ*ywMCߛ/]wmB=9%FNU@rd7 ѫMu0cȼ5L2D%L=mg?G0oS:867]2\" H(;AGTYghu>Ub1`!k񅖟m qUq.碐Gnh 7`+8"T{}0-uСPdkt(}XNA]AsqfA6IVWd<8h;(lߏn ,+ fȣa=WA>KGOs'.aw@nf@n ^1d7t<_%=`q_r_r_r_߲DnӋVE1¨f"EgҪvxuz&!wX dEA-k|5bWd? B):mVdrN⽆ }:O.{dV |A#_jr6廿&j@ endstream endobj 2066 0 obj << /Length 594 /Filter /FlateDecode >> stream xVo0~_u/Aj$N*I{ۚ7Z485Ĵп~$Sn6!qv|}g|>z7d0!F$8$ _ϗ+K.Ezj98!D$U&C.g s@̆\16 HALAv` $f,)EIW=P. ~Dbࢸ.v>tEҔ"/7N=iThGgz? b.]f.L`3ռ}Q̙FOm`BGolk\=P^Fuit' *xa7'4p88nv|ӗ3y e wvw;XNՒc$osh׃ݬ[= #ܛ?LowOϻ̽6:1(қ Pf)x0Ka|P>}=aꠑJ)%"z!Pf)VԔhr2٩=PTvN@Btϴdk3ֳ endstream endobj 2077 0 obj << /Length 1274 /Filter /FlateDecode >> stream xWYo6~ϯ"uy>hx6E ˴F\Jb|gx(|$M ,D#qodu~9i~v9932 ЙF)Sϙ/П1swvNOf,GaR6A ֏?\a@ҙ1 M&[$? iJg8eͬnY5eXBo548ªx ndt(62~<9*BP.ײk9~DXUM `x^_f0#H̗*)ҵYR<,657,U%ձX@#fؕgn?|ϟl"}\V=IYS%d!r!wЮnpk-7{+[/tS; GlYO" ^n0puUg3,DmgŽ$ޤiCH9qn4g|-tqBXH"2ْ|?@fj`dO1~Hu~AM"ϵeI-Z EkC;N"z.?L %V?ɮ]26#U;g];9 ۏp wzȓW|<8[>$#Voh?7l u_#> stream xڭW[o6~ 2!)Rb{Hf萗5SZL8ްCQ%GI 0 Rwn9A>,fa,D( C%M2eUiG>@c8QiZލG7.Px6)8EǽNb8ytSnS+ iCOӘ>C4եU[kwkXtӀ`Bّ<)_]IڊvB"׳hxj#/cbf ڷ.p,(EAKx ѮAB6[w鏐A;BN"%DAQP|& zr2= ܬyy97NCS7ZmʑcGtN8lk\0t;N[Ik& Go7VCUupUUuy0ʤG$ މR&X^NMv[ c$zBkE py9<]Va՝]\ewlyV[Rۦ_>jvW(Qd]8/\ei=6|+kK-D4>NdAHtH64y @gv):Y`ZFRkΡ[W-[׊[KZy~B%ܞ^*z4Ŭc^#nղ-w5PY+0沰wUь*Q9莒]5@$}Ļy?D99ꦟԗKm7u1l:07J˷u/ Nh_HRI9+tjck6H#CM1ۄÚrKl 1f]xZMpk_~rs6uf+[W% ]C1 endstream endobj 2102 0 obj << /Length 844 /Filter /FlateDecode >> stream xVKo@WŖ>X#8mPEI:v:43u;q @qv3;of)"o_p'0 h`3; DC{GH˸L|y)"u+"M]غ,ތmDU!r)Dt{)ץ/[ذwV银AZ|IZT.k:$ʍw1OKSF̸-),9_椘K8D~OYV܏"+I/b]/ݶぇ}dn b t_ΑG)= }>VYTv 8 1ݮԟ.k})v۾aľs\ť5eΤUJx|<4)~' tBd.*X,ٶAzpS=j,^XFS b5DBOs7 ڈ6 7[fhh|ӑrɖ]t2gF~\4;#J| LdHd;U\/WK{Qԃ>QVsbN혼_oa.&ZĉTgǖO̎ )n°Lf*bidJ7FPaա]*ڦTFuͨ acV@:)t:"竬L]`Z~`֖GU0ߗ雙|;,,4S0dd'jDDoP@`H)Ǟw@8gRa]. ED`V/L" |kn.8嬙(hs 9SCD `wΙ%%C endstream endobj 2108 0 obj << /Length 1123 /Filter /FlateDecode >> stream xڽW_o6 0:`*ّc = nXN9v`;mþHQvTvOhIۀ?H gy ,"L&q,CSUozU2s"YnhXŅLd EI)Itꭩ+uJO]]HYA$bKA:>-Eq>I llZUo"pHӹ<֛FUUGOQ3wD2iG뉎GB).g UI}"Mqz {H.Pj6;|t(`Ԟ]F2,<&vMz $rKALAKtZND@bByܗM<;r2JWd$I> \6`\;\4+̐ U!P,ZN/'k'5)T„bQQdY$8 &ĘU hpP}[[8gY6eThxmI*@u8渮e܀hJfRTlR^85%#߅pL4݌NuC5ܐdRj:@r;B6Gл/,ᓣ>s&*Ow\U}FW0~wLzZz(,,TRyL)07Wr!^CpZ?vKr:=|IT :o6'thɫ-q6Irl,KoXf%S&gֺ)N/2פL}dBיmr:^y~Ks9KdjxPxdqD~b lS0+I$.u/KYD@bA}3ivg_;S8pUN : 4z@I uNATlCPeou7GdP-~^O9|ۼXesXW endstream endobj 2120 0 obj << /Length 761 /Filter /FlateDecode >> stream xU[k0~ϯ0)Ɗ|/c{ )>S3_*Rڄ>IMiR YOa#5q: $4BzgL9gFıidcm8bJ/ZQ *X3T\fjA!WMqRJ;Ӳ]tMBG 0[ubr|%):9vAa Rvrsrt"+E>N7δWJAc޴ Eqҷ$ɛLiD,cbk.vGOX 6uƋ3,=ys,&u3&G.`UwXP]Ʊf  ll{ۤGYժY~-P3*A{.$T.vrr尨Ãki8ԗvd2[ V nB5K58͊<5U**c[Ţs~_xіK/\cҡ{6@VvO,`.hƔp2rxӣxs3vY&x:>'f,>m T2ߪ!آ*M-SM1.ƨY*׶]u"M䊮AEQTti6VTya>> stream xڵX[sF~ׯ$EY,ؙtܴ8 P4o $8Va~{ƛ`|Fv #긆bĨmLb㣙dwW"xy&YŋѧɯS6; d ĒgOguZz 5,jO晴06#c.@$`rlIgUX%yE ÏHKuao_ͯNWzɇ g$FᏞ{D{w"8I@mK~qRH5Y+*wp29 p26HC411fDO؈%hrpʡ0NQOf Y 1j6O.9 >YVaSq CwB=c%k^FEv\du >,J< +5Jk?a zaՠ\E>jA@͓lRK7ʋpGXG;k::]ç[ a33~ 2 ,\"(.&jW2yҏ, a7hD.|Y",K/6/ k.1|lgMl_d1Wjr 9ԃjY7Yz| ȒlHk *@@ ,pVI٪!6!Ī:>?]q$'2o_CY@T } ܼ yߎ{ 5IjU_ Z˾@7Lj|Y%i?B*lZpaƧNLv'Cj-x畳|Da*(G]3֞C5V(ͷ 7;"[6HW~[u~vQ6'&i2Wky(U}Rs+zٽ/hImK?Q et#F;df-le;vj)Ju1b{a2eCͰbO42OSX7r([>6P)MFuw?Y@%lYiC#`㣔߹a^'iR?aw=5\kXPᆹ^ rXWs *`Rς#*^ ++5RM J10I t"vädݛwy'9YL"t endstream endobj 2146 0 obj << /Length 1003 /Filter /FlateDecode >> stream xV[o6~ <MT{( + lM\`@WLBu$'ΐCRrfx.wxxB4y51q׈ 8CI28szFI`ʤEER˴Ћ,-Z] |LNUK#BO%#l0B4ܬ6naZ7`ʧf6~'=.޵K7Lw/|vzr̘|ZݢT.P5poK 7{f3hDQICT'-n򟓓+{u3lbJAɱMdNupx'%нɧ\eYyȷYBAGgDg[>9.ɶҵjJӻw+͠)9q eg_=ue>BQO>|$h8B7V4GR,y.'> wpPP(G?R0P~?ozƥ]+09z' +Cp/ٖhpo(:kc39K[owLSs@p MEs+@D5\*:VPbk$uZ&+ ~Zm -8'{K;c;6Hk/+խ[g0^?5߅!K3RFkR0g>'̛oѨ\hG1PKFolez&`"F;} jݲnld!\e~QP?@烨Ͽ% {apB IUtrn'Ë̂=MZ<P9>$2&̜F6 hjum"oMىmTM'MfP=9Uwz康m2on]" ,B4 #t+= H_ ]E)a, WԴ? S endstream endobj 2062 0 obj << /Type /ObjStm /N 100 /First 972 /Length 2074 /Filter /FlateDecode >> stream xZmoF_}I>d3 pvhbӮpd!RTd˪hKː<;;; Wl\VFEyx('E)fMYxe!(Q9"$"t "X QD93DP&14.a/cǠC*rN0S(zAk E`{:$=w1' da#A%x D/&"xG`d[FL.Fe HIly7+8F6ʊ?"T|fYlA8&{ h9@*րHla՜xŴc"@>H6Y99Le8+F*d4\SwԍI@Cy#(o8^BBp,+ﰬ>(PY#W>u QE",WpR7 !N! v\7ȲE*rb@'K*Tc/kV%!9L2 )d-Â͐$Hf<9Rg,vR E^yf[>${ 23TnkUNgIuҖ9ͯgn~Q}~L:/[jǪjx␴AZ 6;uz U}iTx2nb'i[ϛNG3̝꧟Q9k&b2m2BvN{,0mmؚZ@m <ͬme'0eY?HF,}@Z,/=rSwx] &OUgEXRTmTgfiH/#K6n_}U%p}ɑNy6c;K7_(G`=́GI: K• uiض[u{1'q -z7b܌*TM/P/p/2dN:`N((4J(y;歱x9mZ]Ƀl 6RTYKݴ.nvF{IG,V- ez|L c )ޅl ,F[N`M{w,_~ҨGP] AS(կdy2z EQ#Qg="kɔTNOz6.~omUu^jWw2lMMW{oL@z4bxi+&ǴCr# RPmΨj[. Զupeγ9(Mʴ7{!BJ{#l98Oˤ^w{B̚DžȚq0s nTNՖBO&b:rȿ\<9L> stream xڽWYoF~ׯ {|({Q'(j$mJ.IR}gҤDNݝۙo8q|r{NB|0JC' ) w+"Ec y}}Hf-1udB-<74gxZ]^$mh]VbjFhYnW*+)*"<]VJg+/\txwH1zJ'fX}C͠$A:T==7ph00RicE\rDܳwFQ8# L#r\_l?eZn/O'L& uDŽN<| "y'wϰkab6lF3Gl4UDN0 A5,?FcP[ 2"`1sN%w#Ե[5m_ԫcD Q3u#Bquh --8b%rOs"$ ʀ #d9,'#N(z)Gc/59/eU?uqS<3~z*0"gf2Z؉ endstream endobj 2177 0 obj << /Length 1203 /Filter /FlateDecode >> stream xڽWmo6_!x+&c5CR"%ˇK C40Ғ+QM<(Rș;dA;ag`ltv"'Bܙ-1|#Qgpn܀Nngήxؒ||Z&-YEe)Q##l isxJҨ|7[)cϳ+/pB8oEDRd0v}̓XΕl0-e.#ѯd,ƻDt`Akʼ*1ۢjY 5o튲LԎ@$AuR|R˶ssCҝnK9x/wzDˈ1}9qfDC'ٌnnMyQע'k|uu[5 Qq81;(BX \(S{~C#kHÔ2~f X P+ýѪɔpW˪L)>\.q D010'3n!ʤH `tNrG-nCCkqm9p4i-H]42U _Zd|I"_.ţTFE^I1mb@\"{,*TA'r(0p(U DԄfG8{B'X[m(Vʮ E)dwoSQh1W.{ӆg8AJQq*{F}"lӡWV;^HiW0X[T  ȸ,Kr ԣ~c (MƩ$b-֬ W/Oղ*Z ;D(?=`c -& tBXLYt`w4l֩Ņy9˺+rlK3G i<@6DԍWqػFo8Qiқ\+>1$T"K&"KevO I̟/# -Ely_ZUlUc*ctd ch?5΅"_Tbkg`==Aun=LA=igl${Chyhk A,zY+inABbmC>^٫\bL"[3 Ax &@𒙶?ǻ.Y=*I@D endstream endobj 2192 0 obj << /Length 1237 /Filter /FlateDecode >> stream xWmo6_!x-&1#]Ś-6YLE:wGR(o[!@Hxk/>O>.&giH[<$S/KD jFhmu}'5Ey;rz=9q@I# e8gQ;a%EHGxfޜ-\[='AlgIOdDI-@iL<ʚ)Ԋ}/vWɴmy+%N5o+VjN`{F_lЉq{&I>\uΛEKgK ܙGN>-&'hYc8%Mr;x~Ƚaz1 hqO~;&j00v1Ұ%Ξ+~؏, I#, 5 /R=X~Ʈ%kDhFh_zJڿrUZ dZ2g4k *%qB۫„v31>DxM1;Ң+"̈́WbNǡ S-BJqȕ=f` yet uk p8F~'bU <[dzD\r *Vu5l~>h̭{۩M \:[Q3w|U9"]_M选ou #į/30;Fw /m^t, 're> )Ih2bV>[G$ڵm> Mi;z_p7<"ٻP'>#t՝son`Vڙ%(n:wL7.]|XΒItPzy팿d`kneæhxofNd_6 97O_=0!IXj G|8b8 >vĹ_wsQ ؈ O4[?.AD̓Y]056KwSpa:".I q.pEN.JT endstream endobj 2208 0 obj << /Length 1010 /Filter /FlateDecode >> stream xVmo6_Ad&5CR(( k mюVr$"%K }{ݑ-AoGMFB!Ub4#JxdL*<4"Nz pܬMugڔVDH cn 1ǂs&E&8 g1@'QPb/gW810* h(lwǔ`d6fۢLo7Em< O=Cn++8Ow,g5ziZ; GRO>r#l8S3O9 &~kI]gU8‚XF1>') R8* LF#%A!'q4[>P#mD;vmޏupmSF)04#H9 ˥dec&HtHI^T;8DE#Oہ_.q:;]UMsTa"P&|rdshR連ٺΊՀ 8A_/;#Rғ|dp1LvS%d\n`<լ7v=ꦩI.h+]C| tnQ9v)뢽z,w/&}AxOob2htH _˲(}F {j3"`cCD קؑquE0  zVIm(25U}.5 q' `t}nO#!lV+`YdyV!BU XÛfìXsS}wtsKlB]{d-=@n kN[+w)ۄ%$,mۺ,̸6ۼ}ހw}Gr?@z>?/'?l8 z=ޔeRp:jALk endstream endobj 2224 0 obj << /Length 1089 /Filter /FlateDecode >> stream xڽWKo8WEbO:䰏f{Yl=EȌ-D\INC٦HLR g7!hcbv~(,a Z"J"AiB -**Mץc*#SeQu,:@  .p©Gf$\y~Sl"9W{\wʂܜ%3YUx3k;@kCߝkv$$Vwه 8# 7/-aXlTb59xTbK%pI͛bu5Be!ާޟПP)w8ud7a[/=]XJKWGWNa=y710v~ͲPW1z!s!Tze&!(lj:cc D_=a aE#y7λ"7cLaJ/r8umgܦh7EYtG52LH_ʶ~@y;.C8ٖ f8^2R.J}Sz:RF< a9!CQ~m.7-;Z:]C/2Qr'<=]>LJ!7~*k3;r5z ;[V] UKN47vnw퇇.~nي+,_Ac1jud OӶԹ9 5U  S md/}=J b;%d_')$Ra0Ͳ[XfJ4K|D9݄T2,' c{f |HP(h 91SP@&^ 3*<wkp2i㷶u@-f.M5*|C0L2܋S T;[eVzArϘ0uZM0V #3qɡML`u } }(ov# endstream endobj 2239 0 obj << /Length 957 /Filter /FlateDecode >> stream xVKo8W݋ ItRl\좗mSZLBe+u;)E#uT(r7AKDлz"R4f1.%Gqy$NzQpRCFWViD+c&3VK=P;D;AcbF˃ѵ`>AO!R,t0eZ''nA,M2gzhN1~ r],"xzڬu]mxxwZ./٦0h-yTxD­ELy-d"PyfϲU(D_QYYw,#l!./ T|ibq5nԘB8mάu9<78V\҄g2B>3S _96>ݿ<_C1RNGh뜜8V(MЮ]ŒG.迓Դa1(JScyn|`4m~ڶ_kʦsU%3~q#7N$",ߺΪ|cr}~n'PYe]hL7 Syۋ[Ͱt''>Zvm[[FUzY>ҁJ~Z{xnP2 2\@ LX0 Gm |ƼI7iiz%% Y1gKv1yw8%r֥{f\U^vz6dn2.>ªY*xoڛl@aXJۿ lrZ&3\+m69%d=Bގ-St\DG-[pӢlfxc ζ&rlcЅoja۰j_룕&_FyYn+_gΌZ,/r󚉳6OeTJhN endstream endobj 2152 0 obj << /Type /ObjStm /N 100 /First 974 /Length 1909 /Filter /FlateDecode >> stream xZMo9W{Ȫ 03 awdV+e2}Vr,[c'X|,_Iqd#Q)_ l8z*cP! |N>=ȣAE2ĐvR`dzC9x AʵAD"A/&5WuwI!%X +$Yx ƢTlĔl( f͛D٬e o|nWvkգO+f1=0d5/og؀tEĵmr7 ?a!')rrC 4dJHVUt˩ͧۦmcx*TQJe !+ Ʋca;X$ 8m\u mUT#YkQX*ߧcRSz)n7%f"6OOuJEPB1חa)^`)؞$Xrd}Ry+FjK5Lis4 TZjhs]Q%dSEɆdAN{LZt6+pV{v<MI(yw,qϫfZ+yӁ%.&/ cJhMq6)hQ.ht:NKF)4I-5(5¹Gz4<\GsߟM^H'boqMp\߫{~>HNT|ݓX,Y;V+,,LJirڎmydés֐/>M푚K-qS> stream xVKo0+]xm_ȶr=QDgJ3` @6=j{o3mA+o ihu(!8`0,M<5ۍ:5le产ZEJ#![aS9]jʁs $R,m S8Ws2IԝvEƽTj3S5iBͽrZ=:I颈 ,ޥw]3XJ#VZ}$C#(B/FyVlh#18z3cS06(M L QF@sIݾ(\7m_Gw@)%_YfvP"WOh7p3.@Ԣ ) ͰJmy_'gc=ա$zS!|n&  kEۺ:8Fh0$/19Ο\&vtxD7eʬ{d:Z#dK/M-QO,;kۮc~ܤY4f.]Lc 7U+r-]K e1/_>hENx!UCsJfsNr THEemݘ!*r7ڧvd`UD>ijQR<~/ߧ endstream endobj 2265 0 obj << /Length 1134 /Filter /FlateDecode >> stream xڵVmO8_uC+vO:tJ'tAyS"Ҥkh%~4۴t'Dϼxf7}t0e^# $( 7-QMd(12UY+-5-6oky&zў3:b3܌ROO;G8|"fŘΞQqvڅ>\ƧaL=WgQVZ}TλFE8^q>6GV9`ilg84A8@5ظ ˉ!o拊hUJegzBg(i4bee\hz#+Q4BTPEtAQBNM(ɖM >mttN1MF){l={5s/$%ޕw5`ę b[Ӟd*'(l8M]G.:d1,:ZdS?d&mN ?|xe,qRg 7VG^1wIz$!f IpUAOfj`iLԓK;tCKA%YhAI4YSֳm;Z@qd 4MߍSR„(1?A BzZ+`7rTG5L1_^=eϛj&uCFaƂnP#%nq-!lliCC e6nmƿ/.zB9չ+[^B@s^^u=:mHm~"3UGnC*Ǘy,m&ri:~Yw9CzSCB{gnНob^Q > !՛SR_M~Bg endstream endobj 2282 0 obj << /Length 2163 /Filter /FlateDecode >> stream xڭYo6_aIb(s%Epӻ!mU|"7áhIQgQ93L8ُW?_}KY4JgC&t!KD46]Rh㐳,F(+*t?܉G`9f(IڣñmvzH0;o@bIB۾ !|χ\)w hBK3(~p5 c󭪤)gE}[W']6U ^za>oh7N}VZS;_"g{# ssjyNU=@w d mJFH.KYGnKW|< wYFsm$ >70`DVMw6-Ȟ67[+kخhttǍN z#(bGw96^!h^ dBj-Ia(3҈=ȋLSc&!X*D:5hZM d{ؔ+JKRmpV2>TQ?KyqZ^E"r6\X,9K|AbrQ̊0}c</s2EAʵ0dgκLX̺PY,FOeOx$1~ԦiՔ<ĺu7켄ٗX^Tew?)"ouZ=^^Wrug7s~9v!T ҌM#pOX܋fv%8(1c4$Rj""UǥL@Vu@Stl) (. SY Th!/jv1GgZPc 8{GtwWO:8M!ٯ!8 a6/G9 %^'lvf8N=n`Z+T'soÝf)Vz-{-e=!>PD5/Mܼ]((~ /TrjJFe}C_'V@_Zx,a?Dїc!qZ('S;1Ψ, -a3K.Xѱe }͡6~l7[Um>xT{ ⃵8'3}]aC=T*zAa5p}&,zv7i/ ,GâmPJŃER^ndJj^b $43WVd#JRZX)b =@$P&PqYz 5`[}d8hOu/RژtT&Yb栉e$(g};Vƨmo*,{Րq[ \# H'G6B[)^P> stream xڭVMo0 W%fEYnk -QRodgm1쿏v4zdG|#)%"t0p\4] Jf<`Yh:G7C_@2ŖI4 &RM 61-O ϟtek_0`*ZÞôt~0$ɣ0<]-@@0z1SjşT tۜ\:](Vr+Ζ%|ύn)3Mo]Lʌ^*&p"R*lruyUpKpy,MS&f>$&q^4$fUs>S8m23(<GRF[^ZmuS9~jc#T{}lG*~KjjҫV"\'eї[QImדHYH|:5:Ats۠4 ; ,ѣȦ{6] |մ1- צ&6@gxvOI7ȴySqӢb.՜6kYئn - mi{&s-Lx V.ןc(2h;"FhBiךa||Lv^uF5"zԯnI,r:#Juʳrg+Pج[υa竰|ЍPPuFET=b%~za~QqYqńZUxp:.Gs ս)G* ۄYp  endstream endobj 2311 0 obj << /Length 1378 /Filter /FlateDecode >> stream xڵWKo6W _z!4mʒKII_T$q>H8of`ɷ])I#U(%BFAQ ,yh3xw}ݒ2'pT-aeBy}'\$<5+L7b}Tb3AjUk2u,kw6YeFk]of@w]+ mWNTmno$|> SOm Ј^?O~<jcDa(>] ЋH[D<L:Hz#NF̊n'Pu@x; ~Ѯɡ>#ޚA$$!j" -D借_{'w sS|Ֆ!Rm}>m{1%0 13 ueS5kA`LtcC2Y=lh'Wu>tjtӚm0B~$"ɲR}9PJ,1-w1:_U[sb|C2gT%k[m=)<}ʋt ?X[d:bs,͓^jP `cm{^H<+1KIm EѸIP,U]X pL |B\w L(ذpo^LJwU> stream xX]o0}16'=My['䀛``hKZMĵ9{ >]}^]0)w\o+o?_p38l$qdQW<.PW6 ktegt!߮ exu%Il;N͐{֟䚰՟TOa9 `v[J <__a x B*)}fv=!"S1-pÚA Y-t_b!cC PG8R B*` \ϏNQ%jY9P2/At.d&=Fw/VJ ӐBDAeI}.j8Id:h@r{1hRDb' flxU21; -Ju}IA]D5 0Яo<#^u6bWfId u4@^ڜS E!OH"p\/=ûilOu7G؛qpz *OZg1vst/EwP 3y~|8_ZYTn."=7+_z,V:ƣ%8C!3A˳boW+9H[vigY"E"V 8dA1u.N;Mm 8y)gQܵ ϼȵ0J$ >E>ݐ0d8v w\1-ve\0ef4ӂ/n7CX9rFv W_&=AG͊-AπP=LiDgCx`\oѿd(:Af$ iyacdjD͘ڧ}5܎{cy7io y18aNI FA0"r5f2ZY{9OrזO6 N. > :O endstream endobj 2249 0 obj << /Type /ObjStm /N 100 /First 967 /Length 2362 /Filter /FlateDecode >> stream xZo~_A/C$g+0H88)֗vȒ+o(ѵc^Ivgofkm4(⠜cV^YJ"e##.Gѥȉ]V1FpBb!|ъjRFB0*ɳJ^F EFY $.'1e.H`QXM#, @AzAd<۩%ȄBD \(1CItee, hc.n(Ή`2E/zE ƀ XmOI'|?!|?aL"ތ8LV:GT HB2#B.@!"D-\XkYc%nbmgdSC<G8k|y6d OًSX l\BMV+2"6k*)ƃÃu=TWYc4aBs>>)xi ôuuCmL:bj3klo^-#co!p?>l_bձj~jwWeywayN+nX/'jË?ifq^ $:]ˆZz\#a\.0 bҲGѦmс*3dxWL HL`烝  |MX%nW [_@-w듾tyXv|h|l^#|gCV"YwBa^d1?ɬG뙞Mv?+DX`)dYru@λYK6 ԲkOۓl_;-E 0l_ȮER7"XlZhUNHOyhdT8J3:>Qh d\ׇe.ͻ_Gʿri6^ꧧןӋy_2[ A 꿭4?C)iah#J?jDÕ1Rf2F qZJK֫='EqcAH9 Na.aEi=i;lUՃΗj]-h|額f9@HY{/f\y$rƸlhImdd)mDkOgԉIYtn-Dl3+& 唝$PyM%,]]J:AG'QŇ}=q6*o4~N|v: rQ`x3sIu'޸ne" gM V0PcA>hQB7i6-6q-xV+_+흼ٲ9WW!T!VaFp5+BeW-jUˮZv2UT-SL2UT-SL2UT-s2W\-s~IQe1+`qw}ҭ6l~Yoȧw(2MNTرb^]LW}itxnw;M(Js {3Jv@hF)ǐn>р`T[.a`zad`HМ~ |TmAtQ@. Q N4PYMھ;W (AeM9[v~wv6LǏ.PUZyS0G[Z}FBKXmΌyw_OȻi-./9G6Uԏ9A#Ѝ hD>: +Y%vzō endstream endobj 2398 0 obj << /Length 1139 /Filter /FlateDecode >> stream xZM6j{,-[LkՕ)u~}lj%nbZ pd>?|)J##?8(\{_fU)4jbx x~\![1<46< ~4|5PIa}~|1a<ixB?eU^=j ǯ?kV_ W:k$us|Apְ<[9 g$n\h<'v%9[UY-bi!( joAB`՟'|mAS * d;,G\dnb8NR-(JbDb3pW ề $|4DQ@DLD[U6 r9c"@Y^1l POĕ*yO-6R>%("&]1ߌxpx)~pz!zZdu%ӵ v Āһw 0 }oS@] y_(cZ*~7F^s?/ȍ̻yά2+7FҺnR 8F8MqoS> stream xZMo6Wh.?DJ@S·dȢ#RP&lzEP,IdYy mO%` ƈ"F`Qp⏯O">,tTemd?xd#G#ŷY-˪4+ Ks#pd/D-%:3B8IRtd)sń^^:x /8":gހbԋvM!3"/v[Y&Pj1q#tj!bhH~ã!L8!]K ae]%x`tMr'<*<k:άa$D`CM(C,Ş\݇P'+tYo)E8 !{V_[eAۻ'gewrl0"",ںj6 ( ǴG"-j ^l_~YtB)0>Rdi>"F_nʃOTK-]ebrNff(iURTjŜA]ǑOsgg_2CB߄h0 YX2TwԨ01r[RC+W#W"zIeƖ{QtPq9>k  s.Vch;{2:QVrYcHo '(ad*@a$dd+|4Գ^OOKz` Y#heN:m҄dYNN3@RT\ vmw$6]ӑ. ~Ѝa -/4)RU<Ŝ$+e\ Tw_lHvR7*Tje${H[맘`ր[BrkCW64kiֿȮ:y|Q1#u)]u<:F(}C=e0\kP=|XBfDM$LܪF'j ȴu;+@*Aq¦1?.6o+LImE6 MD " ԕIlTUf3'7檘Su-';C x(Ӵo|7wmN|>e~GឃD)WsG緿x{Yw˛MfZH endstream endobj 2447 0 obj << /Length 1620 /Filter /FlateDecode >> stream xڵW[6~ϯll,ߙ@)0e30}&:rbp/]l+1KXO|%9yux"Jux?8$:ܲ:Ѵۢo"_z}ٵOW^ @;,>zƒ!u(( :[ʁOiʥ'4a\:xOO(ωiBb32=z #i5K?vrBB=guR뛥+J}ت,(J#HR]m%$H56kkiߔvYy \A-H"ECVbBVsN G:B#g3#10 ReBh/o,W>^[ڈvXR y_e"+?Bm|%ƈa,x鍭ЀDQ8 ڏed M8))GRfYJ$6/5j{c cݟ)z)b2g"zyĬ ji t6ґBIɷ'F7+S>iVw\q'9κ8?a2P|滢YTf{^W!W?-pԿՋ >n2^G4$sSX(\꣚; FQ}-zRưHC `$|ҷ5jq] N%d̐DP3H||nkMlK o04\:}'Þℤt_0d$_ec)$iDL J; v [i"(Sj*P z:hUj2뤼vB<"?.EQ›3RKv&@2wBo{Z=n OIfۗ NVN5MAR^T fIT]QloC$m[\G#k] 7Q\ T֖ Ðb6rՑUm JԄʩw9fnb':}N$kn/nZ(ox0/kϿr<}xY52 rGLݿ‘Jr8)}АfK n ˒)U#xqT ?r҉Q#/02KhO_߅`,awjH ՛RT;7w7yLl Rg 8Bۖ:=EFp=q=c[T +wPޘߔ pT)\-_>* fH2}fdu˞Ljr&"뤦'\d^>Af+U#7Fd_%"(lNX+z_=j45Ģ~X$掓t1rJsC'i/7Ռ}U24kkQeei> E ]6'ſѶ_= endstream endobj 2356 0 obj << /Type /ObjStm /N 100 /First 1027 /Length 3108 /Filter /FlateDecode >> stream xڽ[ێ} `,8 I ?p$ΘkԒԮ'_lJㅸZ=êi*-Li4iaiqȜ|4,4PLJhT.e I;,|gqէ_X\W;g2zQi`~kp!Ĉ9lKxm`ZIF$ 骓L{!i.}*}g .4v+ihpIi\+%:YI/^3sxà -Ѥ9)J$9#Iat5H'A1/tieL%2oe1́}'TgX #3i^<`L7 0Q4JlQ!ѰhUgYt,ҁb#``Chah!C8o ӽpU0AF C _C5iH~>iVHl2GٔiٔN oXg1FM$fSɒFa6-Aݥ lZnlpHBa6X)#f? 414NнJ62$M$`.XZҽncKV uӱfo۶6?Mn]@6c{>mۑ$?h^8.P4.e^͏l]6?}h;=iǺ+;}EHsFcy>pUD5mˢ@(1xM0Tj95|? 07)YneY5 uhJRJ -\|;,*IIY-e>^E;Ͷg'Z'e!ZU.GE,pb}8Oٰc[jEΖE|SkYu +H;Q|EKWPzQm gBE lv+\q=*@-?EC=ތblno!ִy~üFYaQC;!(,BU7}]yd { Ttߓoa*wwM]% و #-7U!̟qy /|KN:L<ȓ,e.KYϒ}d%,g>KYϒC%,9d!KYrȒC8IuWS34#,}>.x^ܥKmзk\"aI (z \W68JԖhtMoFeE=8<F} AOfD]rH3fAmKA咫|e8{@UQ JE/ f@ b6?<Y/-ޱK,FyV^Cyy}X. +֠XuuSҴeLq9 56zlU0\# BJ˥ ~S!DŽYxƣ4H;b1>v%72q]:z)N[ mv(j y D5A̹3O<߆0=օԸ D{XJHiG}. +zkHrXm GR%eƇxm;[qEÉ!jEu> RGlBC%mQEAo}~S7Vsӈ/ke/YR ;4r:հpwcQQբeV A*U4R\w}sW Dun2YKK(}AD3KR&֬Y嶫m@ 6 VSCu<ʂCLL }+[%N Vr+І )Lq)3<C63P}q:pRr8d];4Z-lJ -wѝ6H&cӞ^` q9>$ʱQveE)1*:̫zwjwܯ\)p>$ej-_Nsβެ<]~ 6NE k=UaHzfZuG H_~Zy.IͽnС>4n_X] ܣד9fbnꟋl-g[;kn|h j~:!ja u>m qXQp`CaV:Ky]并ˆAAb\CYHRq^f ҮXv}Gy*Ly.4:S.V'|̕·ocf9zLf91 aBKjG헶X`[ݤv |=e}/bEs>\7M~$>|źn_f}~JS"Zhn[9 ҲMYr(SM>?S#1]k;_b|3k=i" L[FR i˗m =$_"W]2('+#JJr'_WHr.wRgdn7q БУm4k&jqYAб3o h\x4qfbt(u3Rop=>#y7TX;BEJ$?e4,Q-1e 1MF?4]k0Mi2O1Mƚ/&zx"GLfIL_)Aiyz{:x^;OLԯM>&T=bJ endstream endobj 2462 0 obj << /Length 1946 /Filter /FlateDecode >> stream xڭXݏ۸_!l*gD}C&;Krr-+D|fLٲw- C5o:_ogoޫ4BQA*]ww(hGɨX:Q-Vp"ä{g2[+a(Κh1R繴ÜM]m7C{Eo~":?:0 >S>: fQF,xpdvHB!tnmMۣU=@-W^wj?]H*"G2yU9b}I[Fw7>h@c5vu%*әgr~9D@>:q'm貒$"d-?=ܣpbπGgw!CRvo0FK LR!śT:g9ao. 6W2F()0A 'ץf<'q[QUsՎ.y=z]0$)~&0X8wb_ hzG4qg1Kmv_ܴݡ2gZ㱬yf59.W:E)6~M)^wےDr1ov;QZb endstream endobj 2475 0 obj << /Length 1049 /Filter /FlateDecode >> stream xWn8}Wu)YY l}J i,"Ël˕6(P,X#^ Ϝ*U''W7i \,E8IQcKtNԋ]fQFonxq=!g9`}yfLHf>2`DXr=D&"*r얮QB4ч?;;M&e$Äfuu%}{Qu8t8RU' +,ί,MF. fJy$jO0x<#Id#.QZӜ'?C!VwR?lO09V5G :2 ۠!v S[Wz "'8xY -zwp=qbaOHy1.!.̖mS*݇,QIь1)H4HIkG^CϐB>Le8!jQddOAZj}BzC{:wO-XblIdZ?VZ kecT#]uk>Qc0;9DVTB"U)jHTʣK+H? ,֢Y6j.dϒqLH`mBmX1?~ť^d-Fvhɕ+| F`>>(;m; 0$HԌ$}؋NF h!mXqO~ .l@uZv]jW0ъ3lP)iM' p ^jt4 8uc.4F+)m*/@)a^ |]Q1'Ѫ wwZ_ zɞ.vSΣ}A*k<48E1qBgUt >Š>:\n΢gy#\0a~ঁn|0(W4n(Abn}d/=A%f ,FX vrZ\\0'v endstream endobj 2490 0 obj << /Length 1198 /Filter /FlateDecode >> stream xڵWn6}W~њ!)Q-6M6q h[.$of6CÛf̜:ijvu"'BQ@gq' 0buV|imG'=BQgr`WhdTe>ESpk J_)YR'ltb0v5"KaId블UEk#f2,Y"J71 ([ ޳|c30_=̿txg7?3C(B, 8}6 "<飹B߃q~#*u!'y}=?QCQ )^JŒ2 #b3T e(ì5i c(C|n!>#> #ϠQqipͱӯ8lqKQԁ{VZaÜ=}q=0ZRC)vu.kzyIITEZlLs&ҰefQ* @zM8-kRˢWo+ vNT"$"v릂{S#8HO0g8hDQS҇@ eG.CܩN7;R(1l/Z&olk5߀/yM,rD$@< :@[٬Pr= !p3tư|uE".j ݗ0 ucqvHdbfnO!4 C,$}zgҮ{}W- Uݙ/1EǑz1)SR n.]Nx(Y(+XF-vbGmM>VR{F(/탔Z)X)2+tAYmaQykظ==Pܟm\(-';bjfh[q?vi"Ct3jfm)I 9ּu#l =Ăƍ̒BaE i*Ad&wʜjT7S~>gZ@a3tVuB]ENp]i`j6VlA|*Ӭ4U 4Y{oo`Ed}sj 9q?K{g7K#_~iͯ>~~?VVQIa17?H1ȯ)/]> stream xVMo8W݋D ICw(MSZL%&;)RYt- 9|3o6wߗ8EbQBp( !C*ѪNl,V*F$nOq. SaZ~"hkd),+QBf7;"arHG%$!}ǀr~D#]0,vZxfm3 %0#Lޔ7J$< #ܐ5 i!4MTs d &or9Iu`qa7];/mqL)}Im911NoUmqNڠ~z:R'Ex=b,GPrƧxMj|j'$őʾHI[֍D:_{RkY)LZ`O0 ;eTMa'P3Ώ88ctȑz+C"/Nr Ne֎B)s]"9$ΆpDob"-6R1GoCLc1XFЌ2<=4rmM#t9{t.J\Eg HW7n~+Y=!9ZN9M-e( ݎuC= \1Qs)r4çAt]fv; `7?xEYV5].틉sP|S3Hސ'n͠'V>^WgtI> stream xWMo8Wa- bHICQdw8qÑ~H\."@(7oAsD_ߧ+ 㞥O#L muHb.ﳴhd֍ -Ea`١ʏcs jeXFwioJP3K&q!fHXdi݌̫ld7ix}׸}6YCٔ|cwQ9LQ^Q̺~vD!׆\8@'QD[O:MdҔv^>qU$MZfV\QQXxǀk4½BF[X୞nځq}a#f:ܛ`Qwt9y^X(OL}D,J恇atC$zg:2*fx]"PiV.*h{#7]UEG§ߞߌŀfo Smp,ڒ-|}z4xmc!f8}BqAT7Ĥr!^?#b3xeqQt6cm魈io.O|t9=hט{6I>B ~`hMs@J ]=x08n>M`d|20,u.n]uҿg&&J`V?B"ʷG-f{=miO&搆ΧD;Bz}ꨯc endstream endobj 2530 0 obj << /Length 1214 /Filter /FlateDecode >> stream xڽWo6~_!t/`3!QҀ=tX3luc1Y$Nȣdɓ$&}}wPoQյLKoy1JKJ"e*W աh(8V'efˌ:Owf6/xldQ=Һ4^]x H޼irګ=\y (1}X)Rer_m h$^m0}ӺrZuoҨa {7 QnNCӖiyHESB\hS{sWPu( HΪN-BN+/O{ |i݃2Vƌ7!C(yQl_Wae݀$y2h׌xMÛ"Uh_So旌 o1_]ݫƒh5ٻez iLB(nvz,";ڭ;/d@[y}}XDX8d<,Lω'AH4aa5`#KA8&q=1XKB!c_t}Wd=Ï}@JhoK6jG*mQg^j/:rgGmD͏yXPfiXECI®V'cnPio>gq5p!uL_̿$9o:_* ^ qɢw˿!Ѥ endstream endobj 2455 0 obj << /Type /ObjStm /N 100 /First 966 /Length 1906 /Filter /FlateDecode >> stream xZQo7~ׯcB Y;NڦyuN,ܤ}UHݏ!gSr&s6?*;-eJ*D(PDDӠ)?60 `GbŤŤ-R2Ȋ%1 P'Pd5)C6쵘%$PYXul R8۲OmbSNW0-UG9 9/,Gl b[~0BҀ FQi"i#Qnsm.Of"LKU%V)67S:r9rj9iR0¶ ang[l/-E]mb,$[XV V1^9I O9Ti;3gɶb>&xxhKO1`xZ^g)\( `Rbc QbL^Wims@'ܫ̆VxWbHb?lI"8’~۽T#mZK,B[lzRUb$J#ٳ_;7a\m'WWv|dj}ޭDzx;_ӟv1tg[&RỈrLc$ϕ={榯+7}9XbO~1_n[{:}' &ߋOUV|1;]t#Q`*ZTk"'n˯Z=Zjx 8N hĉPtyF C'"PMtb6CcIaY ±Y 8">}^Kn 7}}ܺw]7%vcɒyfu>6;~\}tq>acKC˨yo-Z-_ v}⸉Kpdi,%_`ܴ`0 j!,ҳךB^OzK5{I/Iʮgdz4I6{,I: {!=)9PBSb<zz|0jNͶl;DzGja/zRR6zPr3FgX"Y~Ȅ..gIBmNP+$ DkECC C3'P4=Ɉ@4!H%^ iG^I$W)I.G ,!X,lR%?/gMw>ξW;ʳ[boVZ` =%p-M>_wLx/K="oo=($h@4$ :Mk<kv8NW^^c{oJ߂UL(Q{ wps})&Z*;ZI(ƅAE֫OnC8[Qm̥e f|B_RW;xXu7;ՎHj=_-Gm&EAGw2 7`0<0t z1m_X^u|\naۧC/(H D`0j8m*`~He-~ˋxU(ƥZsVvٕS &gbA et}df;t{hs> "xy~tu&\}%^:ʾO&ѭU] endstream endobj 2551 0 obj << /Length 1470 /Filter /FlateDecode >> stream xX[o6~Y͒u+ևnk ESdD;Be;6eI|.-=}>z^҈F|Q"/0 szRf׫Q3`a+!<eQQ'Fugg4Vg! m)kx9 -C+aP5jj>Yp,o6 ߔtHfݢn w\ҌOLW? ',Hn#r/ TB񲴪x@|'^fRT{+(~Z|@myv/r^;Ml]KYܕD܌Wb6/]S_vVƭ֎ 8ku;2gVvˋ칍cqG$6ădBMP DKW˓g؃QtL3~Fʷ0(iHI*h ja+ZQ;&Ű0Ba4?f&C1 `݇t(4d+F{!)9HYI" 0D43qy>pSr31(z *#}fxcZ*!(F(FQ^Pu^4d"1еJԈR h'xU_ ;PZei2\poªv@P8 ڤ,GB6z $z+y(EI3㑋tͬXwDt:'ۦzjt}^(HAo]y3K Tw`{011.qxQS3,S.\U]ifK"X@QX3z3 ֮* x2VΖ\SrC31 f \bdհPF~}$F$9. LPBT9P'zf*Vw"0*>1@9D4/?8?8)E$ ?\Щ=>S: 9ݑI5Y]}Ř.7aaL‘VM#zԟ6> stream xWo6_!tI}fC%݆`@{Hi[,:"];wQm$% A|LΓ,I$4 MBs\Ma-ӛr&+-o6}ur;g4P- W=cF;z$ xy\4 a!m] w1Z sf8EdʬV *[*{ D TiH4xzӵ+_OA'6}x2\f;Fwό%ȵu'2.rlH#+Y`Gj@s_ž'* \Th|DCcwFޯR_(KYQVM95\ɢ=ūg#1>K<}T P?^ >\xr 3+9-dE  @Ń_乊O \ ڏXCE)'QIwAohF46*iο@Uf;%S{m: 'Yu cVXVnz޿<í?~> gK.IAs4ߏx#|бVM9/,q\/fKN-˷[e= endstream endobj 2576 0 obj << /Length 946 /Filter /FlateDecode >> stream xVKo8Wae b-t)uOi%Cؾo_RRE|ޣOgg4RF8򖹇 $8-3O(8 ! FiyP\4Aeku .Il] Xmj)\]@JRB BV5En n$9bDEZp8[>#S5_B|ӋɃCWm5pb迨OUdzKy(yiv7Y 0OXW6zYV_:>!CIj ĘLw_4\ )@X@ۿsvQ(8@7dph`p&3Y'ZsknpXADIP&cVK奇 kO?9G6ѿtvͿo:_za;slUt@\ %[?rJlWLed#U>;Kov۵֮1RNׂbRTPRĊ̟c٧v^͛9Oa"ߎUR+~wwEVR4P%LgjYMxLI&idsHYBxZj(Dsl_!낗a ٘/S嵣hs@K4կJO 4[N ^WQCM*v(,^ER:jӧID $P^ծBSeDҫJ,?٧yc-A$xh? #0[sب0w}njonR51[I]_FJAGC*6swU9r]0Fzfߎ= x9G1;zbyj2R@C0$F191j `l) endstream endobj 2593 0 obj << /Length 1273 /Filter /FlateDecode >> stream xڵW[o6~R"u)Їnk#Ӗ0YrENwxc[/~sU_f$ r'4 fˀ`bii0[a6⮖弮-هK=g4] {iqd@0CYl# slZ4F9HZi18)cgtQ2IJx X-5e!4=7BxV/) :P,RNQ;94M8{Jk6bPF\̫3 ?wɗ 8 H8G<)֓8X,Zud3?wIp) 0$1%O^~lć{v珶Fӈrnn$FYjk?U`cM6jE@pN] Yʂn feȫeUKdT)VI\x=Z-F/*njG}$AI`X_'7Wsk u-U7Pm_6gq|wl@-i-F3:?QrFpFr쯲md!"="NR4HXtz69 Z&eC*w}?gawE/W;^jmoV߽|A'D'zЪ)s>='p쇬x endstream endobj 2604 0 obj << /Length 770 /Filter /FlateDecode >> stream xV[O0~ϯK"K6@&M dHIώB 1 !;|LG·; S4"&#i8B0d8q;>FHR 4T8ʹWeqd})GcsI%Rify??ѽ`1M[or:޵M'!r%g({uTB)؈9z/WӒN #3K.y;Fgvb2H !ȽH貲b*[]酡 z8%Ӯ};1HX c&eU\bs((:tBԟix@7Ҭ|B7`l,j.\ +fzKaGǠqodu SP vN\!v+D /A^;'gPE^0Wu{.R=zTQE!D1}2iڛۧ?i%)D &ȕ42}m.yWfȳqKCLntf1E2҆ռ5[& { a,*L8r>J5DcWhHK֨5ꍑfuՄ,7yDr-4_l$\.;.LvۧΏMg{/6M=`C0Y/=ܩaX φqrő2J͖eM9 m *Uy7 endstream endobj 2620 0 obj << /Length 1207 /Filter /FlateDecode >> stream xWmo6_!x 1ђ^aC)) Fm-r_H> CJ>y=<`яћw$HQ:` ƈE a 6Y(V.|O;&(N5Ic2~!2S3Y"/ty&#(,r^(=ce)UMaN;ӄPj#U|+VNX9mN_OÄŝQóR EV$ǍFX`xsL0Aا S#e )y#ˆ&i4|&Qv{9 6r)j_f)l.vn$t2P2#/ ~|u5vlt5bT;D])}980H\Atj "#6?'FQxj4M5K7*?Vj5_3a_„bxoV G akJW4jQXo%\;ٽ܈ c"bE.׼tPT^XVكX;;P_JޭPBB}!5rjT:?~>0S>%2YwzZS9^-`Oa[)#oB ~@X'i>Ii(~8y*۪2y[\ +9FYkt_C{h k]>IDs@d7gBW^&\]cFp2lSE=I+osEm3j]5\I> AG|͢6⍝o&m?EvZ3~m鶝&|v.wTQ`٩'y?>ųS)4Q%}Cf8r\TA'HsH$xEBr;z;="H{U[mQG ދflHvm`0~:iYo.hw8`my& 1 g_ ݭH Kʚ[Tk/xZם62{L>nap9|FjbI ө+''ƈiLiDL'F="-*BI0֥x06r -Cs(.c}W0~W#QMC2#`}66-eJӱF(uFyB.v endstream endobj 2541 0 obj << /Type /ObjStm /N 100 /First 975 /Length 2092 /Filter /FlateDecode >> stream xZs~_ŏ]Ɍ̴3ٝiEl6 钧-ȓĊCS`OALdM!>%I$S}ņ]QA!zS / ECT˄Ţ#b9E7!͊R֡:E`dpyRĺjK~6~\+0ըʑl.dI4#n_-KLzI ­ [aB/^G~TlxѼ?\4/WU5j~j~C9"Î RSY WPޘ˷KCva3mWwsXnޭ38+8uyj `&yE9ai=FG;)K*C*Yj8 HD#ʼnFh_$5lx@P Yۇ)l:k/#q F&\J6q\v~r3Ϻ//fȞ |AN&=N'8ZNqC":g_Qwbk'갌/ WZ-E D5&`@6z;Ci}{#h}@g2wǐޡm}&"r*Ż`GsrNn]95uGfzJ)Ԭ! :GNbr6mMj;oi/ۧfjQ}Z/H8ؤ㡸 q>SӾOʘ~CyqosU-n(H-)qb!H-sx=[wc2la=1`3G^,۞ǵv9B\qʩ\VEN;G{\ms@cG%[Ro: endstream endobj 2639 0 obj << /Length 2299 /Filter /FlateDecode >> stream xڽY{ߟB0}{4(LQ+1EeOߙ]>$ 83;Pb_݋l,nς0^$Ϣ@,6,]d,3Y:?T r6WHphQK{ kN^?~$cŊ,j{&1^7|֭4n؞j_]/WǷpm~Xi^E |}s , ŠsEqh-7̽ C,E=H5r??|Pb"^h^ojC\{暶+ZIZa[e+iHH$ט##++:91C1?HIC(|^vhjuYh!HXgi,wnQJd)b̫>-UY|\8y;_IchzAFYx/rF41oUs ݱjIf CTjN\J$`F`'kr-iOC(T1d^*9)Aìj`qL7<*7?nq Xwҩ%85XLk^LxڣR]O\ܵZ&7ڞܞYRHAB [@Ւ~4цPu#Bcf#\MC䲽}^4[c S%B[ܧDdQ&Szzi wCdA}|ۣ购͟I>K>5QҚMYܣ=[c&w=^YHw.9l*'}'R nr;U Ik0Cle\kg'}-5= +@sfoW c~ O8?\Bf`OW ܫ @ܫja+]ӿ~c~2s^݋ endstream endobj 2657 0 obj << /Length 1233 /Filter /FlateDecode >> stream xWmo6_!djzۇnk ņu.- E5reVR EusCGW&(RF<&sQJDyqDI(7y̛Zg 9-䷋0;PF5gUYn/ʰ3{o~OCnoF~HGezf|g8Niʖ[Ph y/9~Xnٔ30h3c߸@h?%v;MyE'yjpLgn4ϴ<"OvkgXd-q-7'g[a9|T1{b> "Y7ږrf&ޝQ] @\z lj:\(`HS0>vt$MضaN|!\1bti+)h oZ0&M_bفL-+2i4l3üTz]#/p:q:`5NXj!VEp?xۊo^%+I tfQq޽ŋ-mxjd J^J/4EC.W$cyxq¡R5CRrώxjjZf{mUMvz3L]-<$8WT&;;fb7LN VlrG7o_~oB>Pئ 78\~Q^/eG<#8b0m[7;$ZB#́A@`;/N'w-O" tWyyV(?٭9<+GlŝESx躍#πƮ#v08ݚ[Ȯ7q]$UM[8@ W],P=UkG/[MiGғIbGA?SB/ U8xyIuܶczLAK6ڧȁII[n5rW%J]g# 6 #t'Nd\8텰k.Um~AnT&FaEmlЁ]ޱm՛Jw=ښXڽY0َ*Kt#}北*mfU`CBe]L ='4Q8Ru{ S_\UFGٮ_4* endstream endobj 2681 0 obj << /Length 971 /Filter /FlateDecode >> stream xX[o6~P ^D]aÚ{}J ChE -{#ے'mFwd;}bvs+c'&qgrDHEܹ0[qsDEfۨjY*I74)(aC{v> Nh-l7ȩ'v_Y,"ՎmT,S$UQ)D>1"SeSVFS3Ҫ̛S&k]J\frT'u}1G5T[\%VCiQ`$}Hr7̵7YlT=@_ 8H.RޜKk3*aD aM=tڃVllӪhngVp~8B'u(Ok+OJ`.]b'>'X!? >b%Bkm*mSW{ @%1(﨧Tו=7j ؼXخej.~nVӽmmkx>N>gud3b=VVy&\>XFΥ aĝt={G & q|j1y=k2F Y2" ?B\Q8#vHԇmQy"tWQfg,0WI?]=8UD׊4P9(j'QV_}Y:'&HNk\_ }a:DѡM?jI+DMnNz։l!j3>F}xFΐ>nV4ݞ{5&&FqNd#Hl R|S sT`iܟ~CtZW6ⱛ8nJk(@H6UpvhJis[VtD.kSu0J2{3ףa>D endstream endobj 2698 0 obj << /Length 806 /Filter /FlateDecode >> stream xV[O0~0F"vi0ia}T<ܤKܵ;Ӑ1Sڱ}1A"hpr(IB4!JE!h. LaCPb.r=V2ע瓳 @yI$E5%F7*v]E+4r6YՓcTO;VvY4aZĉxQTTqaY.]'\R)8cR)H%f[  , !ཪsK'CjEbPB74튅jdqD5R2;[:A0Mt͍ꇲ?y,[#08Xo7 dJ٢,TëVP.%8 ;?]{=kRF` !cb K\d\"o205C}  XTiG |<8hςq$ѿiNGZ6/@C|pyM6 sS#߃Bк%T%y~>?wu΅IL6j81Ԥ[P2_SA1ot9Uq cџ86>zY°O~UZʅΪ~ޭJ̖uaƹ~ =dU)̳m+ 54Q%|G`iTa,Y=ljebΎæ؛/}> stream xX[o6~2EaO] (Z`KS J+'Z MMSF R'Ycjۼp#emeFV'zb ?JkZdg>Zk lTAB$/77{)*~ㄠ3X௖v-7`$9&ӯb.^AQ&~ECY{-s?LOUNCv}I%."*sm"Y$ Y*: JlҩP3w,<2oeQ2D`-hP}ȣ',0|H3r6_HHQIӊCn8'Nw5 w"^z<DؼATx[Hlŕauf[B4ԠJW)"A6@Oq]ujc4isgy)ɥAPFSgt3W4eHA²2G ɶGObyL9;@METJ:dxm.%CiӋBTU_ۨ&G +u +URWm3|T/n*i$3 ~tCaqDPGfz<͊H*ՃI( h#zUuUo #<_,ph"yWrUh@ ` ̈qƟ!bWۈ9IU#eS9!;6P?S*K{nPS1"e"K=l(rR6mtWEWE.lЙ5P6-l9fMHL2$wowzĠb8 $ ?CU_ !mfZd*3RʌUGBR( 䙄׺X 4Ð̼!T-ATgKΩ6;.O%u/;\<_5] endstream endobj 2628 0 obj << /Type /ObjStm /N 100 /First 998 /Length 2579 /Filter /FlateDecode >> stream x[KsWѾ {*d+JRe\RDa ,%D [zRH,]w1oݽ#Q()&Eނବcyc$`V1%!Nf[Ȥ(W6 %d) !OY(P(uuVFtX%EePfy簑{`PX&<,yA( d 2ˮk(zِ0)K Nd,Ϭ,)Xfȱ#+BTsw.7271[Yмhá8%Ԅa> s/R#!3 >#| dB̠`Q|eWe) 8Tac) p(t#"‘32>/ Cb 8L(P! 328m2"%MdȠ|`AY u $_yH*=wV)?U"oJDAr%P2&zfMocE#A72N`؈& }.Ӡd\!aTa;D֖\]& -vl :{l\gK5?%l9_ޞ}]bӧj 2vBՇ?@U= 27yj;F~~B^:uewlۮ~z6{6AySX6o>7gړbfEć㟭,Vqk=AJJJJ?gW<\8}.mh27TTskQOVG>WqyNZ~M{R8Oaj?}\U,ȰSvd*իkf#KI[÷" mv8x{Dxbp r ˰6pyhFHKL,}4y2?,e>pT} ގ>\Z |gI;+\ wEm6ݓ'w`/3Ȕiyk2ōڣJ6Z۝ _f/fv=RDX}lEF+ǺrtD:K`3(FQ|kb#պl{o>^6nԪQkrÄ", A\@ry`@Zۉ&v{?-(Gm gCЭQ-D`Kз+A6 UiY3;oAVC/{GI s~0|aNes6T"8JYrt]{I5{2Go>Vᖵ|6%cGK/)'fhNG Mgg]뜾Gsxp>kkkkҙO?|4|4|[" 6$=,J\Ϋa^n9Һv7U%6RSb9_Nٮ"TX!"mhΗew5i/nGp6g#uBmL0hsG;iL$qLnLa#u Hpd/) GTsיyL^q%48< jxg7,@Hn>Qk`.)O8X$RIF-Rag-*%_^Kr> =q|*+*Ur'\ [2ՕLue+S]\W2וue=['.W} ɞRu݉wG;As21<މ쒿dG$ؤ\3>I( B|DwcK16Anެ41o_%p"Y5#"A8rL }> Z9_Fn8 }Ʋ忱1`+'js5@4 endstream endobj 2758 0 obj << /Length 860 /Filter /FlateDecode >> stream xW[o0~ﯰiqma ![yڦM6jZ!;$m5v|߹1@Cݠ?#Fڎ|A&`KcN xu&gy=?szC!V`dK4 &/?j%SV8@0{2<a-д\$Mn^"b Pj( # B.RwR_݇%"٧hE<3)*MG+P|C/?Zv;XQTi4cv3iYEB*P2 kK`8z9&qu>s`I$(7!KŸ|JW]X8r娻k !=RʨH2;gD7 >ޱrvEb c,Ӈ"O*f|5-Ԙ]c9/XY a5J#ikWsѷR㇢n%H3Y10Cbqt&~0qE[B!\j0IzwW){pDUIpEu۞_n谵Vej{l̳(4guUfͮ $A: maۚe; ._T{@GτyWA> stream xWK6Wi6ZqIJ$C -P:$0vJr>䕴NŤ39vXZ/nnY({G0FA=1b֩~IpG= PD#2J{Y7jV-I~hl`ThEy}>WCsm yhXD  g/eyngJl6b1 0fMzxagC௶:9څ_@vnU7(*7nc5F4iNN !}/*''puzwA`S2 N"q}3۷C G M9hjtY١騠Xr{ .@LړJ3٪eYqW-%<_OTHOmwGs?SS;wQ}X#̌}{J+˳%H~zwj~OjE|/t/-JS~FS xPht#<. OZ&Ɩ9!FTk?{=G_`ʦ 2MlSifBty)nf:)_ _QO,}SPY2_TVIOTY>hP NE3L)NٰOOEhRipQ7(||wwVG_ endstream endobj 2783 0 obj << /Length 2513 /Filter /FlateDecode >> stream xڵY[o8~ϯP$ERR}`]LK;hʒG9Љ3}HDSwCdW?^~/@"O(e"F<&LPX=hU)kS~ # `.gar 3 ) qϱ4)^ UOè8U0C0x"ňbYw5qe+MOk*%`bxy!Saz1xHZ\08qbٰl61YdyO,y22#Z9,Xd&ǜ 8YZdf( &)Ny7Mmt)ty8ؘE1Ed%4# EOJŬG{,#K>}[X?IaQ&a D?UJ! AF޾s<}&A }$-`Xg4@aZJǀ=3ݺX*K+X\bP]-A^VV1?` nnFM?ӯ6PwBh.@w-9HT;Exz/?໾R*\kau}EG,ٲlT9)#¡|DyeJj68qݔmjl7ţX훁Nݻs5R6m#?jl}y ^R{OWAyl;Y:Z~HZXpQ9_V1Wc |Ԧ> +>¶i^̼t `4d!DwL^~'c%(GMј!Y wƦ3퇝j\ k>e#Ɗζj\ w.ǘkV5IqX+}fi7=ѧa5'Q> ~A3%Fx շvm}<qSĒ? bœTX_B+<?f" [uhOq6zajl>?sL(`[$[998g);wpĐ\+K7cM!әfDf&C x+3/q95[R`ZA6W_&YQ 1p 1{ejsxxeú x)MےZېk/}n챪77=_ĨLIU~30)EZWʋEo,vt够EbvL)^F+f_C PnMd;}oAH/ͱfqn`CZW/pUsa:lc+7us'4u7dnxvpg_UZqSLvKYC׈Dڵ-G`AIs l)Y5i;`C*'9.1i$nk+!q.↜㜏r@;(ԿJj#Ǚ!]%vg?LysZh9 endstream endobj 2800 0 obj << /Length 679 /Filter /FlateDecode >> stream xݗ[o0)a05ؓImiO-{jR'Esg0 &rǿ0 OēW@A%` eDAN /Y_GBn C¥jIedkzE\inތDuɪ|:/s,LRzچ]lF8 |}YݔɅѯ2eR' "JSF䳩ExY<Z楯h0n!,_v-^CPp$ip5ilr@3o>KeH'&%K g.3Ɵe~yjQj8FP9vGM_-^_P7

nk.ܢh*.[IE[a~3uE9bP2n8hHHcXΆcAa~zzemq>2"WC<L>H %Ęň^ԦZr*L&v>\vG._ooOmW;i v2cI7&ͷѩjTo}Ɠ_,C endstream endobj 2810 0 obj << /Length 995 /Filter /FlateDecode >> stream xVmo6_Ax(&67Rذf0}ʊl3~XA,;A8~5Y^E1Jp%s!rVkt7ʲ.gW?-ď'IHg4!M(H8qm`ΐ8X%8@sl|3əb6E/"A*]LtNa 1Q?;ȼbx*P}jHviYC4R XٲEM7-M\DyP\$h x̡j*T\3aK׿ϯ'E"8 R`8\g !AVCPMAWii k}#4 Q L In̤rKکMi-#uhNe'AK '{^yTAg9G:@OكG (W"?m H@]-| ]njV553:W|Ԙ%@{toJ)ߧ5K5dB[=6zPs"ZiU\>msne0 =I.t4ɠYFUjT}4}qDzy8Wmj@m4Ǩܠv?UJ S&3;#k/9{YpA7Ử5m˞ao+ש( e2u5^fh;.WAt2^ x?+_^j%n\^U;s*2vRNT)07~2g?S}?6dKߚ$> stream xVmo6_Ax( 1CRoOaҢ`d*KD5#DNX<ɻ{{F~Z.d4ELkA$f4 YȍY\ӓOR0d5E>uVU;|*J\1r猃?.n>2w|Gw$&arI~]- 9CN@DNzy#;f)' Fvwb ''A)<Иt"aqSQ].jk e4˯* x(5Quۙȓ;BiAܺcT!WA=s•%މQyvy,VȣX#h;wr&ٹָFl),b\ĘhrjϮ 91^>gfLHNُ#El|/ֳȾj]LfW1Bo+&4Yr8 G$4 S%N`;a(`Cu/$^0ؕZԈD?-(ˆ1j&MOABYMv#s3_8̗!ImeBUڴjZ;߷`^Ǘ,Q]KZ$%Mm2QW6qRvX@m,E॥ On3A׵ҋ}gA[h}+oqSL%7vJmQ/nwPk[rO˲ X@`dRi?_Ff{xкxQZg1]~u84e! )^s7M\P J6;wG|Ο(}udeLn50Pͷ{̑Ź8,'xe,&b6: z x&s|?_B7*wߛ5s endstream endobj 2836 0 obj << /Length 792 /Filter /FlateDecode >> stream xVmO0_amH{Ih6>QLi.?~~IҤ]hB>y@[s=C'> FA,8d:g2ɦi2t؆/F|2ϕjRSmSG_O-='"m6[Q?07C+l`Mhs6`wI \|mvӿE>"ْgC6 k[J\NFҔ2,=\VS&Ȇ=e A=,I]ʛ}s6lfF UR3xxRIkze2X>"9QWc9Il:8b}5ʮXȴHb U>1Z$ijY 1wƙ4F3ȆـCǞĵQ5lZmjyo &`> stream xYo7 ~_!`/ND(il6H:`['<;/CK&ćIL_>QE~t6ZVFh"UAlE\[:R>b"8ŜD*7*,O DrBH*f*YبRdUNIy/Udsy`q b!(?9'X)7Y`9YEr*XemH.{%>B:1<X*VFYJV V Xgr9ܹ9Qea1`%7bbSV.;'a@J4r{ddbdd3bћ8Ĕ!e5yeλ$# pg>Vj'"D_g'ŦhR!; `7$e$!E#`ϲ`V,Ng+΄k 1*X# <-KD*8/xkp  E@Z/!a˫c. E,'DB0S ex&K@Hp,EqYų@2pXFPa/p22IMwחjgy7jNNQj8o |l~i~m^egzo( N;D:XPzPD5?UF=;4z㫩Nf]X>W/_t0ɚpXFZto>IFU_m=rav5~|Lt0VSE3elC=6 Рrn$U-kI:kU"@!Jg`:4S ItGYWbjJC0f9a{b*_ywռ}swN}LvԼƴ[J+%SY[ճ*ř5JJtȚH9+|%/W*w-^E$:Q5*I~~_M#qz׼դE;>Nz Ǩ7fonYCAMtV CorF6ݝFcL*ڰ^LYKsxpPfhϺ|֜4*>wEӬMίe/.џ?].o mpO1-W&A6c㒯Eљu𐷕h]zv7v7vΥl97988;z6x{<݇fZrA)Jhg@6w/߉=Erw"ѓ sqqq^Y!8LteNte(Yk3܆spfvC7˩ 8/gKrVŠar5#B]9ѽ@IۍXvo(|CYn4BSDsFvաmZ=* F= Tm 9CO(!ǂk{Vsn^|R_XZHf-8a0p1uDOj`9r1F{UtډI:@ip6OtcuN3aහ°qvv=]@nBO];mv!޽hPʣ m?(/7kaphld HZ<ʡIG12 7d ;GdH85uKumo#ddA{U[⍼ɨ\-j rtw Uh.SXd (B<ex4}rEp97%M?:;l~o6'3KY<>q ̼ y9O6aV'G עM*{Sfε`泭Ee+.S_\uu;\gקL/P/^p{[6yN1/w endstream endobj 2861 0 obj << /Length 800 /Filter /FlateDecode >> stream xW[o0~ϯxY#ח\'@&MiMBMcIID{9C :f$H@`!HD!%`{5:= )b?сg6W\*bj.BZ/ɦ'4v}Q4LLxi`ؾO7Qt;>cjW>\bǚBBM, ;7]Z`kҤsLZcчٷ7egF}o>v!ԱRebT^m|M@n2s~9XBoOL2G23j8Ywֆ /rS,/2Z36OykyU^صCnx%Ww3)>_{6ݲtIuE[CŲ/v/)ׇuj&$ë` tB?b?6ERFUHۊk0iX~6$ڐן j4gV5_8Eƃe#hIy*GH>_2!x U+\{~{I3Z9oG!˃D!D}D>Vzmazw9塆4m:>W7 fF F å]>r6+*y> stream xW]o6} ܇ɀM)m̀/k=!) Eflt>m3 "̫{+_M$2"X< ix X,mmd֨by~~dPFKi!Ks3wѷKp{$gv=[Rt(ƐЮ3iGw,Y#Q 8PqE06Rί븘`ĉ{oF )ʯKX@r$ȺsR(,ߔNtg|c2 aa5#5E|:6T:-Re&mj!WoF*f3Acx2g@01q!ᐎSػYS3osvShla,9 I[q(X7mi<ΒٿiZEё209N3p2 )Y8o*^iלQIIU m?0;B_]oDn{]I.k33;tAءfemm c쟌,"&q$Tˏ'D@556fKQ`Yja^53\M&334G(.*v}EI_hSF*y7}?YW"_5i+)l?5:Gy_ըg6/DPtFt, amAt ^D x"c\O}&ix4Dd6דBt p9;Uz)Bq7Wq;0%\SFEBohUv2=F#qyKY)\n ]9妧%ੰ)o 6y> stream x͘[o0~+HMB{V$s6R V ! #/EfqeE|ϣ0&IheInnWquh]V;.K t&޸ַac#2AH3/!E3H>R6$} l sc7gek=>If(/`felDtb{aWEYD0+㘦c=W0JxQshܔGWߖzEƬ|uZl`ZV\@̼# bQ/baJ fA4 ľ@ǎ5#&١0_0•3'cX _4%&cLiC)%ɜiibʷ|jG1iڿbQaq2yJpGޯeas/bm#]2rk[kOM eI=$@v d 34CYb8+VPR:J9<]bT6ovChZ;yW L ^pTZZƁ%F~W  Kr݀v| ۍeVցȀG΀{.f5ֶb{z,xxI3J.huz*uw޿ endstream endobj 2945 0 obj << /Length 878 /Filter /FlateDecode >> stream xXQo0~ϯc xoV6{j+䀓298Up2TZEQCw}-j, `!#+)Kʼe#ρW%1`C׍6A>R+Y: vHooZUn "WuyW,~Ft| x Jw[e8=6;BW#Q{!a@?9v2s{ ~5ÞQia88۔ݸ&mj}]ON:!L ~gɷQl: ʤ7G|MQR }BB9f1w7xHb&h&kۅthCsٖ;rmr[!!}/&'&o#'fG'bFYndZjskj^F7./@i^++kB˛F~&7R(x̃/yqU_k=5_z%FSG=Au|B׿Z endstream endobj 2843 0 obj << /Type /ObjStm /N 100 /First 998 /Length 2773 /Filter /FlateDecode >> stream xZ]o}X/7YGa\ i/Am<mF&Uʵ{fŕ$R$H=93K`%V wfFC`^X=+ѓY0L#XWdQ@`R@A64L9&iHɠd.nHm1 .!0[@wf]˴$Ǵ=!] L[/4^3i0Щ D!aXH;`yNw1:ό.0㕦kI̊4/`%adI8f&{ )0wl7!m}Lk ?hOc/lp-A$Ȣ#X y08IE4ަp(=!< %#] M QRش/06pR;RH $]RxRPI fhVE4Mc.i-}HX|pBHDKa2U$ȶQcݦfgmۍgۋ1[Ӿ?[K@[u׷28[#{(0DwVF=cl-ҽ9eZUͺ횯v 3ˑa)"a[ׅjK>ղ" QH_KL~:sP"r5]ҫ, ϖcӵ׋t=ϋHc`5ŦoZwSs+M)ryP؇r n%F;ߙ捶og֒;Dy\ڏE׎ɟ^`)ĥ^X ~X@h9 [/`7=tǗU}8PJ n/aӵ׫eom\!D !_V=PVW̟J`\|B$Xf6kYi~cBphLit61aٵo"X㽐U bIA!R)X-M7C)k9U#7*AجhͲZ Oy}s뺽ˢ r":qdWeiK"5рZe0ue`>D(HC/H,he Rxeq(o zs0E\2Bb. =]FAP N*\$̩a2oA#_{)qꛉˬeV2v}^~W-=G xe|KZda߾l}uQ;Q4NA64VnYv"kj.qj aϏ11i'7Tu2?1NVH1ъSր;c4lOikvvZ^W}Ҍ_f>tJ-usG#AS|h`zљUꙣh\9XwkTetj-/I.&N!Bn;v 9A CN!'Ȑd 2f1kYs̚c5Ǭ9NdTtLl\|Bf5KYfhOU lD!B]]geѪ-Bu.4uB'#.Vh_ҡpH;|*[u[mS^nh0=% eTۖS["'˻庣^rYT (SD wrx$\e|JhbdA.1(.QwEEUCt@0p"3?XX4}0u%ln% 5>aUGˮ]DiUi ]JyGH crolSCq> Z.Դtts`ŷ(33A@Phg-VȔBqttySZM+Pv?n,l8:Շ/hOHy>? E' _$Е$Cƺ.1D+/'ITd\ؓՏő\Dzp(ıM5S|6]Ypes U-T[*VyʃUsU~V:wru֬f5Yg:kYΚMltrBJ>@>Ksv^&:6f'WC.v@3Q?L.U(CρkqrMp> stream xڭnF_vBClEQQ,BckTQdK() C̙sܼ_ܼs'D% | IߋC,J?&0(mLoJإc~YW5;0PU@FoMc7az{̃& }UFo%⦉gjo]afsWmߕIUr.PJ(y~6VW$ Ҝ.6@1m3 rX۹v:Y=Љ ~`.Gq0oOI ^t&<y0J槟:K,9#E)Q_,qʝ0#Ьqg i.=`T 1~۵:1Y>2m¯yV5Z;O'nJ:tTzsa<|%ǮwjēAOQeՙ¶]ez^:lL'7+6 9Z\$re4at@YVuͳUJR q ;Q;̎ K8 1v^ 1JrsSmpe]rЁ( ƵJX]?%%-75CJM [EYؠ '&pF4%lD-QK(vdRuP zԃ* jB= `@ܡ+ZyV0Bf]\R\] JfHi.{.;]u os##i9EIQD<|0FօZo,V,G^p,жm'/4}BZQ^UQ"5 ) lw8{^}?%h'ܶsHp IbT^,MEKdNJ8!yccbg4+tFjO ~8ͫDĺ[pj@c=C <}XqP O/SSlo>;%,M/3@[N(q|>܋{I8qxA= {BγXm{M`KA|x$S}; 8 'pDčla 0#%\b#d[(}=F6q߈p4:誱y|i,4[ WA7(mwqT{)=$D9,WG)I^gGjN /QkFYIr[!Y)ͣ0uW;j7"R.e-)ⓃHԫK=,Wvh4^;bW`'4?Ȗw?]H~c|  endstream endobj 2979 0 obj << /Length 1217 /Filter /FlateDecode >> stream xڽWKo6Wh E })-rh)]D;*W>$[6iZH΋7Cbg`ُU; J"9C0FA9<ˆԙΝۈ([q_U'+l$j#!L.ac*N P>RfD>%(!3K2LO9iO;鴆XhAJ9lq倚!ώM;Ѷ͑ڧekr$E{45|H]g_gLab"R'{}N YONHar~?*v ~D!qchH 5%1`dD 0YTz nB [?y>e}Ebߊ\K]Ǖ^xA {z7.- t ̂[`maUXAdӿseVQH-!f j?6kUW8[M P'1 /7F-l8}vm hs_աl5-*MBŽRvV#t#1u[mb0tN/a4d0>شxeP0:AܢcY a(E, "jYAlژbؗ]ڮ鳮oAD3#Gʶ#ivUhl";Q8oz(N2:A)c!)u_Cc%D>*_@FAk5n4zMJCbB#C]ʲ^<)Ux2L{n=Dn.ҏ/mbDv &)Tq<8vY( H4%O^OTP;֨e%"7Y+@2$|>5~KP9Ʉ06JC4wP7]C%P4Kgo6AnWMuE&z6Ν !+Y}>m ڢDz b, 7iOn4"eh}R g*E75^i=PʟeiQE]xHUޏrJPSp]V?ү# #$Fז.X/" endstream endobj 2993 0 obj << /Length 1710 /Filter /FlateDecode >> stream xXm6 _at_Q-Y~kO8JձSKH8qιNEQ$E>. 6A1}1y&΃ K:aH"i8bbL)MfxdgQIHcdZ9tcoUTRVee';ś( 2p2hbYf8 gqa㜆$U2ơYCYp]ˣ^ю9#$V 6r+Qr-+%UR#f>Eg0Jr Rz+Jw_k쨶͝vn±/]yݼY_0ˣ+7 ӲhluYoZ 6pe%TJzuHlBTvrn8d~D)pAW9q[B{q"q>5AWͱ玚R#·?8QU w^ѓ׋ɗ €xSJhnr) VZ(ς;ú 8< Or4tzGisNXy賱W];DD 8N )HkC\zKG zWJC Ǵ@}k_lv84z;QÛqG-e!:7MMMcdh@kS:tm+xZ@@Ȍ8=N-ٷ-+'ؖ<+w4{f1S>S[&sbQ@׈3tdz $"<\ a:Pvvɜ4eCQ[qs>.줩GsHNNCs<#a>Hx#^<\{R,mMn>CEhS[z,gSQWcJ1BuEnpc5R셋RKQKxaQvР=@=&-J{_0zR1>N.,eD endstream endobj 3006 0 obj << /Length 705 /Filter /FlateDecode >> stream xVO0_aV"v;F>`C(&MNd$0& ;.eO;81XrQr(!89(`(Y٤TkV^U'#Q $BJ0iS*Ki 3Q:mzΕiǁ@1(# z3#B&:n]\?5 L` XZ-J"beuїWj[9oݲeZgVվlሞyQ*UfsiThmŚ >XR(LUUVv¶A`^Ae 7RfSHNΎ^KI{uAT G AtG2aq(C4f-@@^A!Xst}ed@ &8xHQH 1dJaOaӖNgtQ"~*}4à2R+]i7Z-:K5v%> stream xV]o0}WX}i f6m4em 9!Y[MﻀICU4 != хyanyZvCCطo(O}I=c@O|-j٣Ǿ9fԼ7OZd@uvS;,?mMNK%#  "FdJ@ "[|&E!npte~v҃{( (';U_W4F&dؒі؎b#ML'Pc-qI0J\/2~a*eKQyh $q 6 eS.{HR SgjMߣs-*zb@e TޒmhRH; gn_ #Žco <.6f\^8{EE Դ(SjB#B2aM!~yk ˋÄNj"]*ͲQ~,i~W ''s d Nd-dƅZlrh&S-S@0U[GtIeKeҒI)v?=-!UFUrfj3] >9U.M'%j /Iz֜+f'ѐUSv̘kV%A֜i6X2j-y+qg"0 endstream endobj 2954 0 obj << /Type /ObjStm /N 100 /First 981 /Length 2262 /Filter /FlateDecode >> stream xZMFW1#@cvd!YJƂ5HZ{.-%"E"Q Y%C;$-GCtĩ$8ZDVGϡ $6hrbgEx2 -`w$aYO d:JǛ~ FPoٴ(AmKŒ/ܕCr{YOL&zs(/pPw cѶKY+5Mop D14QzE@VoiʶE59ARk1U4fIKT2Xdc NNS(5a!{@xoZ"fOk3V7uxӂ:\vC; [t/h]0r>`BƘ?`]|S"H1[~jӸUܖ/mVӸ9z&ݿզ$rF8$mD !UP0a峈Nz!B0E\❕X'} ٢h~j?>)-ɒedvS^z+fy_B#NP(D,39]Wכ妬b<[2éXۮKx]Q?+V8L=Ǝv衕h[e:[i#66kT;-g<)OC% U>^$eI Y%1EEI }I l/E2kcMxG?y &v;NCׯ2H>O;dH Z-G\F<>kř08iL#sLA㏕4Bh9m S|xNp;cޥh6XXeCYPL̬諉9I$Vf~j 0|4y+ﯞKwE/@+7EAm޺'}##xХx8ҨYJ?s:t?)Nm1%xej$y9mō8viƌվ|3{NIqF)Wݿ +, fNSG/E5|m8z++:=sI`M0Usٛ n3I6O?H !cTu39vm¿Ɏf"1DcZFM_j_b)XIj&!^p{!B ZBHg$ݯuW᪬?sQ&RINb}[vo,&~#'?.N {x?k(DfY &m4+ t{xĒ.ǟ!UďUKw'LB [c躾^Y /(. endstream endobj 3043 0 obj << /Length 860 /Filter /FlateDecode >> stream xVR0}WhC"kg@)0З'`űSY9sЖv2^iwٳ 0 >a&! 8!HD!%`<DQ+\8Ӣ<E]]E%w/'at$Q Ɖ:4@6xc I$l68D`"'@<3c #} _VWF]\ B8Zŵw=_&1# D?# .HX+鞙=w1y4:Fn( _Í Tې0k2^%^aq FC^?FјuYxz]Zx0ґT7-h5]8`UnYNפ9On8JrR>$0ߚt6B[ڗL 5Z.P '6[2>t{ l^׬WsfX߯픿 ]"YfYMlV ~gゐ`ŖSUL͒ճyɄʹ0:oWd^њjy22Rmu,0 =]m }9b޼#w{2k>&u.-Ϛ9+Y0eiv.c:m5 Myv!90.>NR4&-̉^>ciYhl> w/{`zΞrȇl׍Z&8> stream xڵVKo6WR@!!, =M":!V\6-w-90` po}ɻ"ͣhƈ%<h~Ą TY#9rJm!KԕQ1᠟?&DQ|,~`)ϢNu% 0za` Plޘ ;`3EyF:hW\Lg4Z׍n`{2vSs(>QHx *U[4zmt] PsE3 inT#Pd@\/<p]wlv 5L.'эS!; +i 3?#0&Wbȕ@i lO?T\)?:#܆ vn:okvHssփ f< fDӷy> ڧup2.~l?I+z򮬋)^cۦKoKG@(̾Oa9W ӵVP)PIJ XMnoT00'Od?Sݡ06`N¦ G5ˠ V 28꿂{?@NEr:F%8aWu0:)4jsSdc㑣WdڲWcҭwHdTXQ.` .).)a*ZzWKWF}E}·JxL gXĿzⅅ*Uv}<4zJy`1dPϢm7ՓUW'צy8%wj絣MѦ޴$A4O> ܅ZSh )JLU/4V{$WFZܮY]v1=SU[W$= jis cZaQN.t}0*He[l_xEZlvyڙ {?ڈAu\^2 endstream endobj 3074 0 obj << /Length 1013 /Filter /FlateDecode >> stream xWn6}W i)>mRt/MObȴ,.DolHI97"@8 ň_f?fK Jv\Z0}XN8!N4-DE+^pBLsyf*Ə+(M3IH@=W#Vv0>UQk^=["%2KTߝrn4]p/ݕ:b~^Ӫx|xs_UF u,l%ۥRuRC9=Xj5z8#JskX)), j,y؅WCf[vw˥d}AJ\:*> stream xWo6~_A"HQJnkE%.,Ӷ6YJDznVQdɖ4m!;w/-A~NE",@`H ])cWAKTm:KsJwDjD! =\`r1iYwNOۛ|@vMmԨ:3uhwVkA{u~}':{p*)GX|IjT<r^ }*nGV7pb(Y>4EpQ5b}grg+1J@u:~"KQUKuQy&d9> 1{_p@aFjMLo, {z&R&qR(.22YYc8B6gNP9߁eRdH`x as"U:$% BxHwRM)@:iX+(dJFK?_ȹmjV3c}® ,;n^AXC= 32’GFc12Ǚ4T@F閴gѧU*gwaQ-~Sx&+15HK %R@Sv (a&+ JH\.V`e͐>grTd OSvzRXao,փA/[خ- nP˕V:+v .xa_nZv?rPRMV*b9QTB'Aۤ-{>Ñ[3M'`t ^=a Na&%78Q@SxF_{eS&Sm>u-Ţێ 4v++0绽D1 B endstream endobj 3107 0 obj << /Length 949 /Filter /FlateDecode >> stream xVIo6WӋD Q"v2ho4c PrH9`A[ "%{; >~XoV KQB0& к@wAךSU66[&,\%8U ĔqK"^POD#yR1]Hs%ĂP'0ngbkbԀAάt~މ-:d*lݽAn&dxZwN8XFkӛƴ#Z-vgssS+wbF f kXr}ʪ|ca_ `fʰTm\_VDMX^T!\ItHkSӘþBW$:#)Tᄜ3^Hq1 Ck&omܾ.>{ ) ` _2"hMޭٸg Ȼ/8&- NMDXYrSY9(c:Fz_:w֗xlY/9T }XƙqSvUky^'\+JK1#אhRa/h}e}|&?Ża٬~5IUImiʅjӾ4cmö}p`Xdƥ cEC885J?!^u1 e1bw,ߖ5`S|ߕ vBep+%$[*r-'7nuN`XĹ ~Dκ5^i-όuVs/V{6jϺQ4%ܡnFM(lSw6ЫG"ӆLتO5/X&)V v"SR& ..I$,' f@bZO> stream xZo~_@_,wg ms~eQO&]R[yXCr73+P(v U/7#BȏH6qBkM,y)x꣈څ bB{bY%,y! ތҀ+ ) Y\4,ij6(ᡥ4qyKu ZL}` H`*Fx).1b ý7aU`Lמp@akN[L$La a1&c` /OCf̖*F~lKpl`HSLǻ`#F^8^Y^|#'oJ-[;FF o[]R!SFE$펰"h8E4]1YH6刁;Ph =6h D"e heD"uXvHa.qY0=@ɻtt||T|D񦮛>_GŏM{^^)~.~QqR-z &ģ{-|)$F(|lDV8ղϟY Do="#Eϫnn#b]/w@e R?%A6¤i 0\?(n@%MZob`twqHI$Zșz:ݫh$WN 9 4m{ck>o;l-̯Ox68q+޷CwX}^ Vuq)f.n(>Uᒓ\sGeL0 \A?Xs̰r;F)JOɱ$gUg.;A=c(״x..eU/nEu=b16g뮯nPY8͒u^`:_51^NLN0PF6*nגR9>3or(>?X%uȦ,ΛEqUrZUeW'_tkoիMiԽG蛦]员JZhmLv qS"iږ :iԭiY_1$t脿vdn7q#s;64TnsqaP\<1غ]^:m[ꋗ][`So )BEMq-cʙ/|FN3 2G\Fskj<[LF?CBX6 oy-kwk{݅NmmdP~ZɫJgV͝saMpߠ&kNցD%؇ 0SQQ?HXXz#͌SbSb30saQ&ou|/ v.d";.>-(0#E{MnxVM7p۶2FjJytZR!82/R< ȪQu7u_~zZ.RxS0ֹY=Ðme"~g٩{*nɵ$+yߝJ=L%I}s*c(Qp088d"]4Υׯ_zT//~ye.ifdR/ ;DT%r\#"gAZtcwmpy?qn0W4wۉbrqʁ=ks׍׫$8btt&{W6DT46F8QPqgKBV: HY;ƇsV[sޠϿe4 9q?C )a~qBt -y`tMpn>ig_m؈& !6Zvr]3>#&3W۪vNmcZ86EZz2, ☨c.5<R endstream endobj 3122 0 obj << /Length 1153 /Filter /FlateDecode >> stream xWo6~_v/2`%GC+Ji]l;Աf!P/PuK$qȞ?MNdb9 @Nn-LDZkO78n?< _豐/ h7Vh\=W ڀI.HmԄq>{Ӌ&ZJ|56N_]>#  nJvsMXnj-,TW|0q${}%:fwemZVòWdFSyut0MI$, B!iqJ{YU8۷Mq~иLpj.}IW~'i S k#x3Mt%"b]}x=Jbkn$ll)Sj*"3:)}YiG9ļ}wl7ln!o(]q4̂N1];K:9 ,L=Ϟu2(ˢF> stream xV[o0~ﯰ%=lHh/lLK&%X-m5"BUK Mi Xa f Q,E0B0RRՂUg3{7HoT0+ x0F^62+4A<3:=!O6v%]ϴg%rdͶOv,Wοh%HL4ܫT*#XLJ <ێ9~ೝ@7#ֻc]zr>|`#&1J)U\97kc 8R2ד{$G]cFLРp ay6{N&0Σ_)uR7T-HGu97j(W4 RoCZZ+~ ]0<6ۖ(W<*Vԍ+sN1gj8m*t%"A ؇[ǢĻgbJ͆͊rf+s!Q:Kq"Nx >X>i54~dIOBc ;E~&3#K1KЉW$mĊ.4GAm ([J*r8r頃}E( û89c8 Fsb%U\;5 r?^t]|L)-݀:xzxnT͠R`fjХ89sMǕl:5@ɪ7#M]-?ϗǐ9{G:|{|>^M1Vvn|iכBv!@4oԗw\ endstream endobj 3152 0 obj << /Length 940 /Filter /FlateDecode >> stream xWKo8WD= & {Ji[,"CRrDr(#r83<8V&PhDF1Jby|nUXCf 2mSTzyW_ i\'&HbN2o&_ZGGY,E$ .߃jaI%8(JH Tw\껭hdRϮȓpRtaVN+.`Ln0J WmuHRL.zQY8C*o.πp(rZCB;Je y]--6=N8jWRîo%tE\_9]z0Xi}Rb%乏|tVj}&/=âyXYirPӮԞP8znn')( ޗrPX'6KU uH1‹g@Ĉtũ ]nAis-7RjUj ^\uwl 0>E)A&|}GcXpbA,-y,KS>klfcu#%L+-Zo2>㵸pJSv|NI&W . :SGJ!R'u`30oMؙK(ƒ@5s`̷gFzkwnyU, ӥl7"Kۿz3&cO 3Qpq*/$=qsC 9cӖص+CK^73׮Sy'a<i7Yp̂C3Cý'k[*+?ىns/[. ,wtmyQ3zPV #]ﷲe3~?Y6{wqL&LƠdXu5O; endstream endobj 3166 0 obj << /Length 1573 /Filter /FlateDecode >> stream xَ6_!lQb@ $hMїn! Dʒ*JݯVBH`9s;;;ެg[;! };C0FcY'ǹ4Hoϣ.*yy-[/a0:Ba,-j\EQ!TӀw, pgI84kq\Jԗߣa?2cI=Pda4&K)[ !>4Ǥ"m%0KOs, Vɓa ZbN2w]izSP^`!Gmmqq8!ubO iK1FU%:RNFbձ J'n.|E53_fRd`vǐw(YPo/'dGjyT|{k:8Ne!T,3PYID(4 $`N*F 2$^'#B ݣxpțً,+ Śܼ4ɮ'4OA0Vv3O%z,rX!>Mckd"(///c#QB榘e[ޮg̈NԤtXf?c'Cp ϝF=8.1a'su;uQ1]0vI+˩; V6I yШHJ=Pc| =6"VHb u p# 7ٔe +T:Fy >?X0׶v"d\eh.p;Z؊RvXâPIot;KA"lΤ [fm]fGW}T]ti2\`VHIw; (#,yY説wMVl* (}˜eiUp-B"dUFtF+#Kue{e`mUFCP^@]ZN%mu@^샌v}5\L=`6/T Q<LEQ`՗{  iN`v6Jymo(Y6QBB=t:h5FڏGf2yrΣC' lR~7@;XU HQGvu6{?!4B# c62 H.,P=FZ݉Qoa31 | BSQ! r!ѓ}IJ氁iOIt7y_f#6P[GGxԃB+43+D7WDG<~Yu)ZEdE)*T-UϪ&޿ਨv7Y[:0]e-IWS?Q1/DcR$C{r endstream endobj 3183 0 obj << /Length 1387 /Filter /FlateDecode >> stream xXo6~_!dOĐ k A%.v#˶0Yr%IEIlىD4E~w(-<}6]\ ixӹG0F 0zәwߦ^\Q8 [Ty.M:3Ce}#\Y]I0N:XS,r o?٠ QqL)4* ٝ4@n]'T`*)cw_əgh#V$*k-((73fS81I+Nk>r<`pJ~|6V!7Lz +^~OS$nγ&W%&# ܵ<}W,/A\:(b6WY03Aܬ"+g &ɀ%QB+S& $ى`wSɿ;>IOfS5}WcQ+)>NGGC-,HhⅫ7%胘VStqb}e]xwf[v p1.Fwa(ҐF')o|̤uPb+ca9b aY[.!crSM%_^vNSA<kkz/YDo?[UkIbXkrwUefSKW#Q)!ԶMC_X ̭.fC!$o3te〿(GN3N?$Ho{4K[Xnd\N@1K=f9/Rx3{v!]ezl&uHJ寮UmjΣ9jwda\Dwj"*,N< (₝bji~Z ,!IZ}rCLnkRޔfjevRA ph6%ȴYDq Y1&[ ݌!!*vj"!E10k]SjTe ^C}!RM') endstream endobj 3198 0 obj << /Length 1413 /Filter /FlateDecode >> stream xX]o6}ϯa%)aaCm%O⤿~%GaXbQ8+fg?]x'3|\(,Ζ}ZUՖyZ?ld=mL VXf^nƦ-Q6*h`I`)A~jj vV P2 k1a<;ew~ ?9ufh (Q3#1u;U{g $D y_W/۴ZJgZ) 0^fݔ=Wr)4)2+.#((suS}1dTc$ ҸnҺyD ,~xZ &>HD` 3qA17M 6<)mf_ {iJ" U"3vj0=f(?%  !Ȫr6ڙˬ*APSd "B/y{3 K-a:"XL8O (( qR8Ȟ h31D쳓iiĻ>1u?[eT'KZOi+mQH[I> H?C2س(=%D^]_)mQT\1 L)2h"F8'80alr3]OӍ~`!9YjFM0'D)JxֹyOScUUv.͔}2ոj2/pMF2kcZӦI*ܵmny;Я̯ ӻ9X&(2&4cHKwkXIסz_dG)I8]dW@`iTz&\ "f94H7ӵйrfB)C6m 92{kFCiTOVBW->'V82.$SȪݾ7PWwe}k`*,۔`zX$5ܧfsj8t@P@vcSZ 1p}]孩͋1H UdΔ39 !A1O~ןEC9Bg)Ԙz#8DPd}ܰav-&U Ə|Y4zWೊqb/3L'ul}/[Oȏ爌 Cl"&G}vϾ0/wkѵזRC!ie5@٘*FCy9AEos|px+y#k_AƮ{ȝu}Pxڵ\&v{6m]vVkϜ*;"m?vݴ(í5ubכ'Qq u endstream endobj 3243 0 obj << /Length 692 /Filter /FlateDecode >> stream x͘Mo0 59D-]b+V[r-yiG4Mb[ B,QKm DЕq\.%8 6=?DQHphnu} ラ^aE`hͮi%;dyp( >NHڹ#(.%1SkS#߃qn{{/rE$I7e-vq']7 l\”b#Ň# D=ghJ`Q[ᷫyc`ZB\)Eb4NƲnY#>F瀸<y񓧐6a vYWQܢ2Cm:9"Hߘ(M5uSLԦ¸A nAR g{*@|BxLutw[CUp4cUepY0a+2a94k/ުSk4HVsk`: h R8Gӵw'FUhefs֎D93 :.kʆL})Ͳs_L2ջzXw^/@ endstream endobj 3115 0 obj << /Type /ObjStm /N 100 /First 981 /Length 2306 /Filter /FlateDecode >> stream xZn9}WؗeE/ cd ,AێvP)J%v>ĩnbTVF9KVY!(d6(oH˛eU 9sw"1'ֱ3䜲˻w2"%e9wYEڶ FNGdij+r74' ɓwh˨.+b*-RBQHP10 /dOGe*=t  ,sgƏ9Z9”6n;&Ho[#v?`rLV$dRFpT eɒT'A>z/@9}Vldv2 ,%;N I^ɚGkH54 Q5y/RǨ4ykĈ~) $ Rt T <,bT j0UH0{\$4HATlEzYlKxyj΃`u =6"%bc*˃8 d@bc#JNzwM^ZV5"yHR7bCaBpX"~eeA *f}уsq_2YH2fN&''j٫ռ0a} %l yIz5{j~}V5گ4O2ԇ?L/I3m}'yvzv["(ўͧW_Gy5a铷:OoF'؁n_-+tqONUHUEadҼߜoj}֮ [KG[ v.h[Pd:cuj߫毫+ռQ?;_nfAr`Dֲ19= znЗ鬟c bEZwGc٬6x|6oq`kXX9ru;=~$\AhB8$>oˮhx6F=nY,>=l)!xh6k6G 9;Lun1wѳ*Pe;*c}|fɢ>FmY{>,\g*݀^AH5,IjюeMpkt$[HrC##UV I  $N$#&SҼ:9)#48V}wȿ>eZ]d^/jOş.˦vm7s8cV fmm$~(،f/U0mhj.He~ǡ]~ 私rU**TsD̎Tٓ΁D| !H/F֖z "N^м!b8!4K!Zy9tR<|$1S bIJB;,,KڞՉQ8jKBk 9oSm~ 't4iF3T_ֆi#h C0m@m%(HSF=9zݶsRŦskkkj4B(UEZI#vpHE_XNTTC :Z(˗}wN`r.H<zm׍z#8c7b$Y*![@$#Ξe?_W(%־^.b>kȐi 56 ,#z\t|:W 0r[:0LjI{ X(O:fn2{: h37}/bx06zP_O٭-6 \OhIxX#(8ns 'z9V>Li 9@mvOh Ck-> ~znt}tVgZpgZJU39,sJx]L#|Q{df==(0Bl@r~iY眯ams֋pD|s`?3'md6g vr4T' KI(Ozr?K%FxukgY7֠_RjǢ,z=P 2zʔ}\TMZ&eMzeF ceg#8eݺ"BP.OODd "HNGb,3YEiB -8}g=)? #""ybL0|=1bDV'`I ZǐA{}RS_ReM55s~m#]PjBޏۊ:1 .$jg&ŅWf*Q3{lR271`)JX> stream xVmO0_au_18}`4A&BY4?'vڦ P6 U՝OQ` !%DGA@]1K`DU02T_8Nٌg:EaJEobdֻB`: ΀1 |)[_jV{J?xO-SL߁P'Xe2Ssw_ {!) |s'/ƲnV,NMm;3K03cZ*a]ҒUtF | endstream endobj 3283 0 obj << /Length 665 /Filter /FlateDecode >> stream xU]o0}WX Hŵ16N=UDg@ƐBLQ{N/_Ostb!gI0B D AJ`vMZʲ^-eZ.u(ZىƻJΎN)@`Ĺ.Ƅ$ 5NIbBa+Ad ! z>E`Nju7TD76o@#cn7`xCQ # <zO@1Bc .o^IMl0B`7)9) gX1JJ gĠ/e>MBcvN× os{$Nox&M[׏( endstream endobj 3296 0 obj << /Length 815 /Filter /FlateDecode >> stream xVKo0 WjW~vt@(Q,ei(KNi=: *H~'eO2HPb'tmQb;\Nѭxy^_qÑ5F&yx=9eK2csZB"F.Ibp{WTI֍cGrF[kɋl")\,:Fm:!]`cW;{KRٽ{aP6%ϫRöè3 x@Śt(19DkBYɵz2'5nlwǥ@˩:\N(%4эl7V90^p{w8(.|3n3c\./gRy9fwaz}3w1.XV Zr">dsyq Nj)c;:+qV՝~3eRrYlfOJ"qEҠ*hd-L xk,M5HvcTg_nC_О?ĔȪq^=^e{meAv0֕:b6)Fa[਑5|""4;~j0#S9WyQHiYWӕHCpe[#A(Xkt{9}:yKpBhhm"z*! & ?ò!o"swReTT_Qr ͜?mz> stream xVQs8~Ф/0d k7sӗKS`0H<avv?JAL~'YGx(!8CwV

    zQ"/(<qFF^_h ޡG s%> CHxc:cm|w%<D-{.Nr5>9IRVZGNzvxT=wg:|PC<]t=JP2m5)`]_g@<: t>df]} ~X9lf#0"2b/ 0a?)WS6h3ц'!:ߨ*{8tPh*8}~esz]bUyfrQ,LrR<"^N@x0(wQ؁\RY endstream endobj 3246 0 obj << /Type /ObjStm /N 100 /First 993 /Length 2317 /Filter /FlateDecode >> stream xZn}Wcv&@61@P#R T"S `}XU]ufZ(a6Bǂ?'\J,xa!2|[Gb!&JF \< VQdN5NE UyS&*b) CZlwFbKY,2>KR/EN[F !0v+K7NY ywgȈm^Y&[Emȟ @ {xQ)dbފ`2>@C{ hQx 'z?bC@ D"/DyDF50w=bHG6&HGvLwHe)u#^I罰 6 8 }q7$%(,yLpx JCQJ91& "iy&ށH$CHR';!XR"3n {2"QFc39tobGa@1%70,[•4V8eѰcCc6*;D2ݻۛZ,~nnw?4ד/fUo($u_|qX/w Kv = #,R2,޽Ob[//ZYmZqMv&_vWo*~^#q"%"PZ? bVu^>JɀS!\=jWޖEwIF'67flXaH$DgY's02˺pH$B5K&q`ͪ 0sSF&ڋ[FFPI{Z'}oݮ..hLg&It|Smn3e^缴̞x8AGrwUI^FTfQb'Ǜf9VM{֛,Nme1q`3uvYt CсYwM/4!{3*%lk٣#uGHdӼsSUY=J:a-mf٭K!GCBH!ħA[_M,kF,P7ۛ9Z+k+{i{yړn*ٽ_@! 6("%i89>v^M*kYT q3EڧX\Z J 6kÅ',znS\ۉn|"J<+w~pX֫&r@638@ąC5%^?aʳAQH(:nqu#AFgu#=ZÁEdm3[uC9.GF4ch"X{$6U{u~!q<o@h5]H:3&PI|o^7Xt2ӆժ:G6Ӓof]BM9l zW@M_iॏE5r#5O#kڰvsѢUa.kx(=V}oߣwXm5^̷ȷȥ9{ݺOHLx,\4#(%:4vh183]cSJ6ʛHDLmߝfj;-=wi Fudf$҃L<=8Q 3\[aTjwj:0%:&uI̔ǹ#73|7E94 }wrm6$?W#f6m\mgWO )> stream xWKo6WEb ba:!]DBpE;|H"q9A >g83|3oէ0uRF$rkAFOeܻ,\}㨏=y1ux҇:늦^EݱV\af9{;ި( #'f?yG+'<>K +mc&((J=SXJYo)YwJ ]!o !v{jfHt@uop ^2( u>2N>4l4@%"-o߲]vb25s=hzatZj'1C(qڍo??Y=(g>۷Q,+p| okm9jdԺ M! VnW6:oESDHqڦu 㝚P WZZks`3(BD(rM9lm؝N iF"U}r[MK?TM*´!sy{w:/)U箁,WI8->j@WZ\ed-%ɩ qXաt̨$Ɍͦ Gi[wMwk̡q[L ,ID)Pk`W(O y ֠qZl4_Qp` 5uN| ᫚XVT* z2Dt- @ JS|z[[n )dd͋|Xݲ?r_fQL=jp`}A; e(c1IFnɢD,)q%.jj'83^NyIy+dd36s"Ր?!w;|g:%o-"[CjVh]:6fFGQ(,ݬMyB5?J@X ODDjbIS ckc-K$ĐQQ?6 }%&^b,F~"QZ:~TǞ:KT1*)`B^/yE/9햔Ґw}qEz- Y$M ~y]9 endstream endobj 3355 0 obj << /Length 1253 /Filter /FlateDecode >> stream xVKo8WڀE;hR0v_ÇdɑS;eer837/bkcah~V"rmVc9Z&dW$\{eҢuT8(2q)4 }WJN*qR{Dނ6%("ޗF 3$lQ7Yul99!mUKq:@VZ;[-u9gs81а!ζ3Shl{( > Ͳh0,L8$7oh{SQEk(}s|`—AҼ~cmwVYa>ooߋ׽;.Uxvʝ )|+䦆.G&xOj81 15oNKW!r}Dlϗ:wVlZqW-hgzO2[bQrxw}Llt3"[ )|[ Bpֳ-̺vUM`3i% vMgnOEQHѺ)&Aj띎a0F/@AD{E\[=l dSpj~2T*kM ^6Ș jc5>0L \]]r\k;ax|V$m6\Xa ]:RoMfG M] JO9 j^ 4e_n{8k-1iRnzO604Zs"f}r.U,je/BWCm=hghe7@4j .mYu~=a@)͸iR%t~;LR#@IJ6ORZ郤Sj_&ߠ*TƱ. Uߠ@#ԫIE&ʟ*CPk4/mk~H};nivxiA_@G$ endstream endobj 3370 0 obj << /Length 1386 /Filter /FlateDecode >> stream xXo6_!x1!ۚC1`m#3VYD9N݉"rl}!dN4X4x3lTAJRUpv0JT+J28H.~~yR+ @mYXˬ*3ݘR7yU^y٘f+CE:wz)7<qfHKH<7G Ii4z[4w]nɳ/ۖZڸ KS[.k}UlX0JR W굱QIjcjT5.`nH VEfZn3o i~C.5{7̵_Zxje}JLmG8SnG > Y7] _prB܈6/#Cҏ5^5nMq:X!bw[0Tu ' S1Q) ="$B}I@>xl|#em-;(s拋 !1 Ĕ~Hq\)a4|3{}6s XߪJEg4XG("$صKA#tQ|h!0z*b!>n:%s,m5 h;́3\L~<ƉKgOfuiM)}rghnt͌ǂ?*<44/F$Qܺ1]T[/Kvm[ꇅ~l^g]6ĥM% k< ^O1ATܿNO}I ëJ0GL|aIp('/auZ6Z]A2QWpAK"S$$IGs$=\NPZ7u28^k[d5C[i-Pik)T%$8=m+B݉ܿ XIDYߛ `.k5x+ehͦGqY |sWG[cw SP>m-$ݽB]m?6Au56+IQX^ÇUvgNYՇTx4ʡvrj!H<ȆgN|Pр66!#ƜRf> stream xV[O0~ϯKc'qKHB'@I:,Ii_?ZTvNC*Ǘ|b 0|NQ?4R0d)$p .MH͘D)[^/G}BbŒRoj%9J{\VL/CÀF>6RzjgwA)LpbJ^!M5v.`pF|DEe`^Ss6f7(ۗnOér&=Mpfy14jkYQT{[GCMkGdbܙ:Ҭ&Sfu@]Z%Ӧ<XlΕÙz!Q aGu5N4/cFAAJ8]!̞Fܾek!>ZqCheO>8n!/*\kD T" )ޫ3ó[w(0$vފDŽSG|y_+}{/ ꎭ#ğ^ 2Kم`h||[]0Gl8~L'Vj,5wWNmDL T~7:_ o68#" wyXm*09XH0Yg?T_ۭBHengI)43%k'ODU.8 0Kjd%Wjd. j7mjMvj.nۘpiU\XJ~qU)^ endstream endobj 3392 0 obj << /Length 1109 /Filter /FlateDecode >> stream xW[o6~ɀ͒/R=dk3`o[ݧhvIr":$^{sfG^"Ayc0II[0ՍCig`s, VutwE+3yOp8vfT6k썮*=4iYl@ޚHRnb\S.|UtO0Ecmv"n=_i2/hV+ЫSȡoΐ`= :tCЌ~V efK/GΎߥ J-77DǼ8%2 !:%‘ᙠ |Κݛg1y4](LCkҶAK@"S{hAyYJDm3H((^ݚjgEH1t0ʷr21Ң]4*$G@\ f"0Nm}Wn?CQHeCK}c۲ձ:G;6љKV`Dtuﳿ#OD'V,fSb)Sԝ˱]㙄L%TjUYFWPEl*jGvRn5ݒrcb34Kme): 0ZlN*O!Ӎ }Z@ifBtS> stream xZnG}W4/!ûvCq@I#C 2~O5ٴ]fhq6Ttzo2{Cʒ7NI|MV5*eM :c6= {kEk;"66B Y)FP!MHDl3y\ArYX9%WK(XN01ceqI$S +eYcE'9NJї;p!FT>&'0 drTpz B1u`?ce\M*FFP"VQ"8I|IRq&C1 #l  A{D$`x/+&źkiolo{-]0 WjIy7ᤚ7^]iο 7۶u'rl-K)MX~R%v)vh &MAf"`LhoE{ [XW_PW!V!U!]HU;{6:R ^єvɪ]oV_^"G- !h ' CR %vѹsg;n<1B>L7jZƴ3gLaOM0L,P+5 P r>|ߎIoEN*Np}+vǵ'}Ƨ'dM9u1''w;d3g+$흦0P%I-oCGb}vC*e7>n,3p}~L+'mF#d GT$Q"<0K*x=o#@R}Μ(w&G=E[^d:P¾qN1щm=ٞll<~?u:`p0bd*Kpg\1l\/3:Fͺ+!W1Ϗ}M>PJ*҇cu`?T;ba6bdz!x-_ֿl]b}xjK|ԇ'h9o `PfG0?s0n&+<>|coчkb\i`AE.S>e{@黫_}aڒf(4Xgm }0_-CbX٧Kc endstream endobj 3409 0 obj << /Length 961 /Filter /FlateDecode >> stream xWmo6_Ath!c1CJ^n6~}Ji,i$(w)Grd-+rx$Bg,L[+1XvH_DiBf4^F,҇0?! f$qv< J p-p`Uxa$70 g~`.J4ۣGFikR >ϲV!wM)ڸVF)&$i.K~]UUS|&ֵ>20;)3FHp#ҵU/6pWL(9]v%Grm維Ŷɏ(p*qOs{PӶ0UY=Nosoj1X+MD:78C8]9;-^ h6f? w5tNJ81< zrLBH83tӪQL)N%z3R!o8("8=y^4X qQ@ys˺vn4R?1|pG{y$ua_6El)п/.lg8mQ<ej#tfYq 0 X?k 8ݤVN4\kk7g:o 9+v Jbc~ՄV~=-+x56ZtZjgxk Z_ }?9_B_SUmxi'pljqcW>-/G 8N<\U`p#Fw>;fƜtE`n';'Zb on.[ endstream endobj 3426 0 obj << /Length 1078 /Filter /FlateDecode >> stream xWo6_!t(&1#İ}ʊh-j{߾#RR,@ ?Cǻy; eRx)(+!aqeiHyʻi_(䔂"+[ _*qSJ(Y5g5(3J`%Nuzraɽ,q$7wjѝnлVERo%z熒]3vP; J|.A۲~m$ pdNN,rP#&`'YjW \P5!) tc ~3ڳ^g8v[NN,>~ 6'vu-Ju4bU%ZŒ5jy"J OBo~IʡC1* = ׿_w|`$HGf#Yzvv9*NVޝz1$̕w5$4sGSe)s5ԟ=)r`~3&W4lz j:yVcÑo]+d%w@QL(a}MG)RDW饮:O 2ASAMxe4wksN99ݾ ZDɮK1{fN[YC%Wj۫B,TBU꺒&\ҺTayp(mK&L\d-N?4M3E<6uDrfaP : ``1[.@HyV`<^i ~746|h d`0J`a\on ՕK;蝪&ة^n!/CpJyRSLs==it11K'+ _Őy1<0& }el̫~TP=}e,&yZNd ǻJh<>l-?u|-^WnOp#irA|ݽ^gtiZxݲ|~r? R*P2Sw? v| endstream endobj 3436 0 obj << /Length 585 /Filter /FlateDecode >> stream xV[o0~ϯK"k;~} endstream endobj 3448 0 obj << /Length 1184 /Filter /FlateDecode >> stream xVKo6W1"6CZ$-=lb0hȒ+QI;Pil=Lf8<ϋ0RF,kzȊ#>٦A6JF> IF`j˶RU,օg,ևhS7bvnq҄@g⅞6i N(e,.7<'\n?\vQPI]Al>SX٪rUM 7CZ6 ?V,7FP#Ot ]%?׀K4_"Sָm ϹMY"T@AmZ`ٶ+%DR[!kDUky%X! WO{-mea/tKxGF(|z*Ox4$4^<> stream xڝWKo6W )z,Pi]xC0ʒ+QɺE{g-[f 8șfYF~}?]݆1Ihz!gAHQ{dIE~? i$NoM#f-\aw&43/Bu3 &ߚ-jȷV E(Eۤk;2pthDfv|׈rr*B,OdOIL- 8Q\ѯUNKz(hs ZK־@L?R_&#Veʫ!oPTYYyo * lz */]*Q6;un Gw@;' KզG11_ⁱʳԏtƫ(v/AMVyhl: ̞#/S.&Ue]mRy#ko9$U:)1(+zcA=E(MbF ᓟWՍ >@7-AIΠ!̆aĐ}ռ/19/l> stream xXo6Bhb"%R* (kOmȲl %Ozt+!扏=~w 6~]L.؋QkрyaR-Vާٗk R0қpojE^ʬV'ʻZL@bExn Vރ8֝>n&wG}ʇ1bqU 5/A!o/zUzF4fsi#cEImһr 2jI4cZ\U2.+:>c A0LuW mH7#aR(28:F hgWfLj#GaE6܎Y] uUZMreNepcp9T$@,ŭ hGA3/ 0)c3q>|uқSP`Rahd! -]#a  S?(^ȭq}bVIA)hٔHЉT窼QxdI]Xa,Hx,#CF,+@.rBdo]]^ƘiSdYhǶ MpuKwTҺCh\i a6 u{¡F!Zh3C ԨS* iaby0,f4CXNB KnHue 4㠔=YNw@E1l{wo L7Y gcwXv_E4Չq>f]_?%SK~ݥMf]գݟ XQ=&]&LZM}fvOIʞѥWG^$]7 jo= mOKn.KX=PпRKH\Wyzs)LV\y:uFa}u( "`Ȃ.9^δ{ L*j_WFXUb2ŏfxj")]f+3\Ti鿸8'T;~g7*Yoho/5ptwnu?wu|ZLư endstream endobj 3493 0 obj << /Length 925 /Filter /FlateDecode >> stream xڽVKo8W-PH@DzmbP$R#H?CRr,q( #r^߼HfgEQBD(} tmJ2f9TWdq{;~8MAAfd^1J; ljfwX0BJ 1r()=H 0ˋja%5|n^:Y/suGTVL$S{gݧ/|2u)|eF At( p(_ͮ?T!x4AkͺB8|kt5oUO# (},; a+k;^Hy!3iᡪ x򎉼ZYf#IbFm;AVλ2=|Y Y{ C-x MQN@PEd4#艔叼xdVYQ!N7vZg&OMmy'cTr^ێ ֌eqv+7#Cv|;CPDl2֘-Vu>)J o}ʚm͌pʏ~2kn H^-XP ta(b;Aa4 `Y^0s;l)uK4?샜7ihm&l8]laSbe}-BZM\ 4Lu%EqN =/lk]תkK๫ ӹ9KjzMNs 7 y RS!X-X&KGrZŘs`ps7fWlh7࿮xrd&fD]5kCio/[#Uk{)th(^Sz!Ss'M/` CPmNj^,O#~ NG$J.(6[|Mx|oY endstream endobj 3418 0 obj << /Type /ObjStm /N 100 /First 978 /Length 2059 /Filter /FlateDecode >> stream xZMo7W%9K90i"pR"!J}Cie'Um}:rg8o2yc $~qG'WdXU AbʁEhHSbKZ!xE `H)uN,$$O2?]Ah&'zcs"YEDX!9EtEPdH%X )ɼ(JN85Ep(`")UMd“MeVGQ-gMj.Ȫ)Xc4a&xcΜp <)&<)`a\fK,6峗w$#)6XXؒ>BeD0XAww>lpTQpR85ßS(H~8!O<˂fhá|!87O<;q@T{wyk ߷݊o4M ~ qB,Mݤֳo1h+->ɻشI$w`Y>=m׹;c+2UBK!_խ0h-gܼ/i}6\N}x±fze0p.WBQ6zeճbz;Vߎ_O7⠪5*K3>pk=WO=to)j;S ZI9찱o+ojV ԏJNَ.ĖYM 6_ݻjx H_]nb3Ƕgm۞9=sV,Iͳǥ3DtEX~Wi=5,*:,k8 '~йl1 %osӀ[KOʼn\Š춂ƣq=)M2ijX.rWIxc1&gEz,6+|3>\3ȗAj۬C9rꦍsM[\wFUIvo%`_MSs o:H^<8Xy|g7Ќۀ[QxjU{> stream xڽX˷rIsAh- L{UVs}iNngyϏ3!w<=~PiTs=#F)RGD =_b&ïO?<~fUAHXn6-mܙ;X}t;C D$<ʏwJ,Rj~1D Ow*"(ђE Mj9!IjXⷧA7[hUā#S=46~p~/yt4-Aja 5mxm h`iTh i:5S [堵Hy?W' _";#|n8Ww"k_6Y"v^py:9ѶnCTD!~#B bO(Y\Lm uz̓;?׶=e5Qh]ƥwy\q԰_a^|s:׹OmtW8od_оBl(J?Ooڷ޿éY]AP߶O"Tն:m 2aQgn6[\]rkB;a9qMl[C{:f]})ZƇ1h\'w B٦%r~ Ƥ$t8j⃭lWN]2bg;C޳vE52ʖt \iv@]]XAӽ,qcp2+L֨ޣZؒd}O VdBVkQFjp_a/fstR=MtY4Oe0I71FLCfMHoD+Hd^Wɨ@=( C]$H8܌m/rp~GNG:(k3Kq T K؜e2/-EB4lJ*!UHsTAzy:NzBsX,<+;zYd&N}t$ ":D?B8Lcqm 즦Pp`Xdu"|-#&;Qi+r,Y_l\-$0 M8; GyP̏EY✏9eU2Cx93+:eV _}>Sz%DW'֭-P+:kյp B_@ kC Q/Oy4p H.]ܻj3;m aV(|áߋr5e,I!Fsd˝mlT'/P6 Gofx[p//x a>vNf8cv!U2 *\/Q0\w!4+V@jwGw^JsAN4]IppX3yIt?[pq01>,g7,"nGu/B(=;o] Jyc+̌+P np_w vRL3)TC ОEMLГ zJuQղ:X'_V2=1>+!ܸՅtSJ/Tg/ D(C\v߶Kږ>Goh. DbrE%bO-Z3: 'w }yVUa `NzP`C [8؄mƄP+yfGF$ɴQn7\Bu%ABUrS!R}xs6,b\mEDd;'`p/% ^{y/0> stream xڭWێ6}WRuIt7M&m[HCh[,94ΐ,^b )Ù3c=]3_9Rz;y?-q-x+']wǍ|DHT5 Q_}q5rp' 2#Q˗&EUsIaL( $ɔ :m ֞ҼHc082ޢ$PJD z aeػQw/*hҪֳbe G@:#ʯGօ/(܎N{؉B:u<y.3jQx~8(=.',Gs/op(|R\%jje^mzDD9ǯVԈ=&ei~ղLwJoHZ1i#oR/VMua8 #QΒn ^VVU&p@X?XVa:xˢL&]npciWz>s(N+LYȉY7DBC'UN7 2UMײ:&u|+k^s{UagyhYlUZ71PT](Xtݢ2A3AsٕCq"wjTlfE K̭x}nҔo5>rx |=!i:dsCZHuM|bk,?:ip<($na1>JdF9Uv9<:xnz.g#%uE̽*%,'E\*@J{}^Ŵ+^7pD-)wzR1&e\OJ+oa?@bY=C_zu)b͊.`֠xB3牞ku}ʏtW׃C7N\UVBn+8~Z-|2'3[H%SP o3 70֬|m3dvo>og7/޾Y8l"/;T؀QOI ξN|1&Ip7%S=P[{T{sW6vd=]BN |<E=lT:NLIB kvV& w|(Gr o G;xo,7z{xu0@AAxuDA߳_t8NM \ VGzOH &Fxz{DB>ǦppϠ߿45;9yrIJmTL2̥>=07O1C 24(ؓ 2*LoDKmn@GHAĦq)ރacu)Z.pjZ /> \ TסW+jv葮CsHJgАqd"gg#h:H:/2 OJUZVP24ri}J@CG͎J@BCMf^]#𿮶)Ь> stream xW[o6~ I@Lˆ=X3ZH,~:-%vۀa iFV'LIJHDdqG8c4#G@ENn<.if~% %(jUnnmaJNb:C$#dC4$fr8H2nH9肼qtma($f1 ίp@4}p*̫5:H:ʹ{6(T$[b%gREFboɶ*Ga9!38J0È%ј2Q|d}3+&CTGSAҦlV9:Ml0giA/kaI/MYHo6 ]VX?upLzu4fYh ҍUd(?(/fᨫЖ2HCw!lHjdJS:/K\ϥDH!Q`jKO`aw]p bm7bp銞K Kaz[ .5 *tvE*3dD446љo/T 1lH'"}s6j>3ç9>pum}RSUs ٮHOPX:?-X-^u%7mj.vNUѥmV#*d_K{~-oY b h{G(\`A[¸t@$^{)MSv8vS J0^;;ݳ*<;؄kk\ܛZFjahdUwO5/C'z˻,eQx?8MQ Uo|i8Pt$=%ud{sU[/C&%#2z4'C@S{ȉƀ }x'x%Aorww9S5Bpƈ.־jAqxLFETݫ?S,)-(ZV| endstream endobj 3794 0 obj << /Length 2231 /Filter /FlateDecode >> stream x\[6}ϯc(_E6EŶf95ɕt/mK,SEr= byH~O`qހ޿*D\pxD4bDz_7}@l!#?- c?=#‘~t:J9̟=QD(Y@IYjUZƷZmNy$3}"(w?Ûo7vYҼV 7MaA z4!#؈Bʈ!ޅWu/A5zImpɳI$?=zi`;:y .:ba.ہw 04ȅΠ].يݠ_ "u߫kFS FbC؈ r9$ӃәckjчPDimUdZ6Z4`` i@ߐy7 >fIo#=+` W#BtDj{E[s'y[^5*蔡鍔|:G-R*(%:mXC1w#eocz$䆀D `zj?x.ɱ)$"OZ)_W}Utxm d<kCLqU{ev&[/?㿓\& '辪D,ķf;S=P =GZ)DD( ۀ)Px~ bbc7ԗo}lowo<t"/jq>NriJf+?vd^QM@hqg.yS=" Qb\.G/vH6m|Ab(+I?iYl8-lqK5,u|,u>c?#!l9]T:Z'wԈ$K#.U-Mnoєs?pvXśMx8+{fbDfFhM9_"TUڔqR0wbAOxp)U rӀ< /ԍoM`c?A)NlOhs-EH/(4E!6m{o ֗L%cD?.d$.K J6}~=ŤGH;rű(4R"0Ir9D0L-7\#`!FXQ`/C>!4,D;`MQd*Wq֟d: 3n%ڮY8KP?<"<}yI얽7*r Y&@1[ NRa2QMVL\Lwp*\|h35ZI~o"& Dq!:`uE Qdp^\t2vIxsr&Cs$pZTܹ1Me3S9O~@] xJ\ϜFV[s ނ̾V*WJOɀQ+5'>G.6]^N(1#w߭9{Vʓs DBY")h&4/h=T~ǂuY[ORhdc!]j9 $z<gB'Y[WN)LA،R*sUU,d moq l Cῗ~TnS;tp{L")6񧷯?yapW7ME]>" endstream endobj 3501 0 obj << /Type /ObjStm /N 100 /First 1004 /Length 2938 /Filter /FlateDecode >> stream xڽ[]o[7}cBs,i$ l7-nF\ɰ s/Kj{ȔD9w8gfx/K\.]]\O5[OK9'8}xSCrR k!aP\Iu1Fb5M.]jwDm*>Q6´ ZedZ7fE*Flb ٥Y~ԥV@/(1f WJȰqtp/`0I< %aP1Ԯ:3d2jxNbn'}C [6[.$3SsH={Um"L%s;< ߊFQh;Fiif2 K:ŋV`ڪG-oWk]j%582wdݵKY<~hXXa'șșșșșȅȅȅԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠPB 5(ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5ԠRJ *5Ԡ489MCAۗ\;#mOgB;:ZT"rQdO!w!ngq=;yFGT8BGEx={ wv! vޠd}xH [Bۊ7S<Zk\|Hʪw,22bkH5",(w>wTZSطg>]\>3ŏC)$Q㸇qDS=L=A1'\qG2q@04̙' MԪD|qCCɾwcW?hs ޺ë bBOh秘ߺ'Q:,Um緹Ԟ՘ytvHkbV9Ünhv$d$DuDO'HxAcD#zl_w9gC=v~h =Ej0#/'lC%==LƮa3Z$1gƶ. t:H"IfoljlX:Dbβ$admI$$q ڙK!Db0kH(vx١nvNN]]7۾߬qdA  mqHZ*{l'ɡ!ٽ{9*T~;A?!b;*;YaO+Opj"QuĬnI:H9nN8 1rloN-AnD~'.\IaȹKVcz;EA&Ylv 99!Į9:?339aw{ 9SHrrNM`gO+Ѧ~w5b endstream endobj 3797 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2075 /Filter /FlateDecode >> stream xڽO$1hT%bpH9ž-9$bL`טOzSk };jկ*izźĺ&%v'55lsZrT1OԹŀ 'o %F(XKM׶DpOL۵T|H_nI?}ʤL|??5%gEP[@qz` z L)C p~Ap2w?P.W֒կr %7T)D,!1FCBn> rh<$dk8rԜȄFAЬP9zQ_@EZNO\!^1#Y<.P+d3="5}{4\Q猖U!Iǁ@JVQf9D;`^0wD&g-2㐨"F6 0$G1#f D19BdqC۝Ayn#xRa[J^FZK^UGR RnO<Y)3QsFsCFha)zz°R% ݄iNee?6{w!S@+w!S ^;á3 rgB9e rgur`0drP/ɝaHND>C%rn䀸 29NN!.8: PN!!A+r@ B r@Z9cWh`pPL ͸*{K{xǙ+qz_y>s>JHG##;rdP9JFToRŞ2ޓX oNE*`߈W^>|!#.gk۽j Q5fKr}D9eX"9JxMwH6zt}H6U~@˦,x宵9C}@A,MeCyL#Ұxw%^`i8&Sa?2\ H88v1#~9 endstream endobj 4058 0 obj << /Length 2278 /Filter /FlateDecode >> stream x][o6~ϯc,x'8= 6UIQHLHnQX:<!. wOw7?.V tww'+_Euz=~WxOU[>p Yǂ ΋8QPM'&RΏ I.\"Õ#8(\MF MϷm^@}`bc/ !a >GRHCDa:Xʯh© 21@ձ~iC6;jD}VF+m@^ny[(f}{1 ;Jh_.Ej]#|*.2x':xE,y.B5!0yWTz$T$tVtj_Ipv!bM'b:f0!@%ftHD*m+TĐN-H)av(nϬir?Ed?@oWW evMe:=W/!EZ=ZTCgsu2#3ԗjqn}i_#q۸8܀O^W|6kMb.$+r;YNcRUco@%qrVg휛N&~MڑcCWl]"?j298Rg䃣C>ku$+=njpEa>AgF3cX}jZB(L&dmHG)Ō* B%$?QlnO(.a(s)\5 DZ\UR9e&ԡQlFQԧzHui2ns`7@_29%9J4хёeuaj洱ضZ4Z..^\?ޅ/xa+ē!t|~ƂaWa$Ok7?҄ KZ>\km_{y*&DIanw^/u ;Puhty-6̺C\ƞ|[UD^RG:~R{"B&mHV`ڰj耇F)"1_it7OdO+ ̤p-3/,i-SItOɇ]"U1Rj#z6V ۮ}9R>>CD/}}F;?v$ӢL/?-L>d+[r*X,F{C+=io3Z x6]*dӨ F;ʰ *"[VSn2 {/~qj*wchHܲx_mzաLj^sRF+P}Rbب> stream xڽM+xL.=bmAI,:("0bTq΁?8;/f[,{RbHE%Pbo%{rJh տ1j@Ok%LfRq9P~\ TzjYK[_b`Q-YJN*$k({od1{odPk޲$8qHu`&7 B.Z9a RRsfQd5H@vD!ޯ ]K>l|*IȒ9\/hEWZگ(i(Կ sBI\P2GCݾޯH(uHE{Rݝ~n 6֯..mPUGN}$MQ^mG#ceJIɭ6Â)JQ\džϯ$ :r"AE\BOsu PPV vq-VԿњ,KMj3%&iӪpͯZG%j!L|Ű]m?|H$ (WfYd͆|,^~Z71ZK8YLHa$qtEXsʉͫW769?ON즨EŖJvطիpzNNß~Mon n>a"@d_Ж\ݤm(.hIOރBuKfF! B7 D7L e#jʺYt1/,,uN{KC~1|?t$3B(oѧcʘ([fi1"}d@xv%߭lB V ?$Zbd@ B7B0llܜ,dັ;Dҍ}BH3^il2?e",T$Y\6KL+G MLj"ڼ+2D^8Vn7$9DY aF-]+V,}b +gq,k"@$˗]Ҡ6{/a+Ⲭ̗ي'oHtWe]9q'+ݯS7ÊhҒJ2GKи>]eWtɶ" H;Hs>]b9C].w.m}!Fe7X֗v;X^A֯BwsCV;Ĩ8b3k;mnإi6N1iX9Fi,vN)0*(`9[V;:(èszQ)Zշh`ZN^j'spN[)8{yiU|& m%=: d S#16)vI17)vI1h7)vxtƠGXmRvڥ戹KK>)xS57[_XZU`XN!>m8JUbGwD*B1ǔb I&OK_KA!GmxBffM/^G/Seg^ y}UCfqbT5SW5;Ĩj+QC:&xԢ^Vx;ǛCxxs~og5!c)j{m,= zK/=m XzNXzb(‷v~"Au[Fs >"QD̻=Jb]v49Zq5k_N?_]GMX޹/`]Rľo;Kio/t,?S 9jP?wvpzc|kws{u>d_>|+?}w[補5[z) _}Duon±&FЈ1j1Ah0 A#QШh44LP&( e2ALP&(3 e2CPf(3 e@Y,P( errrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr2L`5D &j0QS(+ eBY *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ *<ƒ:<(qxPAk$4FAЀ2ALP&( e2APf(3 e2CPf('('('('('('('('('('( e@Y,P( ÃהOl*yCL/ Yp0U& +_paJ0p|2\3rA`}+_9 endstream endobj 4060 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2013 /Filter /FlateDecode >> stream xڽM7+tL.jE 0$Y`w=%9 KTKJ}P5gGdՈRk)7(pQo US * dopFxKQ-ohtIf&o@Z[-Kfjf|t}x\b I1ЊA{Ɛܒ:H0'o+e\S1~g%(3`\ܬa!;jȜ{-d1XR$^X+ f=RoI?ѠTG=;R->ځ*צC >s(-`|5[(J,*H:`\}nh|epi_KJ^Ta)0g^,`ok} ms{K gT(Q8TbNwkijB}F}V5,| R@{tJR’S*;'_N?paJ*|;Dw/ 0Jw7K0]"8u4&Fc^囕sw+lWa4Fc7 7 )ݻOC}o?~O!|Svÿ?ٸĖUa,(L]nӧpK!V:|m`_&i,0ҒE0!%0M'S1C)dIcm)F"a `fP@@K1C@R{&Q΃Rl,!3:k iɏp7’`0XL,@Sq;špOE_3 F^,dR E'V/lsC%!vF0JNL] a^D5nRHV D -P-!.P Q 8_QN"ѐ-5b@\# N|ֲ^UN HxA, [̀fI!rAD3(@A i@Aѿ?c3D5-fL µK~C$Bk~MI5= y\2 + A0?QTAS'WcbDGk[S*k,1]B2B!0si^g@;sq`~2GQU"3)Tq~(gl0~{I]dAY4tZf =%ryUt(dC)G&/{ƾ8'ܙ#+H3P ܡ>g۶&ujd;񆝑 A!1Keb%?HrpKR9+ _Q"Q9Yr?@g]@u5ub1K΄%DbD2K rB \2 r2L\A\r rBL\B\+3!B-_  Uv*A1r5 "Aq՚\VB jP1gliVH4S+TQsˎ*j(gyU'(jA1%΄+Ŭ=AaŨ>AWQy/)rTㅦ'(~Ď> stream xڽKܸWplج& *3AyA E}NI:f/(jK"&T^SIҺ'8IRũ^(q_q 4QzDj4E㺟mlo'raQIԪ%PIb ib?Yb>ƿyb7xJ>uF qy/hjރ)mq~oO*۽Rmkq_PBFhb1bɨ 1d;d+=o*Y\01Bʩ~VReTeW-UcCZ1}M=UmTړTV֯Q8Neܶ{M{ X7K*a5NȶS[hvM5a&?D5mS1qfF8C0mw~֞S%qS9#NB~>WZ4bkĴ#v-"DKHT4ýDqmCF"Rvz#/@o! zk5t18 11o"jeSX`h""7`}fmZAhfCGoBU>>}xߟ?_|?}ӿ񛯿/?d|'>|f*c@ } `%Cs+0 ygfI_9<̀6"@pG2FYrʥȔJȃxڎD2Ju!DGmFDRjFޕ2aOo)B {{9! 퍁X1~ZAgcmBV2$+6٪hI1՜bej=,?:FΠYޤ Lw505Sv :k:K>keo@\PN<JHtBš7(V*C5ߤ3>I uH20R2nnfk5R0H,)La)ʜ/Pz*OUF# Gpr>wBoʠ #2gK1s@Jy(Z{p(b)R}WȚHV^=i TKFQp"9t"LTޓ{ʰt cz >gXQm t8dԨTFxSl``$!@V*aP'E载ARXRA42"J,*7QoQ-7METf #!/p( kRbz@XXpHqJ+t±1ynBqJOCs*- % ,ޠX8JZY= LF~w8|A7J endstream endobj 4348 0 obj << /Length 2475 /Filter /FlateDecode >> stream x][4~_Eb~E<< mf&L')Iqڤvj5Qb󝫝pq߾ͻ/D (ۻa`4 ⷷ7~y 0!PACofw<}>~YKil2/d{HB(f8`}L#|QԳ΋8z!hiVEdmEH· i؏7!>%Eђ :8(GbzsԿYOB'S܄Σ _!igXK|c1/V_ۼJN6]a7qx %KxY?k`MӆqIA:a4=np*ܔVq*)+ٔOj5l߃hZ50c)5h0 M@)5h+ _` `A[~(ٻTs8ٰhѿL Lc24J,~0n%ߌI uHsǺ8" \Hg0$5Z_q/~ sb[CÏz %:Aȸbe-d!k}ir_,$$j)rf}˘ ]g$!)A^z˸xPoD pȩyS"XR8hwHk(G6]Dbsy?5 J0=utvkGg`/ާ 5:6t/DžxC3*-؅a0΍;(<!P.]'ŲAx.Z,qJƹ{M|U0ι^y!=^]΍X01@Ag}yo?9|aNRV/J gHR-iғ}lKW0"BO a)XW8Ö ʉ t tqt{B ժpB9E"'E-ЏNhTx2X }}ap )c8vuJz0Ϡ\O؁җO1 >Z'hS[(ez?P(|P8hL6MƮjN ^1ʤ(ixJe;hhu1Va H]Ʈr+=%#U|_j/8S"-i:\7l6)eVdeݥN*BFeRUi֊corVis HxrP|3'< :h'2ͳӭ>n 05`stZkqQÒ:GxXxSxxjSZS؞|)ֆzn{1Nb|LsHms8OqUuVRF3|N &)K}Iߪ? |wΡӵ:GrQ_4ۚr׍\q1CbBJSwfi/VyZ gԂX2#y=ؑyMt|CJOK7Rk(:}oHFl*|khppݮ7oLNr endstream endobj 4062 0 obj << /Type /ObjStm /N 100 /First 1029 /Length 2969 /Filter /FlateDecode >> stream xڽ͋$WѾdWD',Ii=W3b?@"^5:ls6{嫬EFfU[%lzALт䍎Րm АPyCC=χR _C+^*x[-Hz&[$Ho~hPQ?O,q^ j$чAk*.CeWIsEy`){ذ|>+*T.zoࣦq^QCGy!)G`V GS吥6ax!^8\1`iKmMr>IC@׋΋UtTHoF J7zäXapvC\_̆[QsqW(Ur A6Y\փõ>qQOw+BA#R} Fls1xI Fr\PEHUEzj@WMEQ[wQM}޴;Ezik 6"Ò|`z7K#xzb5w ĻDo q`8R![*#N:zKuKj[vz_dqpg=q[pc%gܶ/No}8}}N=~ b{w/pw!&፳ /^ӏ721|-XLsSu M4 ^`B=FU]rR\c1~l=$Dѣ4 W2{S"NlrؽdPp] yaeDXǴn%!'ʈSOU(L+!QWQ&rB [0uf(H>LY F#˷K> (Ʋ#mvG0XY]ƏXhzCݘ/f})H6!|yk3^QE' -&JKݓVLInD=N=s{zXz/v@w ;S %?ߺ9,Isfb-Qك:!~?D̘(,M r1P]̄BSՃ&r7Lg&ʊH̨bFm75vQPn:7tHD>7 .mc@a&I܊TZ.&}|`z@4PVa:M=<,a3>I؏26qcJG,\~=,>Ӎ}=Xgnչ<<œyxCVk&- ń@&N^IOg=gثDe{UҐJ}OPL')U؟*(z_JF6&xZ9_I0멉{ɟP G$>BF&"Cx) :kw&^zt!C8 .IH/ɯ>Wsٿ m? CMe qzÏ0N_ 7}~?=z!o crϨZKhIb?BK1؟xsm v{qQhlQ76 eʕʕʕʕʕʕʍʍʍʍʍʍʍʍʍʍʝʝʝʝʝʝʝʝʝ}W=6 c#(lT6T* BePY,T* JeRYTV*+JeQ٨lT6*FeQ9Q9Q9Q9Q9Q9Q9Q9Q9Q9Q9S9S9S9S !Œ33 ɠA!B 2(dPȠA!B 2(dPȠA!B 2(dPȠA!B 2(dPȠA!B *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA%J *T2dPɠA#F 42hdȠA#F 42hdȠA#F 42hdȠA#F 42hdȠA#F 42hdȠA#F 58fTMkj"^ Ƌ>\ŕE…!m׭Dk0^S*F7IcS+70ӄm! OMԅ Cfr1rl>S+0/J5'=,DA-~ۈ= fZaf`9X CQ22|ʗ `,QQpb"&s+Wg@ӐTF3-͉فhB 2h`ir(| me~&,Т)ZoG|t(gxinb8ckv1aHksKCҐ8Ld'Q)VE4\ J_:> stream xڽA$ +tL.j"%Xpb S"YA].Uŭ>H ,vj'JU9C.6?()j?A vHZBV8&[hk# Y%FQ搵:Dͼ\-?@A (5w`n!_AJVg' ~TCaWPDyJC8P }@ew\Wb+4K~*M-l I_kArU 1N΂˄{LH޸W0BYX)PS)*ZzBe#[@rGM(T5oajT8ܣ& F,-4aK4Ъk-4l4M#Y)(@ Zةp[ʭ'-A,%' !A-i(kleg[f_A}@Z!?wo'.BG]eyTuEojo {c, ]躧 Ih荨 ʇ]n EW7ިuB+r>!͐0ۇo~S}oo?}_~ΐyo??>5|t#%*ń.!IQMq޷Çp>/]/?NJ | (DR4[FЃE} j (iȔR ,d9z2OC@gS8!O!te$.բ!1 Hم;]!>SL)3`Fc%1iؼF(p$<Х.(@ɀYQ>%H(>B Q1GD!PVE1 jbhT^5hl!DPcwM!4B{up@PN3ʼ j(PF9aҋ̪H^_)^WD/Ph"ȬpK f@1^w cXE ЄWXD_pyY5Fj GPTScK.Aɲ0Q.3DEydF"Ah]o'QOVA+`1\'aa5cL0ajO@5f 8 %;.@Z ­Ae֕,A%/AJdCMAVI.6b|IbV֖}wd`崅EoR^ungo @ƭUr%EU#h8&)ʷd8&9C] qTSoȏ ӥPzגl/ڻ}8j^ C؉Gʹ0[F _D @T endstream endobj 4351 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2090 /Filter /FlateDecode >> stream xڽAݶWplx9r<p UEE6w37`ؒ+~"RRHAr@L~C.H\@C%ԖvdZ D-H[fՏr m!q#@jr $#'Њ2\͡2t*Č.qTxoYJ.- Q%oD~ ~?_z;t^GqZ7fJ2BRSוqq"KVJkU{W-VZ kB[ q`G# W t+sW`u JJU-$ tBDؠa.S)wtz#g5FEA)Ə,E)foknp>/}7o_gB|_dJ#eXlPd5`0Ȑ-i2H8HPѼ% ' -zB` 4J]Fe! UqB$@*:a(!r VԾLQ@|&_|FC'Df>P Iy@`^{D Gt62Q5z\BLU-vIg|M[l +ȗi3]#KalONB9R*rL"cr9,`6BM-UM+Dmct }&d'r8i݆GfNaNUԌ%4T" KF>b KBM[G'9"U<2Vo3QX ubBD_.!v֮#cO0rጜ=!I{=F^Bx~𽅑 ֣|UAVE`Es0o_N E`0lŰuǀÇ"e|.Jv gfbc2R/S,:=)^,'3Ub%<z,C+I$\Al P1ɃȖ+'ۙE .K=JBHe@sPC '|]2I{xPD.[eL&MܷgR+ҥNY)opψh^+ jbU%Ѱ3b+Q9&ĨK>LQ9[bT% c2ʱdh QM:D#F[c Xm_E/l.QFB_#lAZ{ B*qTQ NbVD;aL#>+Uԇ?8(a-(vP;fҐU掙&ObW-}Ǝل;fKvw?Ǝٚ q1[2 9&CC"^Cm;_1Ob(gB^.DyN)ډڬ;%Df4f6QCX~W>+ؿʗDMa&0VKXMa[0Vbdt3)Yb%cQo[}l !Ouojfaj)لfB Sw1'05Kb q75k^Nk\I{z6׼g*Qz0`(,NxBk0̊awpB"Dд~r!GYg: $υ0xh拖rM$vvf I~fRrMOȖl4 wjWQפ5M!ΌU!NFR+! bѨG[p4Ґ+2\kaN|yL8_șđ3Wvb%9a endstream endobj 4640 0 obj << /Length 2455 /Filter /FlateDecode >> stream x][F~_TǪ٨UՇfVj+cCۘ5fkEѮ"9|9'l5sg~x{# Q:z7~aGuOo = kA΅U_&A;fv}ya P)E{_<//hz&}{Y=Qߡw*_őxn K|S,p`5 7oKM0O.ҡyjop}כ`{"T_GDwт+#&=NS hw!Iej#= yD):Sf̾3vȷo$>F§V@R_W/j@P{\]_ULFb0dm7ViÀ'<;64}3ydXx j~E}aWeIQ"xF!ExJQD WQ";a0(z'J-d&"_0jQZWzCm/.?%zڙJK\-ysK@@NV_v4+b% 00\xʹ膠CUQ RDiӉmuN j0yLBdCv(uzF35 ?G&*'j2@ +Davk&!3eFVd_4.oc'z^i$&^E%cfa0\65JobTB7m Vb-'՜8ޞ]õ%YkQS-,aE|wR'tf+1s_Euj\>!I!2r"JE_&&Cb+Nvh&~Xs1;Xf?ܳ<̢e lS6yO qps0 R(vIZr_X/2Hfg,tLHp$Q4h.`nӯ 6QvƩitY>Ջ#+;nz 9U:=:Tނ/"u?!P!ſ.T!b0,ںm O)*"\Q$գ t-ڸ;M!J3̈́}<ث {y F0|pPcI`"SށMTo{+9ܺ͂7D! H0y,h{(|)MD|ʑ/]< COb~\ *&mOv1#2Ԕ9-m/GDnlyпUpi_c53Ojc[ 'vvGO-XC:żH7B{U]Wrݣe ]Y>~ M͟7:ؾxAiVSΞB\~%XAHW3/9Ky "TeYQy y5'[.P+%ўcns`GJߨ F`A-3O0ԀkԽFIO<^ x b`|:B_3+cp{Wa|0֕qʃ dLy^_dAod`SԖ#jPiCZjEay5qWuO2eߦfwneBkk^Md>>NOqrF 7&]Øk(K`䘳 vRqZkO46p{lPv5]v0@I/Iz0?1E4Vѱ zW=?@;22 q?,M )7|^0RzW>C?A;SIۨ$ԣWx'[q';0nlUJuy;ENl.`G;-x"ڊUCŵO>g$g=kd,v :=R{ԞX̶ԼsW_whB$!l>sS"Vtc̥Թ]a07 iXJ5Ty3ʠ$||6Oy(xZN{`{ uu:Ĕ͗!8 endstream endobj 4352 0 obj << /Type /ObjStm /N 100 /First 1033 /Length 2831 /Filter /FlateDecode >> stream xڽ͏W\8*`$@Ny1>ϫ~- ^z5Yb5GjJ!)\7J(ּOi;$AxC]YC-r!~4C\l۩jM!ѽeZB)f15BZGIp[r(m;ZpcRC*]hzB*-TKP[F)[Wvy֊m-?T-~ r6ߊ4_QAsCƸ䠢>jRjwA[q\Q;X6 zvޓ{+6RsaTJ%#V]R{\tN)dV tct'LLN fb!ϩ 7b6csl|M5Nۨ56l=f[ny; [Coy+DoQZvV4ފFXGoedAoP:zCHX7׆ʘ$b~-0K :Lo.rQK`XZ#jB &oi-&0*0Rz2ƍHrPƄQ c dŢѐ%"\aFiM]h&DSȊ+.8v# d@O4+ׂC kd.pVRZ4!Qbj B:34i Ä _-JZX &#"be\.ɘ  ^k~(  A{f  1#bmQW6ke37E8VĦ),=QP0a'|nbih6x^ej23S_bt.4G deQL(XS b7sGw;y`=f=7a3kc)o9i|cf'D=FE>UlUb.V&}p7f__9xSe}wxJzBf=!Qă=kC/WPz :[ѕ!XU:!]ʗt-X騘m1'AaS7a> stream xڽM%WhltTU*}`p ^ Y8B`Ƙ"o%ٸu>#c.:Fr5=*NEQ]j(\5U!kVDb=:zHuFQ[qTR{:)Z䘓XX9c F۞\[ 3l jcpqBDD0Ѣb'h"Y Gc@JnŊheb^r{1d NCqC)<Е UTSͶ1;wnuZS .KlGe${`Ґ0FlT]8MLX,)'F,v|<< &u@d K{/"R\&]xӒ|RS@p<W))Un⇅IXNjjeWSmWZtb)jd(&@|C^%L/@CnrV 41,at: /@9( DچR롚6)Лơ?H@K'MQ*Fdzd Ѹ 04HnP o޽=~/?}~{|n}{ӯ??gia}5ᵯݻw{|O7_~_W_g 1S'36PP[uT'[X%F J_Ѫtrc00t^kɰS$aCxav G(;PVN`b;&45 E)B`x5?^F?M Ek@p &[BluZ#,zb2d|]?;}a4B$E*1K3)0H'54ې J yb*r9H}%Y]r:'[w. M ,GHHgxR(u/v06#d ݛJ9O5 9쯍H2d;D[ 7YQ`.!{ ѓ5 qg W/'C_Ebk/Lh!.X(;ځp3N{vt+ c@ XAl̡-!Jq6!zH I/Z1۽c tRM D,Ʒb29OUn_c" W鿦V-![!ZB lՀjqV-skmՀj BaYy>-F]Ҁ&5/lp %w93Y !FE*K3zRI {T/q ylD䟐<} P 7Dnxb~5)z%0`k7ѣ:J(*v'8]BlCGD?Y8L#]ClU^aJYae;7UjvEi2 !] qqBb`b`v`_^`QxS4!+kiFǔ@7fOF'QY2\Q` 'bd1#sKnJO^`_S'&EOWl8 aDM5?k 9EqvqPkZ#gVk1%{w;%D&Dϲ+,P,ْ7OC^% uųdp+DÈW; ]o endstream endobj 4643 0 obj << /Type /ObjStm /N 100 /First 1014 /Length 2043 /Filter /FlateDecode >> stream xڽK\ShltURf`@*3;Ef08|K-I: ӪJR74d-ި[7ZP)Zo@8x):}*@i ԚK)d- 99d6 {%jK9pRQe !j=N1(oQb. * H8A a=t@+Yz/Gv*Z޳{nO8`"ށ,JJ +JEWJik(ߞrP2@{.u,T--T&gWR ͡TPe˅ Yj`].ԂZLӚnzYCK,ǐ-^ M3W Ъisji g(CœQͭ b(I_&FˡTk jd _- 8) S&>y<6DjY ~'c|vjs79mAZ>E0e&Hօ&eЄ4N>M Ç˧_|˯DŽȐ?#oϯ#I Hؠ^R>%!旿)߆{ß  pvALT)֠BTL<1ט[tyUI3=`JAO w[ lL"X'Bg@R9gJ-zJe(Bjj6<8 R"H1! Ί @P>i5;DI&"FAP jHw*E*)`^!mxMDGz8UL{1)h[|0 SQ?qI)oDb*O0̠Tv)b'ΆH(Ez(t2l#doCQL' RF7v9B0rA|*ך`X͢I(Dm=+kBe' dBpU`8%%Vn|2T!J_x_q,C0è0 W 24=X (%vA*XdPUjQ}/&ECA IMU宋'@1)fMh57m]ɪIw{zNYQl)2]ਢHҌQ uRLGS:(n)Q't=EQ't=EQ'p- jMz_`Q9]M}1MFe8p͏~:!n!N%. J7K|T@YUH5k f*6?KJӨ/ըw(Xk}'S:a1 #JYJ{qr/~`; kww gwo1{1bv"Nj1j qt29 WP^e4 _PiO?E"Z{ڞsDu DH?)yǀ%[Lr?,l1f^#k)9(fE=ĪY㣃@Idc t2L1\ 5L^PP₝1vv&Y3\ J:( >y\?.דu]yQ߯s{WKQ"/ ٣^ "' ,S^CíKYO8L b$-xy;ؘo$ פ#0d}q@kzd(E #u!VJ] ɋƱY[W|Nk' $+}ekBĂЄ y\v{ăX/lUoĸM86тo-?e(|bD,mvXtsqT0HhgnL  a-m[7wgLsN 'odg܍fJOv/Y?ٙg\bsrKH endstream endobj 4717 0 obj << /Length 1082 /Filter /FlateDecode >> stream xYmoH_Vj6,UNUU mFŀ`olH.``w833,|%Bܛ/=P #Foy??=⒩'H(GaƶK">X~?Mij,uq.tI4LԚ/;c8|![ ?P}"%}D8QcA#>`GUk]0 $cabق04|rNa 1L caF1ּaLa%55Q/\! Q!W^$l@RT D/:K"S8BI!nG5IHT8H#i"HĮvH(ScLTtiq0zWq`1lk>ICA!ȽC$]$ރv]n& {SJ!7@MoBLnb'v(j;%=I[T,Vʏi]O8<鸓0c(trl^d$GNӪDT :AeFyrVStlal/G[$~unƂVn2Vi&W"nױ5e0a%Vn[O &SbruNlYQ %vq2A?U}'GcUz|<k8@L"|+v~˻ r p0Ab6bijDOϕ0S&q C=|ГI|k.TcB4aB'3˷W?h@sRb?]v2oy:kWMǣs_q՞Mf3\i$>k| ~@Χ[L -˸(5l jzyq t5w.g H=?8ul# ,2%S*[i"+@oo"muݟasu7L2I.A@|ƌ*G~E_:O_䭍fxu ^I^8ky~ )= endstream endobj 4644 0 obj << /Type /ObjStm /N 100 /First 1018 /Length 2453 /Filter /FlateDecode >> stream xڽ[]}_яK߮ꏪa-=L̮VꙚk?xf!}ĪgO}--BidkH].zh QپN(5VDݡUxiZ-PZH[K42] #΁++q ,1ޠx1` =KI!/.n}dYV#[7hWj(7r(, ޲,&%RC-doTZ݂M (xګqXaWc j} K2ZCb\j Me|C$t@AX9GJ2<QBIE#ܭArd}4 4hi%CRP1j3tS2ɡS6썤^1bIj !"?M"@%4ihLMyyLh+tꐙBIL 1^аBQ!N⡵(9ADu " 2ۢiN Cx YƩgZx̌:zRzGo0zh ԏ&zc/0A3)4cBG낀2ro.o4.>~ӻ_rw4..| rX"<f5PPS#((I$P$"1E-OW%tLhԃ$Gϰ\3A]zi0w`.翂J "9B?_=>< Wl1 zz!%hqO܃}~*\ Azxl+{Ϗ_>}峿Ӈo TW8^ Z(M|^a,_z67[ꍾ6jyQՑ#WG9rs͑#7Gn98888888888::::::::::rwݑ#wG;rw"*Ա6 #####ӂ~N`8 ά5 &0$ɖQ2(Vdǹs*i.9j+Z#33$WbwbKQzLbfdeC 5Hp$U"nWҢm`}&/P&RgrUFOz@`F7= Zr8r8P̡Q ѷB ʥr0e#Α9DfI~"+D>&sD[[!8LOK$O S\Y'2ێV(Iԥsp<4L]n,ւEaS9mh40lRJa[,S;)I;KNp8$!9x:056\$ށ![XWRws$1[5qKd1[s =^; Bǀw4Himo/v;&ˍ#Sv G$b]':fQd~eev4*E{R" B'mO)'6- GeV>we;\}#DbM3QnSNف AVԫ!b v!XTX̊|e? Yei'FZjgUwyH$㴧]u`<)el4ŢlȈʻ1[O6K 7vt7H(vvAşVNd9m<3Ij̩,OGw5VyHpO> stream xUMSAWQ/cz>`eU ԰?AU1BwꪞyJ$&#;d2TRxaΠ.Ơ8<%+DbT%tP5`dVu`P!XWCԉE4&un(Q30)u"#ub 1HWWbpC7/dp (FчPFC4@'s J'VW0Љ%XL ,,&}'U&U>WXQ}V21{T*\Kl>BG}XF֡чqj>2 viNºNRYG]!> {G T:Hƀ1q>@ʵhAD>E:"Jk`AsbXtDܼfxlm8D+呇) nZmstzH`V7D>hn-M[WGtt11tsuQpБGfN|{%$YщjynZο`wns\r<^,:|v֘qҙ8#U/#@tr<]r|~~M;h\y.É0> stream x332V0PP06S02U01SH1*24 (Bes< ͸=\ %E\N \. ц \.  33qzrrJi` endstream endobj 4726 0 obj << /Length 122 /Filter /FlateDecode >> stream x-ɱA($ \vTSHB $:@\#Q_TQUE&MG-nu8M [Yð,ΐV]'v=WN;S3uz3x:cE_ endstream endobj 4740 0 obj << /Length1 3100 /Length2 22727 /Length3 0 /Length 24274 /Filter /FlateDecode >> stream xڜT& qa$wIA2;{sf5}Kn(HTM퍁v. L<i;{;g{##/K6zOAfb)ԁNΖv<:\@1#+ o `f0sp>Xh40T-N.bf%{ hgc j}!*ǿd TX?cI' uF2$c?A)vJhkdtcANAϟ9\sߌ.N?T&w? @>\(2g{?k p? C rE7g'/_ q_:6_(elf @ W{ a|wIk)k_vC9뢢{SgzL?TdԱMc724i@0*V\%W!OG4hbPq2^de NᨯN}:ˬ[+͒ǣQגĬ$S1vdM؋?L8{ .4€<tœ>βs+.J@M6YFj.Զ5s TP9̛{ yF#I9# Sy cZǿ؄"FT9T7yۋ%G;CAi2uG:4;?)UbNP&D:<[[cGHh\Ié`MhO[3;wYK8?R^ B^5e7~2ks<.;'߈[(uݨȂX+=MXܐeH`pBSs*.'Ɗm;Ix隍%&ʢ4%?P_~i q8$*&})x۞#ǫ䕮]. [ƒS"nS}q}}yp#x|?Ww0wوV*x`֥Ft׺m~#|}O}e \S68*dF3bxotMK";bǼxŦ =&)?x ٕ9 1V ĽwC)qRM<.ՋCWvOR1_upO_NtB#Ǵ-ǦȠcWKWPV]TPpA\{=͟Ύ:5UdH|?G<wR<-0NBk"m4X(ŗg}y]`?Bax~ٶMAYKY0򳆇1O`:;SEx&eSzmvoZ5k)Eo WocF7w)9Q%=cɃOuD:?NCuH (u=kxjVmv %RiZs76__w_UtzVr2G5=:R_~di|yKMm6kZOY^9kh4X=ъi-Ep6-dܚaoR6DCwc[)b}lEf!4u<$1_[XUv%e4/ ֻTugluk:A_MCnp M'g*Đ1L5S[hXE(BDG#D$sBx9ho 9gחB!r"ŀw 0a?}!2CC6wf0n6.2n (9xJdt|U> mHF^hect&M)"1JLɮ%^PXkNG9~ڳ#= ^HiQ/TBsHF\{a~f6Xw۰L=SLؽՈa| ;LhI^v?c Jt{J۴8\f7jfaӕ/, 8Ύl"w!dNYT?&-|#zS۬jNCa3kGy@ÀMP#a@t:+ xtFrGϐ !4SqѬ_en匰t?{ɧopMRVNj1ftDd5Kncq㢉v|ys_K5&uuVHJϩ!TղۚibB/SuPY+kú[چ)[&Meʵ քw%u֟>axGLι?ݍ/m"n>)Շvx^?f4'&1T=ig.Vw#zViZY;L{uXSj]uZKh ^,1UOW e;l_:;^r+">$MYѿ,c䁾El0HMݭkO 0ǼafSDJmq9be/e`EbMV5 C3G44"bGfO@‘V(T]pm/T mT~(-sCܙyuL`*s5rMl1{7xlԉ 47$C,|,d9 \%D4s Pӕ]-lL|++1cNLu?1ut@.Z\9X+q3 Q SA4 w^HWA Q6.VЋl|7`û>Bǘ{阄ksUKl0~嗸'R=RvSeA!3p+ ׼*OmFmdR(?m̻^o*q5bGjILZ` !!AnIekx'Ll iHB9U@UB>A '${OHs2AćN4!05#:~%'RWF`9Z GOsrV 㭌Xq58fE|njy ]&Ap~6: |e=܄(vq̈vR/=S[)۔s !?֦$*qRKN\M-?-4mpB< yTN~Ob##rcXc~sTФVuOF=Ao!meMs*^_ <8ḃ&~Do1Y \*]P,M,NK_X~o:^2zº͡v ^\d{lKzC.e3 )>X9VIgȇ3i11Il(0]BFVگj X`x<ݙ`aR ./`@M莅9_x2&1 3Io 4j~?oͷc&$  a.Cf iFΨ5gLWly@ǙshdUqLIAΎvV5q&AWomqcxVL+v]+QWiQp n YnD?^Q&ſ][xL),euWW"`Dap;>`?",$6Y% ǰ%$~VԻr+<9*)p: &?k&G7cenߗP$4Sa W ^_ /CqJAÔ#U[ESR/;m`XS9'oCo^c yj ,fua<MݔOwGC|m2cHwlш] ܛk8J(x6d[o ()UFi]g[W)@͹OeeNT5W?*nZmtVjo+eYrRP[/$$G4NW]QԻ1J~A'߫r"'3._8«>}B];h\x!} ئ롾*j۷a‚M6Ynl +OSQ ?3(g-~k%ЦpFmr|,6_XiͽAJ,^G :*7}( V,<,|tGkH5S!q%y9D7JVB?XwGŶ{QHq)9MZs_!fs΄Ke`w+rU<)pI:~fǓx= ^s _82 [1?BlYdA5ޠyVzpmf&-_UImN=Ss8)SNPS#ƿ;QG0) 8WF[%yV&ؐA'@i+\5nFNa{uie&q,xGWe}ea T?/tg+/O(32#j96^0r/T'3|{~*s2˕S.B|z4їoL_EiU5Y;;bXY_y ՗;8< GOt*#O UB0WRR1s}LL64IkiDX z}Ϗ=utjBamPDjFZZ5a8a aOD`ְg;95ʴ059K ma4D w;VAs)[qb^w<- 2Q'S 7q Fr?D J_F>DGaOzXөΤ~jATg :P}>"ϯl&'kY*ue? vf2Xɏ‡翗 ȇv]*kDNAсz[B~`.曎A~hk)XqF^lY 폤f36sKs=$\E6vB_ lM\7B JvB1ޡܛ&(%"wZLFqG>~p ȆŖy#-BUnw7"}Nl6.rsT3 e(+}%YXzg-\86"Pa+u^e5-U{u}y9m'޴%Ae3[ ҒX%6=@kd>N۷G?v` ևV~]`$ Gp< mXT;/Vb#׾6z[>[qӑ T,toʮ0H;O-;";p9rfFRa5o^c_$[TP9_a*ƿv}\6Јr`t09ZMJa{M8֖CbV'p5rfGlwF >8j5[!j͞Мmq1pzpS;w8v ;ۻ &\ƞ鴌Q=C!dvA1FtBɃ1*E%Qb!r uifHAlE \R%uO s=.:>M{-]h#+O񥅇B,IzBhѕ9כeSvHS<YEf86V![P pI-ouƒByVZ +O@\?@1/Jk/LJuY/[!}n`_o3f@R9c)'x]vvSO=zS΍EUAlӖF]RѨ̞ݏ5$zӎ{tv$vR _ u#u7A&z=<ˊqM# OU ;$i"-,] ąE5ZՀ9J#BF5g5|Vx<QGWwS`WMp\{28怋%fEʇBj Mәsva  j׊}vCoj%6q̖CTZ l>~宓p6>#W*תq2XIfS3(0KxzpI:($ &iv'^uIfF6x VQ[_ yN\QeJqMn~|;PJj`dSݛM[k;33͡$17&6r.wY %fJޠ}X_xnB%g.GM/`e;OҌ|< } 5 G :r/IOIwWT2򙔢'qOݣ&~GY6;+ /ߡ#hRx^Il}~sի:2T#1 }i[s ;Op NiҚOY`Q<>g"&8O0Y*޺8;7ybW@&2EM)YvE;YASMkR̎ᾏ& Q6zo ݹ`zVO1>J֨ Lk&*R}Xfxqp,x8юs̓ê\IpdlZٷ=h%ԛB `eaoZtB[lЦQ1v 5ř歿2駆rTnXÖ7h3-Vj@`" `ܦ䂱k֣z1]4k͝>BBy76|LN3W3D__Y6V22tZU1rbT̡rs 1M8BD,Нokj0$PP#+gs! >x3z{Q;So|U f}NƄ);InjUᘪDα`v_C8 i"F[o!;/bES J;ƲS |UG'onuF=Oܣ$OKTc;'UptnT:dK%5+6O V*0 e0tHhyJi>3_k61 y2V)ݿQjZKٔr#gV\|@uY; A>+$Qr.3|f &+݄SGyr^v'0j!Ë8j"ބEj}1?e-0Q;RbכP 1u` tyk*ܼy9G"5?}]o5zcx"ct,u@T&/E 6m4Nfe;n#N1!YiǬ*B)r fi:q#G]+\Fnp4"IlŎ'$%cfts7sKF’%Fx?_z{ @ݎH]c}~ve dAwm9JJ=,y> ;:6-b[iȓ Tp 8醾v >oȖK[7>N*/*W`J-UQ0Ndq Yg̅ 3TAusiDn8K F{&0s#vm *2O2Hgd$cwM2aUܟJؗyAK.=&ՄRm@L9/<2:eS NHZM4hҿ3%OiĹz7,X\Hd<% 1X!҅ƍzPW:[ %nvDA]u޺O61eJ'>r+(ݦQ2{ Ðʕ6#Ntnɓ $„pEGG;pIMA^Jp&*Q{7uadkFQ.@N9Ŧ!^l3/>/H?چ\T\~V?lRt!?t3KGc.&`5rƂ+kFSүV 5H\tQjDHǦي@ZV@9Avj k;bW9ӡ\iJ+y8%.K0]tڈP|<](T5YƬAј}<oYqeA.>Z~ڵ0wPɁa . FYct'5ld01 Ζذ"N7r>]f+KlThif ջ ϔ?JDef[au""vuʆhjm؋(`0W;ՀY=p ,1Ea oQ$qF(;?J 1&VKj]wØnou'vٔ[Db"e5aa,do6u| q0K-eIMx^qEf;Rާ#~6NB6?$2[gSȃDpDku@ ]&:^ིC0sfvozc(&ɚX\{y>jqI"ܙfy ݇n.,}H:9bX{ I "!U%6U$CkNLJ+"Hm+~I1O2-0msnR_3^[GWUi9t? S$S~>C{gb ۧEb1.ҵ 빮Ov߉xaoĭ'gHmDӎhwlpFam tnq"/QzNR8?PLKuyIN}%~}ܱ1Tp罞2!ԑh x/LG2;#~m} $S̗?3\C|uY"HȺ|H,lܼ7|xI,oJ9Fn}Xrr8MZ%,w<`|.f]Cۿvڸ'St>xJ) {.5 `HX RSuQϋR` !i؞(4$) ,Jז%rty[;@Do%;K'x\w8"Rd?o^#zV3 q!o:nIxݼ?[/5Ȯ7좽;8a (!cdhge9J$*_]ٯ\uZO sI˒׈M7ѧٯ|`.uzE߽;w~Ve%:#x-ƓHX]GijcxCŖba0$TDd˺G;Вa):Ǹ~f9uKBFsB瀐A6?ؓT/QOi[C>b\~ٰPڃ|4r퍬ĕufbyj]Z x ІM~RT~@'D,LIx쀌 Cqzz(ϔ ߯nĤ&kgE% NBbYQjR>ERCFj/@42 BV'v:~)B[Xo B6FA.(xt(ª;J+(&㒬r u(Tye*N* 9]lV"0Aq<lZa_ aR>R (-z^or?8-gs_[&zuLA.-=MiC)u,TB #jyUf0B+Bk#TtQ ֌g4\m^PLj3=3zPn/K QLf4ve. Ǫ]DkLdkZ¢4 V w+\xyJ_Go"t*RhOk쒖cg ˶ctǻx#7*!xDzOu/a׻lS:-9=XORaq" 5w_oG#go_&8J|tXprgy$[zE4>{mWA.~6ދ8]XY^үW_(p 5;[Z#_G|a+zב {>gWW05ߪ/Mגvfug5km^G(`&"qMg9arK4nwv&k>$w#q|o'YY'{ʍR"Q:d;k/,Qҥ:XݟF8 jhr[߷˳xהqUiz{%Rmƻ ,zf8*yd(g+ovrwz6x1{'mWތ<{/aD8J`A`eȯh.LkA:dϭ!"%/̉wUfi1O#Ut/ S^^y9s #&߱򅩍k!پ=t8yM#{B Fpr ^i'Y#{\Qq'5YǵDm5ڣdf/s|TY3皀{zS"6}X]iwb\mh.? >Lw舲;ӝ!7cUSh~h&C޼YG0\њWQS`/|Jv! O$wg ')Q޸i+(1=`%tc8_Bg|0ia.:ì|- 6RGK:JW* wN2):n~]'IVK'A9'֑+{=2 A} cpCP=$.uHŦC)D0fe* u&-{VɇOOw αNjO/BxCk~7Фbvn8h7ֱc'Wo{GriciТ-x )}wZGjƬBS?⭢,nݬ=1}\P}KDd0~3ƉAcFq7^)xol^b1f$p3 9-N(1)ITf>@xB;ȩ6[4:UnAMA;(Lv&4 OֶzJ쨜8-9W*(D:3f|?/|e4?J}֞&|ʶ__?F,_\>'|m\)=\dBC=CcD'7VdA Z[Q)"_^E-u{'ّ)#h}CuJM[,fi&Ŋshe5IF8%b"+1VcAK.\.L෼^ge36ǟvbB>@xFl#~.d<BQσV3O ^u1zzrBݼuU@Pv,uY&s*鸔>]`y`EVuy_2j0D-,C*ËU L?4e>a=7b~)ЖV%"q \D)f~_8w];4Q^GlfXEԉ\bS k3*艪_,h%L /֭p[AK_M.T w36'o^XW">z _ө[gzvXޜr @fsu9h%${7ܒ'38C&2}C+.3ttn56` tmCl&d,AGS@"&,3d\fcx-E/;wR'7^d'\8 N^!1L,Q+Q؜rsQozRoZ<*kXߡ'~Vd,m `9ċ ֯3/;!{a)c9W]zoJضۇh˲gǁBl†e9KA5ncc*% qww0JEuHw-Öd]o˽O:W5L1nD;AoXwA <|_`p(%35o] ;>=iO͡%pI{'jzR@d,a7e!H/cq~Y5a v}oa?=e7Hƙ1r*]'{JyOl~e a2$!&csN:nU+!Lk ng T? WnsHQ7ץg# o,<;YKTuSW@  e& Fn,i!aM%pݻZ ʐIt~_\%S $Eޣ ,ѳ{kL EvrXz&'75 rmQƆ_fmv1䌊N-4S'=6@(K*ݠ'ۯU1y+{,,PlOKNe ?ő\ʱoY`;;cGՃN|̲3C`ꇭ#gicA5SipI:*XDN)NەH`vsr* Bҙxǚl@%JXASZ)#g‚ A/fC7D2I''s?ʵc]z70suQTDP:V] Y^Ք|cKPh_ЦDuyXIWpb,0L^ MM<`ݬࣖ[: p%ndٴnN,~W4ue`tYZ'=5%{lJ L7%~ن.v1 Nڝ`hZd HdOq&8 /CنP>cDieyW5)D}6޷KLmIŤPtdL$V CIs?n֑$*'wM>)o$$ո"} ֠/c{uFb|l;D"&Sya$3$].g's߂,g j ճWZPynNtįfQ1˖cQx( J5!gl #r#! {!y-YùHN@ݪF? OkMS)5A:@4ERh9?d%SqeE&OwGH}|Olc"f%;?PCZ#2q :P?UjhUd// D` )T8SgA{ˬg"Ń )U,W!dzi%DC@p{K gT`0AϏ?I.N$E 0Mʅdm L  fNI:W)m&&&>Q")8Z A>aKܧYƼDos҉TsQ~zBCEɮ:+ⲻXM(+5 fTePhn9xv{͛G%c;Q3WVK#;|.߀iiF`ZҧU@]< ~Mfm2x8YiUx1rh;ƽւZJFL;`?VR/_AiN> }缍ȏ(-z̋Q`xmVBZSP'#|E1pzB!r%3-8o۸ oΓFw)<6S JMA9 Z i-v "u'T+VG"4(Q=f/!D́D k's #"F/?SDT'lTs4tm(HܨX㿂DB4_܋ciXMg_wwv]R?nGgi1wO؀Ϊsg-x rk 8wxn۷f$wT8HV(t0ce;Q4&G sGW8U=ccD\oGtrIɑ=)h1ٰQ^Ux* 9Ubh#J[ »rYQQ9f9B"k6LZ[7K,#҅"ao"6k@S-61 <~6o 0񒹶=zh4)|.Imq 1J`?(# A(nL;DҊ ` OAGё?@#d_8@Z8>tGS>{0 ^VU48Zt!ƯQ]GyX'+\+9f~؛]GٸC@:GĐ p[x- #jᣴ4/]3]񙳾7toаٿ |,1/(`iN k (ƴnS_dKK.چ?ϊ\Z'+|Q7zhW`_uq~rv XQ'Æ+ nuzp!(_~XKbJ:V,#/?r\J(6D(}3/EL*:Bk[o~.N͓/$c,P9Wɀ#Q gߗDjuqf}o{gOn n4:!C5]<[S[?\j+DVTOr6l| ^d H(_1Wzy, Io]P燂hgI`ಚؐy7q&d8m$\آF ƹ\}H٭0i)\3RԐ߸CUÜz.]vTkwIEz33;_8@ bP .E ,"@NF5YD\S|V3bU'`aP4b8.r"jlQ;,0}/e]cCל%|v(|?ݧګ<$)Rwru,4GdI ^VɁ \%4nÇl3 <.ߑ mLJ$=Iz@GN|!+Ւ-)&8KF;4iqY<PŐ/=yȴ4h3K ,ȋasX1os`AxHe9?_ǒUfOmIRCD \\R%z* sNP/L-`9 䉱M: kqi/j`%9z"p0ʼnd") v{Z(?+:{GpP4ӏ*ۧ1rʝ*kiLLE5uLqXQwjD\g{o/ s|W nɁn](?@pNʂ/2Uӌ~Mgϩ H-X[Ks6v^AOV1DJoL faiZԴ1T筀cx5ef|.;3sw(û!Sxf198.MUUCGCT%fMbhwj%4A^ґZ )h',Tx@ Qyi[1KCIPoAw^DRUw!"BH Z'#r\y׻R74u_di~VQbFFJ_1lBQD͕˨.ٓ eIu_hQyFegG6W4#{ $k2 X U3iH=BA"ﮑ)DR2Ah]N;ϐ" Tk,:AQs&hEW.׋ xZhFb/6(qNCFS"8Xk.| k]6CGEN݈FPL|+ЊzaJv rQZXK/lf!uB[ endstream endobj 4742 0 obj << /Length1 1386 /Length2 6039 /Length3 0 /Length 6990 /Filter /FlateDecode >> stream xڍxTSۺ5Ҥ#H7 & wAjHB$t^7J](]zQA^x}kk͹d30T`($F$[ PT!4c$f04 T,PD@4HBRQU C!ahN% III(<0 8ܰ!`0H#K {{{ B('y^7 0a^0(=hB$g8/1 C!H(0Ðul$W?ѿ#p #H/ Fa^`8n PW2 cBh8׌¿`Y UA4ɯT0v}+{GBt6Ex´T`&ۜ`PJ\\ =| ¿ ~;31`pG 0@tsEps#Ik9ƞ`0( 7 iݷ43(@PJ@ 1@?X-# W}e?#^?s顰̅xMtk;_YWwGo?_v#| `UjPs_ՅAn€jPB:a-+Vp /e77 3@0( |XA\w4]0YW AAMDL`|~ ,Da!쌁GɯؙhW98rLV{[0 B2?Ȅ8UbP欁gՈ" zX]tQeg: MqDmLПg'Dl* XG.d44Zxzl.˞#wN+-n"7Z^w D8N$Ytfom%7k2SiCu&'NwiW`O4(4zgGl)ð {x1)QMmX㸅ȣc7RՙݵwۍF=UsRպ\RfAd'dPYcBA{hۊQK,Uw ^4mu gxš? D?|p{jn+Aݥң"ę7Ej:"v"7[Q$[>S 7;<Qdnef&NJ[DVҡ5r=gUw8(BJ3{9Πsuwo!!|_mTEQkWM%i݈{1:O;̴LVAOE;747LE?!һ$}MaR4͕zWd'~ 3C?~ՖSv[&-Nn䃼@jie5{左[F׽Ts UIȧFr):]JZY4%P!M?WșhϏ$ءaSzGQ4cQ˚]WV?X[t8 4"Se =y<#0lZp\7.E{:pU"U^hzzIǶHaITX>oxYPb'yq)F~Oi7&lT?ˮge(l~90qV9]\|>\*Zdxv]W}[?+gM)e Pjo}q}G.Aj`{ƴ5=G3WC*IDzZ3+W- u˳m7fHqw0LgJ+hR7RI[<]6C3WILggdgltyͱJR%5j0[0r'm>8i(s>{meǏlp|in|;ԙvgn]I0S? !0j)n-R}E:/!#G㨛U9:o۴?5f>b?^\sNMܥb=!ڌ8wnc\6΂'2,Uϼr`}Ʀk^%]q[9NJ [x;N&"- 5z.6B<{5B޾K~'\}BЄeG4lz}]g$-!JXo*T2.?`gl`)V !d~oѣnW?wݑH ]@ O7}oz]y)1X R|[727r4UE]zaEi-U'U7yYhc-b0kx'8tx.Dѳkx%{@! f njuɁby蕋Iv|Ho J8 3$%ͽl˾&wIbpa[rfR cG(]S6!bs~P^Ξ}<ѐ&A$㰓[v²s&>'+Su oR!Oωm") gK[A!ţըC~moC| [P輱:Rǯ.n"cd67wK6Ù_'Sp|,F|a.2))9 \++ĺ| ,"bBnUhME3ƢQ/~;XT悔 MqwQ,;[П!%7QM9J0XHtvdK.8JpS\dYiہQļ J)N|[!=͚QbY%F~=Q?cґF՛^gl᦭*Ҫd_-Ei;·'Mc]L]ecgz z 6R kSHXܕj^TQ J̐e4>c V/cbje`rbqؙaΌ O`kn_EkV2BDKW i7Y͎rK%ȑ/ɷkhԵW{|Czn,)v_-vwı{ e yѼ5OR d;, ]kA\8]vn>&אY8Ca"r7q֚啢s;<5 Ll@.Or%Ռǣ==+䂓6sS/n2~ }URڈV0fo0pj22fm˨@.g^pdt,Pb쎆DY0g+*mռ?sngS~)nFXN`fLe鳨N}t2m `^uyu'cS]0 `%O)Ĕ J(RK0)a䫌  "MO-5Y@+횃-aF $O8fh1*N>niȩ.38Ep:Z=g\P_kn+:Xh߄oqʑxXv:#-"]SY 4{r#}1E(BuY0ՊcyOB4/rky8H»rCo 27n'EPf^X|;8Ԃ&Q`YKFY4@F3nfyXܤE)b /c=u1r5|!*x]m:1LJukgsC:!a\ ݅xVfO^z3z:G/NT+t kNQg7ʯ62OWNm7w|PlU((?=$F_d2R^_EU\UE"||wp_*IA؅ӊ)AĨq\ݱD?jTI?"+!r S ;/B،1ПKfv#{POlduk"'r OP5KֺAyY9XbiD*NQz)hrM3Sv{COEW=U#sSc/$.gK!Aj Cb%\cV 1B&m.T 2@"fUR_B>kqQy'E w؋,%t=/齗AA]ޣߑRFɓfab<Șp[Ci$q6qnyQ 7(%CYFXfr9bR3ȓPW@яPHVrJU͋7p,lk_*Oh}'yIk|N-LKR}şua sjR8Ė8w_noUmNf S`{*js,W|ƩI)i"flvX=5S]j}1w,oPN5b* ]*"KzKM%)։u.MCI.LDb#P3pAk˪kSE]u.z_|>M`qX>u"9=zڳaz s}%p^5`,hoN~Jxd~;B jwgTFCVclSd,iRоTsIXa-s*:EG-t>ğJX"[ss=d_SK hǧ'y~{j2K` ÍexlTI&yʞZԁ~᪸ nUmV}BWQ9MD`Ͼqn /ο`i$TעKr3ݬk-=mxA] Hb`#b\ ^y)Dgw06|bNmP`f&2E%{ E{S0d3)Fy!Pש݆mO/O&h@*-.>͍$lmKPYg5PCk-Ǧ *\Z&_&FLX?o-X=8~8 .+"=`Yδߜ7W@Ce+37q㼮Tw;?Fz0| /|;ܘ:o) Ds =K-a鴨\gWE > stream xuSyQa"AXHx\dDg"B+1+|&WY#]AĆ#t rt&TA>Z4s:¢gBvP#X4L,SB ]3i̜!>@͝[q?,fδ6Ptw'alPXp+c62@gH4Lx`Ѹp;џb B;E`B !@5|SGa5 V ku^(o>H0fn_T06x)"o1WB;Blľ  îWALd3Ep?5wO-47˝dq\xӽsiiWsYw! 10uL 2)5,fμ87 `px.1"`P @7C0sN0aB0 Q̯4xf.=eςAp+P/AIg'ϐc0nYXm,Zn+t^fD6r)m`9o9L{c" j湥i0=gCT~Ф5EkcϝWFWO;T&#񺓛Qz|%1͏(u#%[҅S.x^Ѡ[ꨂJvU}E*&6޼d(۴dzt̬]ӣ뫻5S^ّX}Dkm60dx0t~zli^Kɚv󶞆{k'֩#%ILf=?x$6wjVurhu(237k<]iu4Mтָ'" ^&?S^PZo#fn=q-ޞ'IS 6Ɖg'v5+:+E-%F#/7삯O$1w_H\W8PAݓҨ@BT9>2hZJ?U7[qf*L&\꺪#oXl-Aih\Fѹw)}ʭDءx5{b 2+: M%w:~uxe[ؤ=j*/ާ z:V]q[e"Y)sa@&YDtd[~Lwp[:eMY1uX|ƹڪ~9qluL,a$+o[{$mr>[4|x~p7>Qi\XZT< 0\8e@<2}llDUޭ\Q=D-)p#1ve9k|U\3)J)}AؾގWuЉ<گ4kli3[}!FW7=81&A[%E R9etI犓%?Hd)g֍{}:drވ>~s@ҞhReQ? {#nq69WxKKԇn7r겜p=*VmI.xu$ #c|?M>ՙe:Y`{Yt2C eͺiۍ{6i8U捞5 K֭^]%+ ڍ#VE\~E"Pk~%lLs+ęyoj UVHF`iͶ8QO 6kKZ$M sSC] ąhv~B1Ja:`:>LcKRa-4&w([nR(UK}5*a㧬'R4>o R:`4V̷(2語rnxjo \s͓T҅ اPPhy`#qRãvEjA fR[SiNuC%eNy՝թsG9޷h{cdE>!Gm,)hi|-M7Q21dՈDZêhEm 쩒\h endstream endobj 4746 0 obj << /Length1 1626 /Length2 12150 /Length3 0 /Length 12988 /Filter /FlateDecode >> stream xڭxeTܒ-]ݥqh]Cpw ݙ{g7ϼk)ٵvV7)2 (uecY9;*l@2@ Thd1trԁ&1 K ٹ;X;(T)ie+`LG 3[5h?NTN@5 ,))'Smg#k c1H 09qlM,j͑K`p[|݌vhv@ GǏ G @ [ckg|MAs}D|>@NvN "bdnWmG7di2v}0^'C [GZF@G0;i8;Zؚ hf`b ttk:_7v;w?9X89M`>j;}6kW$mMAFMt{@  C;h K/r( L'+'k_.}wh1gkk9Cxd m @ClXX=ZB kI:~DC:-,܀& NSCymW5:X[?t{ZF[[%?\@[!Օe4@%pRqȂLy FHed20~ܽBl Mɿu5trphwI`DmA&ǦC/Gy{@71<Ș;2-3ݩ+whBDr(خAYiRL8uWjoӚ;xMB[FN@W~y1'IAmokBQI3T;=K;;DXF0;+]r/|F{Λ#p.γO#HF5*xE'}Crd8=(.G P? p'PjTmqa}-|Y墭>\qŚXGO[OѸ1N΋C7?W<#C}F&$ d пex%!(ڍSq9Q쥛?Os6baoY坥bS9=˲[̣&;fvG8OZow&Ȋ8ݷhb inݝrF13%O-KIBY Fo-.z}FW½y6;rND;RMR@痽j移:/Nqَ0}|Ksw`oZ17M^Yq5p~㛕"EGWTo#vl2JnnΙhq@,U^xA]]91ܐs ( Rʘ],xjq t )/9ؕv s3߁)Cm¤lײ4%*_!_w07Li L誥2@;IMz,]AuEކ-+5׸OFrƠ5ohv8Gqf[㑕;33^򛹌xErVjxLEq~[\`⃐Y.dVU+;ݮ0M_~݁q2ͫn™KhKو7aqsxx3IT|䂰WL.JSBN_R3L<Mq(‚]pbl\nH*%UB@WI} }O8D˱@-zXb 3 Ol$QC;viB>'5&]罃n: gc@,_ 7* "y7;%e '?k97*.7"Es_~'}C)ұMkY$ kb}ޙ)yVh {8E8K(G~.b9*)Hv}Vo8 r;FEZ^M]lJ5mN+Ԅ<6i(ט Ty H6 gꩴ&f5H @]Է-5 >º7rYD/|){Bv~v.LLmSv8*A*6wC׸l7 9MW`Wvı;3C,^9],_ ,5k)FVT?&엉bv}Zn䵝FqNIAL.,C۝ 8&ә~_FKVѡd'"a^`2B~ԥ^jӱ,99u@„c<ToiME 60זvR?bg>^|]'gqH2dIV"jh/h;VZ Olj, NF,w݃oq9 }xkMeb͜ыU6 I+a֨)3,o1s.PhA`Q(ji+c,ޣELN8 [˵:-5`#)_zxsM1KJv^i Z<2O0TN+6TVlhܹODlܛA~> lk4^gI̢Ip(CUY%qUhNvl.nR_?ϕ" ױ%cU0/)av7AgؙP=z^5qMg1ѲBG W+uipt/wXÓ $g#] 21M;H]5}o #bp (F>pgJ8՘IX'̻>.&ǥím\n>ŪgH;9[Xykk/x~gDC^"|!4\eL0PI1]ROrE,_EDyC4d]f^0g,+O{4 gҢ>(rTmַ58btO}U#F_6ii$Ugq9Cֲ^t tw5] (bX~wkZ$Yn4gq &U#Rߕe«tlٽzOagy|]uυ;|6cŽǗ1o0?sb,H^kB 3v4/#YdwV26Tچ-5C8wk)7h}3E.]`E,UeQNeaWS+ߧft:Vi.JTI@^^]WY\{}wG!F'WWёhJ-4O:ezk`ѵC,Ļo](WJppF 7m-[9{+z4aL*OMC+;َmQY0/DR{9뫕C}'彃~4ĆN9KT; }Ekb.,X2ǩчqL7 eƍcd^ /<GQvCp!s\<A7rj$K464 04Yy=Kh?NeTr!2,q1#ʼn|ޮs MTg*;#'5-Z nUc+K uhQ}4>(:{+a>_.Mp XiGtsf#Ӂ;y 3#z#\Op΀4*G<ubf`h \JaZ)ͭ.T{ej6XlCp=rK"~$ ..>d+ވy-\3k]w<&$mJ9Mʷ]K0lmؘ+Ӫ"'|:> >E>T1U\G&␖3WسX:3|Q<^}\Fuը?x'.(`Vl 7h}8ș.\&fåӚGE~DHmeiD['>GD bɖ5E2dr[uu{7_H@{0^Lsz" `2\+iFmOⰈJ*dJgfݞdSB_ڂoV&aIzg~~[PjNxx7X9֨bD6- q#տ OƯ--В}"84%@ײJ *,yf#?i=#0q/542ʦEbz>#c˪A-zYKNKk8ap k&mV =s=sp;h0ox[˾͸DRLe';H:p(D:!we*%dC͘))N'QA޺X-^ ڸ9XҶly7v,N:yʆnt%U2I7XmyWA+4̓r]*C8J:e\,dTmw6)193P/&PrBԭnuP;Y {8gbHFP5XDCjCVL_oq>#Z otShITZc5Ȉm|t-#xA9(̡nH0ȣb=O21^+(k-k18(kAKT&o`z\CѷZ=_sdCQg9?L˵y&%l0R;98:<<UboHAZ1\`+}5 }ؑr BQx͐"AKVi=SH^M5UDny̙ ෧fd h b~4,J>]uYzʁ֓S=Di/en Sp!Ud$ ka > ߿iod,krŵ#߻ Sb>aU^ЃW Rx{f6>w l4fI̭3DeV#^L)c`0oDG"V:],htlꊩF'^jMؙⴭ-&$/T-4:6j5E ձt>U&1JnC ue'q$+"P3ɕD&l{k T^dPLKƆwq&M3ޚ^_*NlHjڹo!D_{MvNȒ C.+u#BZJaW,̀*/^ $x'Ah0Y~zuWql "RXzb2&[Ý9vyZE-­T闙 . { ڈP=cv ]Emʗ/!uErT˰ůICJc/q`OHGR&I/D#KE] a KnsVQuz0>%Ңr6.SzsHn>P ȱ.|NJ;x*\(#iwjuy(`s^loag'2nT5Bԣ/H_t`.f 1_]BtGZV|r%v͎C[Msy;&[{1Jɦ)h”\ -?_mts,3r]'1DyERH29^\ZH>GZk4F;QXG _`Jz2yAi.e O3``wƵu|r1\ċ!P))NR31GR6zʁ!>KX>9 =L(o3Y~~uiLG\Ǟ "GyTVX1i.<C_Y53}yǜLZ;*qMA ס{ &IYe@ă#P\֐J&S @Eі 3n zBfT\Lbq$ =7$6-ٕ^Zc/.@8lX]x`L8-UHuΉ0V*`qS#{qϙT?!@lqQ֍dU*m#fFb7]L!]=j2F$s6|ԯ<_GW~' jN'Bҋy EU@McHNOi4U FXM$6ݡU4g1+k=wJVqpݛo%ES'|wljM;_ud nWEۉyQ4nTȠli~?5k.wæc/(Ab3Q2 c[*XML.nA5(, *0+01!"zV`hcS3Veopaܕ|1t6(E_L=jȜ$yj5W&iL緣S[\m^] ]Ɗ;β`4^#mr)ͷy'K`s,J).W~>*#k_^~?P$#ŠRPL)L_*]IKS$R@AnS;JN#XgzSЧŞy1 eY+2halO ڠ=S9gבAH!MFϚED a\ǝ`̑[u7- 4ʚҩcIrw~">i#OJO\ׄAؖ\f.ƒZ7wp~f2 .?̄ʽHM6aUYh|UoiE#*N Lcq~_ŦҸ\^ؓW OGZbt8V]e;|BpĭsLIüS_ xcVWN Q@}F”b5t9 -Ly'(Y2*?6Vn{Jn CsvB啃- Q ~!Bp}[ӔqH=aҐ0(Zl ElLRoD.8Y_~Uh )ak‹n?$,bm*x tgH_={I.g}_]J}mt }L=U5S>S&/NKF? ?ǦXQSQGu&׫h -2]F/.D) +:U^p[YlڅZN"JRx[!A~w؁lf)pyf,&F-w`xm5~$ba YOtHF/Ha==ܬ!5Y?$h aʿJfFZ?70Y,r('į+Y"ʰ8|; vA͈tNpvѐߔEdž}uRw0K6mi!f_vȒ[/Egz+mvG.Ip5 ´e~WrLW_b)P FwZ. "5t7f31 2%vxqiK1;FS*p[kP W'o}oFw؜3X]^zPNҔ 6X{;ΰAAJɂC=U{90AW"]kw%=vT2(5IvVNH16VIO78M^Es B}|iVB˾#O^|M Ū:߿5öVq}, Y.6xA:KhW4U4,Gh ϭ,,ٔȑP2`V ֑2[fu RlkG[=I}\ '&GZՏ'M{,j\H50d_e$a>ch/?'?;޶|@O`)㜳q&maA?W!zrb5 Qyb]ZD0~eRy-Qx"1l'K$hÿpʒǪ;(TJ\an~t#S2 0·D5}uv~W5n^]ܜ;Qe|;]7Lwcq۝h4y_ƻcX)Zxf `-~fLz=W7Lgdݲ-r^u@ʷ\+H"؈Yٷ]d&OeO\,ոNoSN5ʽQ}8z`ED.A,ezWUW\i NQWt5s3b&: P;[>4"sHgT l.I8ۉHz ;\if-RYS#G ;|qoDXmȡc)jdzԊ]f1J[|R_$E{G{Yg@NlP *).'F-L_[V6HB2R3lڿN+Q{+Ym]5"ȣ͇?t k_mP0S< t`xI+10 .Q3WZYڌUFTYԄS4'g {/H꺂ލSv-5_|Q퀅hr P{O:#_![8įh.O7K}&$tQfM!\ I\Pz|pŝf2GE,*&^OJqfC?߈R+AiTe5( ujM%tټY?§riLQNQ's)Jc/Уҗɦ瓏4^1AjG82RH\#O /TN{vP_+v|ɫAX36|oO?L(-@:=.“.Y#ǎZFQ/D?:5Peu?k+!Qutq/T`\n ߺ2g[h2Fԗg}ucPT@fGhm  Ɓj 1Z/ JfKt!{_e:債W"+BkhUg!%m[Ld끆*G63*D> stream xڬStm&bXm۶ۮNŶTl۪V%v}xk`+ 9%]x Vv&n.*v r*@ 7_=;,93^Ā3777,9@@IMKK\&^7@hhw  \-s+[ @TQI[ZA@%mJn&V9+S ` 0u75X.c#otrq rX8ۻU_`J..VY$]?]=Li_0V.W?L3+G[c9:[ 7+{ 0v6tOҽ׿?kruښ32i7=,?"mo`f܁?;Cc3{[/QoJ }$7PB oWG+=2w ;blOQ[r`Ү"lo&+\$o#eMjŅ#vBgx>;?eh0m){S~(?9i 2.4c|n~p0iM)CduM@^Noх Z_t~A|rh|td67?c'ě;Krvf-J7ޟ7"]4+VB/a&xY08$: Y55Ft58+tbO *!,D3) &. emhYN\Arqh{B%?]{@jr.G2{D=:1 i[\v!, ͞ 4˖A{5hK\)\| . 77FVewfFq%<߰aS N yF . ףʌ>2Z0ٞj4xDc7Dہś}{ނGV~c4{rB~^ A7G[_O (|cS֬;dfje͘i-f*8(ڴ@r ^gγ\P\3W֗FҌJkTc\X ]`Xagg#:S0Q Y AX " L0$w_Y,_a9I>U͞ m/F>~BߚFRko@^t\A?L.TH8~"o-LgIZq1/@F~̌lK͎@klU` 3joJY`3zq$e.O\ Z %;e HOj43ֿاkXԙ99x o:?<$/ZA\X172GZEճӲ&sfiF8"M*gkZ;W{, a뾉8(e?WOɂ4Ts4VͅNZ6wRwH  ؔl6=y\ى]7B}Ƙ9M2f\?̿*?xAU( ~:м}]p7Aqož"yXo<6H̡o['kȕ+rm,|6JdK3˛hT{5k *q<g4妝Jk0OMgՏ2;Ԛ䯌WQ&@V9Yt)<[_,_~C0GZAMvy7mIC)+Y/-ֻ8lX-1 -m +ѧgI6(lDIw\LOLEo: ˃8lmZE"E0˘0$®} d':AaùBrY79pRtX#3O5TL>o?3ע ڶ_3ak~Ut/Ȼyypq7>kW{t@ zRjы< q 6`a@׃1l'sNMNdn;3fAGUD#OFPA+5(E! 0lAWimWDXxJ$Y=$8o9S%עX=+P; &,aĤcHfjO& v'jgvل|/-|5C.kDbfhԆ;{ 5ۂ͈t3` s"6<~hp__(Y|}Œ).US"ptcIoCO?%!p r|;Tu[bY.5,pTsa&Ĭ|rNo}%(Oh})oq3?/*\gQui`+G=ȤT1\y SliK9{SLɤ&j2$`78aM yp2|)ҚȞbfN#>Hc. qpL`n %P$`7aBhV|fbA4y{ d2oטcAف2xy/YKWx[vF y9CmBY xHlƞ'6XD\X6K;ŏ4QO+&l!JEݡcl V$1{Nm|t&uN簿 >I0 H={ūۏ\;~Z9Wʳ}uҏ7zr 9%}vӪpڨ ?.9<2Z\:v|K;InGRW4Xml@6#xl\L&NsN&ݛLVeHB*E >ƨ) IE%vzK_ENil8cN=Ӧ 9l,$=4_PgeU01AL;.6 &Fm:8zuQ2PKAٳaa1G}#o: Ds/{9ZZ-mEk>bxKd~>:3$GpDr^;֌gknKB4Xvw\Z3w%?M¬[>ZR\?,y{&Bз[pvT4rz޷aBdTY^~-J@^fk_}gj ( Ź7Ňs8~Ailx~.Y\*ݖs &TL&b/=t >E^dt&iUۂ>(&#dI-`_lYg:O?buT2L]-[[ye%H7ؓw>h\{_"\h il#fQ8/GOf. tq삝J2XF1;/wlOX&Y{bͳWP'l|F|x2\!!)yhP^QE;ysLK MIukfUW>'ִf݅DyeP5oxzھ*+%L> raYMj9сŘmbe>qԉ E7oJJ Pbɂ GIK 5 YxW$s]vߏ`D `UBW"e^rQէZΤSpMLmKd2N/c\*^8m^ܼί8d0÷ gh[K<2Qp9, eZ'{EꝊ!zKTgwz/>&l=K1Gȶ[DAiKkхƸ*|=W(oKmk9 (T'Ts/z`ݧ#UbT7U'3nf Om,|FS)xM kzlx7HmlZϒJ*ii-ٓV]OEq G~= pM$Gې;'b7Wj/,aFRsUyO -h^CO9D/2EF}j$RN/xnyJ$M[h?Hƫt yUG l[ud/Z)wpTpYx?'88k:.|/AbR3Ru׳{ kc n4C}qMvWj}SalWeuH\Ȑ'Vjw*2>Hgr}c8?9  b)OC=)peDe-f¡ kZ}ŔI>kɧq#PhAbz8%oYsFcٯS:`ifd~%.v'f WFՔ/|^BX9P|^{ݸ7^^Xڍacc V!^7'GSX aD¼ֵTN [ 7#Z;P #ߚa J/+9JY.BP3X ywHcFiM"kCKی6'B+Se񹬲 Y5hzڈ퓱_3.q:/Wv4qGf*PȬ DFfi,^m+9yJЇ^1Jlc-PQ1 -T!j<vQr3b?3T-Q g5[]9O^VGes}NJFT0P}Kw;[Ez3̔s蘘9őW<͉bľÌKa!GxqF6 [33F =`!fjQP;:?TLH4CP7-*\1+nֱsuDdy}u4^ީd[֊Dzkv5YXsܞ ]NQkPq/]9`~!,5^FQÆދr[QNBEHs =eؔ]-EpWEpt%4Rmn0ǡAIfOmnLѴu|O;BCjSkN ɷ0EJلDZ/>V0o2̝wA[ft +TY#Hwx4'(髽^Qq]z[ܛ"d|ZK%/'ɏq 0%+FWEe*5$ӿvTS{,S"BITBB'$\ ,dƝ LIp}>L >f,7u['N/Р'';,%;zw~2iJNKA\L;Nv!tAPWh@M~ON(mTfd%Ԑ>\$s%͹>*t^czCs{;zSRAbY3M;HUTe?z9ݏGMpw4{II h,6ϷM[=*:j"$dB4u 2CiwLZVjԕq=Fp9 ( ~tj5DrM GO<\Gr8_]'HgѻI\GO dzt⁧s]|-W95֡uv7z\[0)ioYTRK& /1e]eL<5RҰn~"܀KElj|W>n'â w̓UE( 7~4B[ݻ&Z\<9G,MVsh`٥--}2􋷅O5YQTJ6O m9>   Xn2 ZXSϡT3Sc9+0O#~uVjiEE /[C% v>j@9W]5\h F'xʼ7/S~"&g;"~xͤnq)k` 2>NlJu#,U9bf װҭA:qZOY]VFB>n ~0 aRlU㟎{H[?(*/8vti1>!ʡ6L.P}3E,jgK\ݜ;QqeɌ'`7<ʼɆ\.m OGW1TtX [T}qX3} 0Z6IHjٱˁz6g*ζU}Z1z Z#DW6.A+AejPq d{#WB^,YN`oGW"!UWgDNxqM 91 =V&>ecb> {K1ܒ<2Z\_$[DFCw1a*!br9lшTY|f.jLq/ʰvwbfevQF2tߩ5n_OC'7r;H M6܈^GK/}XR~1vBowH}.+W\]CYk^-Ư4jxχU 'Q]36gA8kkl&$ ,d+ȣӻB<Paz~fþa,wuoMDّ,TArkHӣO ?GL~oTLuvZvbVSk.M:Qqk}4ܞ_p> D~NZ2TNz.ລ[5]F$jH\E;1x8] UQW==tҙ < YAA]C#qbh'q Vެ_!)u[4p0=oIW HX44W]`(/p| >˜#]b.e;h^ ~gFR@Ia224SrbI^CvKzS۫ AIz/9ՠG&yb;$=mS&Y_aXñV{G7 -uH;}l~YONX;;_t F}Iud;+*串Wsͭr"AL[Y`k$' ]"Ұ1/O{Q%"$hwQbFwq>೚HH@/Y&q;]̋+V |R.QuQ](-wUN@aPG&xlbǙSձȍyX3p裧^80NhXt{|(~X[Vh&{y B˒ljEFP02.:U RɷO:vf+}p7 ^ܚBPb-(9y[ͨ0kV&o|td yD;(%PfHu7ou>I=>k㋓( N3nY\> :p yHbaN ͡(ܵ(̡$(ۋ萲r[ H+_&CE)[T ޢ}v:7)CE$=ća/13-\AM*眹T3[V]KYouU9!N9QI(1YjCLsZMCCJJJ˪5G~}4se/T`zG - 84 5ލѴuݻ}!P8Qʮ|93!#ỦN/#ʴD)uv/j8v(׬^#?:IbXSIBg#IBq5 ^z n=gba-7?!2ӠFh6Lo*~vsm0ʃ?d6br~aQ"qSCUu!,d* B,=#<H@ʔ"Y8 `=L^|UF*H/,:!xHyCW~0mVlk*P& EZ?C9g@ٕ?ي%z3NxȰWgMZ{Wv?{.>a*=65u (K)"$N/)N|7a*Hi{ծ5Ӟ~S H*G~vY˨ { 7\āCw ;w,9v&'ovb*u KODzG f=+Z c X[Sѱ_E1̡&ָzxk.BKA5IUn"S>misb>K8>$>*ɶig-[\:}wV* }|9bmI] C00Zo*4Z H8V&%3DfPU@,0Ǔ*-?Bֈ$ Yypup 6:AmyJEݒ0^V~[AF>x{,;(c9_46mS$e+'?"HWhaӕ%Oa`^.VKLۃ?FX\'!^eZP/F;kH*4b䖏SE5:=D?kbI}{.*%^6"qfk[{sgUWSe0?yCgdDG7=VF!QN.WLƽ)EqCJV-HѪזv`]E;=]2Sv" 2Yhն!q| ͗ Z\y5E' +2[ (l莉k QօQ$/nLunHW9g̋s9v(}]̷*ZenQa pK?C OiVEaT^>umPOKX,|x$?cB"l;XۖDkE;'%t'36h_%']fc &11>Gri,47]i^F]5Gx /.yԳ:*gcM:Dú퉹[0rm3#Del4\yYtCI{eCYdlt{c%j+Sk^͋C=Š/ibh}_Ur<.`)@߹Oumgl>+}z=-ÂB.M؀{Yq siqmHib7ޢq_eN|5,u)xe Fߓ@QL:KVo>H?:=oW{#x -ֱū"%*c}NPA!:$M] Ě"olM/6oPn{O*IzVԤE E[7x:\ScP h7V0Q;Ч4S >g1}D ť|+V?fE)C`GڳqA62iܕGTXV#=*0/^E]y ^?O4WϭF !<W9e DAbKX8Li4:@D?8:5{MTT3BMw+τ7qr} W%A>'(;Kh=+BEVj/)`}6=ݖJ;"F-9v[Q`tX[Mp@9*ņ<5mmoư/5ogi-Q _nlQג;)w4Q+=Sr4d.':oU? wIl&ҹ=94L+3N$/K&$mc"!-4j5h Lj '6\5{8Ms?caOmFI/טmʸ=D@폙 FĽ9FzߦCv8ڷ_׿ֺ,10 ٭:[kOz(,(n+>1ۦf[[6b-B1< dփ6*.i-k⑍}rn}E&Z}`0(ʬftUL׀ڡfX/鶃uN}ٺ>/uGL|ZK Y$`g.\6OmYVDTܛ'-s@"$.+[EӼ?Is"y}\l~5΍uJVoи%i9T> %pܯ.vwRziXf̾NO^Nzx΃OQ2b@vr["6&NS]ŸtCm^o; &Qev֏qC ct8=9{br'Yyt'DyQQ$$L5#ZPc Ȍ_YsK! 0DqpL%rpo#̎Ӽy)dx8e5Law A<~Dŵüg*xb=8Pз+>_*2|q*)?+"@3_w.XC9,}Z!6*+H5;ISv ǻe"gd"7r1JnEnQ p#SXtj%[M0jh\"(;D:H-;";XCw!5//'g^[VqG˜FIIj=Ddž;CazS5m]}ad$n " 1hd;v,z¬K^E2>F3|H(*c<|:c uVuQ%OM(uJM¨iȚ:ܰ 9H/zb9ɩ³J3@up41ۿZj:Nk*PV>JɖU#+K;S'45#:DnbPH`툴$*+gM̤v&UG6ʪ"nT[$ l08K?g??gB^ /47Ԭoݘd%1}X63D'%""&g&6gG(}&p]C_z[}|YI|7GL]DHXylkhيA 3%Mg%uUe2̋ Ʉ˫HǏ%<6Uzq 8%!y!N<B'ۮCuZ3>`0b;"4/ݣk#}+m S} bp|}K4v]+`8!r5 Cp!u~RQ.H?O@ M̟|DԿBFx`ro8ϮYkVWO}b`fB8S%ʯC%!mr]XD 2g`;[2Љ/П!"xeW!s`Y^4w=PQW1#FuC;#yTh~%JnuUn{垨\C32)iea<9Gn ıB26s.%M=V& 2{]67rSjqsȗA/hRՆO|RW}U.-R2hmn nprʇrݘpE"SaB U0_n)4 7xHfNF Fvdžqe#!,B9ԽYjx)8fk n!RX Abuf.y@S8ۚ $di4!ʥE*|:ǡgD!cr]|!u&8lU`4[VhŬZqp(\:& ѤU4YCy\#$ Eir î̡H?H8VZz@<֊':=y4Z:ʦCOhY7KHw3~S1Si Õv;,%mS wN=/jj1QڥrIټ+Yk>`t s@.5ہP5"/[ѾŽƺHꩅ;<`*6A;8lԊn+k LJNlyĽn"Fy;b5]3E>`7Հڨ+Xl,ֽ9 WZxww7eqY{ﶫAȩ; #A(*Bv>GG?шہYa,t}i#_Rj¼'h6:)ɒ']*:vQ#.[DZhrgSLV_\HH+=)㐼;L)Hx]y/A醪S'( :N6Q b"bfГǁ ővI` ̎֨LuѨtwi6G98[]~ƍi,}⤐{e[9㤎NQ'-$SUv| v<(Kނ## QϮi _#z((]uڸVYύ6Ehʏ%Yve%s,ˏNLk}yp_R둔C4%dCI>IkETw+. qmo2UC>Ė5Zd)3nYűaYv|w]_em`w=ijdDabfWt{{wH>$ؾ'E`M hm.jl{x&f:Bk4^%E{(q 5&zʡjͥ6FRwRHUX_8ǗiQtޒ1x@!z}gFm\I <> oSh+`΀[Z[G "DWyDnOF:2:gf&LnmkޓI%] mW6B/ZgRйz;L:,>GR-~yu[~Ts<-n®uEjSK"Fށ71qL\N0ј}Xy+9-8#RΪS12.0lDS_<c }BЩpw4hd9P3R@V$4lx<F1? h8fhڲ?ut[Uܖ?);mE m[N^= 44<Ž*1whCAi-ę߯CKQwr>|H:<r,˚&\=f,L&]C脈4s6^ iL$D6ǶҘދ_fՎ$3u?xpMƣvVMF~˴#RzUhKmE 2'wcdBH[=uT e'H4ˣ(q^gIf xL>WoM*u3|.Gϻ[֎Xm|ɓ1h\zTΑu#PqUޗ=iL|T0oZs>EqxCqpƪJ}qGR ~ 7w(XѨabĸ|7ğxbDz" ,":EWˋfˠ55oYޘ~ BdEaZuڀS%.+lA~e"?!W  'aJ:-*EEn`*iv-h6CX+#?5cǘ'wuKBqd[ t}ɴb©Fkٳx-aSy\{ѕ{vb~Ed @Iҿ|8 e'iKܠ_ d6<;$B Jy朮e _l*T u>{ 85qN|'FM= N$"pBSm֢ez[ b|MY#ܥℭ Pz$<#Ecиd `7GVkvCzdcfuw +Lޯк|>-_0KdIQ]Jwp`o"!hf /KuJ6Gt8y ~+ {nGbC̑$|3s95XĆ~(oLqb뛫f^L kq<[rɀXg$2@2jm 釱l1LZl5 =Z]}ڠ+>` PLRS=yD ` 1X4 )eSTWYUI> -kKXL1 9եe 7+9p)SsǓDĨNE ,Zz!U>˪۴J`W-M:K~,vn+(X`gy[̛.?Wݨ4]_$߿atF@i\ ?cXmVz%&BJ,QrbӔW?NLGm?7 Mߛu%sWENW*Z VTGYSo#v7*ˉ-e _]VmPʅ5qW, e q (i:ÉkҤgņ9ܬ 9sr g|XjhMpj[hOc+o ʼnorEFwITjmX@C}8hє!Qyp;cU"ˡ-j%8@u[O@/Yr(}P5?%,f٦("F?͗Fe$xOQ X:TMՖ6pe>!6;Q\WK1]V¾8u_Yje2'Q@ =aܟ)M;+D@ 22T|Ӳcr8vlw˗-wӛlCI><:^ P-˽5ĻHN>FT 6@z ;Pėi_QM&P ę1gY-y*@;6=^w /ߵ  $SQZnUVø' Y>@y/E)S٦Ax{ė93?y\C#.0*j Ws*G7P<?ZZ3[EAƖ}\׽;tl6U⸗׳TS6 N+1t\wJ"\+l$䷸]yor0j_' M/ T_Ւ/Q, 20 `1{i!QkAQ#(LceCC6oyNmJW0,hN oHt垚TFwj6MM `wHovHY2b-v#3aolrh:~#`M~%JmÉL*e@]~σe'D䣟Z'i*/fW=1yK6zYF/|-|ڐV~pM-Eso5]5wV endstream endobj 4750 0 obj << /Length1 1644 /Length2 11944 /Length3 0 /Length 12800 /Filter /FlateDecode >> stream xڭweTܲ%-@q @Np \%w!ߝެ;3fޯS.uWS*1ٛ$\Xy`[WgU{[E{yU+΁DI)ā. ^ 2Xxxx(bN` K-=0lazwٹS?;@Kl))(Jh5R ;jb6ȃMAv Z_oiΌ\" d ~wyB '[w`sy=lgjj7w? 98ٿ߰}ɔ]M.73؛47u[?;;9\@.cf`g{w2'?i:,+'N;U'`?33Ɯ={l Y30n$fv639{Hʌ}"7H"F#}N-jc} @w7-ﷵ@JB&|oŻ4 <<Β`2`y?v ;3 ?003n 6+ǿ ٿW.?50ijJjjKA}،X8>. `o/Nn+7k|O":+]=fFffwҿc`o4vfIhg>{i :9K:x?d4oodR?*PR^Xݾ/-l:aseGnw׆+tOCNc~+aɇ#(Yu]NfQUgOmlNgwnu8 5n u]!ωE&&x;]כ>q9We5i}HpB4JBqŒ~Fl&,fU9ݿ /ӁQ.wM1Czն7J&*ٰK3M!M~R9D<_dݣY꾆zY)][I|2g[TnB)B<"Qgbj0e o,m~FFGHBŚ,n?=AiA%U8DRQ:l#zeVowϽv7z%CL;ԧ;LeN,44I+K}LCQ#CzV>Ke$O'MU%eF^']W$-^anRjQ묉~rS& s.v%rRu_E4S7 R2S\i>MPbȫz9ecs6oFx^iE(xRDg~ۜ/a'XK9hG>XyW|/r,w1DwVV u&;PV Ŋ̢ş5UñlM]3J瘀02h|eLW~7Q;.Xsj1}ZM'x:/㴕VY>cU,\|+/RG i pX~g@C [}H6YAaLoך~D&^а ;;x^[_4JglFEYʡgdmnT}찅t~V5mʬistL-H+g>/@_</:-.!^HPB2랐&SɈwH>rLTlVp30j= 4 [b~ωp6Uk߱8¯ :&KEV*# Y)g l -.OVRjdpE<ӌp"y@l)r~W Xϴtxl20 f}LMp;EGc~ A${0JL}t-΋j7]-haY@{:Y.z 0đl*rd<ȠZ/qS\̓vfj_]ybz}=$xDo;)(c'y"4f̉XE3V]0ʓț,?WP{n`cmVÎ-Ik2'#pWI[cwLaxJ usM6YzS c_M A!_w +c)PDT'㮩~z*h@o)v+z772jaDRKy03o٭eOYL.uMEN]J}cXY j4=s:3MqJQTE2?.#V;<^h 9%狗Q?*yz @}<1F ڸq`TU7zY̅{UqHFS` )jh8{@:6ݴnEQ.T"=9tbu5KP?g+:q%:^ q>?+}R`n(~x=ʺ{1)ˤm5'w-&ŨﵻBUwS.ѻy|煍ve[M<"~ "w9f^? KiT2 _P>I%:Xy eV'P+K{_=oD3p7ZJ z,gZ~[U1;# S"X2nnjW7"#eɼ']\6x.Si'% E]rzw=1jLXJįp9Zy$DHS5^v A D;.ru#CsN1NF6)-.Mc`U+e,lD5(5=q>ݢl`N+Xܥ`B0^nbE׭fxd,ֵ8搞F.Z'Ң}, tr JvzPtڗq7~7h1NUb @粰[:4b2:GF䢼{Jx}mVG LZM2?}巄'yם9;78zO]կeDԁp#uFvϦ_sa7"\SX tBI>2;LmëÅTN&O>'%,,6q ߏOeCQ_꽥F=⺬HV>\loPRL5b(PrJ6jy~Aڒ)-, iܺY5D b#w-NfQ` w=fLd){5Ab]nKr>m?4i 7jL5_ (+\<ɞq茇J}|6?2‰ni7V裏*nb*vN9Vk&S:G]yύlFIkc , TvPҀ iމ_HB:_;-Ywk2t5?ЮU̞h$GQ!^9 D3zx<>$+,pMaNP})?釨9EO6h~J&n␕vSڞL~d JûkՑWe꥓V&=@EhpiL[ ϒИRY[ʌ_}OO;amp E+$ {d}M> 54-DU2;2MTWaZF9$9|W^2Z X&Q mWM2 m,4~3CSF@34z/R hp#+%qF)`A. ,B` sTU셶9Jʮ(ޣ؈AYIb8oZ IS7!dzSZINƽ0Ե|O*D#>/ᵗm@74ZݗB*l=8U?["HnwLU؝W0?Tm]< (ok^{k~j*ɷ@FE'$n~J>K 3SNmC\#դ#TeAK>j=lȠ:o' trplQ/+H[GavEVP!vK<0wިQ;h.w*H I/Q$ߜb(JP/֨xDZ7FK  9xiHXC=Yط8C|UPX;>v-Gh/cԳcʺ|p֤kAR}ˣn)>~YOc'^Nǡ:?tqECNpoVڝ f]Ň Jezy Fz arKj,%6Iac:.`,|nyuVw$3ŭCtt.f'f*$JGs.FsfɪUGQ2{O9#~W9b%P %^&wWՒgk Y@H,?=ü)㴂elSw.z:kp|sAIʦǁ s=RÖI ؇qߓ,Z7 : R]RAB-8@pFJlKd+Z{<ƒLhuD걔PmP^ Ek/ĘS@66Efm_9%n@զRi ? U rK\HWǏIiu#id06n햏`&, La͗K,([1#u` ]ЛՊFK.dSv[Qi見K"D HJGCbpl:/8]PHN*(HE<Rƥ6|%ϫW1߰?",_68Mц{G:\arHCDAa0F^O`K'vydK ~GN%Z*3$YHaۭۋ:^>ECÈXUK5S6a1APWm:A~Sv{5?}x,P8 ޶dnr35rE$;WժGŖ}D,s.C mn/y%df'Par$]4d} YYdRrV1-շa|*RO8&]Nlę1sUtg8dK $O,mu Sɥ IH5Qw&w3@PׁCxd[SLxyv%yC3Ei&"`ˣs=sre)5jdQ?hD awV0J aV?r;g+pcWG*Ah#t$h&ut|=rK?>XLMqiy u!_b1.~한qFqylUpwDMߡeq)8!)Uҵ;E}]._xQ ޷;(1"FĻV岋YO{y2 227%I̐@Gd I[ZMωCGq$E"`\|88kWdMC,(ݚ?Jq # ئa;՘V]cP./pN\2ԉN\˾FUK,kg3|FcOJ샿O^(?N_}T^se{,,{&\di=%McµRSa@B*XUvlUGI2DX6=9!h Vv<0DX@#y bgзKZPF/d;/M)4}XzG%t&y>x|9SGX]0-C״\>Iu,yt̄)sT}>gK m6AWlY9!\6+`?T)cxSfs"%Lg[Iؤ%lє02B"#Ua/c3{ +R 1lprǦfÄ:3^ٹpc!}mQ?^9Yӂ#[?lL{JdFOmROgەγV 4o%i2Pł$2{`Ye <3V2 &d*bBmUq]([^BbQ4*OB^BN.=P S>bm4Vfo>"§=Pn7?̓Lgח)w8EYT1#t~xfi #HD&cBϿ]}1JRРxmLD|x=iqĉc&6Ͻ&}FP"PV%'[=kH`OSuxeDݽv" |b_p|^1gdtK dWeѿe},΂ .1\t1]xAuE4UViu88k.nQPs{~yKc7,[$}qsJì+pD#}h{ [IE &귾gmD6`@Id"%:Я9Ś\ 5"S<'qjk<;%AYy@'AG%)COQ7҆Vԧw+R{ 4 t#9ʫ= wIx}u!׮Cpx6^NY+"[逨Y:Wة wbrH%Q<#mZWIjx.Ͳc+ϱEMT<%X @?tU:tA;hHFLch!<- בP\ |$*[d魎rvBTBDhMKuf\"='"[]hlfL_`8Hb_+[p4nI̯Yp:'wuͺaI% !˵ 1>Ĭ" #;4ÉLZBE&lB~bR H0H*ibPppA:/An5O?tByqH CmvGN56~>` '' Q~Seo쑙n 6&Gqz 6gC &퀮b2oǁ p9m#J불B?lq3 ^QA2+- `H+8vJovGhr |\mmI6+]1>D4D\#͗yMO AKmK*?dGZwPHJH rT)عݽ!iD(VLu+aw,Puww)XizfLF5:P[{l0.|Ϟ[Y, !=/t,)/p?4$a\˧^w' 0FnB]HsL7UY> k{ #l*NDwpS)\uE!ZI4k^obShxp뚅A==-rCoI݃a*NT3Z l;_p:Ѥۉb& /]bޡ9XDj?9] mzl6^L7c3ɫmBR5UYiueNmZ\ko~ye뼧} '? WL/13zYޔ]se!uyI膔BO]0ER xcU"!D#Д{W𶖛na^ukpXs kcT#~Ұ{(#fG;)}`z;_5sŒ zKpc76Ue욱:-V 4<[4⑳yR!-0N|ܗ{ނ/f#~:)H|M{֤XX6Ȭ̤RNɆߚ* eN{{VȎMgoj'2;>2Qgs hݶկ-yEKLib@D H!R&1^~@rp2FG-tY"*r X%5g Xnwχ;s1ٵ26^$'JqD_^|Ts3d[&.čM1#f&?5%"{5?x>s\6L[V3!H VTFMl'(Z.gf:{N[}' ^jYc/3q-~GrWYۢu#_VPh' ףGYE撕26ё C :RiO1I ?sw-pdzOz&K$dII:PDmyNm$#Ol=l'2d -תv#Y~72ut:JhW5#x% xHժF9bxl3o5i(SE.7Zp#9P|1cUy)-12OO?gAStƏce g-yx-~hAqO; ,tH{n+$3A5|cy2'g'EW#mp^WLeyC,a( w>Zgi,stu v'@#ꉸzaq=ޠ:%Qn~P^,y*;'!C94KN6ȁ'?k|O}Uɞ~de0d79f'0,x桐|zKď-_Zwvu^&|3yu"lʴHzrfYOFDSa<@ 6>m03ri)E' LU`1ńiܜ?lF=!v8?7]R'5tlڗ,zji#d ~=2gPĜ|Q+Bu߬`+Jԯ𕽳m^B ۠d z)#سJtAIN;2{)kHxb@%[\;YTHKE53*Oj=BD{o72P#qjsf endstream endobj 4752 0 obj << /Length1 1647 /Length2 16271 /Length3 0 /Length 17123 /Filter /FlateDecode >> stream xڬctm.ctl۶mTmmwܱq:m}c~TללOQj "@I+ #3/@E^сGAh&jbq PP9M\@&@^ 4Xxxx(b^֖Vj U-::0䯥%@hh6TV@ #(RHA@I(Ỳ  `dnOj.D\&G_3tvq vX:\ށdffO ᯆ_/+Weqjeo0⯦?) Ku5\2̭]LKl0\A=hilntqK<[&v^v``D`aoKk?"p0[n;_DO d0Z 0):u 2\J?R+6SK)mEc5y?±6[yQ/ۅoX89-v+[Y,L^޿ s5߿F S6S C@o4aKYoW{9˓u0pXqaep/":+:[{Y_'F#2s0\M@[?fn+m78k@Oʢ_MzVkNЄ^/ PcOmam^ #vT=B?rB .`&23o>W[P̚;*0DlpWO4_:QN(~ @S&z;4~urC@p#;{ϾRw݅Lꋦ\jd DgrI֑Ǚ^=0ljdvk>\NEdf<52fj\V]#BT{uSi>L,Yr*%mY~S8q,]0 .{ 㹳jiZ!so V\~d}oZXk Mz6p+rW9ê9!.riRTNbkJwQ}$+3KqNZM92'_IhN`o<Ҏ(6A6g_<Sׂž%~QCYF,_Ko s'I1:<2mO*o U*1nl~N]YCcɮ>*hIa_}5p~K^7%^ H<cJ&ЛԖuc)? r&'K(cdobO Hz^_y000ю݋1qҜ"Vg6TO'&!iF~(N'ݶWT~:]c F}ȿZ&+8]@=\&p͔U{ʞgDAwGn2%q"&՜ə8o^z ,ӫ 4 We`+PDWL (;Tx)Eِcgj=,Ҽ<ٴɾ\g(jGWF+^.ޘxxĚZ9enɝt_ADȠS_}\BpJ3+KY8*5 z:{{֑i +tn4?XTi U-`uXQ| =NvW8m;4/폮Þ26nlQŕcX us6Ø ajc46:%|cW2K+M8\EGKt1@ja`'UIfUPLzm~ˡu^|tߓ2J/jLxcvAbu%NUg:k% ޓ62& %\UuhMnĝ%F$"C0$\w![${FFN~`_Ox)XGpŒd/ 0V=*\l8{9Q)/3-xmŗ?]mrUu3V&q(6z\=2%9xq^-ǥ=6X0dY"{ˏMϵ^]xΧْ}|41ᶽ&3PxVYPF bx L)Erp5űYjN\R8=n.˵-b\RUp&:oiVg+ED#I܍#dX;g[ٳwt:&F[$u! 6 4GKؠ-C. ,ku:+v[}d VIe_PYq8p]ٝ$<ֈze*p="Xiև>uwymv raPT3kKug錤Sj2Ga eZdnx e( yL՘_DML _KPu$U`-~F"h:J!z7,G㶰U\8dDEVrb4$pe,2PMGo^_Ne'-{2aTU<. G+1IC"{_v?(40C,h9cm#WGeWsc^ 9Pbv-% w%|JfB$&@4w-t 'AA}5u=ԂS]xM 5nUǘyK(JTrk(z|Ih BHf$nF% ;9Y+D'3x`}/E6эԙVz7o,]Ctɶzjo؀/W T#%vTK;;j">x|dդ,Rp ]rD,;R/6I"ƒ-|\@`Ns GgNaẈ!ҭƋθ0V@]!Ȩw̯39pYn_v1YSqNN.x"!\moS;8Eߝ[`j xVp1Zj[ÙVH3dǜ"fww荃f&NJY[d\U)gX £v'GIǪ~yleb=&vΟ}lNuvloD?Z"wR}J".7'pu<(ܻihq$ 2K1x˾I iT<-z?49j}O,_|JsaGW!k8` @oI;tNRf\aEhͼJh4K`fs<} wpݚǬ6[]B$ExpD&y|d@g{]]\TɓO%7AV qE`܊a՜b#Ġސ'QvV?4?ΨM E0N|4J"^ qp; 7Y+@6*Jľ$KpXfvɎ:3ƩaoJ'3pfǨJy-,2G9رEIxBaY9 ̣`ʺ'G Iݷ!{)z2 !TZHmᏪjTPNs%i 18sc^`h\Ǵvpȧ{K:* Uʋ(9E5|888.f:pcn&|BGPNkݕ'$~h.-C2̐I+&ĶԪ 㘷Z$Fd4_] d2f52ol&7ec-ۆٗJVDʔ< 7eB2!!I C6^D~KSLD-ihV ԉ1knR0%AV[C\o7@*#Ëfd }fY sXA' 2qeW R$!wTʗfrW?&x LpIg'rrͭx1,HJ׳8RϿa/02 g#1ϭ=aLb)_^"['~"9sIxQHe<"_֢X(ZRѪdzDhķ~Jr\Ѣ &c$90u59_^9%ϜtG Qa``aɈ;FNsf*z9u8"Ұ||88f vIsg!}? `ESnkkn q >ItӡgQ{{\1;쒑UE0ݱ%w‚w z{(;dLV`/gNS7X8ESSk7"CPx C-CxGb _ \WhUĵ ?Uc^c t0.Lv2\ɯGp?VV D0aYH% 2Cŭ"Z@tw3&KX$( kkY'6%zIvH}o]c~y,UE#/ [w|_w;4aвya5eD]?`M*$>%!L?}+(ocpUlĘd^15S,:u"<`(',6&oJD:mQEoJW+ʃS8KT_[(紺0RN %<ŏv:/qvP>}Qm6j"~&AռrIJX{k+;9A5}I .o_ 5>+d-Tx,;!x󗜍Y^jxb9osW\"JFQ)@qjB'y hzͭb4ݰj0 y}@`|Ѣa[j~ ndѺ~~q{vt2C󻈢q_aVD%gݺhiʊ.VnGyKTkՉ|Ϧ.slBN ߥx@HIo A#ſ@ˠ 52iTsDwgwވz䶝'HBӦmQ62Ǝ̂,b ^`B%UB:<l1W׿Pls">yΒb݋,>L l'J\Sj᭼#g ߛI*ǣh%وTš˘1W[M2QD{XQ .PoLme(h߉񮘐8J/V#[Bv5;J{ޝۺN1;r#@h.i`eTxJ(zҎޱ :_G&W!ޢC% BpnDy|SjѭX S߷vCqF-)Os޻l /j`])6cD;R吾!oxo,* e+l/J9?ѵ9Ы ZBR|MgSkrp2RPJF8Ȝl^od}o.{bEJ9qU&2R'{[vuUj&r:w[^@NpQ|bT]txe9@Ӓ3K5 fbcַ%8=NȂo:QwëDĽPFE_32qBeᱽV;:0kpTwmxiN~EK,\ oeUo\NAB&lH}tQ6O>YUFkVOPdE#a5^4V28mOKϊ FptԪaZ1P$%vJݐSig<chma(f3*8c}EJ݈Feih[8 XR}1e/wdw?>^JA}QpZ+ޭIP6՗ hKJb`|\Rl]Hψ0_h@7ۢ>-7&G2դ2NE>FR+?$^H0C+I3Bp[_ɜ։~!FGqgвOGQ)܊/ۏ׃2JEN>M?FwB|7K!'J i{I @qwi^Lڏ>p$F;},*jl@Eͪ.IFyۆ%X/D׀lK$",nvn(hGc3bACsjgp6È&I>Rr -w Nkԩ|8ueL*AnU1djEm_IV6]5U$sy&HC7˧.˶, 289\MCA/C\Œ} 8fFsΊGMexg:%FW6Lw t_\=nh3V0 {N0S*d݀qe93)`iC:VatUM_M݇oA$'^[:HyEc)h "% hpiV\ `>TzCVΚ±ղ=yĚk);?lC aڧqdĽbd57lğԱTBY?j{%hX:[s*-G~o(qq+v ⻻[F$!o(Af'}j@H['k_H ^?+ e)$N@"X5zS,|}^~QSqY4a7@U(UdsQ9i\LkrhwTJIi@pm膀,R6Ώ3+;^Jf.QBȕA 4p T>U9o}\l󃟢x2}ͪ"?°aFPhb>Ccm΂)ohT9N5ӭޜzN/~ 憿(+1*Bã06r>Bƽګ%]`ѓA9FT{ {lSF[֦C`d]p* 4۷ZT\jTZd6+^ ߗ[(!l61U[~u}tL^Ex\就Vz!5hjB dtnRt㍧fo#|}7;BhfYjZ rHвPVXfns 8-ålih [PgkKIJvGS*0"ܫRThYz=42)0"wΝ*Fi7|o:¬O)l=O)J$L֜QSقVO:HM_~,6L"sTj&]4+__gZ{ڷNhf>ANJ-&(4Q(/zxZLaq>(h_wJ 푌bd KOgԡ[T=e6O;Ou\vTk_0}F͖\wo_C6,a }Lbg"s//3`5OyQv597^ rduY޵*̓sTS##-iVjíA@Vǧ#K5q՘`2-CU XѰꏦ~MgYr}kn#q bL@Ьݸ"e;xW )tO*nUN+y"yDžE,Ȁe0HpU;6I6֑\`״\> #u$ZMGl02/.ӧR0Mok0`7Z+75!,dBK4zCbJ*Vg& =sB o/QXI˄$&ZF\$c^ IqTXȶoqiWtIЀލL/dG0ѦHO`S?A }2e4@hU.=C/b->U F,ƀHjӇ**{^ה,yM70o,?!D`HG /'&g-\( d/s˦%kj8O%0UҪIX4r#%?>F{q@;idJc )j_GKe<ś3쌀+9޳pU_F9~Klt9a q c'/6+?E,jD6Â|F&0}GX6\$6t Zkp˥b1:E5YWuEkFJm$8%vRڃha0cU;ף5^^:kBBSX.)6:K 4چ"U?coĤf'W`-~ii@ ]'-+eP/jzv Dְ;L(UAXj$EFa s6Zרy^.GhC2w#8/Mn t?~)V-Z$_bfxy~S=o<$),{\}: WOyGug۟Vu{tڠWRb` X1\CnBpSZky ' v}"*Qt3|!R}~Xy,9ƷA7:A}{U$aq9#EAcrW{C_#K28 GiDF_ҷ)Hy \HBK ƼI5+; qipC1p.aw}V~ ~ZVW!۵ksc*\8b; !/uud΀V薭4S-KTe|ȹXS4Bߝ`[Fv-PqkRa$ D;?W6RiUN}V[@m4Z_[+x` ^FCS'79+]Gl3><6[ǹb a3qx0p .fqZ595VWlFeqm+CPz"<'j #RZ7 dNj_Ըp<%P7nșE k6 mVrisGP wUoy.B4.d*FsK\xql{D 8CD65C7'HZ!)|_igTu]+_BB->3 W*SzK`F,Eƽ*VN,U6I a Qz|Xn]22 w"iuP]f%ܹLƝpr tݭAo=ԴBYR*bɦ#- b7yO$tN9ZPdEYExY\|)n|p)2q[#qMdr\0?WS^"MMŜK9WWzk E󦯮ouQ; Ml ھEk!A~y,¯wlL~5OH#0HVYL0ݧ}J$d!ze.F /g'hm1!|PΫ ʋd% ~JDdI,SWyծCsÏ8LiZYGF3ͭ?(t. Tp°C Ԇ^N.c##O/W oT_ReE`wwvz+~هN_@uuD@p평g~ iJB`1jĹ8]Ԟ+#EĻ=cקUI㖜GZFkV4i>]zg-ᆤzV+k )Ӑʊ>C֙ lsj,M mnhr2b~٣d7fzvOn%`[WA612sUyс/E#nfe+a.1/hpע/{Sǫ&'ϑQMrMg(&w9[ޘ'*dO8z7IP((FAiL`ʛwL`r6-R4&[ tܫCs`_.~'w.PVYBSkǠh$>LZ]/L(>܎'+_/]۶#FG*+C9x ;$XAA!G2\fFgD53 ̓& صSpdNovb/l[H B[tā 8lfmŒkCkJxfҳ6 1+4ͦbN# Ql" m1OV ßU:b[֌ՆsQ&s{kEj `7pg7QtyUyS *kcW~O`lQ]1V(x̺ql(EMV*,/gD-C!jx-dX!9&w\@#b%gP*ftpeG@_@7eg`1j A&0Y@![+hqOTQ'5H?:~O(^19G9Ҷ+ue,64DMkfci}*1YW\->EK[ϣ2]uÉjb g*[=f+t#.˞O J{Ԙ8)A,JREQBH1z^x06m""z I VP>;C.)X@| RH \P-:jg]#߆>k8F zr_gPެϼ{.O&שe1|`HzL]ZPj m;[ ,21T7UbL 96-񰊅"849*Vћ ;ǻ'&l^}FcᅣV>q-Y}MfL%Pџd f. 2Ў9+Rn4OX{@2!Eٿ$=P=G%]BB8녍%$\3wv74H^{bO92%ȧ]~O@vjhDJA 6&TAʒlۓǞ~AcD}wFmd' r{rލgt|w3{[o"nSmRQPtt{ktJKYL_jr:jϖXL߫AeLֿe d狒 uN6Ol$['0V),:}bRnDBL;S өc  +#ͼa]FƇҼ%Gc8}jv|=C;~$z|ץm})CdYD* s<ᩞY |%3 SX}E)$_^p"|JլesK/S:Ppv#3f*uiH>_z.%C(*Q}(h6r ..M7n*=rB/M1s BPj"3;v J&O[Ja ~ng}[]9V ̥dzKP-R5}#-_Ōk @CbB)ܺq/KhWxẙ^C(xW@݆BR)"5Ύ/yC}`{N3Z Mxֈ2!PiOplKh2:j@JxK-'SsNv"jyRT0k7wCMޒ14,1,5E#|ES( oE~diAٜ=',1;k҉C}7値&j{bLT2jɅ d|WT x<3!z3kc M/*g9)vk*f;}q^"}$JC!gCƔujfwIVǢ0%㗅Cfce mV]RK[2 Uz;3H64 `j%s*]AMÃ\XNXNp|'Z]e5yyǶ툿k4]!BVH类UA h",LLǵ[I}=(Xyܚ*Ra?RYCҽýp@h{[O3ghn䈔/izM4s[WgL2nXѷ]kFX=-w#)Hm)͑~ |fsVcq^#Dbt;%n:R@07Xy endstream endobj 4754 0 obj << /Length 900 /Filter /FlateDecode >> stream xmUMo:W5?$R. d9M eCkmCp;;w~>|3E_?O]5߶w]Occ]=~?}Oyh9%?۹׬B|Ɯ>);vw%g43>\ 6 EJ78 1{~`W(-;]%=xe_,b+-O;q\L}UI--=BKE1p[! Mߊyu>.N5K)Wb٬8i[_uʕMzQ)V(Txޢjy!Z2P="Zd0\ÃGR\).2*Шa!U,H`+j.5Nα@VK-x%3%AYӀzΚ>kP#5m0Woþj.ZT$X/)n)#Wo(oRZ $Kp4Z-b\1ܰJ P"GXQi/8k^Zq:Zs9dB )sL-7xJ`aɽ)f$1 dъcCZC<73JgznHȰYɚTa,_-O87}KԴܗLloK+gJ.GZyVc48Wt]:P~`rZq.n1] S/Pu7Ue:?&?!d&1yHn5)yғBx#1ޞ]Go׏M?X endstream endobj 4755 0 obj << /Length 665 /Filter /FlateDecode >> stream xmTMk0WhFG*! miʲVZCcYy#9햅ļ{3񸟤e&Oo]&C]]Mq>zwt߉Ǯ)n.pCx?nڽVgx=itO"i [\l\WM}'ԭ̚t4pXeȉeU oq yM\-CnCW_Ey}wP dZz891euB)] W-\v\]~[S!8&+Zce"'2Ɍ5I@|"B2AQhSlLء28a}ɑFq5ҍnnbfǮCG= Wܢe$g;A,:sx l=NOTƘ$0_س/vЧQ%~Zx pX2]$^qnaK??q FqMyc0=) &l(mi,3|d &\c ]͹&ӈ9w{d-tx\ \cΜekqLJs?<@>qhx .׷8wl~1V<*m"mmDa endstream endobj 4756 0 obj << /Length 664 /Filter /FlateDecode >> stream xmTMo0WxvB+8l[jWHL7RI;onDo3ތ?n~<&Y$ŝK_IsE77E[^N\5sߖ;7|[lzmS_*7F?h3΃;mc-bB`ew\_7oK׽;(2Z.ETz}ܟ~o9V^MVK7-\f\S}[S!pcSs|TXo1/ȡ aeuC> stream xmTMo0WxvB+8l[+ML7RI;onDo3ތ?n~<&YվI|/ŋ;t硋nn\3<:Wj\=?-wn6pGۦ|Tnʽgxté7~qzxKlqrnX7UޞMjuSAxHiQ,'wͱ 1}hW7q{UEݥ-rG*F>NNL7u]tNhWS;wE )b,#TTHy=)9>*QKr7P:MȡQ^s$LD6aȑ*s.$S56`>ƄmÁ#TL 5kd}WXssc*zRh/#? bE$L|ږ8^y>eSQc̯bV̯cNa'_OAJ195kd3EH@8ܰ%~As*=F 0`{RLPh33Y$LƹǬ oqMsȼ tx\ \cΜ-eksL ?"@>qhx ׷=l~1֍>*]!MBa endstream endobj 4758 0 obj << /Length 665 /Filter /FlateDecode >> stream xmTn0C6U@"mTt@;olvR3ތm~<&YվI|+œ;t羋<]3;Wj|{}[ mmᆂMv{Kt=c_~B?zxoBS6wBJ)X7UaMuSxHiQV,4$O;nC-bD/OCnC_n^ѻs׽9X2Z.ET~{~ʶrn_~߼h!R,6ew*ؔb%k e+Kӄ$a"1x*s.$S56P>Ƅm„A Fs 5577vرϾ+uaя6R:!,əCxg+ѧy*JcL|*m:fvuiWUꧏɩ\g%<Ϛ"sÖ0_:3x0kjhyIYx0aCnOg3$cx0<<v5O#ܵu7A 6*sZ ZcΜ-ܠeYksL ?"@>qh|tngk;dGGM@c endstream endobj 4759 0 obj << /Length 665 /Filter /FlateDecode >> stream xmTn0C6U@"mTt@;olvR3ތm~<&YվI|+œ;t羋<]3;Wj|{}[ mmᆂMv{Kt=cߚ~B?zxoBS6wBJ)X7UaMuSxHiQV,4$O;nC-bD/OCnC_n^ѻs׽9X2Z.ET~{~ʶrn_~߼h!R,6ew*ؔb%k e+Kӄ$a"1x*s.$S56P>Ƅm„A Fs 5577vرϾ+uaя6R:!,əCxg+ѧy*JcL|*m:fvuiWUꧏɩ\g%<Ϛ"sÖ0_:3x0kjhyIYx0aCnOg3$cx0<<v5O#ܵu7A 6*sZ ZcΜ-ܠeYksL ?"@>qh|tngk;dGGMc endstream endobj 4760 0 obj << /Length 799 /Filter /FlateDecode >> stream xuUn@+HɁkc{!5&Q^ үル!zya/W7/~jyld}{9}N=C'u\W;oέO*k`~?''3Ɖt3\;WS]Q?SVk ]{9FSѤoG^ 32j$WC0h޼O~wC4Sy<&>U]Rn·ÛB~,{_=ڰfhm_}4zu|sH]Wb MLD!E!B FAC\dQQ(%T<#h^QqKƊL0cF F͌a._Q mPG9'+X38)+ι7\'~5:r%%Β뤧$1$܋a %aN*Atg&W̡`92/X[B|fAlI)dKdgI$[d$[H$[hv-|9~ddK%[w-t--d ~)BO)Rd dK|ɖNK)K)++Ζ]Rd]Oz͜|x8?<ᤥNO]?p@}_:h? endstream endobj 4761 0 obj << /Length 550 /Filter /FlateDecode >> stream xmSˎ0+$1$#8h@Bܰp`~ +8*=SJ]sCM&ESݮ`w z\ħmbo'ޚr028~}uHXz_z.XA_`1o"xR:bct\$7҈٘TmH@ ]W0ywznͩV+1r]oś}X 6g1ͭnm{!^ ' bނP48YhC`୤\UY=0ZĎiơ 7([4r;"A"e"qDgs"2dK$#В%#KDDNs5&]J[/G endstream endobj 4720 0 obj << /Type /ObjStm /N 100 /First 906 /Length 2837 /Filter /FlateDecode >> stream xZob>X8s_@:lu7 hGI74d[-䛃~GR\JARr5^.!JUڊXh3a6cN؁'zܮ{@HX*M'ضD(l=ۊ{K@mA$qN(`z9"-- ö&J`[ Q9Q+GShħ шxhħшOID#>AKf >A|%IC ⓠU@A#sȮ"buvY4DeRfQm)E$G%Xǡ,Rz(X92I\(8Ip@++jZfCށ045, i_մ1Z5XԯUQNiS^w]8 v*&G5 eynN@s?lpG 2_PG gABxE(g| L1.6 L%,b@ ri1/\ӧ=zx֣'Yhbѣjc„Wl|"#B y?9; d>-yI, BFUOɏ?ʹ=ě=Y3]1,=?4+2pɸ~17"{{ysyYO1{ 9Ǜp.и m22HK %2U'&̮mЙ_L'?\gfPp6<<8* e &x`[. c 9얿0!Hc8hbCW\?sߑ5( !xLn#GE |vЗWs2yk)x~ȇ:O5#^/ eU,{Όk,ܙ%[zaVt4y2L8plymu 墥hǻ[3 $LW2(.y|qO*X'vKu۟674֘0?{~a54j︾+8,RF8>3xjҟ~=ecunCY5l?56Of @?tm,: g9ЗJ+zD1=w?[ZjtSOj,mI}gT Ҏ~"ƑN߯7' Y|ѐ gN~!>tZƯAӫМ(acN굯-Rlw{WٮB.sK~;fwwkXBvA{*UZ[n{Rq gnR++<e|tnyJ^@Vi#eLi]ϱܣ#X,sgD>ыL:ၬ|7oeM_QFG8Ew}+rәHq%5i8iLpxr"Y.Sğ!ބNi=0$'ۈ- { +d˘q L?mz T0?-MI!uIK$M"|ӄ7 endstream endobj 4766 0 obj << /Type /ObjStm /N 100 /First 902 /Length 1783 /Filter /FlateDecode >> stream xڥ͊% qI763^ 0 t}ffTLP&sY952N`Je2{x"58A+3X~c&sywO?w_'^xfxjc^c]~l,?>;({7,5C[W|a6K^a!< Nsrz<_zu]UBoZjB_1^>^㚪ER8k*g1 Y算u-y\M4=ʕx}k^kͫ8_'Va-~?k\z3~Ӝc\bZwϧ߄@.a8 }0KNI&a0" $\pF$aKNˆ$ 0r IaF.a8 #0L% 'aDI$H0 #0I&a0" $\pF$aKNˆ$ 0r IaF.a8 #0L% 'aDI$H0 #0I&a0" $\pF$aKNˆ$ 0r IaF.a8 $` endstream endobj 4770 0 obj << /Type /ObjStm /N 100 /First 930 /Length 3952 /Filter /FlateDecode >> stream xڕ[Q6~_Qؒ%[`dw>T=q5_*S6-2喨GJdG$Dy?!U!5 d A ~C&%!A~A)Eor0$ *䀘 I& , [5͂~) Tb/=BT@N2t9Wx%(>WBU9 r ^ +_6pٯn>~'_ '`"Pp` B,5Dp`rK`EAv`y:`S휁Mu1`<\`\)y<:@@O 9=@h.z @\ )*,MȂ(.,,@  A2.r @ȸ)2@rSD<" xD@LHH.44NHH\)I<I @2 ^Ӡ휁=A [@M8^rw*_N7^yݩƫnPt;x .WorAjTj6(\lESWؠru*WN5^YթƫjPT:xE *WOrA1jTJ4(\hES WРrs*OvNSl!x N}Mkyph6,SQ֓Ys?AfØҁﻟ>9|^',ކ$OxTQ)uM6 ה&'O7TLCs}G=5zk*hSMJ  4Oxm |~ˉ'H"J"k*/(`axVSnxJk 2N)02z :Cw)88@jL]5.s:(ʵP.kT殥`-Cw)޲l)F;7?\Wf&HZ'=Uֶqg53مcO%?5^^^0ub?\wDQL*"mBQ=ž|olN?ܿ嵭c=:tgurCp,=kַ:+$W`wHTch jYSݐYű֜l_uk5oq_dw5'刌;'ve`{]9sdYټZWr;e pջ 33>Ykٟݬ,<0pư?~xTH B7*Tv[YfDGTYLn7ZqSUXU~T=yq/c%TvP˥1(4"<7w{y手 Aڌ­/(t!TG3gFw .͹zs.vIEY!nݲy>7d) V?q }='ddnMoÄ݃~&>8vqZv/"t6Y&j[̹PuAml? Y7,}yxX!v<3S1f*z(lމ'z{Úp^c{1ko#ڥVPb*K#XP5nh1d䅢hoLpb hBNd8͙\ %'iOKt81d'@&=cŭKy0ųu^}idyC[MS?Նgi_CȀ9c};NQ)66 `x|םSnlfaɛrP2ڱJǔ~^ڦ_T阬7^]Z sV+CP.`oC\BٌoKJ "Rk4^ʃbP ]GTgUw;xfת͗䘬wfhe]C_ow:`nZKU}{W[6lɈ/"wWt*茥-<~.Gʡ :uٔxJq-!>A8SpMr]_mNanrgP.ǟA`.yYg;^|6v]҃.,I'҅,;q"-Y^{Ǣ-Z <%?iK{S^TtB*/#,<#^KqkIU(Y V:P\`ֈi};O> stream xڝK8+|C&+&氱ٍvಕrn[ǿ_@9I#RvۨҪ[٨(zCJJiɭK$+ +ŧk|J+dQt6F+ g ~P @hlXS\9 SZ5aW<,x>kVt8"=U_Ut 1&VIkntDZ%kI *%8`u 'tPYE]@".c0w82j@ \DGp^ >jvyXMm`'h^XM`gմ"j:F,U} N`5c;*;!b,Ypa\Yp@a5"1 Vs01>jt(MVT^vAy2X8y\VW~6_a3V 0>j8BDfBr'W<*<OALx|ՀGX 4%Z%ghX-C`TҝJphug0!T!G 5<:8I O0G8= ?V_/~/oχǯoϿ]KOOaOo{d\'u;.OA.=oQrX>W©y1:@_nx&קMbk iwOͷimOoaȆ%! DyqX8 Kc~z1<+1ii}1E[}=nbUqt25MY|n\ҡ%AJV\q \ S}/:6;6Mhؕ'7 /#9l~-s)YWTeaw# 举C2'X=KrlCeg tq== ,zK_(\v vk}wa-q_ş_wkApE jq]yu,O}X}>w"{XWDy+_^.-gqRU #51OzXPY5=NL ‚~$ܗe5Y:^/o wFrDpñ ǹXy Ie`K?0i7̼O/8 ϻg;]!;h꿓#9 \[ H8a1]~ܛHҿ6rL~J:dU5%x񼬗rK3 Ő)W ?^ϔGΎNLvY:.C :{x/sq)eT<<M8c:4~0.^qzJz|øOO &W8p/dn9e|84}~8M~T{t;-G d↶K96j*|;\׾_v,Ȝ#`7G!8\Toʬktde0Ԇ89y^P]{ 'ˢ3M;G2LwUو.d?6fbH<:[iqܾ? .1z?P; }Gfn'tWE}Ō~}Ll1E߀UcqpNpYoXVu=O[Txk )KT)M/-9bt-GK/ ǡy=tQ/a=V+src}k7&)(row|maC09|\\l^wT1g2k~@ { uoce"Ym7s}m;SLu)oozxU>u#T4g+޾Of Y[R uM>dbc?/zH[c5獾7)|=[O}Lj?2kfeuze|f]m596hE ߵ߿Do3?O=.)˘ cTdį†e8}q[z?pzn??w ^|*HѬf4N8;a?7 ”o""QȠ(0EHj5N؜%NMVq@n"PsB}>V!3v(bA,"9}@ЫbdRBXAXy_+[?ez4R FZHK-@ĥAMWz5 f5ҌNVE 2G  AT, O^B48#a07yyA^feqw˳2^l1iyiyԢ>TO^><_ku..B\+0^BA1q!:MG>l+WX,~ o?;eg"$T .=Ç˫&2&"!!p>val{0_ O$r=. =_l4C"|O,9>1Xe 3LO>0;#c 9%&@c"{ ӒO9J*trbe|b:'0T&f /LGn<+{VQ\V`n#|mڭi( P` 6([c.PL͇ծ+qxN%`NǪGfvyrL]SR[lرWd- ;|DVE|C1$2n@B,$2n yR%.@rb^b)7`L0! )n$<,#a97"s#цuѠ_qkRDB,G${DB,G$,Wc8I5` DOM %s: 8̮hX/Tay=!0oLgJw8Lѝ_74 C_d reCr/AM'"pb*ĚH.9 ដLN|p"<-4P< :(*b>x&'QK̓ mjZ IVЂذ%\9X+'npk{/ܖX XEC3%b-@#֜@<&!A07<憘9 DzZ h8a<1ļ1ObEP{]\e|@ MZkL̽\Ix;l*ڦf[m V`r5!b+,!!F<]L#^l#ۮLNĽ!GĽ!Gă؅F8e|@ BYEjn29˝3l>YerKrg 5A*^x^~f=!1s# \` F8mY~b.d Iss xG NN ^'yEb,{F #('sY"Fb\1p##6ˏHeř4l-*+N_2!y#GE^~f->QEYbl TQ A";\DKEv'L_Me)0RvB3Mb4pbPAM-fFSr焜}Z h@E=fUs 55Q\B [Ns)hj1h=51IysCRשF<)V6{SLYFi3S4~r.s65) hAe]6 e ';FÎ{ϱb4?u6%ԅn%#ELaQ ϋ,Bp;i@K$AI5tYתy "$sS195sYb. ?׉NiHȝ@ _'ސi\G:"k%\&Ḥ؋Ḥ8ƑGq^1[^lȋ,BAOr.DśiM_;fZ'.LDRc3/BRI'} B;M/ݣ\}2El)iζi~4ij%NVq'P[3!w$BI^ ÿKwINe: +͢˩8"╵QD|3&"MQ]wFt(K^W4FtQ0EEΣ{ HiFtҌ&z4`ivFtjh.Y z]4 iFTj 2i]B3#L;ZwRе{rI8?'4OMN@nSk-b r3m#%%{1?MI;KЍt,A]t=8܉T* endstream endobj 4921 0 obj << /Type /ObjStm /N 100 /First 1041 /Length 4138 /Filter /FlateDecode >> stream xڍ\Ms] Weԙ:L&3ɮ.e,?j#i =:;1]@pRy-e+]Q75Y/Э RֶTZVߒ[&|cdirPPb"[6nkm%)zER\GMaoXUV(-VUX$FU=l$6)=&R@WXB򦩄}R6-%z*ʦxK7 We%w[mѷo&9zf9r,VZCJ&)lR琴0m=McS ||ǀ>i=<͊7'+ +b|S"0T[ |6se'?5ז#?[n0Rn-+ևwHajJe:X"'akO2ku Q^7/=@wS%FjU徦XS}^F=Xֻϩkd+o6k\ϫ7]rhh6}xӵiRtsmCo6-h&צkKM#MIkSgtm3Mfa7]lEӵoh6_ڬcJM.U}3ۏKB.$7ȕ r#y]m}W_p6}ᄏDH$Yy\wWixj+7w˩?Fs9{@ :Bz#R>N+Mzqz@|ٍlr0 gs;dt;9ya ®N:,/)J;*vL!g des ڱ*@'pێ '4Z\^ /Zpl@}ђjxn<гaVnFktz%zӫt>':uE:t>vz 9a rvCp#,;\v?m-3A;T c&$jaa@o4ӊS8hc_V6wXxym3ntb)eybzuaFdUͲ۔`eOtm,{6hcb2f'j]:!" 5NM*0T`ΧpbJ=@NQ@NQ@.,䔙rE"3@)0*Rs`Gs>k4f$ l, t &NsTGHB, BU(@C:ͩt>4:, \$>kćQpM rVFF_@),9:m g9: )5(eY9jICt/ { ЅzXSsRJiOɋЩ,+sJ:d- 2(eY A:@Rtn4ys:XΘ ,PjSqL3Ac&\w]jƒ31 ](5H@I@+_ uK. IK\RC5+U0fSYVAkYL|J- YIPʑ *8#UpFYRsRL_*f%`3yPF~VAOm{1(uYT0J OmL*(#iUPF P ^IȲ@`j(f#L X9Se|2ŵJ](L iV>4+teX:(mY40M~k`BN)ͪ 1@ik )tpi tHH#5FYTm.Pڲ@iBk )00\U6$tWdHzgHJ(A4MJ @Ù-)b*`F"BGhTxl@ tƔ_6`LTtEHL"\B{}+#&NTaŧGpj mBgS\q%oIQt`LѬů)ľ[WȣQF?\Ϗ_xg4F?6;hѿ?`gmoo?߽:<6iß69^ JyvAtgpxG17w}~{`}ׇ}G~Nq/^|E3pǩx\:^2zΌIݳOr-Cvy6Tjꙇv/t;7lq>}@继G( xҀ' xtv.5~͏og³~0<Kh1yXDpX=~f#= *J2OǧO|s7w׏?}e PuRVr΢g+w6ꀡ꘬:L||>bI{e2 e̒ Vq`pF:'$ǁQXuجf갂_=/*_4 uXlۈY qSz #O:xYFo+NJznIBIBkm[z拮H ZF\?fuͥ, h @G o2qLs93S5y7hѸ ',<_}>b|?r7l4h\-p<_}>vOËT=]^y)95ň0_b_NbsD>bqxN endstream endobj 5045 0 obj << /Producer (pdfTeX-1.40.22) /Author(\376\377\000J\000i\000m\000\040\000H\000e\000s\000t\000e\000r\000;\000\040\000F\000l\000o\000r\000e\000n\000t\000\040\000A\000n\000g\000l\000y\000;\000\040\000R\000u\000s\000s\000\040\000H\000y\000d\000e\000;\000\040\000M\000i\000c\000h\000a\000e\000l\000\040\000C\000h\000i\000r\000i\000c\000o\000;\000\040\000K\000u\000n\000\040\000R\000e\000n\000;\000\040\000A\000l\000e\000x\000a\000n\000d\000e\000r\000\040\000R\000o\000s\000e\000n\000s\000t\000o\000c\000k\000;\000\040\000I\000n\000d\000r\000a\000j\000e\000e\000t\000\040\000P\000a\000t\000i\000l)/Title(\376\377\000l\000i\000n\000t\000r\000:\000\040\000A\000\040\000'\000L\000i\000n\000t\000e\000r\000'\000\040\000f\000o\000r\000\040\000R\000\040\000C\000o\000d\000e)/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20240324150938-07'00') /ModDate (D:20240324150938-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) kpathsea version 6.3.4/dev) >> endobj 5022 0 obj << /Type /ObjStm /N 23 /First 218 /Length 702 /Filter /FlateDecode >> stream xڥn0z .Es/T`8j K /)yR(igڔLiP`cIjް,}Y쳈&'JfA&X$as\ѥҤSlȕClސ5!QC!X.Ґ=491[$)k]R!?XocY+1< .껺Mju[ﶯN}캸X%c qb[d‹`@0C y/q/ /P0( R%^ BUAY/!xM` < < <O`D'£yh((('̏'xvPPP(ȣy]^V BTE D^hjD8?Wc>h2.w7_աM>W?Y\ozz6b*Ei!ChJNwա^?Vc~y(NY?O󧆓ձ=I$4?Hd'v ```1,bX?}{毳;uޢ1bK:'~ 3}Sa  m柉'&YL/pZj҃gJzY[4j (4R>+w7{SھR 5e endstream endobj 5046 0 obj << /Type /XRef /Index [0 5047] /Size 5047 /W [1 3 1] /Root 5044 0 R /Info 5045 0 R /ID [ ] /Length 11981 /Filter /FlateDecode >> stream x%yl[owy^I^E)[^䕤Wyljj@)I g&E:/Fd^t6HiYmPelE–4]8yIy./sJRݾT/R?_O%'vI~{#v2XދvV;j3 ΋ֈ=;ls- };d3RcQ!+T! a[Z#&,6"*vHl&vBlCXN%v_,os7Rr6axHtظ.M>b;lƁ8$~m6C1kbB 8[mMbqHNifg`\XγpNذp+%b"\ l^+bqCU&v\,k:c)pn[g6$sZ+mރb["C#bkl>b"mS{bl> bE_jZ6_+31\^bmwb1cb߈ ^0 =+flFEj7ؗ^q'i/b&,.-ŦwR션]{d'b#8(6*&[NL/f5ٔ%Y9seⰘsq1yQf/nsg^4*Ib$ϼi4.swH^ U>3a`_,Ih/.Ԧ_LrȫE} 6 CbE}ebm6E}}☘􌱰h/p34nG91';6 bN(3U=$v˦xElh/^;'væxCXlƃo=f3^Xn*dݯ!g،x_,9FbϗlƁU qL#bN3l 8#^kJ̗LH7bHgmF2{r8Ĝ(fNیI j&Š"03f7`{cuy?KO 0 vVe\1+`K6쫛Gl5bq\ b:06CV,! Ja͍0,,?& 5lnb{mbqf;.l]b!/os79W39񇂘oxHtX|]6 da3DﭭA<"'m6C1bB 8[m18~qHNibm^:}.psbqy.˜  6/8YƫpM,^7k:J#n-lކ;b>w7mރ#x Ijp<)g)f_/yg7*Im|3|ސ`,Vy$?eʹKp^j <7I4<.Wm.mpN d`, ð 6 #  `7 {apF#pq8'a Ni8pS p.eWau*8W[EӰ .Ga7܆g Up\+^X쇍p\̶JplQw |v͵1O\(%Lg.iQ8w{n- w.|>@O=~&?P!~.?F?A sjssZ#s_z0D_KR~$^mȅVu)Y=W]CvC\Ux$;eTxc3k`vQp7op^&gk}/[0k17>/,wm_@[Wzl-F`8;a{`/p!8ԙ?G;1}~|_p k_6a  G_'kaM)r耭.og3qsp nԅߊb]p5._`9;|{p<}'<1S3fj1f$uě| o>gs;į _S!)RS~Mvr7 1"aj/RIr<IS3~ko-Io-JHo- Go-Eo-Do5olù&kR&kR&kRm($#.p>kʥ6p8 eʥfP>Rr=Zt/,;\L k)坩Uo*hL5f0 T3ja I?alՌ8 S`~*]SI؋DID93dqMn:HM0jBM.'Z24j&k[|ss葻IjD÷}ߦʨ`,ؗ~<O[a TwZw08 \n)4vPT Jc!y( `?ocp8 804q8 <\@cW\pTG;lq.ympyyƴG=8~ . :܀{p!I7tnmw/G`cxo~[o7sx/w>Gz%ܼ];Ro^/Ƽܘ~6GMRK?R+wHCc5/_70Ȝ 淂pn!M1&cKtW̗e?xn^˵y6/e|Lpy}竢c5/ԼjDoqR@2q|><8I5˼|c Keɼ%1?O<?E9|y%[`>>py'I Ƀyy01@b8端ԑeFDI$Y+`%AEdx50X`+,ORc/Y |2< (w]|oAuhH^T.N*r;쀝 Fa$Iu9{!.gPv%3|pb;WOA̪K= 1z̛_×U03:|̇G%]U 0|wE,>9'`&s=IeakïXKT>gBwH~$?i/%R*XIr!D^ie_1P %P %%K/_$ *ɗH%~K|])_o%~Kl$}F%K$H.\2WTW.*~Uދ%KԖ${f Kb6R쀼y%KW$۱!W,l] fKė'Kqen,8/E{cހ(Rb~[ &֝(Z췌V*IN*e/oɃ[t? IrNoߢe8o-C0&~K`" CK3:&V d`lFM=Vm >!I^T~A x~8Mw0vp*q8'a 8spNǮ. :(O<,$o~%v@,ʃ,;,&b|g5H&sfU5fcR/^2Hg9|I3kb/$ϒIZ5mf0p C;$g`B$Q \kpnMPs]ކ;`m><C=g/y/I6^+x o-|ɏ0ki_ Q}uk螤{I'~M'CXOFzdoȵh"y2vC$yIQ39;]ĮH$yI'eĤ&)NSm'鞌KI'O%ɿ%{I'cӘvIj'_GMx$Սq>$瓜Or>$N:Id]j!y 1j *~ Jhuyҷĥ3՘4`UyOCZejViX壺>oUBVj\ZXeMqLW퍼j4W73UoiU.ڒToUǓ'AT 2S%`Jc*UǾWV5ު,T9O6ؽ]U3*{@r5 GWٯ_%Jh*UB!XXJc1=m^#=&MPMPMPMPMP5vMF>>MUdž 4>HlTa|Ezlmwۈm0 `3l0`;6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6_6A6A6A666$}||"}caaaaaaU*iZ?VOU*i 4iuZ?VOM^x T*ixw^(5SxUZLǼӡKwbZZLKii1--cZcCx hL/e^Fxd ӒaZ2LKi0-ӬNoK؁R`Z LKi)0M4ӼMG)WhIJZLˈwѻL6}:{{Р{:fo9yįx.$O<ަ$}oŮ螦{騣=M4NS;Mmlf!/?|iΧ?mmڎɤZevo.̴c V۬ 6m.\l6̴&}Os mm#Mc6m. nm?ScQmLkطmmKc;:y"z{'}ގUt3ݎXؾQۦmilئMc&Mh[uc`6o=.~~N;~Ս~eVJH*y⾤xjXka2_h&la膲׍3y ٤b# .K/bt!`}ACp8sp v%}?;18 cp.E \~џܽ!cpn @'g3Щ}9Ͼtex zohz#8߈/@@蹗G^{]wE3E3wwwwwwػFGdBdĂX ;`]Wl<.J[A ,Hh_aH^ؙ҉xZ `A, / i\`pjz_ / 7NY9 2gAQKPQKP1~EX yBWةY9y nppao[WsC)WXc/g`9~ѣ1V(ƻ7&գF{X}ף!a%&9o$ocWF5nj}1Q>GGZ `. 2sL1=GsQ; y9H#yN[ޅ9oc·#yas縜r.C?}osۓ;{ v.v.w|͑<;I/.ƞ' _ss뺜w9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9ry.]λw9T} X iXa A:XY al-F`lvnA > !8q *y'Ձ#vNISp8sp.E \kpnM܅o܇cxOLsx/7{|/`$J?)8g> gȜsnιM+,ESɩ,YxEN+CN1U%.S!)܋FA9Ke,3q3FΔPNJ΂97rzrr 9 99M999-99m9 9&99]/9җLNkXNE=4Μrrsڦs̹9+g%ߜrsZr*9 &TrYf#UNN9KL{]s)7sY:9+LܼsU 97̝ZNl9d9Kbs%L,*Yy?`Mv\T@_}n˛D_cXY|*_X[|*_W竣 k ϱt+.UJRT Rk pKRk cǪRT*UJRk-UƊR;LYaYKe-\Wr]Yue-ו\Wr]Yue-ו\Wr]Yue-ו\Wr]Yue-ו\Wr]Yue-ו\Wr]Yue-ו\WYtecy+uek+㕭XVbYYee-XVbYYcf:VXYceE-DVYY+kek[h+kmͬZ+[+kuU5-XVbYYee-XVbYYee:VXYcecu,hek[3kiloM5Z+k٬lxceoM5Z+kM5ƒ:D_`,Ұ VX u0`# & [`+6;`'ݐ<`}ACpQ818 Y8\KpU܄[p]}x<'<^kxo>gA !4cH3666fwr!JCvE};d!Y8xhf8Ye]u4mZ endstream endobj startxref 420181 %%EOF lintr/build/stage23.rdb0000644000176200001440000001315014600122235014435 0ustar liggesusers][sȕ(ɺؖm}ɖHf,˒Gu1E; g8 $1%1^o{1lUڇdT~@}p骵PlsFp"H&zd/G=D/I/1Dq(D "rۍ2QDST#d^$sᆲX02'̯.//܉uMH8,Jk]%I&TKXJһ. /sw\"]5_Dw=vp?]7 5h߁Od^Z02? Be713|U% Z:WrKtv9cTk,.==duIg3q>,26n嗤ijnq֗[xƔ2it4]߇P4n^d2|QU! ')\6U>ԝM>o|32b4qm+?~y@tɃPUM> F9\DRdARϪ~=R!WGwW='[: | l+b hDf9a"+)tDKMq)Q-4R&V 89T`UWc4ΗvOF6hF idҁ0WxOxuM, mdy Dv׋:18ր!(O>/ h܀7P[/bQ )[੕yqOJ/g|zn9I)f}eBa~n}b>%U bs43$[DON///{2>_Z$CM\ *>َ"EldLsLr+*$5ߨHDR /PgR^p!5ʹUIOO_2>b44ʕ5Y5qHF,՚x1A@CT1SnI W׉ :l!HsOږ6{QYխݯ&`6dxO GQ<|d-y4"&ʛ8 +(ch)LlVT/`r@jcHg)d}EL{#jwT N.5uoiݐHJ|]6h<=Ŧ7쨨9E;OP~YQgܺI%ֹ-6xCca()'ٷ'Ѯ~F\댺Dķ(~_a!nnxG5ʿZo"%x7ߢ<. mԩ 5Y$x)qK&^@9Tl,e·T%y^+x0>M OJF+\ar7{ :\Iժx;4n)T uG&~/\,?5±?$BzhSRФ>jDi l6Fk Q@0 ^kes|z1%?B>k @C-g>k (ʣrxls1r{r':ИDf W"fTbL!EnlI>s(uF5(jғ@ t,%hg[>\k(:?,}i齉sV()K֜|R5ZIeE9ZS5ޥwC kx}s Vr˩E :LK:jMOj8d x嫑W3+cf OLXFc˦a}PV,9 tl֣D!3Z=6bJ53i J(KO +"Еђ +IlioXkϞ5)-`tR _ed؏ !PϱsQ~p?2fC|}-pd8v`J4Bt:ൊXA9SH;o dS#ު%V L]WEZf㘹6ړ6(W VexM 1Xcvn^DmWk2 ~3.†b1 ǪtC#|5 ~np;ȆC2"0姑ުHBI>Ӑh\O[n톗TWv $BKil_jc( YUjuJK(Ƿ:k}_dhtAGa6"6M2&WP>^+6ZSjvTĨ('^|-6S6 xFf0:wp-8rId8rorާ`T@:0PHQ1\Ƅ'P>F #D99wЕm3;S}1ISߢmd}FESNE¸0A6"rqEOѤ)sT-0$Mn#W-sqlm(|2 p̳{ÖmX{$ k,ac4-4p ř3\ɸ𪬍:u8|aTc_`H#uH)Eb@\Y-=6K> ^!+W_MXP؈WjVj*+}͠w| d&a0sɜ8:,.&q)߸˗}+or ӝNAM5K?ߔ9%ƃ0U'mcn^H5Q]B{.Ѝflj3yf <l5ƫ,R7F~ng 1|ͼE4o0ol1oj7# o3'OP:d< ayd?;c揻A0F00Ω|#0fE=;M xk/9SZ#v=1fu;bOyFϑXMGe$h0 @s_A] W1.mcWx_d{tǿUZfEQa9D ױs]n/1o0^!W6qGtRC@v9zv5Tyr1tv(Vbvl}1ZOX;l q0!a6Nfgv[=#(D'iA/0ڽcI\$^ !>BQl6 e]۪~ĖdcVw{Am<֖MFVTوWL!s@f?%'QܓF:,˻gP}vۓ{lQ_ABy*6oD!-!96Ў`9Q^rs( ,%+_e!:!Hu띱óH9(,f1@DyKvl3D#=nHJ9G=ʿ͌6IG_!f5 ?.Y ?f5@JW5 G?#f-7Z0WAs"Z2..M}Ag*؉0EoU^TʎyVPŷL%D7_̲H؟XDc|vm_Zebv>7uZcHf$y KP[N9/ q co*.3  cjuY =;SL}u]c :Kcg՚9|m|L[޼Thhp$y1Y/Wlr-ћتlojufLM7ہ92=VR^6_ T0%V#T"gGh{|W*Śm!#]UQt=V6M޿xY?fyЦ\F cm+dKPUǖh#"ɍx?[T1ɖ)Ep6PwwmZd~#G鮾 Ҏ}t3oKJDg߅>y߀z=W/6L+~7\LjkD= uM18 iE/8DJmZ^T }Mՠ63l{eo~ ne #|il'G g]K%Et8)bakɹEUгdz:ui,{q`;F~_Ȩkve-88j#䬳PfdOGcoMڏ:9%Z,ٛ[벡jnЬguIxg߻w/+T'R39%KHomS/sxf> !䞹}ˣ: / ̏F\u-#/gdf LVXcFotJQ'Z&K{KrO3YBg3q:Qw봂vƶh~IWڵl(*`Fֲ3fdB[ϑLAO/t4tp~%zcitJ{z ̿,CFe^29Ĥz6){Х{ :}9ɬ~7[ǻwid7SHPf<?d51''Wlintr/tests/0000755000176200001440000000000014577203024012550 5ustar liggesuserslintr/tests/testthat/0000755000176200001440000000000014600212472014402 5ustar liggesuserslintr/tests/testthat/test-make_linter_from_regex.R0000644000176200001440000000032714457657444022241 0ustar liggesuserstest_that("test make_linter_from_regex works", { linter <- lintr:::make_linter_from_regex("-", "style", "Silly lint.")() expect_lint("a <- 2L", "Silly lint.", linter) expect_lint("a = '2-3'", NULL, linter) }) lintr/tests/testthat/test-unnecessary_lambda_linter.R0000644000176200001440000001315114577055276022743 0ustar liggesuserstest_that("unnecessary_lambda_linter skips allowed usages", { linter <- unnecessary_lambda_linter() expect_lint("lapply(DF, sum)", NULL, linter) expect_lint("apply(M, 1, sum, na.rm = TRUE)", NULL, linter) # the first argument may be ... or have a cumbersome name, so an anonymous # function may be preferable (e.g. this is often the case for grep() calls) expect_lint("sapply(x, function(xi) foo(1, xi))", NULL, linter) expect_lint("sapply(x, function(xi) return(foo(1, xi)))", NULL, linter) # if the argument is re-used, that's also a no-go expect_lint("dendrapply(x, function(xi) foo(xi, xi))", NULL, linter) # at any nesting level expect_lint("parLapply(cl, x, function(xi) foo(xi, 2, bar(baz(xi))))", NULL, linter) # multi-expression case expect_lint("lapply(x, function(xi) { print(xi); xi^2 })", NULL, linter) # multi-expression, multi-line cases expect_lint( trim_some(" lapply(x, function(xi) { print(xi); xi^2 }) "), NULL, linter ) expect_lint( trim_some(" lapply(x, function(xi) { print(xi) xi^2 }) "), NULL, linter ) # This _could_ be lapply(x, `%in%`, tbl), but don't force infix into lambda expect_lint("lapply(x, function(xi) xi %in% tbl)", NULL, linter) # This one could not expect_lint("lapply(x, function(xi) tbl %in% xi)", NULL, linter) # would require multiple lapply() loops expect_lint("lapply(x, function(xi) foo(bar(xi)))", NULL, linter) expect_lint("lapply(x, function(xi) return(foo(bar(xi))))", NULL, linter) # extractions, #2231 expect_lint("lapply(l, function(x) rle(x)$values)", NULL, linter) expect_lint('lapply(l, function(x) rle(x)["values"])', NULL, linter) expect_lint('lapply(l, function(x) rle(x)[["values"]])', NULL, linter) expect_lint("lapply(l, function(x) rle(x)@values)", NULL, linter) # Other operators, #2247 expect_lint("lapply(l, function(x) foo(x) - 1)", NULL, linter) expect_lint("lapply(l, function(x) foo(x) * 2)", NULL, linter) expect_lint("lapply(l, function(x) foo(x) ^ 3)", NULL, linter) expect_lint("lapply(l, function(x) foo(x) %% 4)", NULL, linter) # Don't include other lambdas, #2249 expect_lint( trim_some('{ lapply(x, function(e) sprintf("%o", e)) lapply(y, function(e) paste(strlpad(e, "0", width))) }'), NULL, linter ) # only call is on RHS of operator, #2310 expect_lint("lapply(l, function(x) 'a' %in% names(x))", NULL, linter) expect_lint("lapply(l, function(x = 1) 'a' %in% names(x))", NULL, linter) }) test_that("unnecessary_lambda_linter blocks simple disallowed usage", { linter <- unnecessary_lambda_linter() expect_lint( "lapply(DF, function(x) sum(x))", rex::rex("Pass sum directly as a symbol to lapply()"), linter ) expect_lint( "lapply(DF, function(x) return(sum(x)))", rex::rex("Pass sum directly as a symbol to lapply()"), linter ) expect_lint( "rapply(l, function(x) is.data.frame(x))", rex::rex("Pass is.data.frame directly as a symbol to rapply()"), linter ) expect_lint( "eapply(env, function(x) sum(x, na.rm = TRUE))", rex::rex("Pass sum directly as a symbol to eapply()"), linter ) expect_lint( "eapply(env, function(x) return(sum(x, na.rm = TRUE)))", rex::rex("Pass sum directly as a symbol to eapply()"), linter ) }) test_that("unnecessary_lambda_linter doesn't apply to keyword args", { expect_lint("lapply(x, function(xi) data.frame(nm = xi))", NULL, unnecessary_lambda_linter()) expect_lint("lapply(x, function(xi) return(data.frame(nm = xi)))", NULL, unnecessary_lambda_linter()) }) test_that("purrr-style anonymous functions are also caught", { linter <- unnecessary_lambda_linter() # TODO(michaelchirico): this is just purrr::flatten(x). We should write another # linter to encourage that usage. expect_lint("purrr::map(x, ~.x)", NULL, linter) expect_lint("purrr::map_df(x, ~lm(y, .x))", NULL, linter) expect_lint("map_dbl(x, ~foo(bar = .x))", NULL, linter) expect_lint( "purrr::map(x, ~foo(.x))", rex::rex("Pass foo directly as a symbol to map()"), linter ) expect_lint( "purrr::map_int(x, ~foo(.x, y))", rex::rex("Pass foo directly as a symbol to map_int()"), linter ) expect_lint( "purrr::map_vec(x, ~foo(.x, y))", rex::rex("Pass foo directly as a symbol to map_vec()"), linter ) }) test_that("cases with braces are caught", { linter <- unnecessary_lambda_linter() lint_msg <- rex::rex("Pass print directly as a symbol to lapply()") expect_lint( "lapply(x, function(xi) { print(xi) })", lint_msg, linter ) expect_lint( trim_some(" lapply(x, function(xi) { print(xi) }) "), lint_msg, linter ) expect_lint( "lapply(x, function(xi) { return(print(xi)) })", lint_msg, linter ) expect_lint( trim_some(" lapply(x, function(xi) { print(xi) }) "), lint_msg, linter ) expect_lint( trim_some(" lapply(x, function(xi) { return(print(xi)) }) "), lint_msg, linter ) expect_lint( trim_some(" lapply(x, function(xi) { print(xi) xi }) "), NULL, linter ) # false positives like #2231, #2247 are avoided with braces too expect_lint("lapply(x, function(xi) { foo(xi)$bar })", NULL, linter) expect_lint("lapply(x, function(xi) { foo(xi) - 1 })", NULL, linter) }) test_that("function shorthand is handled", { skip_if_not_r_version("4.1.0") expect_lint( "lapply(DF, \\(x) sum(x))", rex::rex("Pass sum directly as a symbol to lapply()"), unnecessary_lambda_linter() ) }) lintr/tests/testthat/test-exclusions.R0000644000176200001440000001211414526172307017706 0ustar liggesuserstest_that("it excludes properly", { withr::local_options( lintr.exclude = "#TeSt_NoLiNt", lintr.exclude_start = "#TeSt_NoLiNt_StArT", lintr.exclude_end = "#TeSt_NoLiNt_EnD" ) lintr:::read_settings(NULL) t1 <- lint("exclusions-test", parse_settings = FALSE) expect_length(t1, 8L) t2 <- lint("exclusions-test", exclusions = list("exclusions-test" = 4L), parse_settings = FALSE) expect_length(t2, 7L) t3 <- lint("exclusions-test", exclusions = list("exclusions-test"), parse_settings = FALSE) expect_length(t3, 0L) cache_path <- file.path(tempdir(), "lintr_cache") clear_cache("exclusions-test", cache_path) for (info in sprintf("caching: pass %s", 1L:4L)) { t4 <- lint("exclusions-test", cache = cache_path, parse_settings = FALSE) expect_identical(length(t4), 8L, info = info) } }) test_that("it doesn't fail when encountering misspecified encodings", { withr::local_options( lintr.exclude = "#TeSt_NoLiNt", lintr.exclude_start = "#TeSt_NoLiNt_StArT", lintr.exclude_end = "#TeSt_NoLiNt_EnD" ) lintr:::read_settings(NULL) expect_length(lintr:::parse_exclusions("dummy_projects/project/cp1252.R"), 0L) }) test_that("it gives the expected error message when there is only one start but no end", { lintr:::read_settings(NULL) expect_error( lintr:::parse_exclusions("dummy_projects/project/one_start_no_end.R"), "has 1 range start (line 3) but only 0 range ends for exclusion from linting", fixed = TRUE ) }) test_that("it gives the expected error message when there is mismatch between multiple starts and ends", { lintr:::read_settings(NULL) expect_error( lintr:::parse_exclusions("dummy_projects/project/mismatched_starts_ends.R"), "has 3 range starts (lines 3, 7, 11) but only 2 range ends (lines 1, 9) for exclusion from linting", fixed = TRUE ) }) test_that("partial matching works for exclusions but warns if no linter found", { lintr:::read_settings(NULL) expect_warning( expect_warning( expect_warning( expect_lint( file = "dummy_projects/project/partially_matched_exclusions.R", checks = rex::rex("semicolons"), parse_settings = FALSE ), rex::rex("Could not find linter named ", anything, "s") ), rex::rex("Could not find linter named ", anything, "bogus_linter") ), rex::rex("Could not find linters named ", anything, "hocus_pocus", anything, "bogus") ) }) test_that("#1413: lint_dir properly excludes files", { withr::local_options(lintr.linter_file = "lintr_test_config") tmp <- withr::local_tempdir() writeLines( trim_some(" linters: linters_with_defaults( line_length_linter(10) ) exclusions: list( 'bad.R' = list( 1, # global exclusions are unnamed line_length_linter = 4:6 ) ) "), file.path(tmp, "lintr_test_config") ) writeLines( trim_some(" tmp = 'value' # comment # long comment # long comment # long comment # comment "), file.path(tmp, "bad.R") ) expect_length(lint(file.path(tmp, "bad.R")), 0L) expect_length(lint_dir(tmp), 0L) }) test_that("#1442: is_excluded_files works if no global exclusions are specified", { withr::local_options(lintr.linter_file = "lintr_test_config") tmp <- withr::local_tempdir() writeLines( trim_some(" linters: linters_with_defaults( line_length_linter(10) ) exclusions: list( 'bad.R' = list( line_length_linter = 4:6 ) ) "), file.path(tmp, "lintr_test_config") ) writeLines( trim_some(" tmp = 'value' # comment # long comment # long comment # long comment # comment "), file.path(tmp, "bad.R") ) # 3 lints: assignment_linter(), quotes_linter() and line_length_linter() expect_lint( file = file.path(tmp, "bad.R"), checks = list( list(linter = "assignment_linter", line_number = 1L), list(linter = "quotes_linter", line_number = 1L), list(linter = "line_length_linter", line_number = 1L) ) ) expect_length(lint_dir(tmp), 3L) }) test_that("next-line exclusion works", { withr::local_options( lintr.exclude = "# NL", lintr.exclude_next = "# NLN", lintr.exclude_linter = default_settings$exclude_linter ) linter <- assignment_linter() # blanket exclusion works expect_lint( trim_some(" # NLN x = 1 "), NULL, linter ) # specific exclusion works expect_lint( trim_some(" # NLN: assignment_linter. x = 1 "), NULL, linter ) expect_lint( trim_some(" # NLN: assignment. x = 1 "), NULL, linter ) expect_lint( trim_some(" # NLN: line_length_linter. x = 1 "), rex::rex("Use <-, not =, for assignment."), list(linter, line_length_linter()) ) # interaction with plain nolint expect_lint( trim_some(" x = 1 # NLN: assignment_linter. x = 2 "), list(rex::rex("Use <-, not =, for assignment."), line_number = 1L), linter ) }) lintr/tests/testthat/test-commented_code_linter.R0000644000176200001440000000565514577052532022053 0ustar liggesuserstest_that("commented_code_linter skips allowed usages", { linter <- commented_code_linter() expect_lint("blah", NULL, linter) expect_lint("#' blah <- 1", NULL, linter) expect_lint(c("a <- 1", "# comment without code"), NULL, linter) expect_lint(c("a <- 1", "# comment without code"), NULL, linter) expect_lint(c("a <- 1", "## whatever"), NULL, linter) expect_lint("TRUE", NULL, linter) expect_lint("#' @examples", NULL, linter) expect_lint("#' foo(1) # list(1)", NULL, linter) # comment in roxygen block ignored expect_lint("1+1 # gives 2", NULL, linter) expect_lint("# Non-existent:", NULL, linter) expect_lint("# 1-a", NULL, linter) # "-" removed from code operators expect_lint('1+1 # for example cat("123")', NULL, linter) # regression test for #451 expect_lint("c('#a#' = 1)", NULL, linter) }) test_that("commented_code_linter blocks disallowed usages", { lint_msg <- rex::rex("Commented code should be removed.") linter <- commented_code_linter() expect_lint("# blah <- 1", lint_msg, linter) expect_lint( "bleurgh <- fun_call(1) # other_call()", list(message = lint_msg, column_number = 26L), linter ) expect_lint( " #blah <- 1", list(message = lint_msg, column_number = 3L), linter ) # Regression test for #742, line number and comment number don't match up expect_lint( trim_some(" # non-code comment line_without_comment <- 42L #blah <- 1 "), list(message = lint_msg, line_number = 3L, column_number = 3L), linter ) expect_lint( trim_some(" d <- t.test( x = dplyr::starwars$height, # var.equal = TRUE, conf.level = .99, mu = 175 ) "), list(message = lint_msg, line_number = 3L), linter ) expect_lint( trim_some(" d <- t.test( x = dplyr::starwars$height #, var.equal = TRUE , conf.level = .99 , mu = 175 ) "), list(message = lint_msg, line_number = 3L), linter ) expect_lint("1+1 # cat('123')", lint_msg, linter) expect_lint("#expect_ftype(1e-12 , t)", lint_msg, linter) }) test_that("commented_code_linter can detect operators in comments and lint correctly", { linter <- commented_code_linter() lint_msg <- rex::rex("Commented code should be removed.") test_ops <- c( "+", "=", "==", "!=", "<=", ">=", "<-", "<<-", "<", ">", "->", "->>", "%%", "/", "^", "*", "**", "|", "||", "&", "&&", "%>%", "%anything%" ) for (op in test_ops) { expect_lint(paste("i", op, "1", collapse = ""), NULL, linter) expect_lint(paste("# something like i", op, "1", collapse = ""), NULL, linter) expect_lint(paste("# i", op, "1", collapse = ""), lint_msg, linter) } }) test_that("commented_code_linter can detect operators in comments and lint correctly", { skip_if_not_r_version("4.1.0") expect_lint( "# 1:3 |> sum()", rex::rex("Commented code should be removed."), commented_code_linter() ) }) lintr/tests/testthat/test-fixed_regex_linter.R0000644000176200001440000003637614577052532021403 0ustar liggesusers# NB: escaping is confusing. We have to double-escape everything -- the first # escape creates a string that will be parse()d, the second escape is normal # escaping that would be done in R code. E.g. in "\\\\.", the R code would # read like "\\.", but in order to create those two slashes, we need to write # "\\\\." in the string here. test_that("fixed_regex_linter skips allowed usages", { linter <- fixed_regex_linter() expect_lint("gsub('^x', '', y)", NULL, linter) expect_lint("grep('x$', '', y)", NULL, linter) expect_lint("sub('[a-zA-Z]', '', y)", NULL, linter) expect_lint("grepl(fmt, y)", NULL, linter) expect_lint("regexec('\\\\s', '', y)", NULL, linter) expect_lint("grep('a(?=b)', x, perl = TRUE)", NULL, linter) expect_lint("grep('0+1', x, perl = TRUE)", NULL, linter) expect_lint("grep('1*2', x)", NULL, linter) expect_lint("grep('a|b', x)", NULL, linter) expect_lint("grep('\\\\[|\\\\]', x)", NULL, linter) # if fixed=TRUE is already set, regex patterns don't matter expect_lint("gsub('\\\\.', '', y, fixed = TRUE)", NULL, linter) # ignore.case=TRUE implies regex interpretation expect_lint("gsub('abcdefg', '', y, ignore.case = TRUE)", NULL, linter) # char classes starting with [] might contain other characters -> not fixed expect_lint("sub('[][]', '', y)", NULL, linter) expect_lint("sub('[][ ]', '', y)", NULL, linter) expect_lint("sub('[],[]', '', y)", NULL, linter) # wrapper functions don't throw expect_lint("gregexpr(pattern = pattern, data, perl = TRUE, ...)", NULL, linter) }) test_that("fixed_regex_linter blocks simple disallowed usages", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("gsub('\\\\.', '', x)", lint_msg, linter) expect_lint("grepl('abcdefg', x)", lint_msg, linter) expect_lint("gregexpr('a-z', y)", lint_msg, linter) expect_lint("regexec('\\\\$', x)", lint_msg, linter) expect_lint("grep('\n', x)", lint_msg, linter) # naming the argument doesn't matter (if it's still used positionally) expect_lint("gregexpr(pattern = 'a-z', y)", lint_msg, linter) }) patrick::with_parameters_test_that( "fixed_regex_linter is robust to unrecognized escapes error", { expect_lint( sprintf("grep('\\\\%s', x)", char), rex::rex("This regular expression is static"), fixed_regex_linter() ) expect_lint( sprintf("strsplit('a%sb', '\\\\%s')", char, char), rex::rex("This regular expression is static"), fixed_regex_linter() ) }, .cases = local({ char <- c( "^", "$", "{", "}", "(", ")", ".", "*", "+", "?", "|", "[", "]", "\\\\", "<", ">", "=", ":", ";", "/", "_", "-", "!", "@", "#", "%", "&", "~" ) data.frame(char = char, .test_name = char, stringsAsFactors = FALSE) }) ) test_that("fixed_regex_linter catches regex like [.] or [$]", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("grep('[.]', x)", lint_msg, linter) expect_lint("grepl('a[*]b', x)", lint_msg, linter) # also catch char classes for [ and ] expect_lint("gregexpr('[]]', x)", lint_msg, linter) }) test_that("fixed_regex_linter catches null calls to strsplit as well", { linter <- fixed_regex_linter() expect_lint("strsplit(x, '^x')", NULL, linter) expect_lint("strsplit(x, '\\\\s')", NULL, linter) expect_lint("strsplit(x, 'a(?=b)', perl = TRUE)", NULL, linter) expect_lint("strsplit(x, '0+1', perl = TRUE)", NULL, linter) expect_lint("strsplit(x, 'a|b')", NULL, linter) expect_lint("tstrsplit(x, '1*2')", NULL, linter) expect_lint("tstrsplit(x, '[a-zA-Z]')", NULL, linter) expect_lint("tstrsplit(x, fmt)", NULL, linter) # if fixed=TRUE is already set, regex patterns don't matter expect_lint("strsplit(x, '\\\\.', fixed = TRUE)", NULL, linter) expect_lint("strsplit(x, '\\\\.', fixed = T)", NULL, linter) }) test_that("fixed_regex_linter catches calls to strsplit as well", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("strsplit(x, '\\\\.')", lint_msg, linter) expect_lint("strsplit(x, '[.]')", lint_msg, linter) expect_lint("tstrsplit(x, 'abcdefg')", lint_msg, linter) }) test_that("fixed_regex_linter is more exact about distinguishing \\s from \\:", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("grep('\\\\s', '', x)", NULL, linter) expect_lint("grep('\\\\:', '', x)", lint_msg, linter) }) ## tests for stringr functions test_that("fixed_regex_linter skips allowed stringr usages", { linter <- fixed_regex_linter() expect_lint("str_replace(y, '[a-zA-Z]', '')", NULL, linter) expect_lint("str_replace_all(y, '^x', '')", NULL, linter) expect_lint("str_detect(y, fmt)", NULL, linter) expect_lint("str_extract(y, '\\\\s')", NULL, linter) expect_lint("str_extract_all(y, '\\\\s')", NULL, linter) expect_lint("str_which(x, '1*2')", NULL, linter) # if fixed() is already set, regex patterns don't matter expect_lint("str_replace(y, fixed('\\\\.'), '')", NULL, linter) # namespace qualification doesn't matter expect_lint("stringr::str_replace(y, stringr::fixed('abcdefg'), '')", NULL, linter) }) test_that("fixed_regex_linter blocks simple disallowed usages of stringr functions", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("str_replace_all(x, '\\\\.', '')", lint_msg, linter) expect_lint("str_detect(x, 'abcdefg')", lint_msg, linter) expect_lint("str_locate(y, 'a-z')", lint_msg, linter) expect_lint("str_subset(x, '\\\\$')", lint_msg, linter) expect_lint("str_which(x, '\n')", lint_msg, linter) # named, positional arguments are still caught expect_lint("str_locate(y, pattern = 'a-z')", lint_msg, linter) # nor do other named arguments throw things off expect_lint("str_starts(x, '\\\\.', negate = TRUE)", lint_msg, linter) }) test_that("fixed_regex_linter catches calls to str_split as well", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("str_split(x, '^x')", NULL, linter) expect_lint("str_split(x, fmt)", NULL, linter) # if fixed() is already set, regex patterns don't matter expect_lint("str_split(x, fixed('\\\\.'))", NULL, linter) expect_lint("str_split(x, '\\\\.')", lint_msg, linter) expect_lint("str_split(x, '[.]')", lint_msg, linter) }) test_that("str_replace_all's multi-replacement version is handled", { linter <- fixed_regex_linter() # While each of the replacements is fixed, and this _could_ in principle be replaced by # a pipeline where each step does one of the replacements and fixed() is used, this is overkill. # Instead, ensure that no lint is returned for this case expect_lint('str_replace_all(x, c("one" = "1", "two" = "2", "three" = "3"))', NULL, linter) expect_lint('grepl(c("a" = "b"), x)', NULL, linter) }) test_that("1- or 2-width octal escape sequences are handled", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint('strsplit(x, "\\1")', lint_msg, linter) }) test_that("one-character character classes with escaped characters are caught", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint("gsub('[\\n]', '', x)", lint_msg, linter) expect_lint("gsub('[\\\"]', '', x)", lint_msg, linter) expect_lint('gsub("\\\\<", "x", x, perl = TRUE)', lint_msg, linter) expect_lint("str_split(x, '[\\1]')", lint_msg, linter) expect_lint("str_split(x, '[\\12]')", lint_msg, linter) expect_lint("str_split(x, '[\\123]')", lint_msg, linter) expect_lint("str_split(x, '[\\xa]')", lint_msg, linter) expect_lint("str_split(x, '[\\x32]')", lint_msg, linter) expect_lint("str_split(x, '[\\uF]')", lint_msg, linter) expect_lint("str_split(x, '[\\u01]')", lint_msg, linter) expect_lint("str_split(x, '[\\u012]')", lint_msg, linter) expect_lint("str_split(x, '[\\u0123]')", lint_msg, linter) expect_lint("str_split(x, '[\\U8]')", lint_msg, linter) expect_lint("str_split(x, '[\\U1d4d7]')", lint_msg, linter) expect_lint("str_split(x, '[\\u{1}]')", lint_msg, linter) expect_lint("str_split(x, '[\\U{F7D5}]')", lint_msg, linter) expect_lint("str_split(x, '[\\U{1D4D7}]')", lint_msg, linter) }) test_that("bracketed unicode escapes are caught", { linter <- fixed_regex_linter() lint_msg <- rex::rex("This regular expression is static") expect_lint('gsub("\\u{A0}", " ", out, useBytes = TRUE)', lint_msg, linter) expect_lint('gsub("abc\\U{A0DEF}ghi", " ", out, useBytes = TRUE)', lint_msg, linter) expect_lint('gsub("\\u{A0}\\U{0001d4d7}", " ", out, useBytes = TRUE)', lint_msg, linter) }) test_that("escaped characters are handled correctly", { linter <- fixed_regex_linter() expect_lint("gsub('\\n+', '', sql)", NULL, linter) expect_lint('gsub("\\n{2,}", "\n", D)', NULL, linter) expect_lint('gsub("[\\r\\n]", "", x)', NULL, linter) expect_lint('gsub("\\n $", "", y)', NULL, linter) expect_lint('gsub("```\\n*```r*\\n*", "", x)', NULL, linter) expect_lint('strsplit(x, "(;|\n)")', NULL, linter) expect_lint('strsplit(x, "(;|\\n)")', NULL, linter) expect_lint('grepl("[\\\\W]", x, perl = TRUE)', NULL, linter) expect_lint('grepl("[\\\\W]", x)', NULL, linter) }) # make sure the logic is properly vectorized test_that("single expression with multiple regexes is OK", { expect_lint('c(grep("^a", x), grep("b$", x))', NULL, fixed_regex_linter()) }) test_that("fixed replacements vectorize and recognize str_detect", { linter <- fixed_regex_linter() # properly vectorized expect_lint( trim_some(" c( grepl('abcdefg', x), grepl('a[.]\\\\.b\\n', x) ) "), list( rex::rex('Here, you can use "abcdefg" with fixed = TRUE'), rex::rex('Here, you can use "a..b\\n" with fixed = TRUE') ), linter ) # stringr hint works expect_lint( "str_detect(x, 'abc')", rex::rex('Here, you can use stringr::fixed("abc") as the pattern'), linter ) }) test_that("fixed replacement is correct with UTF-8", { skip_if( .Platform$OS.type == "windows" && !hasName(R.Version(), "crt"), message = "UTF-8 support is required" ) expect_lint( "grepl('[\\U{1D4D7}]', x)", rex::rex('Here, you can use "\U1D4D7" with fixed = TRUE'), fixed_regex_linter() ) }) # TODO(michaelchirico): one difference for stringr functions vs. base is that # stringr is much friendlier to piping, so that # > str %>% str_replace_all("x$", "y") # actually doesn't need fixed(), but the logic now is only looking at "y" # since it's the second argument and a non-regex string. Similarly, # > str %>% str_detect("x") # is a false negative. thankfully there appear to be few false positives here # TODO(michaelchirico): we could in principle build in logic to detect whether # perl=TRUE and interpret "regex or not" accordingly. One place # up in practice is for '\<', which is a special character in default # regex but not in PCRE. Empirically relevant for HTML-related regex e.g. \\ #' Generate a string with a non-printable Unicode entry robust to test environment #' #' Non-printable unicode behaves wildly different with `encodeString()` #' across R versions and platforms. Nonetheless, all of these expressions #' are valid replacements. #' @noRd robust_non_printable_unicode <- function() { if (getRversion() < "4.1.0") { "abc\\U000a0defghi" } else if (.Platform$OS.type == "windows") { "abc\U{0a0def}ghi" } else { "abc\\U{0a0def}ghi" } } # styler: off patrick::with_parameters_test_that("fixed replacements are correct", { skip_if( regex_expr %in% c("abc\\U{A0DEF}ghi", "[\\U1d4d7]", "[\\U{1D4D7}]", "\\u{A0}\\U{0001d4d7}") && .Platform$OS.type == "windows" && !hasName(R.Version(), "crt"), message = "UTF-8 support is required" ) expect_lint( sprintf("grepl('%s', x)", regex_expr), rex::rex(sprintf('Here, you can use "%s" with fixed = TRUE', fixed_expr)), fixed_regex_linter() ) }, .cases = tibble::tribble( ~.test_name, ~regex_expr, ~fixed_expr, "[.]", "[.]", ".", '[\\\"]', '[\\\"]', '\\"', "[]]", "[]]", "]", "\\\\.", "\\\\.", ".", "\\\\:", "\\\\:", ":", "\\\\<", "\\\\<", "<", "\\\\$", "\\\\$", "$", "[\\1]", "[\\1]", "\\001", "\\1", "\\1", "\\001", "[\\12]", "[\\12]", "\\n", "[\\123]", "[\\123]", "S", "a[*]b", "a[*]b", "a*b", "abcdefg", "abcdefg", "abcdefg", "abc\\U{A0DEF}ghi", "abc\\U{A0DEF}ghi", robust_non_printable_unicode(), "a-z", "a-z", "a-z", "[\\n]", "[\\n]", "\\n", "\\n", "\n", "\\n", "[\\u01]", "[\\u01]", "\\001", "[\\u012]", "[\\u012]", "\\022", "[\\u0123]", "[\\u0123]", "\u0123", "[\\u{1}]", "[\\u{1}]", "\\001", "[\\U1d4d7]", "[\\U1d4d7]", "\U1D4D7", "[\\U{1D4D7}]", "[\\U{1D4D7}]", "\U1D4D7", "[\\U8]", "[\\U8]", "\\b", "\\u{A0}", "\\u{A0}", "\uA0", "\\u{A0}\\U{0001d4d7}", "\\u{A0}\\U{0001d4d7}", "\uA0\U1D4D7", "[\\uF]", "[\\uF]", "\\017", "[\\U{F7D5}]", "[\\U{F7D5}]", "\UF7D5", "[\\x32]", "[\\x32]", "2", "[\\xa]", "[\\xa]", "\\n" )) # styler: on test_that("'unescaped' regex can optionally be skipped", { linter <- fixed_regex_linter(allow_unescaped = TRUE) expect_lint("grepl('a', x)", NULL, linter) expect_lint("str_detect(x, 'a')", NULL, linter) expect_lint("grepl('[$]', x)", rex::rex('Here, you can use "$"'), linter) }) local({ pipes <- pipes(exclude = c("%$%", "%T>%")) patrick::with_parameters_test_that( "linter is pipe-aware", { linter <- fixed_regex_linter() lint_msg <- "This regular expression is static" expect_lint(paste("x", pipe, "grepl(pattern = 'a')"), lint_msg, linter) expect_lint(paste("x", pipe, "grepl(pattern = '^a')"), NULL, linter) expect_lint(paste("x", pipe, "grepl(pattern = 'a', fixed = TRUE)"), NULL, linter) expect_lint(paste("x", pipe, "str_detect('a')"), lint_msg, linter) expect_lint(paste("x", pipe, "str_detect('^a')"), NULL, linter) expect_lint(paste("x", pipe, "str_detect(fixed('a'))"), NULL, linter) expect_lint(paste("x", pipe, "gsub(pattern = 'a', replacement = '')"), lint_msg, linter) expect_lint(paste("x", pipe, "gsub(pattern = '^a', replacement = '')"), NULL, linter) expect_lint(paste("x", pipe, "gsub(pattern = 'a', replacement = '', fixed = TRUE)"), NULL, linter) expect_lint(paste("x", pipe, "str_replace('a', '')"), lint_msg, linter) expect_lint(paste("x", pipe, "str_replace('^a', '')"), NULL, linter) expect_lint(paste("x", pipe, "str_replace(fixed('a'), '')"), NULL, linter) }, pipe = pipes, .test_name = names(pipes) ) }) lintr/tests/testthat/exclusions-test0000644000176200001440000000043114250050336017474 0ustar liggesusersa <- function() { x = 1 #TeSt_NoLiNt b = 2 c = 3 #TeSt_NoLiNt_StArT b + 1 d = 4 e = 5 #TeSt_NoLiNt_EnD f = 6 f + 1 a.b = 42 #TeSt_NoLiNt: assignment_linter. #TeSt_NoLiNt_StArT: assignment_linter, object_name_linter. a.b.c = 33 F = T #TeSt_NoLiNt_EnD } lintr/tests/testthat/test-whitespace_linter.R0000644000176200001440000000332214457657444021241 0ustar liggesuserstest_that("whitespace_linter skips allowed usages", { linter <- whitespace_linter() expect_lint("blah", NULL, linter) expect_lint(" blah", NULL, linter) expect_lint(" blah", NULL, linter) expect_lint("#\tblah", NULL, linter) }) test_that("whitespace_linter skips allowed tab usages inside strings", { linter <- whitespace_linter() expect_lint( 'lint_msg <- "dont flag tabs if\tthey are inside a string."', NULL, linter ) expect_lint( 'lint_msg <- "dont flag tabs if\n\tthey are inside multiline strings."', NULL, linter ) }) test_that("whitespace_linter blocks disallowed usages", { linter <- whitespace_linter() lint_msg <- rex::rex("Use spaces to indent, not tabs.") expect_lint( "\tblah", list(message = lint_msg, line_number = 1L, column_number = 1L, ranges = list(c(1L, 1L))), linter ) expect_lint( "\n\t\t\tblah", list(message = lint_msg, line_number = 2L, column_number = 1L), linter ) }) test_that("whitespace_linter blocks disallowed usages with a pipe", { skip_if_not_r_version("4.1.0") linter <- whitespace_linter() lint_msg <- rex::rex("Use spaces to indent, not tabs.") expect_lint( "a %>%\n\tb()", list(message = lint_msg, line_number = 2L, column_number = 1L, ranges = list(c(1L, 1L))), linter ) expect_lint( "a |>\n\tb()", list(message = lint_msg, line_number = 2L, column_number = 1L, ranges = list(c(1L, 1L))), linter ) }) test_that("no_tab_linter id deprecated", { expect_warning( { old_linter <- no_tab_linter() }, "Use whitespace_linter instead", fixed = TRUE ) expect_lint(" f(a, b, c)", NULL, old_linter) expect_lint("\tf(a, b, c)", "not tabs", old_linter) }) lintr/tests/testthat/test-function_argument_linter.R0000644000176200001440000000761614506330025022620 0ustar liggesuserstest_that("function_argument_linter skips allowed usages", { linter <- function_argument_linter() expect_lint("function(x, y = 1, z = 2) {}", NULL, linter) expect_lint("function(x, y, z = 1) {}", NULL, linter) # ... handled correctly expect_lint("function(x, y, z = 1, ...) {}", NULL, linter) expect_lint("function(x, y, ..., z = 1) {}", NULL, linter) }) test_that("function_argument_linter blocks disallowed usages", { linter <- function_argument_linter() lint_msg <- rex::rex("Arguments without defaults should come before arguments with defaults.") expect_lint("function(x, y = 1, z) {}", list(message = lint_msg, column_number = 20L), linter) expect_lint("function(x, y = 1, z, w = 2) {}", list(message = lint_msg, column_number = 20L), linter) expect_lint("function(x, y, z = 1, ..., w) {}", list(message = lint_msg, column_number = 28L), linter) # Multi-line also works expect_lint( trim_some(" function(x, y = 1, z) { } "), list(message = lint_msg, line_number = 3L), linter ) expect_lint( trim_some(" function(x, y # comment for distraction = 1, z, w = 2) { } "), list(message = lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" function(x, y = 1, z = 2) { } "), NULL, linter ) }) test_that("function_argument_linter also lints lambda expressions", { skip_if_not_r_version("4.1.0") linter <- function_argument_linter() lint_msg <- rex::rex("Arguments without defaults should come before arguments with defaults.") expect_lint("\\(x, y = 1, z) {}", list(message = lint_msg, column_number = 13L), linter) expect_lint("\\(x, y = 1, z, w = 2) {}", list(message = lint_msg, column_number = 13L), linter) expect_lint("\\(x, y = 1, z = 2) {}", NULL, linter) expect_lint("\\(x, y, z = 1) {}", NULL, linter) }) test_that("Use of missing() is reported in the lint message", { linter <- function_argument_linter() simple_msg <- "Arguments without defaults should come before arguments with defaults." expect_lint( trim_some(" function(x, y = 1, z) { if (missing(z)) { z <- 2 } } "), rex::rex(simple_msg, anything, "missing()"), linter ) expect_lint( trim_some(" function(x, y = 1, z) { if (y > 1) { if (missing(z)) { z <- 2 } } } "), rex::rex(simple_msg, anything, "missing()"), linter ) # inline function expect_lint( "function(x = 2, y) if (missing(y)) x else y", rex::rex(simple_msg, anything, "missing()"), linter ) # missing() used for a different argument expect_lint( trim_some(" function(x, y = 1, z) { if (missing(x)) { z <- 2 } } "), rex::rex(simple_msg, end), linter ) # missing() used in the signature (not quite the same, and easier to spot) expect_lint( trim_some(" function(x = 1, y, z = missing(y)) { x } "), rex::rex(simple_msg, end), linter ) }) test_that("multiple lints give correct message", { simple_msg <- "Arguments without defaults should come before arguments with defaults." expect_lint( trim_some("{ function(x, y = 1, z) { x } function(x, y = 1, z) { if (missing(z)) { z <- 2 } } function(x, y = 1, z, w) { if (missing(z)) { z <- 2 } } }"), list( list(rex::rex(simple_msg, end), line_number = 2L), list(rex::rex(simple_msg, anything, "missing()"), line_number = 5L), list(rex::rex(simple_msg, anything, "missing()"), line_number = 10L, column_number = 22L), list(rex::rex(simple_msg, end), line_number = 10L, column_number = 25L) ), function_argument_linter() ) }) lintr/tests/testthat/test-library_call_linter.R0000644000176200001440000001044514577052532021536 0ustar liggesuserstest_that("library_call_linter skips allowed usages", { linter <- library_call_linter() expect_lint( trim_some(" library(dplyr) print('test') "), NULL, linter ) expect_lint("print('test')", NULL, linter ) expect_lint( trim_some(" # comment library(dplyr) "), NULL, linter ) expect_lint( trim_some(" print('test') # library(dplyr) "), NULL, linter ) expect_lint( trim_some(" suppressPackageStartupMessages({ library(dplyr) library(knitr) }) "), NULL, linter ) }) test_that("library_call_linter warns on disallowed usages", { linter <- library_call_linter() lint_message <- rex::rex("Move all library calls to the top of the script.") expect_lint( trim_some(" library(dplyr) print('test') library(tidyr) "), lint_message, linter ) expect_lint( trim_some(" library(dplyr) print('test') library(tidyr) library(purrr) "), list( list(lint_message, line_number = 3L, column_number = 1L), list(lint_message, line_number = 4L, column_number = 1L) ), linter ) expect_lint( trim_some(" library(dplyr) print('test') print('test') library(tidyr) "), lint_message, linter ) expect_lint( trim_some(" library(dplyr) print('test') library(tidyr) print('test') "), lint_message, linter ) expect_lint( trim_some(" library(dplyr) print('test') library(tidyr) print('test') library(purrr) "), list( list(lint_message, line_number = 3L, column_number = 1L), list(lint_message, line_number = 5L, column_number = 1L) ), linter ) expect_lint( trim_some(" library(dplyr) print('test') suppressMessages(library(tidyr)) print('test') "), lint_message, linter ) }) test_that("require() treated the same as library()", { linter <- library_call_linter() lint_message_library <- rex::rex("Move all library calls to the top of the script.") lint_message_require <- rex::rex("Move all require calls to the top of the script.") expect_lint( trim_some(' library(dplyr) require("tidyr") '), NULL, linter ) expect_lint( trim_some(' library(dplyr) print(letters) require("tidyr") '), list(lint_message_require, line_number = 3L), linter ) expect_lint( trim_some(' library(dplyr) print(letters) library(dbplyr) require("tidyr") '), list( list(lint_message_library, line_number = 3L), list(lint_message_require, line_number = 4L) ), linter ) }) test_that("allow_preamble applies as intended", { linter_preamble <- library_call_linter(allow_preamble = TRUE) linter_no_preamble <- library_call_linter(allow_preamble = FALSE) lint_msg <- rex::rex("Move all library calls to the top of the script.") lines <- trim_some(" opts_chunk$set(eval = FALSE) library(dplyr) library(knitr) print(letters) ") expect_lint(lines, NULL, linter_preamble) expect_lint(lines, list(list(line_number = 2L), list(line_number = 3L)), linter_no_preamble) lines <- trim_some(" opts_chunk$set(eval = FALSE) suppressPackageStartupMessages({ library(dplyr) library(knitr) }) print(letters) ") expect_lint(lines, NULL, linter_preamble) expect_lint(lines, list(list(line_number = 3L), list(line_number = 4L)), linter_no_preamble) lines <- trim_some(" opts_chunk$set(eval = FALSE) suppressPackageStartupMessages(library(dplyr)) library(knitr) print(letters) ") expect_lint(lines, NULL, linter_preamble) expect_lint(lines, list(list(line_number = 2L), list(line_number = 3L)), linter_no_preamble) lines <- trim_some(" opts_chunk$set(eval = FALSE) library(dplyr) suppressPackageStartupMessages(library(knitr)) print(letters) ") expect_lint(lines, NULL, linter_preamble) expect_lint(lines, list(list(line_number = 2L), list(line_number = 3L)), linter_no_preamble) lines <- trim_some(" fun() library(moreFun) oops() ") expect_lint(lines, NULL, linter_preamble) expect_lint(lines, lint_msg, linter_no_preamble) }) lintr/tests/testthat/test-pipe_continuation_linter.R0000644000176200001440000001001514577052532022617 0ustar liggesuserstest_that("pipe-continuation correctly handles stand-alone expressions", { linter <- pipe_continuation_linter() lint_msg <- rex::rex("`%>%` should always have a space before it and a new line after it,") # Expressions without pipes are ignored expect_lint("blah", NULL, linter) # Pipe expressions on a single line are ignored expect_lint("foo %>% bar() %>% baz()", NULL, linter) # Pipe expressions spanning multiple lines with each expression on a line are ignored expect_lint( trim_some(" foo %>% bar() %>% baz() "), NULL, linter ) # Pipe expressions with multiple expression on a line are linted expect_lint( trim_some(" foo %>% bar() %>% baz() "), lint_msg, linter ) expect_lint( trim_some(" foo %>% bar() %>% baz() %>% qux() "), lint_msg, linter ) }) test_that("pipe-continuation linter correctly handles nesting", { linter <- pipe_continuation_linter() lint_msg <- rex::rex("`%>%` should always have a space before it and a new line after it,") expect_lint( trim_some(" my_fun <- function(){ a %>% b() %>% c() } "), list(list(message = lint_msg, line_number = 2L)), linter ) expect_lint( trim_some(" my_fun <- function(){ a %>% b() %>% c() } "), list(list(message = lint_msg, line_number = 3L)), linter ) # but no lints here expect_lint( trim_some(" 1:4 %>% { (.) %>% sum() } "), NULL, linter ) }) test_that("pipe-continuation linter handles native pipe", { skip_if_not_r_version("4.1.0") linter <- pipe_continuation_linter() lint_msg_native <- rex::rex("`|>` should always have a space before it and a new line after it,") lint_msg_magrittr <- rex::rex("`%>%` should always have a space before it and a new line after it,") expect_lint("foo |> bar() |> baz()", NULL, linter) expect_lint( trim_some(" foo |> bar() |> baz() "), NULL, linter ) expect_lint( trim_some(" foo |> bar() |> baz() "), lint_msg_native, linter ) # mixing pipes expect_lint( trim_some(" foo %>% bar() |> baz() "), lint_msg_native, linter ) expect_lint( trim_some(" foo |> bar() %>% baz() "), lint_msg_magrittr, linter ) expect_lint( trim_some(" list( foo |> bar() |> baz(), foo %>% bar() %>% baz() ) "), list( lint_msg_native, lint_msg_magrittr ), linter ) }) local({ linter <- pipe_continuation_linter() valid_code <- c( # all on one line trim_some(" my_fun <- function() { a %>% b() } "), trim_some(" my_fun <- function() { a %>% b() %>% c() } "), trim_some(" with( diamonds, x %>% head(10) %>% tail(5) ) "), trim_some(" test_that('blah', { test_data <- diamonds %>% head(10) %>% tail(5) }) "), # two different single-line pipelines trim_some(" { x <- a %>% b %>% c y <- c %>% b %>% a } "), # at most one pipe-character per line trim_some(" my_fun <- function() { a %>% b() %>% c() } ") ) patrick::with_parameters_test_that( "valid nesting is handled", { expect_lint(code_string, NULL, linter) }, .test_name = valid_code, code_string = valid_code ) }) local({ linter <- pipe_continuation_linter() pipes <- pipes() cases <- expand.grid(pipe1 = pipes, pipe2 = pipes, stringsAsFactors = FALSE) cases <- within(cases, { .test_name <- sprintf("(%s, %s)", pipe1, pipe2) }) patrick::with_parameters_test_that( "Various pipes are linted correctly", expect_lint( sprintf("a %s b() %s\n c()", pipe1, pipe2), rex::rex(sprintf("`%s` should always have a space before it", pipe2)), linter ), .cases = cases ) }) lintr/tests/testthat/test-regex_subset_linter.R0000644000176200001440000000341414577052532021574 0ustar liggesuserstest_that("regex_subset_linter skips allowed usages", { expect_lint("y[grepl(ptn, x)]", NULL, regex_subset_linter()) expect_lint("x[grepl(ptn, foo(x))]", NULL, regex_subset_linter()) }) test_that("regex_subset_linter blocks simple disallowed usages", { expect_lint( "x[grep(ptn, x)]", rex::rex("Prefer grep(pattern, x, ..., value = TRUE)"), regex_subset_linter() ) expect_lint( "names(y)[grepl(ptn, names(y), perl = TRUE)]", rex::rex("Prefer grep(pattern, x, ..., value = TRUE)"), regex_subset_linter() ) expect_lint( "names(foo(y))[grepl(ptn, names(foo(y)), fixed = TRUE)]", rex::rex("Prefer grep(pattern, x, ..., value = TRUE)"), regex_subset_linter() ) }) test_that("regex_subset_linter skips grep/grepl subassignment", { linter <- regex_subset_linter() expect_lint("x[grep(ptn, x)] <- ''", NULL, linter) expect_lint("x[grepl(ptn, x)] <- ''", NULL, linter) expect_lint("x[grep(ptn, x, perl = TRUE)] = ''", NULL, linter) expect_lint("'' -> x[grep(ptn, x, ignore.case = TRUE)] = ''", NULL, linter) }) test_that("regex_subset_linter skips allowed usages for stringr equivalents", { linter <- regex_subset_linter() expect_lint("y[str_detect(x, ptn)]", NULL, linter) expect_lint("x[str_detect(foo(x), ptn)]", NULL, linter) expect_lint("x[str_detect(x, ptn)] <- ''", NULL, linter) expect_lint("x[str_detect(x, ptn)] <- ''", NULL, linter) }) test_that("regex_subset_linter blocks disallowed usages for stringr equivalents", { expect_lint( "x[str_which(x, ptn)]", rex::rex("Prefer stringr::str_subset(x, pattern) over"), regex_subset_linter() ) expect_lint( "names(y)[str_detect(names(y), ptn, negate = TRUE)]", rex::rex("Prefer stringr::str_subset(x, pattern) over"), regex_subset_linter() ) }) lintr/tests/testthat/test-trailing_whitespace_linter.R0000644000176200001440000000361014577052532023120 0ustar liggesuserstest_that("returns the correct linting", { linter <- trailing_whitespace_linter() expect_lint("blah", NULL, linter) expect_lint( "blah <- 1 ", list(message = rex::rex("Trailing whitespace is superfluous."), column_number = 10L), linter ) expect_lint( "blah <- 1 \n'hi'", rex::rex("Trailing whitespace is superfluous."), linter ) expect_lint( "blah <- 1\n'hi'\na <- 2 ", list(message = rex::rex("Trailing whitespace is superfluous."), line_number = 3L), linter ) }) test_that("also handles completely empty lines per allow_empty_lines argument", { linter <- trailing_whitespace_linter() expect_lint( "blah <- 1\n \n'hi'\na <- 2", list(message = rex::rex("Trailing whitespace is superfluous."), line_number = 2L), linter ) expect_lint( "blah <- 1 ", list(message = rex::rex("Trailing whitespace is superfluous."), column_number = 10L), trailing_whitespace_linter(allow_empty_lines = TRUE) ) expect_lint( "blah <- 1\n \n'hi'\na <- 2", NULL, trailing_whitespace_linter(allow_empty_lines = TRUE) ) }) test_that("also handles trailing whitespace in string constants", { linter <- trailing_whitespace_linter() lint_msg <- rex::rex("Trailing whitespace is superfluous.") expect_lint("blah <- ' \n \n'", NULL, linter) # Don't exclude past the end of string expect_lint( "blah <- ' \n \n' ", list(message = lint_msg, line_number = 3L), linter ) # can be enabled with allow_in_strings = FALSE expect_lint( "blah <- ' \n \n'", list(message = lint_msg, line_number = 1L), trailing_whitespace_linter(allow_empty_lines = TRUE, allow_in_strings = FALSE) ) expect_lint( "blah <- ' \n \n'", list( list(message = lint_msg, line_number = 1L), list(message = lint_msg, line_number = 2L) ), trailing_whitespace_linter(allow_in_strings = FALSE) ) }) lintr/tests/testthat/test-settings.R0000644000176200001440000002372414577052532017366 0ustar liggesuserstest_that("it uses default settings if none provided", { lintr:::read_settings(NULL) lapply(ls(settings), function(setting) { expect_identical(settings[[setting]], default_settings[[setting]]) }) }) test_that("it uses option settings if provided", { withr::local_options(list(lintr.exclude = "test")) lintr:::read_settings(NULL) expect_identical(settings$exclude, "test") }) test_that("it uses config settings in same directory if provided", { test_dir <- tempdir() file <- withr::local_tempfile(tmpdir = test_dir) local_config(test_dir, 'exclude: "test"') lintr:::read_settings(file) lapply(setdiff(ls(settings), "exclude"), function(setting) { expect_identical(settings[[setting]], default_settings[[setting]]) }) expect_identical(settings$exclude, "test") }) test_that("it uses config home directory settings if provided", { path <- withr::local_tempdir() home_path <- withr::local_tempdir() file <- withr::local_tempfile(tmpdir = path) local_config(home_path, 'exclude: "test"') withr::with_envvar(c(HOME = home_path), lintr:::read_settings(file)) lapply(setdiff(ls(settings), "exclude"), function(setting) { expect_identical(settings[[setting]], default_settings[[setting]]) }) expect_identical(settings$exclude, "test") }) test_that("it uses system config directory settings if provided", { path <- withr::local_tempdir() config_parent_path <- withr::local_tempdir("config") config_path <- file.path(config_parent_path, "R", "lintr") dir.create(config_path, recursive = TRUE) file <- withr::local_tempfile(tmpdir = path) local_config(config_path, 'exclude: "test"', filename = "config") withr::with_envvar(c(R_USER_CONFIG_DIR = config_parent_path), lintr:::read_settings(file)) lapply(setdiff(ls(settings), "exclude"), function(setting) { expect_identical(settings[[setting]], default_settings[[setting]]) }) expect_identical(settings$exclude, "test") }) test_that("it errors if the config file does not end in a newline", { f <- withr::local_tempfile() cat("linters: linters_with_defaults(closed_curly_linter = NULL)", file = f) withr::local_options(list(lintr.linter_file = f)) expect_error(lintr:::read_settings("foo"), "Malformed config file") }) test_that("it gives informative errors if the config file contains errors", { f <- withr::local_tempfile( lines = c( "linters: linters_with_defaults(", " closed_curly_linter = NULL,", " )" ) ) withr::local_options(list(lintr.linter_file = f)) expect_error(lintr:::read_settings("foo"), "Malformed config setting 'linters'") }) test_that("rot utility works as intended", { expect_identical(lintr:::rot(letters), c(letters[14L:26L], LETTERS[1L:13L])) }) test_that("logical_env utility works as intended", { test_env <- "LINTR_TEST_LOGICAL_ENV_" withr::with_envvar( setNames("true", test_env), expect_true(lintr:::logical_env(test_env)) ) withr::with_envvar( setNames("F", test_env), expect_false(lintr:::logical_env(test_env)) ) withr::with_envvar( setNames("", test_env), expect_null(lintr:::logical_env(test_env)) ) withr::with_envvar( setNames(list(NULL), test_env), expect_null(lintr:::logical_env(test_env)) ) }) # fixing #774 test_that("linters_with_defaults doesn't break on very long input", { expect_named( linters_with_defaults( defaults = list(), lintr::undesirable_function_linter(c( detach = paste( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ) )) ), "undesirable_function_linter" ) }) test_that("it has a smart default for encodings", { lintr:::read_settings(NULL) expect_identical(settings$encoding, "UTF-8") proj_file <- test_path("dummy_projects", "project", "cp1252.R") pkg_file <- test_path("dummy_packages", "cp1252", "R", "cp1252.R") expect_identical( normalizePath(find_rproj_at(find_package(proj_file, allow_rproj = TRUE)), winslash = "/"), normalizePath(test_path("dummy_projects", "project", "project.Rproj"), winslash = "/") ) expect_identical( normalizePath(find_package(pkg_file), winslash = "/"), normalizePath(test_path("dummy_packages", "cp1252"), winslash = "/") ) expect_identical(lintr:::find_default_encoding(proj_file), "ISO8859-1") expect_identical(lintr:::find_default_encoding(pkg_file), "ISO8859-1") lintr:::read_settings(proj_file) expect_identical(settings$encoding, "ISO8859-1") lintr:::read_settings(pkg_file) expect_identical(settings$encoding, "ISO8859-1") }) test_that("validate_config_file() detects improperly-formed settings", { .lintr <- withr::local_tempfile() withr::local_options(lintr.linter_file = .lintr) withr::local_dir(withr::local_tempdir()) writeLines("asdf: 1", .lintr) expect_warning(lint_dir(), "Found unused settings in config", fixed = TRUE) writeLines("a=1", "aaa.R") writeLines(c('exclusions: list("aaa.R")', "asdf: 1"), .lintr) expect_warning(lint_dir(), "Found unused settings in config", fixed = TRUE) writeLines("encoding: FALSE", .lintr) expect_error(lint_dir(), "Setting 'encoding' should be a character string, not 'FALSE'", fixed = TRUE) writeLines("encoding: NA_character_", .lintr) expect_error(lint_dir(), "Setting 'encoding' should be a character string, not 'NA'", fixed = TRUE) writeLines('encoding: c("a", "b")', .lintr) expect_error(lint_dir(), "Setting 'encoding' should be a character string, not 'a, b'") writeLines("exclude: FALSE", .lintr) expect_error(lint_dir(), "Setting 'exclude' should be a single regular expression, not 'FALSE'", fixed = TRUE) writeLines(c('exclusions: list("aaa.R")', "exclude: FALSE"), .lintr) expect_error(lint_dir(), "Setting 'exclude' should be a single regular expression, not 'FALSE'", fixed = TRUE) writeLines('exclude: "("', .lintr) expect_error(lint_dir(), "Setting 'exclude' should be a single regular expression, not '('", fixed = TRUE) writeLines('comment_bot: "a"', .lintr) expect_error(lint_dir(), "Setting 'comment_bot' should be TRUE or FALSE, not 'a'", fixed = TRUE) writeLines("comment_bot: NA", .lintr) expect_error(lint_dir(), "Setting 'comment_bot' should be TRUE or FALSE, not 'NA'", fixed = TRUE) writeLines("comment_bot: c(TRUE, FALSE)", .lintr) expect_error(lint_dir(), "Setting 'comment_bot' should be TRUE or FALSE, not 'TRUE, FALSE'", fixed = TRUE) writeLines("linters: list(1)", .lintr) expect_error(lint_dir(), "Setting 'linters' should be a list of linters", fixed = TRUE) writeLines("linters: list(assignment_linter(), 1)", .lintr) expect_error(lint_dir(), "Setting 'linters' should be a list of linters", fixed = TRUE) writeLines("exclusions: list(1L)", .lintr) expect_error(lint_dir(), "Unnamed entries of setting 'exclusions' should be strings", fixed = TRUE) writeLines('exclusions: list("aaa.R", 1L)', .lintr) expect_error(lint_dir(), "Unnamed entries of setting 'exclusions' should be strings", fixed = TRUE) writeLines("exclusions: list(letters)", .lintr) expect_error(lint_dir(), "Unnamed entries of setting 'exclusions' should be strings", fixed = TRUE) writeLines("exclusions: list(NA_character_)", .lintr) expect_error(lint_dir(), "Unnamed entries of setting 'exclusions' should be strings", fixed = TRUE) writeLines('exclusions: list(aaa.R = "abc")', .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) writeLines("exclusions: list(aaa.R = NA_integer_)", .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) writeLines('exclusions: list(aaa.R = list("abc"))', .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) writeLines("exclusions: list(aaa.R = list(NA_integer_))", .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) writeLines('exclusions: list(aaa.R = list(assignment_linter = "abc"))', .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) writeLines("exclusions: list(aaa.R = list(assignment_linter = NA_integer_))", .lintr) expect_error(lint_dir(), "Named entries of setting 'exclusions' should designate line numbers", fixed = TRUE) }) test_that("exclusions can be a character vector", { withr::local_dir(withr::local_tempdir()) # exclusions are relative to dirname(.lintr), so must create it here .lintr <- withr::local_tempfile(tmpdir = getwd()) withr::local_options(lintr.linter_file = .lintr) writeLines('exclusions: "aaa.R"', .lintr) writeLines("a<-1", "aaa.R") writeLines("b<-1", "bbb.R") expect_length(lint_dir(linters = infix_spaces_linter()), 1L) writeLines('exclusions: c("aaa.R", "bbb.R")', .lintr) expect_length(lint_dir(linters = infix_spaces_linter()), 0L) }) test_that("lines Inf means 'all lines'", { withr::local_dir(withr::local_tempdir()) # exclusions are relative to dirname(.lintr), so must create it here .lintr <- withr::local_tempfile(tmpdir = getwd()) withr::local_options(lintr.linter_file = .lintr) writeLines("exclusions: list(aaa.R = Inf)", .lintr) writeLines("a<-1", "aaa.R") expect_length(lint_dir(linters = infix_spaces_linter()), 0L) writeLines("exclusions: list(aaa.R = list(infix_spaces_linter = Inf))", .lintr) # exclude infix_spaces_linter, include assignment_linter() writeLines("a=1", "aaa.R") expect_length(lint_dir(linters = list(assignment_linter(), infix_spaces_linter())), 1L) }) lintr/tests/testthat/test-routine_registration_linter.R0000644000176200001440000000067014577052532023355 0ustar liggesuserspatrick::with_parameters_test_that( "lints correctly", { linter <- routine_registration_linter() expect_lint(sprintf("%s(ROUTINE, 1)", caller), NULL, linter) expect_lint( sprintf("%s('ROUTINE', PACKAGE = 'foo')", caller), "Register your native code routines with useDynLib", linter ) }, .test_name = c(".C", ".Call", ".External", ".Fortran"), caller = c(".C", ".Call", ".External", ".Fortran") ) lintr/tests/testthat/test-is_lint_level.R0000644000176200001440000000044014250050336020330 0ustar liggesuserstest_that("is_lint_level works", { pc <- list(parsed_content = 1L) expect_true(is_lint_level(pc, "expression")) expect_false(is_lint_level(pc, "file")) names(pc) <- "full_parsed_content" expect_true(is_lint_level(pc, "file")) expect_false(is_lint_level(pc, "expression")) }) lintr/tests/testthat/test-spaces_inside_linter.R0000644000176200001440000001204514506330025021672 0ustar liggesuserstest_that("spaces_inside_linter skips allowed usages", { linter <- spaces_inside_linter() expect_lint("blah", NULL, linter) expect_lint("print(blah)", NULL, linter) expect_lint("base::print(blah)", NULL, linter) expect_lint("a[, ]", NULL, linter) expect_lint("a[1]", NULL, linter) expect_lint("fun(\na[1]\n )", NULL, linter) expect_lint("a(, )", NULL, linter) expect_lint("a(,)", NULL, linter) expect_lint("a(1)", NULL, linter) expect_lint('"a( 1 )"', NULL, linter) # trailing comments are OK (#636) expect_lint( trim_some(" or( #code x, y ) "), NULL, linter ) expect_lint( trim_some(" fun( # this is another comment a = 42, # because 42 is always the answer b = Inf ) "), NULL, linter ) }) test_that("spaces_inside_linter blocks diallowed usages", { linter <- spaces_inside_linter() expect_lint( "a[1 ]", list( message = "Do not place spaces before square brackets", line_number = 1L, column_number = 4L, type = "style" ), linter ) expect_lint( "a[[1 ]]", list( message = "Do not place spaces before square brackets", line_number = 1L, column_number = 5L, type = "style" ), linter ) expect_lint( "\n\na[ 1]", list( message = "Do not place spaces after square brackets", line_number = 3L, column_number = 3L, type = "style" ), linter ) expect_lint( "a[ 1 ]", list( list( message = "Do not place spaces after square brackets", line_number = 1L, column_number = 3L, type = "style" ), list( message = "Do not place spaces before square brackets", line_number = 1L, column_number = 5L, type = "style" ) ), linter ) expect_lint( "a(1 )", list( message = "Do not place spaces before parentheses", line_number = 1L, column_number = 4L, type = "style" ), linter ) expect_lint( "a[[ 1]]", list( message = "Do not place spaces after square brackets", line_number = 1L, column_number = 4L, type = "style" ), linter ) expect_lint( "a( 1)", list( message = "Do not place spaces after parentheses", line_number = 1L, column_number = 3L, type = "style" ), linter ) expect_lint( "x[[ 1L ]]", list( list( message = "Do not place spaces after square brackets", line_number = 1L, column_number = 4L, type = "style" ), list( message = "Do not place spaces before square brackets", line_number = 1L, column_number = 7L, type = "style" ) ), linter ) expect_lint( "a( 1 )", list( list( message = "Do not place spaces after parentheses", line_number = 1L, column_number = 3L, type = "style" ), list( message = "Do not place spaces before parentheses", line_number = 1L, column_number = 5L, type = "style" ) ), linter ) # range covers all whitespace expect_lint( "a( blah )", list( list( message = "Do not place spaces after parentheses", line_number = 1L, column_number = 3L, ranges = list(c(3L, 4L)), type = "style" ), list( message = "Do not place spaces before parentheses", line_number = 1L, column_number = 9L, ranges = list(c(9L, 10L)), type = "style" ) ), linter ) }) test_that("multi-line expressions have good markers", { expect_lint( trim_some(" ( x | y ) "), list( list(line_number = 1L, ranges = list(c(2L, 2L)), message = "Do not place spaces after parentheses"), list(line_number = 2L, ranges = list(c(4L, 4L)), message = "Do not place spaces before parentheses") ), spaces_inside_linter() ) }) test_that("spaces_inside_linter blocks disallowed usages with a pipe", { skip_if_not_r_version("4.1.0") linter <- spaces_inside_linter() expect_lint( "letters[1:3] %>% paste0( )", list( list( message = "Do not place spaces after parentheses", line_number = 1L, column_number = 25L, type = "style" ), list( message = "Do not place spaces before parentheses", line_number = 1L, column_number = 25L, type = "style" ) ), linter ) expect_lint( "letters[1:3] |> paste0( )", list( list( message = "Do not place spaces after parentheses", line_number = 1L, column_number = 24L, type = "style" ), list( message = "Do not place spaces before parentheses", line_number = 1L, column_number = 24L, type = "style" ) ), linter ) }) test_that("terminal missing keyword arguments are OK", { expect_lint("alist(missing_arg = )", NULL, spaces_inside_linter()) }) lintr/tests/testthat/test-knitr_extended_formats.R0000644000176200001440000000145414457657444022276 0ustar liggesuserstest_that("marginfigure engine from tufte package doesn't cause problems", { skip_if_not_installed("tufte", minimum_version = "0.12.4") # for rstudio/tufte#117 loadNamespace("tufte") # to register additional engines expect_lint( file = test_path("knitr_extended_formats", "tufte.Rmd"), checks = list(rex::rex("Use <-, not =, for assignment."), line_number = 11L), default_linters, parse_settings = FALSE ) }) test_that("engines from bookdown package cause no problems", { skip_if_not_installed("bookdown") loadNamespace("bookdown") # to register additional engines expect_lint( file = test_path("knitr_extended_formats", "bookdown.Rmd"), checks = list(rex::rex("Use <-, not =, for assignment."), line_number = 14L), default_linters, parse_settings = FALSE ) }) lintr/tests/testthat/test-length_levels_linter.R0000644000176200001440000000052414577052532021727 0ustar liggesuserstest_that("length_levels_linter skips allowed usages", { expect_lint("length(c(levels(x), 'a'))", NULL, length_levels_linter()) }) test_that("length_levels_linter blocks simple disallowed usages", { expect_lint( "2:length(levels(x))", rex::rex("nlevels(x) is better than length(levels(x))."), length_levels_linter() ) }) lintr/tests/testthat/test-strings_as_factors_linter.R0000644000176200001440000000701614577052532022774 0ustar liggesuserstest_that("strings_as_factors_linter skips allowed usages", { linter <- strings_as_factors_linter() expect_lint("data.frame(1:3)", NULL, linter) expect_lint("data.frame(x = 1:3)", NULL, linter) expect_lint("data.frame(x = 'a', stringsAsFactors = TRUE)", NULL, linter) expect_lint("data.frame(x = 'a', stringsAsFactors = FALSE)", NULL, linter) expect_lint("data.frame(x = c('a', 'b'), stringsAsFactors = FALSE)", NULL, linter) # strings in argument names to c() don't get linted expect_lint("data.frame(x = c('a b' = 1L, 'b c' = 2L))", NULL, linter) # characters supplied to row.names are not affected expect_lint("data.frame(x = 1:3, row.names = c('a', 'b', 'c'))", NULL, linter) # ambiguous cases passes expect_lint("data.frame(x = c(xx, 'a'))", NULL, linter) expect_lint("data.frame(x = c(foo(y), 'a'))", NULL, linter) }) test_that("strings_as_factors_linter blocks simple disallowed usages", { linter <- strings_as_factors_linter() lint_msg <- "This code relies on the default value of stringsAsFactors" expect_lint("data.frame('a')", lint_msg, linter) expect_lint("data.frame(c('a', 'b'))", lint_msg, linter) expect_lint("data.frame(x = 1:5, y = 'b')", lint_msg, linter) expect_lint("data.frame(x = 1:5, y, z = 'b')", lint_msg, linter) # catch row.names when combined with a character vector expect_lint("data.frame(x = c('c', 'd', 'e'), row.names = c('a', 'b', 'c'))", lint_msg, linter) expect_lint("data.frame(row.names = c('c', 'd', 'e'), x = c('a', 'b', 'c'))", lint_msg, linter) # when everything is a literal, type promotion means the column is character expect_lint("data.frame(x = c(TRUE, 'a'))", lint_msg, linter) }) test_that("strings_as_factors_linters catches rep(char) usages", { linter <- strings_as_factors_linter() lint_msg <- "This code relies on the default value of stringsAsFactors" expect_lint("data.frame(rep('a', 10L))", lint_msg, linter) expect_lint("data.frame(rep(c('a', 'b'), 10L))", lint_msg, linter) # literal char, not mixed or non-char expect_lint("data.frame(rep(1L, 10L))", NULL, linter) expect_lint("data.frame(rep(c(x, 'a'), 10L))", NULL, linter) # however, type promotion of literals is caught expect_lint("data.frame(rep(c(TRUE, 'a'), 10L))", lint_msg, linter) }) test_that("strings_as_factors_linter catches character(), as.character() usages", { linter <- strings_as_factors_linter() lint_msg <- "This code relies on the default value of stringsAsFactors" expect_lint("data.frame(a = character())", lint_msg, linter) expect_lint("data.frame(a = character(1L))", lint_msg, linter) expect_lint("data.frame(a = as.character(x))", lint_msg, linter) # but not for row.names expect_lint("data.frame(a = 1:10, row.names = as.character(1:10))", NULL, linter) }) test_that("strings_as_factors_linter catches more functions with string output", { linter <- strings_as_factors_linter() lint_msg <- "This code relies on the default value of stringsAsFactors" expect_lint("data.frame(a = paste(1, 2, 3))", lint_msg, linter) expect_lint("data.frame(a = sprintf('%d', 1:10))", lint_msg, linter) expect_lint("data.frame(a = format(x, just = 'right'))", lint_msg, linter) expect_lint("data.frame(a = formatC(x, format = '%d'))", lint_msg, linter) expect_lint("data.frame(a = prettyNum(x, big.mark = ','))", lint_msg, linter) expect_lint("data.frame(a = toString(x))", lint_msg, linter) expect_lint("data.frame(a = encodeString(x))", lint_msg, linter) # but not for row.names expect_lint("data.frame(a = 1:10, row.names = paste(1:10))", NULL, linter) }) lintr/tests/testthat/test-defaults.R0000644000176200001440000000277614577052532017341 0ustar liggesuserstest_that("linters", { # non-empty named list of functions x <- default_linters expect_type(x, "list") expect_gt(length(x), 0L) expect_true(all(names(x) != "")) expect_true(all(vapply(x, inherits, logical(1L), "linter"))) expect_true(all(vapply(x, is.function, logical(1L)))) }) test_that("undesirable functions and operators", { # non-empty named list of NAs and character strings vars <- list( all_undesirable_functions, default_undesirable_functions, all_undesirable_operators, default_undesirable_operators ) for (x in vars) { expect_type(x, "list") expect_gt(length(x), 0L) expect_true(all(names(x) != "")) expect_true(all(vapply(x, function(x) is.na(x) || is.character(x), logical(1L)))) expect_true(all(lengths(x) == 1L)) } }) test_that("settings", { # non-empty named list x <- default_settings expect_type(default_settings, "list") expect_gt(length(default_settings), 0L) expect_true(all(nzchar(names(default_settings)))) }) patrick::with_parameters_test_that( "all default linters throw lints with their name on 'default_linter_testcode.R'", { lints <- lint( test_path("default_linter_testcode.R"), linters = default_linters[[linter_name]], parse_settings = FALSE ) lint_df <- as.data.frame(lints) expect_gt(nrow(lint_df), 0L) reported_linter_name <- lint_df[["linter"]][1L] expect_identical(reported_linter_name, linter_name) }, linter_name = names(default_linters), .test_name = names(default_linters) ) lintr/tests/testthat/test-lint_package.R0000644000176200001440000001741114577052532020143 0ustar liggesusers# When called from inside a package: # > lint_package(".") # .. should give the same results as when called from outside the package # with: # > lint_package(path_to_package) # Template packages for use in testing are stored in # tests/testthat/dummy_packages/ # These packages should not have a .lintr file: Hardcoding a .lintr in a # dummy package throws problems during `R CMD check` (they are flagged as # hidden files, but can't be added to RBuildIgnore since they should be # available during `R CMD check` tests) test_that( "`lint_package` does not depend on path to pkg - no excluded files", { withr::local_options(lintr.linter_file = "lintr_test_config") # This dummy package does not have a .lintr file, so no files / lines should # be excluded from analysis pkg_path <- test_path("dummy_packages", "assignmentLinter") expected_lines <- c( # from abc.R "abc = 123", # from jkl.R "jkl = 456", "mno = 789", # from exec/script.R "x = 1:4" ) lints_from_outside <- lint_package( pkg_path, linters = list(assignment_linter()) ) lints_from_pkg_root <- withr::with_dir( pkg_path, lint_package(".", linters = list(assignment_linter()), parse_settings = FALSE) ) lints_from_a_subdir <- withr::with_dir( file.path(pkg_path, "R"), lint_package("..", linters = list(assignment_linter()), parse_settings = FALSE) ) expect_identical( as.data.frame(lints_from_outside)[["line"]], expected_lines ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_pkg_root), info = paste( "lint_package() finds the same lints from pkg-root as from outside a pkg", "(no .lintr config present)" ) ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_a_subdir), info = paste( "lint_package() finds the same lints from a subdir as from outside a pkg", "(no .lintr config present)" ) ) } ) test_that( "`lint_package` does not depend on path to pkg - with excluded files", { # Since excluded regions can be specified in two ways # list( # filename = line_numbers, # approach 1 # filename # approach 2 # ), # the test checks both approaches pkg_path <- test_path("dummy_packages", "assignmentLinter") # Add a .lintr that excludes the whole of `abc.R` and the first line of # `jkl.R` (and remove it on finishing this test) local_config(pkg_path, "exclusions: list('R/abc.R', 'R/jkl.R' = 1)") expected_lines <- c("mno = 789", "x = 1:4") lints_from_outside <- lint_package( pkg_path, linters = list(assignment_linter()) ) lints_from_pkg_root <- withr::with_dir( pkg_path, lint_package(".", linters = list(assignment_linter())) ) lints_from_a_subdir <- withr::with_dir( file.path(pkg_path, "R"), lint_package(".", linters = list(assignment_linter())) ) lints_from_a_subsubdir <- withr::with_dir( file.path(pkg_path, "tests", "testthat"), lint_package(".", linters = list(assignment_linter())) ) expect_identical( as.data.frame(lints_from_outside)[["line"]], expected_lines ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_pkg_root), info = paste( "lint_package() finds the same lints from pkg-root as from outside a pkg", "(.lintr config present)" ) ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_a_subdir), info = paste( "lint_package() finds the same lints from a subdir as from outside a pkg", "(.lintr config present)" ) ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_a_subsubdir), info = paste( "lint_package() finds the same lints from a sub-subdir as from outside a pkg", "(.lintr config present)" ) ) } ) test_that("lint_package returns early if no package is found", { temp_pkg <- withr::local_tempdir("dir") expect_warning( { l <- lint_package(temp_pkg) }, "Didn't find any R package", fixed = TRUE ) expect_null(l) # ignore a folder named DESCRIPTION, #702 file.copy(test_path("dummy_packages", "desc_dir_pkg"), temp_pkg, recursive = TRUE) expect_warning( lint_package(file.path(temp_pkg, "desc_dir_pkg", "DESCRIPTION", "R")), "Didn't find any R package", fixed = TRUE ) }) test_that("length(path)>1 is not supported", { expect_error(lint_package(letters), "one package at a time", fixed = TRUE) }) test_that( "`lint_package` will use a `.lintr` file in `.github/linters/` directory the same as the package root", { withr::local_options(lintr.linter_file = "lintr_test_config") pkg_path <- test_path("dummy_packages", "github_lintr_file") # First, ensure that the package has lint messages in the absence of a # custom configuration: pkg_lints_before <- withr::with_dir( pkg_path, lint_package(".", linters = list(quotes_linter())) ) expect_identical( as.data.frame(pkg_lints_before)[["line"]], c("'abc'", "'abc'"), "linting the `github_lintr_file` package should fail" ) # In `github/linters`add a `.lintr` file dir.create( path = file.path(pkg_path, ".github", "linters/"), recursive = TRUE ) on.exit(unlink(file.path(pkg_path, ".github"), recursive = TRUE), add = TRUE) local_config( file.path(pkg_path, ".github", "linters"), "linters: linters_with_defaults(quotes_linter(\"'\"))", filename = "lintr_test_config" ) pkg_lints <- withr::with_dir(pkg_path, lint_package(".")) expect_length(pkg_lints, 0L) subdir_lints <- withr::with_dir(pkg_path, lint_dir("tests/testthat")) expect_length(subdir_lints, 0L) } ) test_that("package using .lintr.R config lints correctly", { withr::local_options(lintr.linter_file = "lintr_test_config") r_config_pkg <- test_path("dummy_packages", "RConfig") lints <- as.data.frame(lint_package(r_config_pkg)) expect_identical(unique(basename(lints$filename)), "lint_me.R") expect_identical(lints$linter, c("infix_spaces_linter", "any_duplicated_linter")) # config has bad R syntax expect_error( lint_package(test_path("dummy_packages", "RConfigInvalid")), "Malformed config file, ensure it is valid R syntax", fixed = TRUE ) # config produces unused variables withr::local_options(lintr.linter_file = "lintr_test_config_extraneous") expect_warning( expect_length(lint_package(r_config_pkg), 2L), "Found unused settings in config", fixed = TRUE ) # R is preferred if multiple matched configs withr::local_options(lintr.linter_file = "lintr_test_config_conflict") lints <- as.data.frame(lint_package(r_config_pkg)) expect_identical(unique(basename(lints$filename)), "testthat.R") expect_identical(lints$linter, c("expect_null_linter", "trailing_blank_lines_linter")) }) test_that("lintr need not be attached for .lintr.R configs to use lintr functions", { skip_if_not_r_version("3.6.0") # unclear error message exprs <- paste( 'options(lintr.linter_file = "lintr_test_config")', sprintf('lints <- lintr::lint_package("%s")', test_path("dummy_packages", "RConfig")), # simplify output to be read from stdout 'cat(paste(as.data.frame(lints)$linter, collapse = "|"), "\n", sep = "")', sep = "; " ) if (tolower(Sys.info()[["sysname"]]) == "windows") { rscript <- file.path(R.home("bin"), "Rscript.exe") } else { rscript <- file.path(R.home("bin"), "Rscript") } expect_identical( system2(rscript, c("-e", shQuote(exprs)), stdout = TRUE), "infix_spaces_linter|any_duplicated_linter" ) }) lintr/tests/testthat/test-object_length_linter.R0000644000176200001440000000377014510656223021703 0ustar liggesuserstest_that("returns the correct linting", { linter <- object_length_linter() lint_msg <- rex::rex("Variable and function names should not be longer than 30 characters.") expect_lint("blah", NULL, linter) expect_lint("very_very_very_very_long_variable_names_are_not_ideal <- 1", lint_msg, linter) expect_lint( "very_very_very_very_long_variable_names_are_not_ideal <<- 'foo'", rex::rex("Variable and function names should not be longer than 40 characters."), object_length_linter(length = 40L) ) }) # Regression tests for #871 test_that("lints S3 generics correctly", { linter <- object_length_linter() lint_msg <- rex::rex("Variable and function names should not be longer than 30 characters.") expect_lint("print.very_very_long_class_name <- 1", NULL, linter) expect_lint("print.very_very_very_very_long_class_name <- 1", lint_msg, linter) expect_lint( trim_some(" very_very_very_long_generic_name <- function(x, ...) { UseMethod(\"very_very_very_long_generic_name\") } very_very_very_long_generic_name.short_class <- function(x, ...) { 42L } very_very_very_long_generic_name.very_very_very_very_long_class_name <- function(x, ...) { 2L } "), list( list(line_number = 1L), list(line_number = 9L) ), linter ) }) test_that("object_length_linter won't fail if an imported namespace is unavailable", { expect_length( lint_package( test_path("dummy_packages", "missing_dep"), linters = object_length_linter(), parse_settings = FALSE ), 3L ) }) test_that("object_length_linter won't fail if dependency has no exports", { expect_length( lint_package( test_path("dummy_packages", "no_export_dep"), linters = object_length_linter(), parse_settings = FALSE ), 1L ) }) test_that("function shorthand is caught", { skip_if_not_r_version("4.1.0") expect_lint( "abcdefghijklm <- \\() NULL", "function names", object_length_linter(length = 10L) ) }) lintr/tests/testthat/test-lengths_linter.R0000644000176200001440000000330214577052532020535 0ustar liggesuserstest_that("lengths_linter skips allowed usages", { linter <- lengths_linter() expect_lint("length(x)", NULL, linter) expect_lint("function(x) length(x) + 1L", NULL, linter) expect_lint("vapply(x, fun, integer(length(y)))", NULL, linter) expect_lint("sapply(x, sqrt, simplify = length(x))", NULL, linter) # TODO(#1570): also throw a lint here, and for map(x, length) expect_lint("lapply(x, length)", NULL, linter) }) test_that("lengths_linter blocks simple disallowed base usages", { linter <- lengths_linter() lint_msg <- rex::rex("Use lengths() to find the length of each element in a list.") expect_lint("sapply(x, length)", lint_msg, linter) expect_lint("sapply(x, FUN = length)", lint_msg, linter) expect_lint("sapply(FUN = length, x)", lint_msg, linter) expect_lint("vapply(x, length, integer(1L))", lint_msg, linter) }) test_that("lengths_linter blocks simple disallowed purrr usages", { linter <- lengths_linter() lint_msg <- rex::rex("Use lengths() to find the length of each element in a list.") expect_lint("purrr::map_dbl(x, length)", lint_msg, linter) expect_lint("map_dbl(x, .f = length)", lint_msg, linter) expect_lint("map_dbl(.f = length, x)", lint_msg, linter) expect_lint("map_int(x, length)", lint_msg, linter) }) test_that("lengths_linter blocks simple disallowed usages with pipes", { skip_if_not_r_version("4.1.0") linter <- lengths_linter() lint_msg <- rex::rex("Use lengths() to find the length of each element in a list.") expect_lint("x |> sapply(length)", lint_msg, linter) expect_lint("x %>% sapply(length)", lint_msg, linter) expect_lint("x |> map_int(length)", lint_msg, linter) expect_lint("x %>% map_int(length)", lint_msg, linter) }) lintr/tests/testthat/test-repeat_linter.R0000644000176200001440000000163314510650642020347 0ustar liggesuserstest_that("test repeat_linter", { linter <- repeat_linter() msg <- rex::rex("Use 'repeat' instead of 'while (TRUE)' for infinite loops.") expect_lint("repeat { }", NULL, linter) expect_lint("while (FALSE) { }", NULL, linter) expect_lint("while (i < 5) { }", NULL, linter) expect_lint("while (j < 5) TRUE", NULL, linter) expect_lint("while (TRUE && j < 5) { ... }", NULL, linter) expect_lint("while (TRUE) { }", msg, linter) expect_lint("for (i in 1:10) { while (TRUE) { if (i == 5) { break } } }", msg, linter) expect_lint("while (TRUE) { while (TRUE) { } }", list(msg, msg), linter) expect_lint( trim_some("{ while (TRUE) { } while (TRUE) { } }"), list( list(message = msg, line_number = 2L, column_number = 3L, ranges = list(c(3L, 14L))), list(message = msg, line_number = 4L, column_number = 3L, ranges = list(c(3L, 14L))) ), linter ) }) lintr/tests/testthat/test-matrix_apply_linter.R0000644000176200001440000000566414577052532021617 0ustar liggesuserstest_that("matrix_apply_linter skips allowed usages", { linter <- matrix_apply_linter() expect_lint("apply(x, 1, prod)", NULL, linter) expect_lint("apply(x, 1, function(i) sum(i[i > 0]))", NULL, linter) # sum as FUN argument expect_lint("apply(x, 1, f, sum)", NULL, linter) # mean() with named arguments other than na.rm is skipped because they are not # implemented in colMeans() or rowMeans() expect_lint("apply(x, 1, mean, trim = 0.2)", NULL, linter) }) test_that("matrix_apply_linter is not implemented for complex MARGIN values", { linter <- matrix_apply_linter() # Could be implemented at some point expect_lint("apply(x, seq(2, 4), sum)", NULL, linter) # No equivalent expect_lint("apply(x, c(2, 4), sum)", NULL, linter) # Beyond the scope of static analysis expect_lint("apply(x, m, sum)", NULL, linter) expect_lint("apply(x, 1 + 2:4, sum)", NULL, linter) }) test_that("matrix_apply_linter simple disallowed usages", { linter <- matrix_apply_linter() lint_message <- rex::rex("rowSums(x)") expect_lint("apply(x, 1, sum)", lint_message, linter) expect_lint("apply(x, MARGIN = 1, FUN = sum)", lint_message, linter) expect_lint("apply(x, 1L, sum)", lint_message, linter) expect_lint("apply(x, 1:4, sum)", rex::rex("rowSums(x, dims = 4)"), linter) expect_lint("apply(x, 2, sum)", rex::rex("rowSums(colSums(x))"), linter) expect_lint("apply(x, 2:4, sum)", rex::rex("rowSums(colSums(x), dims = 3)"), linter) lint_message <- rex::rex("rowMeans") expect_lint("apply(x, 1, mean)", lint_message, linter) expect_lint("apply(x, MARGIN = 1, FUN = mean)", lint_message, linter) # Works with extra args in mean() expect_lint("apply(x, 1, mean, na.rm = TRUE)", lint_message, linter) lint_message <- rex::rex("colMeans") expect_lint("apply(x, 2, mean)", lint_message, linter) expect_lint("apply(x, 2:4, mean)", lint_message, linter) }) test_that("matrix_apply_linter recommendation includes na.rm if present in original call", { linter <- matrix_apply_linter() lint_message <- rex::rex("na.rm = TRUE") expect_lint("apply(x, 1, sum, na.rm = TRUE)", lint_message, linter) expect_lint("apply(x, 2, sum, na.rm = TRUE)", lint_message, linter) expect_lint("apply(x, 1, mean, na.rm = TRUE)", lint_message, linter) expect_lint("apply(x, 2, mean, na.rm = TRUE)", lint_message, linter) lint_message <- rex::rex("rowSums(x)") expect_lint("apply(x, 1, sum)", lint_message, linter) lint_message <- rex::rex("na.rm = foo") expect_lint("apply(x, 1, sum, na.rm = foo)", lint_message, linter) }) test_that("matrix_apply_linter works with multiple lints in a single expression", { linter <- matrix_apply_linter() expect_lint( "rbind( apply(x, 1, sum), apply(y, 2:4, mean, na.rm = TRUE) )", list( rex::rex("Use rowSums(x)"), rex::rex("Use rowMeans(colMeans(y, na.rm = TRUE), dims = 3) or colMeans(y, na.rm = TRUE) if y has 4 dimensions") ), linter ) }) lintr/tests/testthat/test-any_duplicated_linter.R0000644000176200001440000001004214577052532022055 0ustar liggesuserstest_that("any_duplicated_linter skips allowed usages", { linter <- any_duplicated_linter() expect_lint("x <- any(y)", NULL, linter) expect_lint("y <- duplicated(z)", NULL, linter) # extended usage of any is not covered expect_lint("any(duplicated(y), b)", NULL, linter) expect_lint("any(b, duplicated(y))", NULL, linter) }) test_that("any_duplicated_linter blocks simple disallowed usages", { expect_lint( "any(duplicated(x))", rex::rex("anyDuplicated(x, ...) > 0 is better"), any_duplicated_linter() ) expect_lint( "any(duplicated(foo(x)))", rex::rex("anyDuplicated(x, ...) > 0 is better"), any_duplicated_linter() ) # na.rm doesn't really matter for this since duplicated can't return NA expect_lint( "any(duplicated(x), na.rm = TRUE)", rex::rex("anyDuplicated(x, ...) > 0 is better"), any_duplicated_linter() ) # also catch nested usage expect_lint( "foo(any(duplicated(x)))", rex::rex("anyDuplicated(x, ...) > 0 is better"), any_duplicated_linter() ) }) test_that("any_duplicated_linter catches length(unique()) equivalencies too", { # non-matches ## different variable expect_lint("length(unique(x)) == length(y)", NULL, any_duplicated_linter()) ## different table expect_lint("length(unique(DF$x)) == nrow(DT)", NULL, any_duplicated_linter()) expect_lint("length(unique(l1$DF$x)) == nrow(l2$DF)", NULL, any_duplicated_linter()) # lintable usage expect_lint( "length(unique(x)) == length(x)", rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), any_duplicated_linter() ) # argument order doesn't matter expect_lint( "length(x) == length(unique(x))", rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), any_duplicated_linter() ) # nrow-style equivalency expect_lint( "nrow(DF) == length(unique(DF$col))", rex::rex("anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)"), any_duplicated_linter() ) expect_lint( "nrow(DF) == length(unique(DF[['col']]))", rex::rex("anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)"), any_duplicated_linter() ) # match with nesting too expect_lint( "nrow(l$DF) == length(unique(l$DF[['col']]))", rex::rex("anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)"), any_duplicated_linter() ) # !=, <, and > usages are all alternative ways of writing a test for dupes # technically, the direction of > / < matter, but writing # length(unique(x)) > length(x) doesn't seem like it would ever happen. expect_lint( "length(unique(x)) != length(x)", rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), any_duplicated_linter() ) expect_lint( "length(unique(x)) < length(x)", rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), any_duplicated_linter() ) expect_lint( "length(x) > length(unique(x))", rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), any_duplicated_linter() ) # TODO(michaelchirico): try and match data.table- and dplyr-specific versions of # this, e.g. DT[, length(unique(col)) == .N] or # > DT %>% filter(length(unique(col)) == n()) }) test_that("any_duplicated_linter catches expression with two types of lint", { expect_lint( "table(any(duplicated(x)), length(unique(DF$col)) == nrow(DF))", list( rex::rex("anyDuplicated(x, ...) > 0 is better"), rex::rex("anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)") ), any_duplicated_linter() ) # ditto for different messages within the length(unique()) tests expect_lint( "table(length(unique(x)) == length(x), length(unique(DF$col)) == nrow(DF))", list( rex::rex("anyDuplicated(x) == 0L is better than length(unique(x)) == length(x)"), rex::rex("anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)") ), any_duplicated_linter() ) }) lintr/tests/testthat/test-comments.R0000644000176200001440000000353414577052532017350 0ustar liggesusersclear_ci_info <- function() { withr::local_envvar( c( JENKINS_URL = NA_character_, GIT_URL = NA_character_, GIT_URL_1 = NA_character_, CHANGE_ID = NA_character_, GIT_COMMIT = NA_character_ ), .local_envir = parent.frame() ) } test_that("it detects CI environments", { clear_ci_info() withr::with_envvar( c(TRAVIS_REPO_SLUG = "foo/bar"), expect_true(lintr:::in_ci()) ) withr::with_envvar( c(TRAVIS_REPO_SLUG = ""), expect_false(lintr:::in_ci()) ) }) test_that("it returns NULL if GIT_URL is not on github", { clear_ci_info() withr::local_envvar(c( JENKINS_URL = "https://jenkins.example.org/", GIT_URL = "https://example.com/user/repo.git", CHANGE_ID = "123" )) expect_false(lintr:::in_ci()) }) test_that("it returns NULL for Jenkins PR build info when git URL is missing", { clear_ci_info() expect_null(lintr:::jenkins_build_info()) }) test_that("it determines Jenkins PR build info", { clear_ci_info() withr::with_envvar( c( JENKINS_URL = "https://jenkins.example.org/", GIT_URL = "https://github.com/user/repo.git", CHANGE_ID = "123" ), { expect_true(lintr:::in_ci()) expect_identical( lintr:::ci_build_info(), list(user = "user", repo = "repo", pull = "123", commit = NULL) ) } ) withr::with_envvar( list(JENKINS_URL = NULL, GIT_URL = NULL, CHANGE_ID = NULL), expect_false(lintr:::in_ci()) ) }) test_that("it determines Jenkins commit build info", { clear_ci_info() withr::local_envvar(c( JENKINS_URL = "https://jenkins.example.org/", GIT_URL_1 = "https://github.com/user/repo.git", GIT_COMMIT = "abcde" )) expect_true(lintr:::in_ci()) expect_identical(lintr:::ci_build_info(), list( user = "user", repo = "repo", pull = NULL, commit = "abcde" )) }) lintr/tests/testthat/test-undesirable_operator_linter.R0000644000176200001440000000515214577052532023306 0ustar liggesuserstest_that("linter returns correct linting", { linter <- undesirable_operator_linter(op = c("$" = "As an alternative, use the `[[` accessor.", "<<-" = NA)) msg_assign <- rex::escape("Operator `<<-` is undesirable.") msg_dollar <- rex::escape("Operator `$` is undesirable. As an alternative, use the `[[` accessor.") expect_lint("x <- foo:::getObj()", NULL, linter) expect_lint("cat(\"10$\")", NULL, linter) expect_lint( "a <<- log(10)", list(message = msg_assign, line_number = 1L, column_number = 3L), linter ) expect_lint( "data$parsed == c(1, 2)", list(message = msg_dollar, line_number = 1L, column_number = 5L), linter ) }) test_that("undesirable_operator_linter handles '=' consistently", { linter <- undesirable_operator_linter(op = c("=" = "As an alternative, use '<-'")) expect_lint("a = 2L", rex::rex("Operator `=` is undesirable."), linter) expect_lint("lm(data = mtcars)", NULL, linter) expect_lint("function(a = 1) { }", NULL, linter) }) test_that("undesirable_operator_linter handles infixes correctly", { linter <- undesirable_operator_linter(list("%oo%" = NA)) expect_lint("a %oo% b", rex::rex("Operator `%oo%` is undesirable"), linter) expect_lint("a %00% b", NULL, linter) # somewhat special case: %% is in infix_metadata expect_lint( "foo(x %% y, x %/% y)", rex::rex("Operator `%%` is undesirable"), undesirable_operator_linter(list("%%" = NA)) ) }) test_that("undesirable_operator_linter vectorizes messages", { expect_lint( "x <<- c(pkg:::foo, bar %oo% baz)", list( rex::rex("`<<-` is undesirable. It assigns"), rex::rex("`:::` is undesirable. It accesses"), rex::rex("`%oo%` is undesirable.", end) ), undesirable_operator_linter(modify_defaults(default_undesirable_operators, "%oo%" = NA)) ) }) test_that("invalid inputs fail correctly", { error_msg <- "'op' should be a non-empty named character vector" expect_error( undesirable_operator_linter("***"), error_msg, fixed = TRUE ) expect_error( undesirable_operator_linter(c("***" = NA, NA)), error_msg, fixed = TRUE ) expect_error( undesirable_operator_linter(op = NULL), error_msg, fixed = TRUE ) expect_error( undesirable_operator_linter(op = character(0L)), error_msg, fixed = TRUE ) expect_error( undesirable_operator_linter(c("***" = NA)), "Did not recognize any valid operators in request for: ***", fixed = TRUE ) expect_error( undesirable_operator_linter(c("***" = NA, "///" = NA)), "Did not recognize any valid operators in request for: ***, ///", fixed = TRUE ) }) lintr/tests/testthat/test-unnecessary_concatenation_linter.R0000644000176200001440000000771114577052532024345 0ustar liggesuserstest_that("unnecessary_concatenation_linter skips allowed usages", { linter <- unnecessary_concatenation_linter() expect_lint("c(x)", NULL, linter) expect_lint("c(1, 2)", NULL, linter) expect_lint("c(x, recursive = TRUE)", NULL, linter) expect_lint("c(1, recursive = FALSE)", NULL, linter) expect_lint("lapply(1, c)", NULL, linter) expect_lint("c(a = 1)", NULL, linter) expect_lint("c('a' = 1)", NULL, linter) }) test_that("unnecessary_concatenation_linter blocks disallowed usages", { linter <- unnecessary_concatenation_linter() msg_c <- rex::escape('Unneeded concatenation of a constant. Remove the "c" call.') msg_e <- rex::escape('Unneeded concatenation without arguments. Replace the "c" call by NULL') expect_lint( "c()", list(message = msg_e, line_number = 1L, column_number = 1L), linter ) expect_lint( "c(NULL)", list(message = msg_c, line_number = 1L, column_number = 1L), linter ) expect_lint( "c(1)", list(message = msg_c, line_number = 1L, column_number = 1L), linter ) expect_lint( "c (\n'a' )", list(message = msg_c, line_number = 1L, column_number = 1L), linter ) expect_lint( "c(y, c('c('),\nc())", list( list(message = msg_c, line_number = 1L, column_number = 6L), list(message = msg_e, line_number = 2L, column_number = 1L) ), linter ) }) local({ pipes <- pipes(exclude = "%$%") linter <- unnecessary_concatenation_linter() patrick::with_parameters_test_that( "Correctly handles concatenation within magrittr pipes", { expect_lint(sprintf('"a" %s c("b")', pipe), NULL, linter) expect_lint( sprintf('"a" %s c()', pipe), "Unneeded concatenation of a constant", linter ) expect_lint( sprintf('"a" %s list("b", c())', pipe), "Unneeded concatenation without arguments", linter ) }, pipe = pipes, .test_name = names(pipes) ) }) test_that("symbolic expressions are allowed, except by request", { expect_lint("c(alpha / 2)", NULL, unnecessary_concatenation_linter()) expect_lint("c(paste0('.', 1:2))", NULL, unnecessary_concatenation_linter()) expect_lint("c(DF[cond > 1, col])", NULL, unnecessary_concatenation_linter()) # allow_single_expression = FALSE turns both into lints linter <- unnecessary_concatenation_linter(allow_single_expression = FALSE) message <- "Unneeded concatenation of a simple expression" expect_lint("c(alpha / 2)", message, linter) expect_lint("c(paste0('.', 1:2))", message, linter) expect_lint("c(DF[cond > 1, col])", message, linter) }) test_that("sequences with : are linted whenever a constant is involved", { linter <- unnecessary_concatenation_linter() expect_lint("c(1:10)", "Unneeded concatenation of a constant", linter) expect_lint("c(1:sum(x))", "Unneeded concatenation of a constant", linter) # this is slightly different if a,b are factors, in which case : does # something like interaction expect_lint("c(a:b)", NULL, linter) expect_lint("c(a:foo(b))", NULL, linter) expect_lint( "c(a:b)", "Unneeded concatenation of a simple expression", unnecessary_concatenation_linter(allow_single_expression = FALSE) ) expect_lint( "c(a:foo(b))", "Unneeded concatenation of a simple expression", unnecessary_concatenation_linter(allow_single_expression = FALSE) ) }) test_that("c(...) does not lint under !allow_single_expression", { expect_lint("c(...)", NULL, unnecessary_concatenation_linter(allow_single_expression = FALSE)) }) test_that("invalid allow_single_expression argument produce informative error messages", { expect_error( expect_lint("c()", NULL, unnecessary_concatenation_linter(allow_single_expression = 1.0)), rex::rex("is.logical(allow_single_expression) is not TRUE") ) expect_error( expect_lint("c()", NULL, unnecessary_concatenation_linter(allow_single_expression = c(TRUE, FALSE))), rex::rex("length(allow_single_expression) == 1L is not TRUE") ) }) lintr/tests/testthat/test-ifelse_censor_linter.R0000644000176200001440000000360414577052532021716 0ustar liggesuserstest_that("ifelse_censor_linter skips allowed usages", { expect_lint("ifelse(x == 2, x, y)", NULL, ifelse_censor_linter()) expect_lint("ifelse(x > 2, x, y)", NULL, ifelse_censor_linter()) }) test_that("ifelse_censor_linter blocks simple disallowed usages", { expect_lint( "ifelse(x < 0, 0, x)", rex::rex("pmax(x, y) is preferable to ifelse(x < y, y, x)"), ifelse_censor_linter() ) # other equivalents to base::ifelse() expect_lint( "if_else(x < 0, 0, x)", rex::rex("pmax(x, y) is preferable to if_else(x < y, y, x)"), ifelse_censor_linter() ) expect_lint( "fifelse(x < 0, 0, x)", rex::rex("pmax(x, y) is preferable to fifelse(x < y, y, x)"), ifelse_censor_linter() ) # other equivalents for censoring expect_lint( "ifelse(x <= 0, 0, x)", rex::rex("pmax(x, y) is preferable to ifelse(x <= y, y, x)"), ifelse_censor_linter() ) expect_lint( "ifelse(x > 0, x, 0)", rex::rex("pmax(x, y) is preferable to ifelse(x > y, x, y)"), ifelse_censor_linter() ) expect_lint( "ifelse(x >= 0, x, 0)", rex::rex("pmax(x, y) is preferable to ifelse(x >= y, x, y)"), ifelse_censor_linter() ) # pairwise min/max (similar to censoring) expect_lint( "ifelse(x < y, x, y)", rex::rex("pmin(x, y) is preferable to ifelse(x < y, x, y)"), ifelse_censor_linter() ) expect_lint( "ifelse(x >= y, y, x)", rex::rex("pmin(x, y) is preferable to ifelse(x >= y, y, x)"), ifelse_censor_linter() ) # more complicated expression still matches lines <- trim_some(" ifelse(2 + p + 104 + 1 > ncols, ncols, 2 + p + 104 + 1 ) ") expect_lint( lines, rex::rex("pmin(x, y) is preferable to ifelse(x > y, y, x)"), ifelse_censor_linter() ) }) # TODO(michaelchirico): how easy would it be to strip parens when considering lint? # e.g. ifelse(x < (kMaxIndex - 1), x, kMaxIndex - 1) lintr/tests/testthat/test-quotes_linter.R0000644000176200001440000000427314577052532020421 0ustar liggesuserstest_that("quotes_linter skips allowed usages", { linter <- quotes_linter() expect_lint("blah", NULL, linter) expect_lint('"blah"', NULL, linter) expect_lint('"\'blah"', NULL, linter) expect_lint('"blah\'"', NULL, linter) expect_lint('"\'blah\'"', NULL, linter) expect_lint("'\"'", NULL, linter) expect_lint("'\"blah\"'", NULL, linter) }) test_that("quotes_linter blocks disallowed usages", { linter <- quotes_linter() lint_msg <- rex::rex("Only use double-quotes.") expect_lint("'blah'", lint_msg, linter) expect_lint("fun('blah')", lint_msg, linter) expect_lint("{'blah'}", lint_msg, linter) expect_lint( " x = 'test '", list(message = lint_msg, ranges = list(c(9L, 13L))), linter ) }) # NB: repeat of above tests with (mostly) opposite results test_that("quotes_linter skips allowed usages of delimiter='", { linter <- quotes_linter(delimiter = "'") expect_lint("blah", NULL, linter) expect_lint('"\'blah"', NULL, linter) expect_lint('"blah\'"', NULL, linter) expect_lint('"\'blah\'"', NULL, linter) expect_lint("'\"'", NULL, linter) expect_lint("'\"blah\"'", NULL, linter) expect_lint("'blah'", NULL, linter) expect_lint("fun('blah')", NULL, linter) expect_lint("{'blah'}", NULL, linter) }) test_that("quotes_linter blocks disallowed usages of delimiter='", { linter <- quotes_linter(delimiter = "'") lint_msg <- rex::rex("Only use single-quotes.") expect_lint('"blah"', lint_msg, linter) expect_lint( ' x = "test "', list(message = lint_msg, ranges = list(c(9L, 13L))), linter ) }) test_that("handles R>=4.0.0 strings", { skip_if_not_r_version("4.0.0") linter <- quotes_linter() expect_lint('R"(hello \'\' there)"', NULL, linter) expect_lint("R'( whoops )'", rex::rex("Only use double-quotes."), linter) expect_lint("R'---[ daisy ]---'", rex::rex("Only use double-quotes."), linter) expect_lint("r'(\")'", NULL, linter) }) test_that("single_quotes_linter is deprecated", { expect_warning( { old_linter <- single_quotes_linter() }, "Use quotes_linter instead", fixed = TRUE ) expect_lint('"blah"', NULL, old_linter) expect_lint("'blah'", "Only use double-quotes", old_linter) }) lintr/tests/testthat/test-paste_linter.R0000644000176200001440000002101014510650642020172 0ustar liggesuserstest_that("paste_linter skips allowed usages for sep=''", { linter <- paste_linter() expect_lint("paste('a', 'b', 'c')", NULL, linter) expect_lint("paste('a', 'b', 'c', sep = ',')", NULL, linter) expect_lint("paste('a', 'b', collapse = '')", NULL, linter) expect_lint("cat(paste('a', 'b'), sep = '')", NULL, linter) expect_lint("sep <- ''; paste('a', sep)", NULL, linter) expect_lint("paste(sep = ',', '', 'a')", NULL, linter) expect_lint("paste0('a', 'b', 'c')", NULL, linter) }) test_that("paste_linter blocks simple disallowed usages for sep=''", { expect_lint( "paste(sep = '', 'a', 'b')", rex::rex('paste0(...) is better than paste(..., sep = "").'), paste_linter() ) expect_lint( "paste('a', 'b', sep = '')", rex::rex('paste0(...) is better than paste(..., sep = "").'), paste_linter() ) }) test_that("paste_linter skips allowed usages for collapse=', '", { linter <- paste_linter() expect_lint("paste('a', 'b', 'c')", NULL, linter) expect_lint("paste(x, sep = ', ')", NULL, linter) expect_lint("paste(x, collapse = ',')", NULL, linter) expect_lint("paste(foo(x), collapse = '/')", NULL, linter) # harder to catch statically expect_lint("collapse <- ', '; paste(x, collapse = collapse)", NULL, linter) # paste(..., sep=sep, collapse=", ") is not a trivial swap to toString expect_lint("paste(x, y, sep = '.', collapse = ', ')", NULL, linter) # any call involving ...length() > 1 will implicitly use the default sep expect_lint("paste(x, y, collapse = ', ')", NULL, linter) expect_lint("paste0(x, y, collapse = ', ')", NULL, linter) expect_lint("toString(x)", NULL, linter) # string match of ", " is OK -- lint only _exact_ match expect_lint('paste(x, collapse = ", \n")', NULL, linter) }) test_that("paste_linter blocks simple disallowed usages for collapse=', '", { expect_lint( "paste(collapse = ', ', x)", rex::rex('toString(.) is more expressive than paste(., collapse = ", ")'), paste_linter() ) expect_lint( "paste0(foo(x), collapse = ', ')", rex::rex('toString(.) is more expressive than paste(., collapse = ", ")'), paste_linter() ) }) test_that("paste_linter respects non-default arguments", { expect_lint("paste(sep = '', 'a', 'b')", NULL, paste_linter(allow_empty_sep = TRUE)) expect_lint("paste('a', 'b', sep = '')", NULL, paste_linter(allow_empty_sep = TRUE)) expect_lint("paste(collapse = ', ', x)", NULL, paste_linter(allow_to_string = TRUE)) expect_lint("paste0(foo(x), collapse = ', ')", NULL, paste_linter(allow_to_string = TRUE)) }) test_that("paste_linter works for raw strings", { skip_if_not_r_version("4.0.0") expect_lint("paste(a, b, sep = R'(xyz)')", NULL, paste_linter()) expect_lint( 'paste(a, b, sep = R"---[]---")', rex::rex('paste0(...) is better than paste(..., sep = "").'), paste_linter() ) expect_lint("paste(x, collapse = R'(,,)')", NULL, paste_linter()) expect_lint( 'paste(c(a, b, c), collapse = R"-{, }-")', rex::rex('toString(.) is more expressive than paste(., collapse = ", ")'), paste_linter() ) }) test_that("paste_linter catches use of paste0 with sep=", { expect_lint( "paste0(x, y, sep = '')", rex::rex("sep= is not a formal argument to paste0();"), paste_linter() ) }) test_that("paste_linter skips allowed usages for strrep()", { linter <- paste_linter() expect_lint("paste(x, collapse = '')", NULL, linter) expect_lint("paste(rep('*', 10), collapse = '+')", NULL, linter) expect_lint("paste(rep(c('a', 'b'), 2), collapse = '')", NULL, linter) expect_lint("paste0(rep('a', 2), 'b', collapse = '')", NULL, linter) # no collapse expect_lint("paste(rep('*', 10))", NULL, linter) # combined before aggregating expect_lint("paste(rep('*', 10), rep('x', 10), collapse = '')", NULL, linter) }) test_that("paste_linter blocks simple disallowed usages", { linter <- paste_linter() lint_msg <- rex::rex("strrep(x, times) is better than paste") expect_lint("paste0(rep('*', 20L), collapse='')", lint_msg, linter) expect_lint("paste(rep('#', width), collapse='')", lint_msg, linter) }) test_that("paste_linter skips allowed usages for file paths", { linter <- paste_linter() expect_lint("paste('a', 'b', 'c')", NULL, linter) expect_lint("paste('a', 'b', 'c', sep = ',')", NULL, linter) expect_lint("paste('a', 'b', collapse = '/')", NULL, linter) expect_lint("cat(paste('a', 'b'), sep = '/')", NULL, linter) expect_lint("sep <- '/'; paste('a', sep)", NULL, linter) expect_lint("paste(sep = ',', '/', 'a')", NULL, linter) # paste(..., sep='/', collapse=collapse) is not a trivial swap to file.path expect_lint("paste(x, y, sep = '/', collapse = ':')", NULL, linter) expect_lint("file.path('a', 'b', 'c')", NULL, linter) # testing the sep starts with / is not enough expect_lint("paste('a', 'b', sep = '//')", NULL, linter) }) test_that("paste_linter blocks simple disallowed usages for file paths", { linter <- paste_linter() lint_msg <- rex::rex("Construct file paths with file.path(...) instead of") expect_lint("paste(sep = '/', 'a', 'b')", lint_msg, linter) expect_lint("paste('a', 'b', sep = '/')", lint_msg, linter) }) test_that("paste_linter ignores non-path cases with paste0", { linter <- paste_linter() expect_lint("paste0(x, y)", NULL, linter) expect_lint("paste0('abc', 'def')", NULL, linter) expect_lint("paste0('/abc', 'def/')", NULL, linter) expect_lint("paste0(x, 'def/')", NULL, linter) expect_lint("paste0('/abc', y)", NULL, linter) expect_lint("paste0(foo(x), y)", NULL, linter) expect_lint("paste0(foo(x), 'def')", NULL, linter) # these might be a different lint (as.character instead, e.g.) but not here expect_lint("paste0(x)", NULL, linter) expect_lint("paste0('a')", NULL, linter) expect_lint("paste0('a', 1)", NULL, linter) # paste0(..., collapse=collapse) not directly mapped to file.path expect_lint("paste0(x, collapse = '/')", NULL, linter) }) test_that("paste_linter detects paths built with '/' and paste0", { linter <- paste_linter() lint_msg <- rex::rex("Construct file paths with file.path(...) instead of") expect_lint("paste0(x, '/', y)", lint_msg, linter) expect_lint("paste0(x, '/', y, '/', z)", lint_msg, linter) expect_lint("paste0(x, '/abc/', 'def/', y)", lint_msg, linter) expect_lint("paste0(foo(x), '/abc/', 'def/', bar(y))", lint_msg, linter) }) test_that("paste_linter skips initial/terminal '/' and repeated '/' for paths", { linter <- paste_linter() expect_lint("paste0('/', x)", NULL, linter) expect_lint("paste0(x, '/')", NULL, linter) expect_lint("paste0(x, '//hey/', y)", NULL, linter) expect_lint("paste0(x, '/hey//', y)", NULL, linter) }) test_that("paste_linter doesn't skip all initial/terminal '/' for paths", { linter <- paste_linter() lint_msg <- rex::rex("Construct file paths with file.path(...) instead of") expect_lint('paste0("/abc/", "def")', lint_msg, linter) expect_lint('paste0("abc/", "def/")', lint_msg, linter) }) test_that("multiple path lints are generated correctly", { linter <- paste_linter() expect_lint( trim_some("{ paste(x, y, sep = '/') paste0(x, '/', y) }"), list( rex::rex('paste(..., sep = "/")'), rex::rex('paste0(x, "/", y, "/", z)') ), linter ) # check vectorization of multiple paste0 file paths expect_lint( trim_some('{ paste0(x, y) paste0("a/", x, y, "/b") paste0("a", "b") paste0("a", x) paste0(a, "x") paste0("a/", NA_character_, "/b") paste0("a/", "b") paste0("a/", "bcd//efg", "/h") paste0("/", a) paste0(a, "/") }'), list(message = rex::rex("Construct file paths with file.path(...)"), line_number = 8L), linter ) }) test_that("allow_file_path argument works", { expect_lint("paste(x, y, sep = '/')", NULL, paste_linter(allow_file_path = "always")) }) test_that("URLs are ignored by default, linted optionally", { linter <- paste_linter() linter_url <- paste_linter(allow_file_path = "never") expect_lint("paste0('http://site.com/', x)", NULL, linter) expect_lint("paste0('http://site.com/', x)", rex::rex("Construct file paths with file.path(...)"), linter_url) }) test_that("raw strings are detected in file path logic", { skip_if_not_r_version("4.0.0") linter <- paste_linter() lint_msg <- rex::rex("Construct file paths with file.path(...) instead of ") expect_lint("paste0(x, R'{abc}', y)", NULL, linter) expect_lint("paste0(x, R'{/abc/}', y)", lint_msg, linter) expect_lint("paste(x, y, sep = R'{//}')", NULL, linter) expect_lint("paste(x, y, sep = R'{/}')", lint_msg, linter) }) lintr/tests/testthat/test-expect_named_linter.R0000644000176200001440000000303214577052532021525 0ustar liggesuserstest_that("expect_named_linter skips allowed usages", { linter <- expect_named_linter() # colnames(), rownames(), and dimnames() tests are not equivalent expect_lint("expect_equal(colnames(x), 'a')", NULL, linter) expect_lint("expect_equal(rownames(x), 'a')", NULL, linter) expect_lint("expect_equal(dimnames(x), 'a')", NULL, linter) expect_lint("expect_equal(nrow(x), 4L)", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_equal(nrow(x), 4L)", NULL, linter) # only check the first argument. yoda tests in the second argument will be # missed, but there are legitimate uses of names() in argument 2 expect_lint("expect_equal(colnames(x), names(y))", NULL, linter) }) test_that("expect_named_linter blocks simple disallowed usages", { expect_lint( "expect_equal(names(x), 'a')", rex::rex("expect_named(x, n) is better than expect_equal(names(x), n)"), expect_named_linter() ) expect_lint( "testthat::expect_equal(names(DF), names(old))", rex::rex("expect_named(x, n) is better than expect_equal(names(x), n)"), expect_named_linter() ) expect_lint( "expect_equal('a', names(x))", rex::rex("expect_named(x, n) is better than expect_equal(names(x), n)"), expect_named_linter() ) }) test_that("expect_named_linter blocks expect_identical usage as well", { expect_lint( "expect_identical(names(x), 'a')", rex::rex("expect_named(x, n) is better than expect_identical(names(x), n)"), expect_named_linter() ) }) lintr/tests/testthat/test-ids_with_token.R0000644000176200001440000000106414577052532020531 0ustar liggesuserstest_that("ids_with_token works as expected", { source_expression <- get_source_expressions("tmp.R", "a <- 42L")$expressions[[1L]] ref <- ids_with_token(source_expression = source_expression, value = "expr") expect_identical(ref, c(1L, 3L, 6L)) expect_identical(source_expression$parsed_content$token[ref], rep_len("expr", length(ref))) # deprecated argument expect_warning( { old_arg <- ids_with_token(source_file = source_expression, value = "expr") }, "Argument source_file was deprecated" ) expect_identical(old_arg, ref) }) lintr/tests/testthat/test-trailing_blank_lines_linter.R0000644000176200001440000001001414577052532023241 0ustar liggesuserstest_that("trailing_blank_lines_linter doesn't block allowed usages", { linter <- trailing_blank_lines_linter() expect_lint("blah", NULL, linter) expect_lint("blah <- 1 ", NULL, linter) expect_lint("blah <- 1\nblah", NULL, linter) expect_lint("blah <- 1\nblah\n \n blah", NULL, linter) tmp <- withr::local_tempfile(lines = "lm(y ~ x)") expect_lint(file = tmp, checks = NULL, linters = linter) }) test_that("trailing_blank_lines_linter detects disallowed usages", { linter <- trailing_blank_lines_linter() lint_msg <- rex::rex("Trailing blank lines are superfluous.") expect_lint("blah <- 1\n", lint_msg, linter) expect_lint("blah <- 1\n ", lint_msg, linter) expect_lint("blah <- 1\n \n ", list(lint_msg, lint_msg), linter) expect_lint("blah <- 1\n\n", list(lint_msg, lint_msg), linter) expect_lint("blah <- 1\n\t\n", list(lint_msg, lint_msg), linter) # Construct a test file without terminal newline # cf. test-get_source_expressions.R tmp2 <- withr::local_tempfile() cat("lm(y ~ x)", file = tmp2) expect_lint( file = tmp2, checks = list( message = rex::rex("Missing terminal newline."), line_number = 1L, column_number = 10L ), linters = linter ) }) test_that("trailing_blank_lines_linter detects missing terminal newlines in Rmd/qmd docs", { linter <- trailing_blank_lines_linter() tmp3 <- withr::local_tempfile(fileext = ".Rmd") cat( trim_some( '--- title: "Some file" --- ```{r} abc = 123 ``` ```{r child="some-file.Rmd"} ```' ), file = tmp3 ) expect_lint( file = tmp3, checks = list( message = rex::rex("Missing terminal newline."), line_number = 10L, # We can't get 4 here because the line is NA-masked in get_source_expressions(), so no line length info exists. column_number = 1L ), linters = linter ) # Construct an Rmd file without R code (#1415) tmp4 <- withr::local_tempfile(fileext = ".Rmd") cat( trim_some( '--- title: "Some file" --- No code and no terminal newline' ), file = tmp4 ) expect_lint( file = tmp4, checks = list( message = rex::rex("Missing terminal newline."), line_number = 5L, # We can't get 4 here because the line is NA-masked in get_source_expressions(), so no line length info exists. column_number = 1L ), linters = linter ) # Construct a qmd file without terminal newline tmp5 <- withr::local_tempfile(fileext = ".qmd") cat( trim_some( '--- title: "Some file" --- ```{r} abc = 123 ``` ```{r child="some-file.qmd"} ```' ), file = tmp5 ) expect_lint( file = tmp5, checks = list( message = rex::rex("Missing terminal newline."), line_number = 10L, # We can't get 4 here because the line is NA-masked in get_source_expressions(), so no line length info exists. column_number = 1L ), linters = linter ) }) test_that("blank lines in knitr chunks produce lints", { linter <- trailing_blank_lines_linter() tmp6 <- withr::local_tempfile( fileext = ".Rmd", lines = trim_some( '--- title: "Some file" --- ```{r} abc = 123 ``` \n' ) ) expect_lint( file = tmp6, checks = list(message = rex::rex("Trailing blank lines are superfluous."), line_number = 7L, column_number = 1L), linters = linter ) tmp7 <- withr::local_tempfile( fileext = ".qmd", lines = trim_some( '--- title: "Some file" --- ```{r} abc = 123 ``` \n' ) ) expect_lint( file = tmp7, checks = list( list(message = rex::rex("Trailing blank lines are superfluous."), line_number = 7L, column_number = 1L), list(message = rex::rex("Trailing blank lines are superfluous."), line_number = 8L, column_number = 1L), list(message = rex::rex("Trailing blank lines are superfluous."), line_number = 9L, column_number = 1L) ), linters = linter ) }) lintr/tests/testthat/test-semicolon_linter.R0000644000176200001440000001125014577052532021062 0ustar liggesuserstest_that("Lint all semicolons", { linter <- semicolon_linter() trail_msg <- "Trailing semicolons are not needed." comp_msg <- "Compound semicolons are discouraged. Replace them by a newline." # No semicolon expect_lint("", NULL, linter) expect_lint("a <- 1", NULL, linter) expect_lint("function() {a <- 1}", NULL, linter) expect_lint("a <- \"foo;bar\"", NULL, linter) expect_lint("function() {a <- \"foo;bar\"}", NULL, linter) expect_lint("a <- FALSE # ok; cool!", NULL, linter) expect_lint("function() {\na <- FALSE # ok; cool!\n}", NULL, linter) # Trailing semicolons expect_lint( "a <- 1;", list(message = trail_msg, line_number = 1L, column_number = 7L), linter ) expect_lint( "function(){a <- 1;}", list(message = trail_msg, line_number = 1L, column_number = 18L), linter ) expect_lint( "a <- 1; \n", list(message = trail_msg, line_number = 1L, column_number = 7L), linter ) expect_lint( "function(){a <- 1; \n}", list(message = trail_msg, line_number = 1L, column_number = 18L), linter ) # Compound semicolons expect_lint( "a <- 1;b <- 2", list(message = comp_msg, line_number = 1L, column_number = 7L), linter ) expect_lint( "function() {a <- 1;b <- 2}\n", list(message = comp_msg, line_number = 1L, column_number = 19L), linter ) expect_lint( "foo <-\n 1 ; foo <- 1.23", list(message = comp_msg, line_number = 2L, column_number = 6L), linter ) expect_lint( "function(){\nfoo <-\n 1 ; foo <- 1.23\n}", list(message = comp_msg, line_number = 3L, column_number = 6L), linter ) # Multiple, mixed semicolons", { expect_lint( "a <- 1 ; b <- 2;\nc <- 3;", list( list(message = comp_msg, line_number = 1L, column_number = 8L), list(message = trail_msg, line_number = 1L, column_number = 16L), list(message = trail_msg, line_number = 2L, column_number = 7L) ), linter ) expect_lint( "function() { a <- 1 ; b <- 2;\nc <- 3;}", list( list(message = comp_msg, line_number = 1L, column_number = 21L), list(message = trail_msg, line_number = 1L, column_number = 29L), list(message = trail_msg, line_number = 2L, column_number = 7L) ), linter ) }) test_that("Compound semicolons only", { linter <- semicolon_linter(allow_trailing = TRUE) expect_lint("a <- 1;", NULL, linter) expect_lint("function(){a <- 1;}", NULL, linter) expect_lint("a <- 1; \n", NULL, linter) expect_lint("function(){a <- 1; \n}", NULL, linter) }) test_that("Trailing semicolons only", { linter <- semicolon_linter(allow_compound = TRUE) expect_lint("a <- 1;b <- 2", NULL, linter) expect_lint("function() {a <- 1;b <- 2}\n", NULL, linter) expect_lint("f <-\n 1 ;f <- 1.23", NULL, linter) expect_lint("function(){\nf <-\n 1 ;f <- 1.23\n}", NULL, linter) }) test_that("Compound semicolons only", { expect_error( lint(text = "a <- 1;", linters = semicolon_linter(allow_trailing = TRUE, allow_compound = TRUE)), "At least one of `allow_compound` or `allow_trailing` must be FALSE, otherwise no lints can be generated.", fixed = TRUE ) }) test_that("deprecation notices for semicolon_terminator_linter succeed, and the deprecated version works", { expect_warning( { linter <- semicolon_terminator_linter() }, "Linter semicolon_terminator_linter was deprecated", fixed = TRUE ) expect_lint("a <- 1", NULL, linter) expect_lint("a <- 1;", rex::rex("Trailing semicolons are not needed."), linter) expect_lint("a <- 1; b <- 2", rex::rex("Compound semicolons are discouraged."), linter) # old string argument gets translated to new boolean arguments expect_warning( { linter <- semicolon_terminator_linter("compound") }, "Linter semicolon_terminator_linter was deprecated", fixed = TRUE ) expect_lint("a <- 1", NULL, linter) expect_lint("a <- 1;", NULL, linter) expect_lint("a <- 1; b <- 2", rex::rex("Compound semicolons are discouraged."), linter) expect_warning( { linter <- semicolon_terminator_linter("trailing") }, "Linter semicolon_terminator_linter was deprecated", fixed = TRUE ) expect_lint("a <- 1", NULL, linter) expect_lint("a <- 1;", rex::rex("Trailing semicolons are not needed."), linter) expect_lint("a <- 1; b <- 2", NULL, linter) # linters_with_defaults warns about now-absent semicolon_terminator_linter expect_warning( expect_true("semicolon_linter" %in% names(linters_with_defaults(semicolon_terminator_linter = NULL))), # regex because the message uses sQuote() --> fancy quotes rex::rex("Trying to remove", anything, "semicolon_terminator_linter", anything, ", which is not in `defaults`.") ) }) lintr/tests/testthat/dummy_packages/0000755000176200001440000000000014510656222017400 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean_subdir/0000755000176200001440000000000014250050336022025 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean_subdir/lintr_test_config0000644000176200001440000000005214250050336025461 0ustar liggesusersexclusions: "R/default_linter_testcode.R" lintr/tests/testthat/dummy_packages/clean_subdir/r/0000755000176200001440000000000014250050336022266 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean_subdir/r/NAMESPACE0000644000176200001440000000002714250050336023504 0ustar liggesusersimportFrom(utils,head) lintr/tests/testthat/dummy_packages/clean_subdir/r/DESCRIPTION0000644000176200001440000000003614250050336023773 0ustar liggesusersPackage: clean Version: 0.0.1 lintr/tests/testthat/dummy_packages/clean_subdir/r/R/0000755000176200001440000000000014250050336022467 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean_subdir/r/R/imported_methods.R0000644000176200001440000000012114250050336026152 0ustar liggesusers#' head on my_s3_object #' @export head.my_s3_object <- function(x, ...) { 1 } lintr/tests/testthat/dummy_packages/clean_subdir/r/R/default_linter_testcode.R0000644000176200001440000000007714250050336027511 0ustar liggesusersf <- function(x, y = 1) x + y message("hello") y <- 2 + (1:10) lintr/tests/testthat/dummy_packages/assignmentLinter/0000755000176200001440000000000014577202775022743 5ustar liggesuserslintr/tests/testthat/dummy_packages/assignmentLinter/NAMESPACE0000644000176200001440000000003714250050336024140 0ustar liggesusersexportPattern("^[[:alpha:]]+") lintr/tests/testthat/dummy_packages/assignmentLinter/exec/0000755000176200001440000000000014457657444023673 5ustar liggesuserslintr/tests/testthat/dummy_packages/assignmentLinter/exec/script.R0000644000176200001440000000014114457657444025316 0ustar liggesusers# lint errors should be included in test-lint_package.R x = 1:4 res<- lapply(x, function(y) y+1) lintr/tests/testthat/dummy_packages/assignmentLinter/DESCRIPTION0000644000176200001440000000053714457657444024462 0ustar liggesusersPackage: assignmentLinter Type: Package Title: What the package does (short line) Version: 1.0 Date: 2019-12-17 Author: Who wrote it Maintainer: Who to complain to Description: More about what it does (maybe more than one line) License: What license is it under? Suggests: testthat (>= 3.0.0) Config/testthat/edition: 3 lintr/tests/testthat/dummy_packages/assignmentLinter/tests/0000755000176200001440000000000014457657444024111 5ustar liggesuserslintr/tests/testthat/dummy_packages/assignmentLinter/tests/testthat/0000755000176200001440000000000014600212472025723 5ustar liggesuserslintr/tests/testthat/dummy_packages/assignmentLinter/tests/testthat/test-abc.R0000644000176200001440000000006414457657444027576 0ustar liggesuserstest_that("test abc", { expect_equal(2 * 2, 4) }) lintr/tests/testthat/dummy_packages/assignmentLinter/tests/testthat.R0000644000176200001440000000011414457657444026070 0ustar liggesuserslibrary(testthat) library(assignmentLinter) test_check("assignmentLinter") lintr/tests/testthat/dummy_packages/assignmentLinter/R/0000755000176200001440000000000014577202775023144 5ustar liggesuserslintr/tests/testthat/dummy_packages/assignmentLinter/R/abc.R0000644000176200001440000000002514250050336023767 0ustar liggesusersabc = 123 ghi <- 456 lintr/tests/testthat/dummy_packages/assignmentLinter/R/jkl.R0000644000176200001440000000002414250050336024021 0ustar liggesusersjkl = 456 mno = 789 lintr/tests/testthat/dummy_packages/desc_dir_pkg/0000755000176200001440000000000014250050336022010 5ustar liggesuserslintr/tests/testthat/dummy_packages/desc_dir_pkg/DESCRIPTION/0000755000176200001440000000000014250050336023573 5ustar liggesuserslintr/tests/testthat/dummy_packages/desc_dir_pkg/DESCRIPTION/R/0000755000176200001440000000000014250050336023774 5ustar liggesuserslintr/tests/testthat/dummy_packages/desc_dir_pkg/DESCRIPTION/R/foo.R0000644000176200001440000000000714250050336024677 0ustar liggesusersx <- 4 lintr/tests/testthat/dummy_packages/clean/0000755000176200001440000000000014457657444020503 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean/NAMESPACE0000644000176200001440000000044614457657444021726 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("names<-",my_custom_class) S3method(drink_me,data.frame) S3method(drink_me,default) S3method(drink_me,list) S3method(eat_me,liiiiiiiiiiiiiiiiiiiiiiiiiiist) S3method(head,my_s3_object) export(drink_me) export(eat_me) importFrom(utils,head) lintr/tests/testthat/dummy_packages/clean/DESCRIPTION0000644000176200001440000000006114457657444022206 0ustar liggesusersPackage: clean Version: 0.0.1 RoxygenNote: 7.2.2 lintr/tests/testthat/dummy_packages/clean/lintr_test_config0000644000176200001440000000005214250050336024111 0ustar liggesusersexclusions: "R/default_linter_testcode.R" lintr/tests/testthat/dummy_packages/clean/R/0000755000176200001440000000000014457657444020704 5ustar liggesuserslintr/tests/testthat/dummy_packages/clean/R/clean_generics.R0000644000176200001440000000140614457657444023771 0ustar liggesusers# Regression test for #737 via expecting clean to be lint-free #' drink_me #' @description empty #' #' @export drink_me <- function(x, ...) { UseMethod("drink_me") } #' drink_me for most things #' @export drink_me.default <- function(x, ...) { 1 } #' drink_me for lists #' @export drink_me.list <- function(x, ...) { NULL } #' drink_me for data.frames #' @export drink_me.data.frame <- function(x, ...) { NULL } #' head on my_s3_object #' @importFrom utils head #' @export head.my_s3_object <- function(x, ...) { NULL } #' assign names for my_custom_class #' @export `names<-.my_custom_class` <- function(x, value) { NULL } #' Defined S3 generic in R/eat_me.R #' Tests #1808 #' @export eat_me.liiiiiiiiiiiiiiiiiiiiiiiiiiist <- function(x, ...) { NULL } lintr/tests/testthat/dummy_packages/clean/R/default_linter_testcode.R0000644000176200001440000000007714250050336025700 0ustar liggesusersf <- function(x, y = 1) x + y message("hello") y <- 2 + (1:10) lintr/tests/testthat/dummy_packages/clean/R/eat_me.R0000644000176200001440000000014514457657444022261 0ustar liggesusers#' eat_me #' @description empty #' #' @export eat_me <- function(x, ...) { UseMethod("drink_me") } lintr/tests/testthat/dummy_packages/RConfigInvalid/0000755000176200001440000000000014510656222022236 5ustar liggesuserslintr/tests/testthat/dummy_packages/RConfigInvalid/DESCRIPTION0000644000176200001440000000005014510656222023737 0ustar liggesusersPackage: RConfigInvalid Version: 0.0.1 lintr/tests/testthat/dummy_packages/RConfigInvalid/lintr_test_config.R0000644000176200001440000000002714510656222026074 0ustar liggesusers# invalid R syntax 1 + lintr/tests/testthat/dummy_packages/RConfigInvalid/R/0000755000176200001440000000000014510656222022437 5ustar liggesuserslintr/tests/testthat/dummy_packages/RConfigInvalid/R/lint_me.R0000644000176200001440000000000714510656222024206 0ustar liggesusersa <- 1 lintr/tests/testthat/dummy_packages/cp1252/0000755000176200001440000000000014600122250020301 5ustar liggesuserslintr/tests/testthat/dummy_packages/cp1252/NAMESPACE0000644000176200001440000000003714250050336021526 0ustar liggesusersexportPattern("^[[:alpha:]]+") lintr/tests/testthat/dummy_packages/cp1252/DESCRIPTION0000644000176200001440000000061714250050336022021 0ustar liggesusersPackage: cp1252 Type: Package Title: What the Package Does (Title Case) Version: 0.1.0 Author: Who wrote it Maintainer: The package maintainer Description: More about what it does (maybe more than one line) Use four spaces when indenting paragraphs within the Description. License: What license is it under? Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) lintr/tests/testthat/dummy_packages/cp1252/cp1252.Rproj0000644000176200001440000000055014250050336022241 0ustar liggesusersVersion: 1.0 RestoreWorkspace: Default SaveWorkspace: Default AlwaysSaveHistory: Default EnableCodeIndexing: Yes UseSpacesForTab: Yes NumSpacesForTab: 2 Encoding: ISO8859-1 RnwWeave: Sweave LaTeX: pdfLaTeX AutoAppendNewline: Yes StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source lintr/tests/testthat/dummy_packages/cp1252/R/0000755000176200001440000000000014250050336020510 5ustar liggesuserslintr/tests/testthat/dummy_packages/cp1252/R/cp1252.R0000644000176200001440000000011414250050336021543 0ustar liggesusers# This file is encoded in Cp-1252 # Comment containing non-ASCII <- 42 lintr/tests/testthat/dummy_packages/package/0000755000176200001440000000000014600122250020760 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/NAMESPACE0000644000176200001440000000015614250050336022207 0ustar liggesusers# Generated by roxygen2: fake comment so roxygen2 overwrites silently. exportPattern("^[^\\.]") import(lintr) lintr/tests/testthat/dummy_packages/package/exec/0000755000176200001440000000000014457657444021740 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/exec/script.R0000644000176200001440000000024214457657444023365 0ustar liggesusers# Each of the default linters should throw at least one lint for assignment_linter or object_name_linter on this file x = 1:4 res <- lapply(x, function(y) y + 1) lintr/tests/testthat/dummy_packages/package/package.Rproj0000644000176200001440000000047014250050336023400 0ustar liggesusersVersion: 1.0 RestoreWorkspace: No SaveWorkspace: No AlwaysSaveHistory: Default EnableCodeIndexing: Yes Encoding: UTF-8 AutoAppendNewline: Yes StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source PackageRoxygenize: rd,collate,namespace lintr/tests/testthat/dummy_packages/package/DESCRIPTION0000644000176200001440000000067514250050336022504 0ustar liggesusersPackage: package Title: What the Package Does (One Line, Title Case) Version: 0.0.0.9000 Authors@R: person(given = "First", family = "Last", role = c("aut", "cre"), email = "first.last@example.com", comment = c(ORCID = "YOUR-ORCID-ID")) Description: What the package does (one paragraph). License: What license it uses Encoding: UTF-8 Imports: lintr LazyData: true Roxygen: list(markdown = TRUE) lintr/tests/testthat/dummy_packages/package/lintr_test_config0000644000176200001440000000004114250050336024420 0ustar liggesuserslinters: linters_with_defaults() lintr/tests/testthat/dummy_packages/package/vignettes/0000755000176200001440000000000014510656222023003 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/vignettes/test.Rnw0000644000176200001440000000142514250050336024447 0ustar liggesusers\documentclass{article} \usepackage{mathpazo} \begin{document} \title{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. <>= a = 1 @ \subtitle{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. <<>>= b <- function(x) { d = 1 } @ <>= a=[] a[0]=1 @ lintr/tests/testthat/dummy_packages/package/vignettes/test.Rmd0000644000176200001440000000205314250050336024421 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 ``` Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} b <- function(x) { d = 1 } ``` ```{r engine="python"} a=[] a[0]=1 ``` ``` Plain code blocks can be written after three or more backticks - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2) ``` Calls to a non-R knitr-engine using {engine_name} syntax. ```{python} # Python that looks like R a = list() b = {2} print(a) ``` ```{python} # Python that's definitely not R a = [] a.append(2) print(a) ``` lintr/tests/testthat/dummy_packages/package/vignettes/test.Rhtml0000644000176200001440000000154014250050336024765 0ustar liggesusers Test

    Test

    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

    Test

    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. lintr/tests/testthat/dummy_packages/package/vignettes/test.Qmd0000644000176200001440000000262114510656222024426 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 ``` Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} b <- function(x) { d = 1 } ``` ```{r engine="python"} a=[] a[0]=1 ``` ``` Plain code blocks can be written after three or more backticks - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2) ``` ```r # This is a non-evaluated block of R code for formatting in markdown. # It should not be linted abc = 123 ``` ```cpp // Some C++ code for formatting by markdown ``` Calls to a non-R knitr-engine using {engine_name} syntax. ```{python} # Python that looks like R a = list() b = {2} print(a) ``` ```{python} # Python that's definitely not R a = [] a.append(2) print(a) ``` The following are only supported by Quarto and shouldn't lint either. ```{.r} 1+1 ``` ```{{r}} 1+1 ``` ```{.python} # Python that's definitely not R a = [] a.append(2) print(a) ``` lintr/tests/testthat/dummy_packages/package/vignettes/test.Rtxt0000644000176200001440000000140114250050336024634 0ustar liggesusers= Test = Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. //begin.rcode a = 1 //end.rcode Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. //begin.rcode trailing b <- function(x) { d = 1 } //end.rcode //begin.rcode engine="python" a=[] a[0]=1 //end.rcode lintr/tests/testthat/dummy_packages/package/vignettes/test.Rrst0000644000176200001440000000134014250050336024627 0ustar liggesusersTest ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. .. {r rst-example} a = 1 .. .. Test ---- Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. .. {r} b <- function(x) { d = 1 } .. .. .. {r engine = "python"} a=[] a[0]=1 .. .. lintr/tests/testthat/dummy_packages/package/vignettes/test.Rtex0000644000176200001440000000151714250050336024625 0ustar liggesusers\documentclass{article} \usepackage{graphicx} \title{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. %% begin.rcode % a = 1 %% end.rcode \subtitle{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. %% begin.rcode test_chunk % b <- function(x) { % d = 1 % } % %% end.rcode %% begin.rcode engine="python" % a=[] % % a[0]=1 %% end.rcode lintr/tests/testthat/dummy_packages/package/data-raw/0000755000176200001440000000000014250050336022466 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/data-raw/default_linter_testcode.R0000644000176200001440000000144614250050336027511 0ustar liggesusers# Each of the default linters should throw at least one lint on this file # assignment # function_left_parentheses # closed_curly # commas # paren_brace f = function (x,y = 1){} # commented_code # some <- commented("out code") # cyclocomp # equals_na # infix_spaces # line_length # object_length # object_name # object_usage # open_curly # T_and_F_symbol someComplicatedFunctionWithALongCamelCaseName <- function(x) { y <- 1 if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F} } # no_tab # pipe_continuation # seq_linter # spaces_inside x <- 1:10 x[ 2] 1:length(x) %>% lapply(function(x) x*2) %>% head() # single_quotes message('single_quotes') # spaces_left_parentheses # trailing_whitespace # semicolon x <- 42; y <- 2 +(1:10) # trailing_blank_lines lintr/tests/testthat/dummy_packages/package/R/0000755000176200001440000000000014250050336021167 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/R/default_linter_testcode.R0000644000176200001440000000144614250050336026212 0ustar liggesusers# Each of the default linters should throw at least one lint on this file # assignment # function_left_parentheses # closed_curly # commas # paren_brace f = function (x,y = 1){} # commented_code # some <- commented("out code") # cyclocomp # equals_na # infix_spaces # line_length # object_length # object_name # object_usage # open_curly # T_and_F_symbol someComplicatedFunctionWithALongCamelCaseName <- function(x) { y <- 1 if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F} } # no_tab # pipe_continuation # seq_linter # spaces_inside x <- 1:10 x[ 2] 1:length(x) %>% lapply(function(x) x*2) %>% head() # single_quotes message('single_quotes') # spaces_left_parentheses # trailing_whitespace # semicolon x <- 42; y <- 2 +(1:10) # trailing_blank_lines lintr/tests/testthat/dummy_packages/package/inst/0000755000176200001440000000000014250050336021743 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/inst/data-raw/0000755000176200001440000000000014250050336023443 5ustar liggesuserslintr/tests/testthat/dummy_packages/package/inst/data-raw/default_linter_testcode.R0000644000176200001440000000144614250050336030466 0ustar liggesusers# Each of the default linters should throw at least one lint on this file # assignment # function_left_parentheses # closed_curly # commas # paren_brace f = function (x,y = 1){} # commented_code # some <- commented("out code") # cyclocomp # equals_na # infix_spaces # line_length # object_length # object_name # object_usage # open_curly # T_and_F_symbol someComplicatedFunctionWithALongCamelCaseName <- function(x) { y <- 1 if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F} } # no_tab # pipe_continuation # seq_linter # spaces_inside x <- 1:10 x[ 2] 1:length(x) %>% lapply(function(x) x*2) %>% head() # single_quotes message('single_quotes') # spaces_left_parentheses # trailing_whitespace # semicolon x <- 42; y <- 2 +(1:10) # trailing_blank_lines lintr/tests/testthat/dummy_packages/github_lintr_file/0000755000176200001440000000000014577202774023105 5ustar liggesuserslintr/tests/testthat/dummy_packages/github_lintr_file/NAMESPACE0000644000176200001440000000003714457657444024331 0ustar liggesusersexportPattern("^[[:alpha:]]+") lintr/tests/testthat/dummy_packages/github_lintr_file/DESCRIPTION0000644000176200001440000000044114457657444024617 0ustar liggesusersPackage: assignmentLinter Type: Package Title: What the package does (short line) Version: 1.0 Date: 2019-12-17 Author: Who wrote it Maintainer: Who to complain to Description: More about what it does (maybe more than one line) License: What license is it under? lintr/tests/testthat/dummy_packages/github_lintr_file/tests/0000755000176200001440000000000014457657444024254 5ustar liggesuserslintr/tests/testthat/dummy_packages/github_lintr_file/tests/testthat/0000755000176200001440000000000014600212472026066 5ustar liggesuserslintr/tests/testthat/dummy_packages/github_lintr_file/tests/testthat/test-abc.R0000644000176200001440000000000614457657444027735 0ustar liggesusers'abc' lintr/tests/testthat/dummy_packages/github_lintr_file/tests/testthat.R0000644000176200001440000000007514457657444026241 0ustar liggesuserslibrary(testthat) test_check('github_lintr_file') # nolint lintr/tests/testthat/dummy_packages/github_lintr_file/R/0000755000176200001440000000000014457657444023313 5ustar liggesuserslintr/tests/testthat/dummy_packages/github_lintr_file/R/abc.R0000644000176200001440000000000614457657444024157 0ustar liggesusers'abc' lintr/tests/testthat/dummy_packages/no_export_dep/0000755000176200001440000000000014312017463022243 5ustar liggesuserslintr/tests/testthat/dummy_packages/no_export_dep/NAMESPACE0000644000176200001440000000002114312017463023453 0ustar liggesusersimport(datasets) lintr/tests/testthat/dummy_packages/no_export_dep/DESCRIPTION0000644000176200001440000000006514312017463023752 0ustar liggesusersPackage: downstream Version: 0.0.1 Imports: datasets lintr/tests/testthat/dummy_packages/no_export_dep/R/0000755000176200001440000000000014312017463022444 5ustar liggesuserslintr/tests/testthat/dummy_packages/no_export_dep/R/foo.R0000644000176200001440000000010414312017463023345 0ustar liggesusersfoo <- function() { a_really_really_long_local_object_name <- 1 } lintr/tests/testthat/dummy_packages/RConfig/0000755000176200001440000000000014510656222020727 5ustar liggesuserslintr/tests/testthat/dummy_packages/RConfig/DESCRIPTION0000644000176200001440000000004114510656222022430 0ustar liggesusersPackage: RConfig Version: 0.0.1 lintr/tests/testthat/dummy_packages/RConfig/tests/0000755000176200001440000000000014510656222022071 5ustar liggesuserslintr/tests/testthat/dummy_packages/RConfig/tests/testthat.R0000644000176200001440000000044614510656222024060 0ustar liggesusers# This file is in 'exclusions' & nothing lints under R config. # Under DCF config, '# SKIP_LINT' is the exclusion & this line won't lint 1+1 # SKIP_LINT # This is included as a linter in the DCF, thus this should lint expect_equal(foo(x), NULL) # trailing blank line next will lint under DCF lintr/tests/testthat/dummy_packages/RConfig/lintr_test_config.R0000644000176200001440000000022714510656222024567 0ustar liggesuserslinters <- linters_with_defaults( any_duplicated_linter(), assignment_linter = NULL ) exclude <- "# NOLINT" exclusions <- list("tests/testthat.R") lintr/tests/testthat/dummy_packages/RConfig/lintr_test_config_conflict0000644000176200001440000000022714510656222026250 0ustar liggesuserslinters: linters_with_defaults( any_duplicated_linter(), assignment_linter = NULL ) exclude: "# NOLINT" exclusions: list("tests/testthat.R") lintr/tests/testthat/dummy_packages/RConfig/lintr_test_config_extraneous.R0000644000176200001440000000055514510656222027050 0ustar liggesusers# here are some extraneous variables that are not part of the config directly non_default_linter <- any_duplicated_linter() attr(non_default_linter, "name") <- "any_duplicated_linter" excluded_files <- "tests/testthat.R" linters <- linters_with_defaults( non_default_linter, assignment_linter = NULL ) exclude <- "# NOLINT" exclusions <- list(excluded_files) lintr/tests/testthat/dummy_packages/RConfig/lintr_test_config_conflict.R0000644000176200001440000000022214510656222026443 0ustar liggesuserslinters <- linters_with_defaults( expect_null_linter(), assignment_linter = NULL ) exclude <- "# SKIP_LINT" exclusions <- list("R/lint_me.R") lintr/tests/testthat/dummy_packages/RConfig/R/0000755000176200001440000000000014510656222021130 5ustar liggesuserslintr/tests/testthat/dummy_packages/RConfig/R/lint_me.R0000644000176200001440000000045414510656222022705 0ustar liggesusers# config excludes assignment_linter() so this doesn't lint a = 1 # default config includes infix_spaces_linter() so this lints b=a + 2 # config extends defaults with any_duplicated_linter() so this lints any(duplicated(b)) # custom exclude setting is also picked up so this doesn't lint 1+1 # NOLINT lintr/tests/testthat/dummy_packages/missing_dep/0000755000176200001440000000000014250525657021711 5ustar liggesuserslintr/tests/testthat/dummy_packages/missing_dep/NAMESPACE0000644000176200001440000000023514250525657023130 0ustar liggesusersimport(myFancyUpstream) importFrom(aGemOfAPackage, a_veritably_dilettantish_function, sPoNgEbOb_cAsE.FuNcTiOn) export(downstream) lintr/tests/testthat/dummy_packages/missing_dep/DESCRIPTION0000644000176200001440000000007414250525657023420 0ustar liggesusersPackage: downstream Version: 0.0.1 Imports: myFancyUpstream lintr/tests/testthat/dummy_packages/missing_dep/R/0000755000176200001440000000000014250525657022112 5ustar liggesuserslintr/tests/testthat/dummy_packages/missing_dep/R/foo.R0000644000176200001440000000057614250525657023030 0ustar liggesusersdownstream <- function(x) { a_really_really_long_local_object_name <- 1 aSilLLyObjEctNaME <- 2 myFancyUpstream::upstreams_really_long_imported_object_name() myFancyUpstream::uPStreams_SilLY.objecT.Name() } # importFrom() functions in unknown namespace a_veritably_dilettantish_function.my_class <- function(x, ...) x sPoNgEbOb_cAsE.FuNcTiOn.my_class <- function(x, ...) x lintr/tests/testthat/test-get_source_expressions.R0000644000176200001440000003256714577052571022337 0ustar liggesuserswith_content_to_parse <- function(content, code) { f <- withr::local_tempfile() local({ con <- file(f, open = "w", encoding = "UTF-8") on.exit(close(con)) writeLines(content, con) }) source_expressions <- get_source_expressions(f) content_env <- new.env() content_env$pc <- lapply(source_expressions[["expressions"]], `[[`, "parsed_content") content_env$error <- source_expressions$error eval(substitute(code), envir = content_env) } test_that("tab positions have been corrected", { with_content_to_parse( "1\n\t", expect_length(pc, 2L) ) with_content_to_parse( "TRUE", expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "TRUE", c("col1", "col2")], use.names = FALSE), c(1L, 4L)) ) with_content_to_parse( "\tTRUE", expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "TRUE", c("col1", "col2")], use.names = FALSE), c(2L, 5L)) ) with_content_to_parse( "\t\tTRUE", expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "TRUE", c("col1", "col2")], use.names = FALSE), c(3L, 6L)) ) with_content_to_parse("x\t<-\tTRUE", { expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "x", c("col1", "col2")], use.names = FALSE), c(1L, 1L)) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "<-", c("col1", "col2")], use.names = FALSE), c(3L, 4L)) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "TRUE", c("col1", "col2")], use.names = FALSE), c(6L, 9L)) }) with_content_to_parse("\tfunction\t(x)\t{\tprint(pc[\t,1])\t;\t}", { expect_identical( unlist(pc[[1L]][pc[[1L]][["text"]] == "function", c("col1", "col2")], use.names = FALSE), c(2L, 9L) ) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "x", c("col1", "col2")], use.names = FALSE), c(12L, 12L)) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "print", c("col1", "col2")], use.names = FALSE), c(17L, 21L)) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == ";", c("col1", "col2")], use.names = FALSE), c(32L, 32L)) expect_identical(unlist(pc[[1L]][pc[[1L]][["text"]] == "}", c("col1", "col2")], use.names = FALSE), c(34L, 34L)) }) with_content_to_parse("# test tab\n\ns <- 'I have \\t a dog'\nrep(\ts, \t3)", { expect_identical( unlist(pc[[2L]][pc[[2L]][["text"]] == "'I have \\t a dog'", c("line1", "col1", "col2")], use.names = FALSE), c(3L, 6L, 22L) ) expect_identical( unlist(pc[[3L]][pc[[3L]][["text"]] == "3", c("line1", "col1", "col2")], use.names = FALSE), c(4L, 10L, 10L) ) }) with_content_to_parse("function(){\nTRUE\n\t}", { expect_identical( unlist(pc[[1L]][1L, c("line1", "col1", "line2", "col2")], use.names = FALSE), c(1L, 1L, 3L, 2L), info = "expression that spans several lines" ) }) }) test_that("Terminal newlines are detected correctly", { content <- "lm(y ~ x)" # NB: need to specify terminal newline explicitly with cat, not writeLines() tmp <- withr::local_tempfile(lines = content) tmp2 <- withr::local_tempfile() cat(content, file = tmp2) expect_true(get_source_expressions(tmp)$expressions[[2L]]$terminal_newline) expect_false(get_source_expressions(tmp2)$expressions[[2L]]$terminal_newline) }) test_that("Multi-byte characters correct columns", { skip_if_not_utf8_locale() with_content_to_parse("`\U2020` <- 1", { # fix_column_numbers corrects the start of <- expect_identical(pc[[1L]]$col1[4L], pc[[1L]]$col1[2L] + 4L) }) }) test_that("Multi-byte character truncated by parser is ignored", { skip_if_not_utf8_locale() # \U2013 is the Unicode character 'en dash', which is # almost identical to a minus sign in monospaced fonts. content <- "y <- x \U2013 42" # message is like ':1:8: unexpected invalid token\n1: ...' with_content_to_parse(content, { base_msg <- conditionMessage(tryCatch(str2lang(content), error = identity)) # Just ensure that the captured message is a substring of the parser error, #2527 expect_true(grepl(error$message, base_msg, fixed = TRUE, useBytes = TRUE)) expect_identical(error$column_number, 8L) }) }) test_that("Can read non UTF-8 file", { file <- test_path("dummy_projects", "project", "cp1252.R") lintr:::read_settings(file) expect_null(get_source_expressions(file)$error) }) test_that("Warns if encoding is misspecified", { file <- test_path("dummy_projects", "project", "cp1252.R") lintr:::read_settings(NULL) the_lint <- lint(filename = file, parse_settings = FALSE)[[1L]] expect_s3_class(the_lint, "lint") lint_msg <- "Invalid multibyte character in parser. Is the encoding correct?" if (!isTRUE(l10n_info()[["UTF-8"]])) { # Prior to R 4.2.0, the Windows parser throws a different error message because the source code is converted to # native encoding. # This results in line 4 becoming <- 42 before the parser sees it. lint_msg <- "unexpected '<'" } expect_identical(the_lint$linter, "error") expect_identical(the_lint$message, lint_msg) expect_identical(the_lint$line_number, 4L) file <- test_path("dummy_projects", "project", "cp1252_parseable.R") lintr:::read_settings(NULL) the_lint <- lint(filename = file, parse_settings = FALSE)[[1L]] expect_s3_class(the_lint, "lint") expect_identical(the_lint$linter, "error") expect_identical(the_lint$message, "Invalid multibyte string. Is the encoding correct?") expect_identical(the_lint$line_number, 1L) }) test_that("Can extract line number from parser errors", { skip_if_not_r_version("4.0.0") # malformed raw string literal at line 2 with_content_to_parse( trim_some(' "ok" R"---a---" '), { expect_identical(error$message, "Malformed raw string literal.") expect_identical(error$line_number, 2L) } ) # invalid \u{xxxx} sequence (line 3) with_content_to_parse( trim_some(' ok ok "\\u{9999" '), { expect_identical(error$message, "Invalid \\u{xxxx} sequence.") expect_identical(error$line_number, 3L) } ) # invalid \u{xxxx} sequence (line 4) with_content_to_parse( trim_some(' ok ok "\\u{9999 '), { # parser erroneously reports line 4 expect_identical(error$message, "Invalid \\u{xxxx} sequence.") expect_identical(error$line_number, 3L) } ) # repeated formal argument 'a' on line 1 with_content_to_parse("function(a, a) {}", { expect_identical(error$message, "Repeated formal argument 'a'.") expect_identical(error$line_number, 1L) }) }) test_that("1- or 2-width octal expressions give the right STR_CONST values", { with_content_to_parse("'\\1'", expect_identical(pc[[1L]][1L, "text"], "'\\1'")) with_content_to_parse('"\\1"', expect_identical(pc[[1L]][1L, "text"], '"\\1"')) # multiple literals with_content_to_parse("'\\1'\n'\\2'", { expect_identical(pc[[1L]][1L, "text"], "'\\1'") expect_identical(pc[[2L]][1L, "text"], "'\\2'") }) # multiple escapes with_content_to_parse("'\\1\\2'", expect_identical(pc[[1L]][1L, "text"], "'\\1\\2'")) # multi-line strings with_content_to_parse("'\n\\1\n'", expect_identical(pc[[1L]][1L, "text"], "'\n\\1\n'")) with_content_to_parse("a <- '\\1\n\\2'", expect_identical(pc[[1L]][5L, "text"], "'\\1\n\\2'")) # mixed-length strings with_content_to_parse("foo('\\1',\n '\n\\2\n')", { expect_identical(pc[[1L]][5L, "text"], "'\\1'") expect_identical(pc[[1L]][8L, "text"], "'\n\\2\n'") }) }) test_that("returned data structure is complete", { lines <- c("line_1", "line_2", "line_3") temp_file <- withr::local_tempfile(lines = lines) lines_with_attr <- setNames(lines, seq_along(lines)) attr(lines_with_attr, "terminal_newline") <- TRUE exprs <- get_source_expressions(temp_file) expect_named(exprs, c("expressions", "error", "lines")) expect_length(exprs$expressions, length(lines) + 1L) for (i in seq_along(lines)) { expr <- exprs$expressions[[i]] expect_named(expr, c("filename", "line", "column", "lines", "parsed_content", "xml_parsed_content", "content")) expect_identical(expr$filename, temp_file) expect_identical(expr$line, i) expect_identical(expr$column, 1L) expect_identical(expr$lines, setNames(lines[i], i)) expect_identical(nrow(expr$parsed_content), 2L) expect_true(xml2::xml_find_lgl(expr$xml_parsed_content, "count(//SYMBOL) > 0")) expect_identical(expr$content, lines[i]) } full_expr <- exprs$expressions[[length(lines) + 1L]] expect_named(full_expr, c( "filename", "file_lines", "content", "full_parsed_content", "full_xml_parsed_content", "terminal_newline" )) expect_identical(full_expr$filename, temp_file) expect_identical(full_expr$file_lines, lines_with_attr) expect_identical(full_expr$content, lines_with_attr) expect_identical(nrow(full_expr$full_parsed_content), 2L * length(lines)) expect_identical( xml2::xml_find_num(full_expr$full_xml_parsed_content, "count(//SYMBOL)"), as.numeric(length(lines)) ) expect_true(full_expr$terminal_newline) expect_null(exprs$error) expect_identical(exprs$lines, lines_with_attr) }) test_that("#1262: xml_parsed_content gets returned as missing even if there's no parsed_content", { tempfile <- withr::local_tempfile(lines = '"\\R"') source_expressions <- get_source_expressions(tempfile) expect_null(source_expressions$expressions[[1L]]$full_parsed_content) expect_identical(source_expressions$expressions[[1L]]$full_xml_parsed_content, xml2::xml_missing()) }) test_that("#743, #879, #1406: get_source_expressions works on R files matching a knitr pattern", { # from #743 tempfile <- withr::local_tempfile( lines = trim_some(' create_template <- function(x) { sprintf(" ```{r code} foo <- function(x) x+%d foo(5) ```", x) } ') ) source_expressions <- get_source_expressions(tempfile) expect_null(source_expressions$error) # from #879 tempfile <- withr::local_tempfile( lines = trim_some(' # `r print("7")` function() 2<=3 ') ) source_expressions <- get_source_expressions(tempfile) expect_null(source_expressions$error) # from #1406 tempfile <- withr::local_tempfile() writeLines(c("x <- '", "```{r}", "'"), con = tempfile) source_expressions <- get_source_expressions(tempfile) expect_null(source_expressions$error) }) test_that("Syntax errors in Rmd or qmd don't choke lintr", { tmp <- withr::local_tempfile(lines = c( "```{r}", "if (TRUE) {", " 1", # missing `}` here "if (TRUE) {", "}", "```" )) expect_silent(get_source_expressions(tmp)) }) test_that("Indented Rmd chunks don't cause spurious whitespace lints", { tmp <- withr::local_tempfile(lines = c( "* An enumeration item with code:", "", " ```{r}", ' "properly indented"', " ```", "", "# New section", "", "```{r unindented_chunk}", ' "improperly indented"', "```", "", "# Third section", "", " ```{r staggered}", ' "leftmost code"', ' "further right"', ' "aligned with code gate"', " ```" )) parsed_lines <- get_source_expressions(tmp)$lines expect_identical(parsed_lines[4L], '"properly indented"', ignore_attr = "names") expect_identical(parsed_lines[10L], ' "improperly indented"', ignore_attr = "names") expect_identical(parsed_lines[16L], '"leftmost code"', ignore_attr = "names") expect_identical(parsed_lines[17L], ' "further right"', ignore_attr = "names") expect_identical(parsed_lines[18L], ' "aligned with code gate"', ignore_attr = "names") }) test_that("Reference chunks in Sweave/Rmd are ignored", { example_rnw <- system.file("Sweave", "example-1.Rnw", package = "utils") # ensure such a chunk continues to exist upstream expect_true(any(grepl("^\\s*<<[^>]*>>\\s*$", readLines(example_rnw)))) expect_silent(lint(example_rnw)) }) # NB: this is just a cursory test for linters not to # fail on files where the XML content is xml_missing; # the main linter test files provide more thorough # evidence that things are working as intended. bad_source <- withr::local_tempfile(lines = c("a <- 1L", "b <- 2L")) expressions <- get_source_expressions(bad_source)$expressions # "zap" the xml_parsed_content to be xml_missing -- this gets # around the issue of creating a file that fails to parse now, # but later fails in a different way -> xml not missing. for (ii in seq_along(expressions)) { if ("xml_parsed_content" %in% names(expressions[[ii]])) { expressions[[ii]]$xml_parsed_content <- xml2::xml_missing() } else { expressions[[ii]]$full_xml_parsed_content <- xml2::xml_missing() } } param_df <- expand.grid( linter = available_linters(tags = NULL)$linter, expression_idx = seq_along(expressions), stringsAsFactors = FALSE ) param_df$.test_name <- with(param_df, sprintf("%s on expression %d", linter, expression_idx)) patrick::with_parameters_test_that( "linters pass with xml_missing() content", { linter <- eval(call(linter)) expression <- expressions[[expression_idx]] expect_no_warning({ lints <- linter(expression) }) expect_length(lints, 0L) }, .test_name = param_df$.test_name, linter = param_df$linter, expression_idx = param_df$expression_idx ) test_that("invalid function definition parser failure lints", { expect_lint( "function(a = 1, a = 1) NULL", rex::rex("Repeated formal argument 'a'."), linters = list() ) }) test_that("Disallowed embedded null gives parser failure lint", { expect_lint( "'\\0'", rex::rex("Nul character not allowed."), linters = list() ) }) lintr/tests/testthat/test-nested_ifelse_linter.R0000644000176200001440000000333214577052532021705 0ustar liggesuserstest_that("nested_ifelse_linter skips allowed usages", { linter <- nested_ifelse_linter() expect_lint("if (TRUE) 1 else 2", NULL, linter) expect_lint("if (TRUE) 1 else if (TRUE) 2 else 3", NULL, linter) expect_lint("ifelse(runif(10) > .2, 4, 6)", NULL, linter) # don't block suggested alternatives expect_lint("fcase(l1, v1, l2, v2)", NULL, linter) expect_lint("case_when(l1 ~ v1, l2 ~ v2)", NULL, linter) }) test_that("nested_ifelse_linter blocks simple disallowed usages", { expect_lint( "ifelse(l1, v1, ifelse(l2, v2, v3))", rex::rex("Don't use nested ifelse() calls"), nested_ifelse_linter() ) expect_lint( "ifelse(l1, ifelse(l2, v1, v2), v3)", rex::rex("Don't use nested ifelse() calls"), nested_ifelse_linter() ) }) test_that("nested_ifelse_linter also catches dplyr::if_else", { expect_lint( "if_else(l1, v1, if_else(l2, v2, v3))", rex::rex("Don't use nested if_else() calls"), nested_ifelse_linter() ) expect_lint( "dplyr::if_else(l1, dplyr::if_else(l2, v1, v2), v3)", rex::rex("Don't use nested if_else() calls"), nested_ifelse_linter() ) }) test_that("nested_ifelse_linter also catches data.table::fifelse", { expect_lint( "fifelse(l1, v1, fifelse(l2, v2, v3))", rex::rex("Don't use nested fifelse() calls"), nested_ifelse_linter() ) expect_lint( "data.table::fifelse(l1, v1, data.table::fifelse(l2, v2, v3))", rex::rex("Don't use nested fifelse() calls"), nested_ifelse_linter() ) # not sure why anyone would do this, but the readability still argument holds expect_lint( "data.table::fifelse(l1, dplyr::if_else(l2, v1, v2), v3)", rex::rex("Don't use nested if_else() calls"), nested_ifelse_linter() ) }) lintr/tests/testthat/test-paren_body_linter.R0000644000176200001440000000537714577052532021231 0ustar liggesuserstestthat::test_that("paren_body_linter returns correct lints", { linter <- paren_body_linter() lint_msg <- "There should be a space between a right parenthesis and a body expression." # No space after the closing parenthesis prompts a lint expect_lint("function()test", lint_msg, linter) expect_lint("print('hello')\nx <- function(x)NULL\nprint('hello')", lint_msg, linter) expect_lint("if (TRUE)test", lint_msg, linter) expect_lint("while (TRUE)test", lint_msg, linter) expect_lint("for (i in seq_along(1))test", lint_msg, linter) # A space after the closing parenthesis does not prompt a lint expect_lint("function() test", NULL, linter) # Symbols after the closing parenthesis of a function call do not prompt a lint expect_lint("head(mtcars)$cyl", NULL, linter) # paren_body_linter returns the correct line number expect_lint( "print('hello')\nx <- function(x)NULL\nprint('hello')", list(line_number = 2L), linter ) expect_lint( "function()test", list( line_number = 1L, column_number = 11L, type = "style", line = "function()test", ranges = list(c(11L, 14L)) ), linter ) # paren_body_linter does not lint when the function body is defined on a new line expect_lint("function()\n test", NULL, linter) # paren_body_linter does not lint comments expect_lint("#function()test", NULL, linter) # multiple lints on the same line expect_lint("function()if(TRUE)while(TRUE)test", list(lint_msg, lint_msg, lint_msg), linter) # No space after the closing parenthesis of an anonymous function prompts a lint skip_if_not_r_version("4.1.0") expect_lint("\\()test", lint_msg, linter) }) test_that("multi-line versions are caught", { expect_lint( trim_some(" function(var )x "), rex::rex("There should be a space between a right parenthesis and a body expression."), paren_body_linter() ) expect_lint( trim_some(" if (cond )x "), rex::rex("There should be a space between a right parenthesis and a body expression."), paren_body_linter() ) expect_lint( trim_some(" while (cond )x "), rex::rex("There should be a space between a right parenthesis and a body expression."), paren_body_linter() ) skip_if_not_r_version("4.1.0") expect_lint( trim_some(" \\(var )x "), rex::rex("There should be a space between a right parenthesis and a body expression."), paren_body_linter() ) }) test_that("function shorthand is handled", { skip_if_not_r_version("4.1.0") linter <- paren_body_linter() lint_msg <- rex::rex("There should be a space between a right parenthesis and a body expression.") expect_lint("\\()test", lint_msg, linter) }) lintr/tests/testthat/test-linter_tags.R0000644000176200001440000002201414577052532020030 0ustar liggesuserstest_that("input validation for available_linters works as expected", { expect_error(available_linters(1L), "`packages` must be a character vector.") expect_error(available_linters(tags = 1L), "`tags` must be a character vector.") expect_error(available_linters(exclude_tags = 1L), "`exclude_tags` must be a character vector.") }) test_that("validate_linter_db works as expected", { df_empty <- data.frame() expect_warning( lintr:::validate_linter_db(df_empty, "mypkg"), "`linters.csv` must contain the columns 'linter' and 'tags'.", fixed = TRUE ) expect_false(suppressWarnings(lintr:::validate_linter_db(df_empty, "mypkg"))) df <- data.frame( linter = "absolute_path_linter", tags = "robustness", stringsAsFactors = FALSE ) expect_true(lintr:::validate_linter_db(df, "mypkg")) }) test_that("available_linters returns a data frame", { avail <- available_linters() avail2 <- available_linters(c("lintr", "not-a-package")) empty <- available_linters("not-a-package") expect_s3_class(avail, "data.frame") expect_identical(avail, avail2) expect_identical(nrow(empty), 0L) expect_named(avail, c("linter", "package", "tags")) expect_type(avail[["linter"]], "character") expect_type(avail[["package"]], "character") expect_type(avail[["tags"]], "list") expect_type(avail[["tags"]][[1L]], "character") }) test_that("available_tags returns a character vector", { tags <- available_tags() tags2 <- available_tags(c("lintr", "not-a-package")) empty <- available_tags("not-a-package") expect_type(tags, "character") expect_identical(tags, tags2) expect_length(empty, 0L) expect_true(all(available_linters()$tags[[1L]] %in% tags)) expect_true(all(unlist(available_linters()$tags) %in% tags)) expect_identical(anyDuplicated(tags), 0L) expect_identical(lintr:::platform_independent_order(tags), seq_along(tags)) }) test_that("default_linters and default tag match up", { avail <- available_linters() tagged_default <- avail[["linter"]][vapply(avail[["tags"]], function(tags) "default" %in% tags, logical(1L))] expect_identical(tagged_default, names(default_linters)) }) test_that("warnings occur only for deprecated linters", { expect_silent(linters_with_tags(tags = NULL)) num_deprecated_linters <- nrow(available_linters(tags = "deprecated", exclude_tags = NULL)) deprecation_warns_seen <- 0L outer_env <- environment() expect_silent({ withCallingHandlers( linters_with_tags(tags = "deprecated", exclude_tags = NULL), warning = function(w) { if (grepl("was deprecated", conditionMessage(w), fixed = TRUE)) { outer_env$deprecation_warns_seen <- outer_env$deprecation_warns_seen + 1L invokeRestart("muffleWarning") } else { w } } ) }) expect_identical(deprecation_warns_seen, num_deprecated_linters) }) test_that("available_linters matches the set of linters available from lintr", { lintr_db <- available_linters(exclude_tags = NULL) linters_in_namespace <- setdiff(ls(asNamespace("lintr"), pattern = "_linter$"), "is_linter") # ensure that the contents of inst/lintr/linters.csv covers all _linter objects in our namespace expect_identical(sort(lintr_db$linter), sort(linters_in_namespace)) # ensure that all _linter objects in our namespace are also exported exported_linters <- setdiff(grep("_linter$", getNamespaceExports("lintr"), value = TRUE), "is_linter") expect_identical(sort(linters_in_namespace), sort(exported_linters)) }) test_that("rownames for available_linters data frame doesn't have missing entries", { lintr_db <- available_linters() expect_identical( tail(rownames(lintr_db), 1L), as.character(nrow(lintr_db)) ) lintr_db2 <- available_linters(exclude_tags = NULL) expect_identical( tail(rownames(lintr_db2), 1L), as.character(nrow(lintr_db2)) ) }) # See the roxygen helpers in R/linter_tags.R for the code used to generate the docs. # This test helps ensure the documentation is up to date with the available_linters() database test_that("lintr help files are up to date", { help_db <- tools::Rd_db("lintr") # e.g. in dev under pkgload::load_all() if (length(help_db) == 0L) { help_db <- tools::Rd_db(dir = test_path("..", "..")) skip_if_not(length(help_db) > 0L, message = "Package help not installed or corrupted") } lintr_db <- available_linters(exclude_tags = NULL) lintr_db$package <- NULL lintr_db$tags <- lapply(lintr_db$tags, function(x) if ("deprecated" %in% x) "deprecated" else sort(x)) lintr_db <- lintr_db[order(lintr_db$linter), ] expect_true("linters.Rd" %in% names(help_db), info = "?linters exists") # NB: objects in help_env are class Rd, see ?as.character.Rd (part of 'tools') rd_as_string <- function(rd) paste(as.character(rd), collapse = "") # Get a character string with the contents of ?linters linter_help_text <- rd_as_string(help_db$linters.Rd) # Test three things about ?linters # (1) the complete list of linters and tags matches that in available_linters() # (2) the tabulation of tags & corresponding count of linters matches that in available_linters() # (3) the 'configurable' tag applies if and only if the linter has parameters # Extract linters & associated tags from ?linters text # Rd markup for items looks like \item{\code{\link{...}} (tags: ...)} # [1] [2] # [1]: linter name; [2]: associated tags help_linters <- rex::re_matches( linter_help_text, rex::rex( "\\item{\\code{\\link{", capture(some_of(letter, number, "_", "."), name = "linter"), "}} (tags: ", capture(some_of(letter, "_", ",", " "), name = "tags"), ")}" ), global = TRUE )[[1L]] help_linters$tags <- lapply(strsplit(help_linters$tags, ", ", fixed = TRUE), sort) help_linters <- help_linters[order(help_linters$linter), ] # (1) the complete list of linters and tags matches that in available_linters() expect_identical( help_linters, # deprecated linters are excluded from this page lintr_db[!vapply(lintr_db$tags, identical, "deprecated", FUN.VALUE = NA), ], info = "Database implied by ?linters is the same as is available_linters()", ignore_attr = "row.names" ) # Counts of tags from available_linters() db_tag_table <- as.data.frame( table(tag = unlist(lintr_db$tags)), responseName = "n_linters", stringsAsFactors = FALSE ) # In ?linters, entries in the enumeration of tags look like # \item{\link[=${TAG}_linters]{${TAG}} (${N_LINTERS_WITH_TAG} linters)} help_tag_table <- rex::re_matches( linter_help_text, rex::rex( "\\item{\\link[=", capture(some_of(letter, "_"), name = "tag_page"), "_linters]{", capture(some_of(letter, "_"), name = "tag"), "} (", capture(numbers, name = "n_linters"), " linters)}" ), global = TRUE )[[1L]] # consistency/sanity check expect_identical(help_tag_table$tag_page, help_tag_table$tag) help_tag_table$tag_page <- NULL help_tag_table$n_linters <- as.integer(help_tag_table$n_linters) # (2) the tabulation of tags & corresponding count of linters matches that in available_linters() expect_identical( help_tag_table[order(help_tag_table$tag), ], db_tag_table[order(db_tag_table$tag), ], ignore_attr = "row.names", info = "Tags and corresponding counts in ?linters is the same as in available_linters()" ) # Now test an analogue to (1) from above for each tag's help page for (tag in db_tag_table$tag) { expect_true(paste0(tag, "_linters.Rd") %in% names(help_db), info = paste0("?", tag, "_linters exists")) tag_help <- help_db[[paste0(tag, "_linters.Rd")]] tag_help_text <- rd_as_string(tag_help) # linters listed in ?${TAG}_linter help_tag_linters <- rex::re_matches( tag_help_text, rex::rex("\\item{\\code{\\link{", capture(some_of(letter, number, "_", "."), name = "linter"), "}}}"), global = TRUE )[[1L]] # those entries in available_linters() with the current tag db_linter_has_tag <- vapply(lintr_db$tags, function(linter_tag) any(tag %in% linter_tag), logical(1L)) expected <- lintr_db$linter[db_linter_has_tag] expect_identical( sort(help_tag_linters$linter), sort(expected), info = paste0("?", tag, "_linters lists all linters with that tag in available_linters()") ) } # (3) the 'configurable' tag applies if and only if the linter has parameters has_args <- 0L < lengths(Map( function(linter, tags) if ("deprecated" %in% tags) NULL else formals(match.fun(linter)), lintr_db$linter, lintr_db$tags )) has_configurable_tag <- vapply(lintr_db$tags, function(tags) "configurable" %in% tags, logical(1L)) expect_identical(has_configurable_tag, unname(has_args)) }) test_that("available_linters gives precedence to included tags", { expect_true("style" %in% unlist(available_linters(tags = "style", exclude_tags = "style")$tags)) # also for the default case expect_identical( available_linters(tags = "deprecated"), available_linters(tags = "deprecated", exclude_tags = NULL) ) }) lintr/tests/testthat/test-equals_na_linter.R0000644000176200001440000000535414577052532021052 0ustar liggesuserstest_that("equals_na_linter skips allowed usages", { linter <- equals_na_linter() expect_lint("blah", NULL, linter) expect_lint(" blah", NULL, linter) expect_lint(" blah", NULL, linter) expect_lint("x=NA", NULL, linter) expect_lint("x = NaN", NULL, linter) expect_lint("x = NA_real_", NULL, linter) expect_lint("is.na(x)", NULL, linter) expect_lint("is.nan(x)", NULL, linter) expect_lint("x[!is.na(x)]", NULL, linter) # equals_na_linter should ignore strings and comments expect_lint("is.na(x) # do not flag x == NA if inside a comment", NULL, linter) expect_lint("lint_msg <- 'do not flag x == NA if inside a string'", NULL, linter) # nested NAs are okay expect_lint("x==f(1, ignore = NA)", NULL, linter) # this should be covered by any_is_na_linter() expect_lint("NA %in% x", NULL, linter) }) skip_if_not_installed("tibble") patrick::with_parameters_test_that( "equals_na_linter blocks disallowed usages for all combinations of operators and types of NAs", expect_lint( paste("x", operation, type_na), rex::rex("Use is.na for comparisons to NA (not == or != or %in%)"), equals_na_linter() ), .cases = tibble::tribble( ~.test_name, ~operation, ~type_na, "equality, logical NA", "==", "NA", "equality, integer NA", "==", "NA_integer_", "equality, real NA", "==", "NA_real_", "equality, complex NA", "==", "NA_complex_", "equality, character NA", "==", "NA_character_", "containment, logical NA", "%in%", "NA", "containment, integer NA", "%in%", "NA_integer_", "containment, real NA", "%in%", "NA_real_", "containment, complex NA", "%in%", "NA_complex_", "containment, character NA", "%in%", "NA_character_", "inequality, logical NA", "!=", "NA", "inequality, integer NA", "!=", "NA_integer_", "inequality, real NA", "!=", "NA_real_", "inequality, complex NA", "!=", "NA_complex_", "inequality, character NA", "!=", "NA_character_" ) ) test_that("equals_na_linter blocks disallowed usages in edge cases", { linter <- equals_na_linter() lint_msg <- rex::rex("Use is.na for comparisons to NA (not == or != or %in%)") # missing spaces around operators expect_lint("x==NA", list(message = lint_msg, line_number = 1L, column_number = 1L), linter) expect_lint("x!=NA", list(message = lint_msg, line_number = 1L, column_number = 1L), linter) # order doesn't matter expect_lint("NA == x", list(message = lint_msg, line_number = 1L, column_number = 1L), linter) # correct line number for multiline code expect_lint("x ==\nNA", list(line_number = 1L, column_number = 1L, ranges = list(c(1L, 4L))), linter) }) lintr/tests/testthat/test-inner_combine_linter.R0000644000176200001440000001043114577052532021701 0ustar liggesuserstest_that("inner_combine_linter lints a false positive-ish usage", { # By default as.POSIXct.character picks up the format to apply from # the first element and, since it succeeds, applies that to the remaining # timestamps. Whereas when run individually, it won't succeed until # the correct format is matched for each input. Nevertheless, it is # still preferable to vectorize the call, while being sure to use a # consistent format for the inputs. In this case, the correct equivalent # call is as.POSIXct(c("2021-01-01 00:00:00", "2021-01-01 01:00:00")). expect_lint( "c(as.POSIXct('2021-01-01'), as.POSIXct('2021-01-01 01:00:00'))", rex::rex("Combine inputs to vectorized functions first"), inner_combine_linter() ) }) local({ vector_funs <- c( "as.Date", "as.POSIXct", "as.POSIXlt", "sin", "cos", "tan", "sinpi", "cospi", "tanpi", "asin", "acos", "atan", "log", "logb", "log2", "log10", "log1p", "exp", "expm1", "sqrt", "abs", "ymd", "ydm", "mdy", "myd", "dmy", "dym", "yq", "ym", "my", "ymd_hms", "ymd_hm", "ymd_h", "dmy_hms", "dmy_hm", "dmy_h", "mdy_hms", "mdy_hm", "mdy_h", "ydm_hms", "ydm_hm", "ydm_h", NULL ) patrick::with_parameters_test_that( "inner_combine_linter blocks simple vectorized calls:", expect_lint( sprintf("c(%1$s(x), %1$s(y))", vector_fun), rex::rex("Combine inputs to vectorized functions first"), inner_combine_linter() ), .test_name = vector_funs, vector_fun = vector_funs ) }) patrick::with_parameters_test_that( "inner_combine_linter blocks as.Date with identical passed arguments:", expect_lint( sprintf("c(as.Date(x, %1$s), as.Date(y, %1$s))", arg), rex::rex("Combine inputs to vectorized functions first"), inner_combine_linter() ), .test_name = c("format", "origin", "tz", "tryFormats", "non-literal", "quoted arg name"), arg = c("format = '%F'", "origin = '1900-01-01'", "tz = 'Asia/Jakarta'", "tryFormats = '%F'", "tz = tz", "'tz' = tz") ) patrick::with_parameters_test_that( "inner_combine_linter blocks as.POSIXct with identical passed arguments:", expect_lint( sprintf("c(as.POSIXct(x, %1$s), as.POSIXct(y, %1$s))", arg), rex::rex("Combine inputs to vectorized functions first"), inner_combine_linter() ), .test_name = c("format", "origin", "tz", "non-literal"), arg = c("format = '%F'", "origin = '1900-01-01'", "tz = 'UTC'", "tz = tz") ) test_that("inner_combine_linter is order-agnostic for matching arguments", { expect_lint( "c(as.Date(x, format = f, tz = t), as.Date(y, tz = t, format = f))", rex::rex("Combine inputs to vectorized functions first"), inner_combine_linter() ) }) test_that("c() with ...length()=1 is OK", { expect_lint("c(exp())", NULL, inner_combine_linter()) }) skip_if_not_installed("tibble") patrick::with_parameters_test_that( "inner_combine_linter skips allowed usages:", expect_lint(expr, NULL, inner_combine_linter()), .cases = tibble::tribble( ~.test_name, ~expr, "simple sin()", "x <- sin(1:10)", "mixed sin()+cos()", "y <- c(sin(1:10), cos(2:20))", "present/absent vector function", "c(log(x), 0.5)", "absent/present vector function", "c(0.5, log(x))", "mismatched arg (Date)", "c(as.Date(x, format = '%y'), as.Date(y, format = '%m'))", "present/absent arg (Date)", "c(as.Date(x, format = '%y'), as.Date(y))", "absent/present arg (Date)", "c(as.Date(x), as.Date(y, format = '%y'))", "matched value, not arg (Date)", "c(as.Date(x, format = '%y'), as.Date(y, tz = '%y'))", "mismatched arg (POSIXct)", "c(as.POSIXct(x, format = '%y'), as.POSIXct(y, format = '%m'))", "present/absent arg (POSIXct)", "c(as.POSIXct(x, format = '%y'), as.POSIXct(y))", "mismatched arg (log)", "c(log(x, base = 4), log(y, base = 5))", "present/absent arg (log)", "c(log(x, base = 4), log(y))" # TODO(michaelchirico): fix the code so these edge cases are covered # "unknown Date method argument", "c(as.Date(x, zoo = zzz), as.Date(y, zoo = zzz))", # "known+unknown Date argument", "c(as.Date(x, format = '%y', zoo = zzz), as.Date(y, format = '%y', zoo = zzz))", # "unknown POSIXct method argument", "c(as.POSIXct(x, zoo = zzz), as.POSIXct(y, zoo = zzz))", ) ) lintr/tests/testthat/test-checkstyle_output.R0000644000176200001440000000160614510656222021270 0ustar liggesuserstest_that("return lint report as checkstyle xml", { lints <- list( Lint( filename = "test_file", line_number = 1L, column_number = 2L, type = "error", line = "a line", message = "foo" ), Lint( filename = "test_file", line_number = 2L, column_number = 1L, type = "style", line = "another line", message = "bar" ), Lint( filename = "test_file2", line_number = 1L, column_number = 1L, type = "warning", line = "yet another line", message = "baz" ) ) class(lints) <- "lints" tmp <- withr::local_tempfile() checkstyle_output(lints, tmp) # The second line is the checkstyle version, so we ignore it during the # check, so we don't have to update the version every release. expect_identical(readLines(tmp)[-2L], readLines(test_path("checkstyle.xml"))[-2L]) }) lintr/tests/testthat/test-namespace_linter.R0000644000176200001440000000452114577052532021031 0ustar liggesuserstest_that("namespace_linter skips allowed usages", { linter <- namespace_linter() expect_lint("stats::sd", NULL, linter) expect_lint("stats::sd(c(1,2,3))", NULL, linter) expect_lint('"stats"::sd(c(1,2,3))', NULL, linter) expect_lint('stats::"sd"(c(1,2,3))', NULL, linter) expect_lint("stats::`sd`(c(1,2,3))", NULL, linter) expect_lint("datasets::mtcars", NULL, linter) expect_lint("stats:::print.formula", NULL, linter) expect_lint('"stats":::print.formula', NULL, linter) }) test_that("namespace_linter respects check_exports and check_nonexports arguments", { expect_lint("stats::ssd(c(1,2,3))", NULL, namespace_linter(check_exports = FALSE)) expect_lint("stats:::ssd(c(1,2,3))", NULL, namespace_linter(check_nonexports = FALSE)) expect_lint("stats:::ssd(c(1,2,3))", NULL, namespace_linter(check_exports = FALSE, check_nonexports = FALSE)) }) test_that("namespace_linter can work with backticked symbols", { skip_if_not_installed("rlang") linter <- namespace_linter() expect_lint("rlang::`%||%`", NULL, linter) expect_lint("rlang::`%||%`()", NULL, linter) expect_lint("rlang::'%||%'", NULL, linter) expect_lint("rlang::'%||%'()", NULL, linter) expect_lint('rlang::"%||%"', NULL, linter) expect_lint('rlang::"%||%"()', NULL, linter) expect_lint("rlang::`%>%`", "'%>%' is not exported from {rlang}.", linter) expect_lint("rlang::'%>%'()", "'%>%' is not exported from {rlang}.", linter) expect_lint('rlang::"%>%"()', "'%>%' is not exported from {rlang}.", linter) }) test_that("namespace_linter blocks disallowed usages", { linter <- namespace_linter() expect_lint( "statts::sd(c(1,2,3))", list(message = rex::rex("Package 'statts' is not installed.")), linter ) expect_lint( "stats::ssd(c(1,2,3))", list(message = rex::rex("'ssd' is not exported from {stats}")), linter ) expect_lint( "stats:::sd(c(1,2,3))", list(message = rex::rex("'sd' is exported from {stats}. Use stats::sd instead.")), linter ) expect_lint( "statts:::sd(c(1,2,3))", list(message = rex::rex("Package 'statts' is not installed.")), linter ) expect_lint( "stats:::sdd(c(1,2,3))", list(message = rex::rex("'sdd' does not exist in {stats}")), linter ) expect_lint( "stats::sd(c(1,2,3))\nstats::sdd(c(1,2,3))", list(line = "stats::sdd(c(1,2,3))"), linter ) }) lintr/tests/testthat/test-xml_nodes_to_lints.R0000644000176200001440000001215214457657444021434 0ustar liggesuserstest_that("it creates basic lints", { code <- "before %+% after" tmpfile <- withr::local_tempfile(lines = code) expr <- get_source_expressions(tmpfile)$expressions[[2L]] xml <- expr$full_xml_parsed_content node <- xml2::xml_find_first(xml, "//SPECIAL") l <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg", type = "warning" ) expect_s3_class(l, "lint") expect_identical(l$filename, tmpfile) expect_identical(l$line_number, 1L) expect_identical(l$column_number, as.integer(xml2::xml_attr(node, "col1"))) expect_identical(l$type, "warning") expect_identical(l$message, "lint_msg") expect_identical(l$line, code) expect_identical(l$ranges, list(as.integer(c(xml2::xml_attr(node, "col1"), xml2::xml_attr(node, "col2"))))) # location XPaths work l_before_col <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg", column_number_xpath = "number(./preceding-sibling::*[1]/@col2 + 1)", range_start_xpath = "number(./preceding-sibling::*[1]/@col1)", range_end_xpath = "number(./following-sibling::*[1]/@col2)" ) expect_identical(l_before_col$column_number, nchar("before") + 1L) expect_identical(l_before_col$ranges, list(c(1L, nchar(code)))) # Vectorization works ll <- xml_nodes_to_lints( xml = xml2::xml_find_all(xml, "//expr"), source_expression = expr, lint_message = "lint_msg" ) expect_s3_class(ll, "lints") expect_length(ll, 3L) expect_s3_class(ll[[1L]], "lint") # Also for plain lists of xml_nodes, usually obtained by c(nodeset_a, nodeset_b) ll_unclassed <- xml_nodes_to_lints( xml = unclass(xml2::xml_find_all(xml, "//expr")), source_expression = expr, lint_message = "lint_msg" ) expect_s3_class(ll_unclassed, "lints") expect_length(ll_unclassed, 3L) expect_s3_class(ll_unclassed[[1L]], "lint") # lint_message as a vector ll_msgvec <- xml_nodes_to_lints( xml = xml2::xml_find_all(xml, "//SYMBOL"), source_expression = expr, lint_message = letters[1L:2L] ) expect_identical(ll_msgvec[[1L]]$message, "a") expect_identical(ll_msgvec[[2L]]$message, "b") }) test_that("it handles multi-line lints correctly", { code <- c("before %+%", " after") tmpfile <- withr::local_tempfile(lines = code) expr <- get_source_expressions(tmpfile)$expressions[[2L]] xml <- expr$full_xml_parsed_content node <- xml2::xml_find_first(xml, "/exprlist/expr") l <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg" ) expect_s3_class(l, "lint") expect_identical(l$filename, tmpfile) expect_identical(l$line_number, 1L) expect_identical(l$column_number, as.integer(xml2::xml_attr(node, "col1"))) expect_identical(l$type, "style") expect_identical(l$message, "lint_msg") expect_identical(l$line, code[[1L]]) expect_identical(l$ranges, list(as.integer(c(xml2::xml_attr(node, "col1"), nchar(code[1L]))))) }) test_that("it doesn't produce invalid lints", { # Part of #1427 code <- "before %+% after" tmpfile <- withr::local_tempfile(lines = code) expr <- get_source_expressions(tmpfile)$expressions[[2L]] xml <- expr$full_xml_parsed_content node <- xml2::xml_find_first(xml, "//SPECIAL") # We can produce invalid location xpaths by requiring any non-existent node xp_column_number <- "number(./preceding-sibling::*[1]/@col2 + 1)" xp_range_start <- "number(./preceding-sibling::*[1]/@col1)" xp_range_end <- "number(./following-sibling::*[1]/@col2)" xp_invalid <- "number(./DOES-NOT-EXIST/@col1)" expect_warning( { lints1 <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg", column_number_xpath = xp_column_number, range_start_xpath = xp_invalid, range_end_xpath = xp_range_end ) }, rex::rex("Could not find range start for lint. Defaulting to start of line.") ) expect_identical(lints1[["column_number"]], nchar("before") + 1L) expect_identical(lints1[["ranges"]], list(c(1L, nchar(code)))) expect_warning( { lints2 <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg", column_number_xpath = xp_column_number, range_start_xpath = xp_range_start, range_end_xpath = xp_invalid ) }, rex::rex("Could not find range end for lint. Defaulting to width 1.") ) expect_identical(lints2[["column_number"]], nchar("before") + 1L) expect_identical(lints2[["ranges"]], list(c(1L, 1L))) expect_warning( { lints3 <- xml_nodes_to_lints( xml = node, source_expression = expr, lint_message = "lint_msg", column_number_xpath = xp_invalid, range_start_xpath = xp_range_start, range_end_xpath = xp_range_end ) }, rex::rex("Could not find location for lint. Defaulting to start of range.") ) expect_identical(lints3[["column_number"]], 1L) expect_identical(lints3[["ranges"]], list(c(1L, nchar(code)))) }) test_that("erroneous input errors", { expect_error(xml_nodes_to_lints(1L), "Expected an xml_nodeset", fixed = TRUE) }) lintr/tests/testthat/test-keyword_quote_linter.R0000644000176200001440000001407114506330025021763 0ustar liggesuserstest_that("keyword_quote_linter skips allowed usages", { linter <- keyword_quote_linter() # main use case: c() expect_lint("x <- c(1, 2, 4, 5)", NULL, linter) expect_lint("x <- c(a = 1, 2)", NULL, linter) expect_lint("x <- c(a = 1, b = 2)", NULL, linter) expect_lint("y <- c(`a b` = 1, `c d` = 2)", NULL, linter) expect_lint('y <- c("a b" = 1, "c d" = 2)', NULL, linter) expect_lint("z <- c('a b' = 1, c = 2)", NULL, linter) # don't catch strings as arguments expect_lint('c(A = "a")', NULL, linter) # don't catch unnamed arguments expect_lint('c(1, 2, "a")', NULL, linter) # don't get thrown off by missing arguments expect_lint("alist(`a b` =)", NULL, linter) # other use cases: switch() and list() expect_lint("list(a = 1, b = list(c = 2))", NULL, linter) expect_lint("list(`a b` = 1, c = 2:6)", NULL, linter) expect_lint("switch(x, a = 1, b = 2)", NULL, linter) expect_lint( "switch(x, `a b` = 1, c = 2:6)", NULL, linter ) }) test_that("keyword_quote_linter blocks simple disallowed usages", { linter <- keyword_quote_linter() lint_msg <- "Only quote named arguments to functions" expect_lint('c("a" = 1, b = 2)', lint_msg, linter) expect_lint( "c('a' = 1, 'b' = 2)", list(lint_msg, lint_msg), linter ) expect_lint( "c(`a` = 1, b = list(`c` = 2))", list(lint_msg, lint_msg), linter ) # missing argument is caught expect_lint("alist(`a` = )", lint_msg, linter) expect_lint( "switch(x, `a` = c('b' = list(\"c\" = 1)))", list(lint_msg, lint_msg, lint_msg), linter ) }) test_that("keyword_quote_linter skips quoting on reserved words", { linter <- keyword_quote_linter() expect_lint("c(`next` = 1, `while` = 2)", NULL, linter) expect_lint("switch(x, `for` = 3, `TRUE` = 4)", NULL, linter) expect_lint("list('NA' = 5, 'Inf' = 6)", NULL, linter) }) test_that("keyword_quote_linter works on more common functions", { linter <- keyword_quote_linter() lint_msg <- "Only quote named arguments to functions" expect_lint("data.frame('a' = 1)", lint_msg, linter) expect_lint("data.table('a' = 1)", lint_msg, linter) expect_lint("data.table::data.table('a' = 1)", lint_msg, linter) expect_lint("rbind('a' = 1)", lint_msg, linter) expect_lint("cbind('a' = 1)", lint_msg, linter) }) test_that("keyword_quote_linter finds blocked usages in any function call", { expect_lint( "foo('a' = 1)", rex::rex("Only quote named arguments to functions"), keyword_quote_linter() ) }) test_that("keyword_quote_linter blocks quoted assignment targets", { linter <- keyword_quote_linter() backtick_msg <- rex::rex("Use backticks to create non-syntactic names, not quotes.") assign_msg <- "Only quote targets of assignment if necessary" expect_lint('"foo bar" <- 1', backtick_msg, linter) expect_lint("'foo bar' = 1", backtick_msg, linter) # valid choice: use backticks expect_lint("`foo bar` = 1", NULL, linter) expect_lint('"foo" <- 1', assign_msg, linter) expect_lint("'foo' = 1", assign_msg, linter) expect_lint("`foo` = 1", assign_msg, linter) # don't include data.table assignments expect_lint('DT[, "a" := 1]', NULL, linter) expect_lint("DT[, 'a' := 1]", NULL, linter) expect_lint("DT[, `a` := 1]", NULL, linter) # include common use cases: [<-/$ methods and infixes expect_lint('"$.my_class" <- function(x, key) { }', backtick_msg, linter) expect_lint("'Setter[<-.my_class' = function(x, idx, value) { }", backtick_msg, linter) expect_lint('"%nin%" <- function(x, table) !x %in% table', backtick_msg, linter) # right assignment expect_lint('1 -> "foo"', assign_msg, linter) expect_lint("1 -> foo", NULL, linter) expect_lint('1 -> "a b"', backtick_msg, linter) }) test_that("keyword_quote_linter blocks quoted $, @ extractions", { linter <- keyword_quote_linter() backtick_msg <- rex::rex("Use backticks to create non-syntactic names, not quotes.") dollar_msg <- rex::rex("Only quote targets of extraction with $ if necessary") at_msg <- rex::rex("Only quote targets of extraction with @ if necessary") expect_lint('x$"foo bar" <- 1', backtick_msg, linter) expect_lint("x$'foo bar' = 1", backtick_msg, linter) expect_lint('x@"foo bar" <- 1', backtick_msg, linter) expect_lint("x@'foo bar' = 1", backtick_msg, linter) # valid choice: non-syntactic name with backticks expect_lint("x@`foo bar` <- 1", NULL, linter) expect_lint("x@`foo bar` = 1", NULL, linter) expect_lint('x$"foo" <- 1', dollar_msg, linter) expect_lint("x$'foo' = 1", dollar_msg, linter) expect_lint('x@"foo" <- 1', at_msg, linter) expect_lint("x@'foo' = 1", at_msg, linter) expect_lint("x@`foo` <- 1", at_msg, linter) expect_lint("x@`foo` = 1", at_msg, linter) }) test_that("multiple lints are generated correctly", { linter <- keyword_quote_linter() expect_lint( trim_some('{ foo("a" = 1) "b" <- 2 x$"c" y@"d" }'), list( "Only quote named arguments", "Only quote targets of assignment", "Only quote targets of extraction with \\$", "Only quote targets of extraction with @" ), linter ) # multiple flavors of assignment lints expect_lint( trim_some('{ "a" <- 1 "a b" <- 1 `a` <- 1 `a b` <- 1 }'), list( "Only quote targets of assignment if necessary", "Use backticks to create non-syntactic names, not quotes", "Only quote targets of assignment if necessary" ), linter ) # multiple flavors of extraction lints expect_lint( trim_some('{ x$"a" x$"a b" <- 1 x$`a` <- 1 x$`a b` <- 1 y@"a" y@"a b" <- 1 y@`a` <- 1 y@`a b` <- 1 }'), list( rex::rex("Only quote targets of extraction with $ if necessary"), rex::rex("Use backticks to create non-syntactic names, not quotes."), rex::rex("Only quote targets of extraction with $ if necessary"), rex::rex("Only quote targets of extraction with @ if necessary"), rex::rex("Use backticks to create non-syntactic names, not quotes."), rex::rex("Only quote targets of extraction with @ if necessary") ), linter ) }) lintr/tests/testthat/testthat-problems.rds0000644000176200001440000006632614577203021020615 0ustar liggesusers |ŕ?ޚKlK,_j-kd ccy4jɃG3bfd;$doȲ܁BA6$lBfs_u7V.K`;^U:_uZ˲BV$BahMToYa1JS(UY!'u^j nǎ_כIENzl`vW_6ULv*ɴtd1u.}b_I79JNeюn?BŚLŘ@)CI('Յ|+qc\ySjh-+z=ԡYѝ.XIgEP,/JLFZY pRjWZxve۝9m.596wTC(wgcͶ ہ-|Ÿ̶^']S&H>sg+fiAR:8fjx|J 镉VXo/#Hf#Y*&M~nVۺmZqRv]SV yz_bC_uݣ>VN.jenM@jfUiJ,_1i/txˤ +H2f2zoɞu.1 kEY.x;UU>K]Ƌ3ni:m'֞֨,6%\֍^!UnʹfjIM{w{L8ȃC j!#Js0G+{>rqk Բ>~SvZNC촻rdTGoz z(:+=/+T 6%~&LskgoX~"NTPe I, J)}b:3|2[J 3Ta1]R8mOw=~Tm[ն4+[Õ<46ExNo&rf+4 _M +buma@J)3xc3-v_uSko!l{ph&q!ziE窋h^{tfv\p!n;GXXzv!M9e¶9a)*1xق{\Py&i#jaT٩IvMIBرc[#{{]Esڳ}=Zklb|PURYg׌sqP&|0`L[a7VZJpq[5BcD~xngG:"G*dLy;|ЪzuN>1?H;|}s-{v<6DERךSn!PZU=O@0aޭS|n zI%|tuLwYwY^憟7YSRb;[U,ד-V.{}65:gm;J.l"Vtq ޚMYwYK*.lnպR/))+&{z):El3+y\=v\*JWixOnG_l5mpRENnG|0YLz7/ TaUk}p]gYiտ=+D ~~XŇ~ >PAط_go,f+"@/ĸ*lGɗJ, -~%O}R:u>gRڡg_8lJ|o]?}6R&@^|3c+K|9υbˁc>k]VyMC/_ψs ,_(4@_~!#e`yؿ<@__|3#Քer@ikatpT3_k.JX^(my__'M0 _Ȉ퓣t=˛~oؿfFl>{*(s$@_8'fJ/dy-^/q9ϭngy};+@Ae,_ (_/_YBkXPb5mu>5獔:@i7QzF_z>ot75@_ \>oO,?glyw<(s ߛt2/bE_qY彯V^x?Ob~U?|ҧX^ (})'2gǧ)}>/? f@HleKF_20~ | 䀹 (}kʏ#:R6X.G)T/q*Y8y7ZxM=끗?,F?CogCA_hO}0?ŪIW,O~}+oXPb7K*Q=W ~5`ϔu~ o]wU>SjUN)y O#U5b?EXU jBد~)㪲,UPz2iWMgz@n}?]i`>(-sVk}lJq' ~>Kxhvb?ZFle}6}VsLc~}h[u>4V.u3bPYeyP>@_ RFl˫6|5??@_o~-# |=϶}]?l}oR6 J\BS//q-^JY~&o_ٌ+]A,( ~36-,(/jNnO!JױP'M&ؿzF0/e~|zJ/}_~9j14_qnd~}n"ros;4bE̫gKt^/^/ S*u? /Ĺ񱒇|Xn,\@)=Z@_"soWC>M~~3?N bB?J>M}3˽A>G ,}>@_L,_ _e~) A,(Q k7_(j-*^} |·J,@_Mۀ_P/?˭ߔ~:@/HF_6.T$Ju̯*Ugy }{W1C1 = @'O0BSSi(6@}.P~s_POE,W/DZHu ]}\~̠4fY̯c~]Y-?uhkWB5bV GGh!r~ڇPg2b^^lFo^O\h9J?q c /З[o}ᗃ6Mla^8Ǫ?Bt}h={hF__F?BP+|)mb*@)uBh|_ (=_J[([4GqÀ7/x~r8?Q::@Wi7r^S]/d<t-,S*hAkXP_rSRg2 lFʲ|.Կy}Q嘤%o}Mb?iM_?POMɪQ:(ݡї_(&+|/t WJntF_w ſ/to ߮OwUEeCxOS |Z%^sS%J*gEMl!>Cm,?cOb>*}w'^BB?iJZFz4?߇E,_(7Jw (>_P܀#6Y:(|}QƏWS$'J"@_*(}5bb b~Uq~2Ysa@i'_k(}u>ߡF_K||}J?`@b_eQOP$@_?_kuo/(?bW>Oq< gBҟX> Plί/l>WۀRj˼yTpu\04G3|].bx5|.kax4E/ dF?i>K7ky7Jn@qʏO7Veu-|'1Cz2ؿzƩV) SiV) ?FFx/|1Ką|&ꟴ/a@}A_!OyIt1\e0 >|U>P|by[})=7?Sub^/^hUܟS}=,Je~/zWgO(c,SC/QƗ7P RbFAoTPz-V{;XSؿϗ ?Oɫ} Y_!?ay ԟ'5{e^Jgy}b72{xPCq}G4_ (Qp}(V>%{+ (1@_o|_"+oP# k71>ߦS/^WX;b{E_)y5%+Gg,5πesJ`y/odkm@OgJOix)_ ([p(bWYO/À }玿;Yi2ObHDF_]\|Pj`yا fb|F(։Lgy i }~J^ 9BPoSZї_3x1|ba|Fۀ<>#-a|FRZ%ePگU+ P꿊[9a@]o|yr8>P:@R*jV볿(];W短Έ/ICKP/nVkyy%\/|,Wȍ,A}ؿ (:@3h->rQ<1@߭b6_/K~J^ ({g}8hyU RDQ/ZFZ7J eqFo& / JYoXV-(s} .2 qP~PyB/s?OG>:@O#'I},?/?_4Oɫ?X_!O#@?4_ˈ67J/|##ϯo|8#oPzDKb@ioj1;?mUP/;n/o|O%_P\}gS,G x5~lU?#cy PbwE_?rPO{S@?z>D%_0fy / aaEyE'gU^ŀ~щS\MR)jUtJ꾤z) ( ЗgƇ ?YG|]:@QUtF_/fa.>,Wh~x߶*_Thc⿥b/~Oɫ}W2(+=,EiF_?ZF(gy}FFo|6kx3/@MR?Yo'%0;@_/>cj]WjJZ忡$(JK28q:X^()5bx5~dUOc}}\?%BGgy Կ-}zkq|)Y(+"8>e|>ΈJ5Jݨ[MR2U9>_L]1ÀK}C%\!(u?^Gs}z>7PS|W/?*ѷ<({K/^WJ_BGgy ԟ}~kq,oKFF8K}^/{?87JbIҪ)sa@|CئJ7%r6O||_*^?/ [ey-OGGi?.J|'@_J_)e~Rh%_(OOY8yϏFxɟY>PgbG4E5;@_/5C7>gV:@}I/eD/,&F_xU/,-@_?_W[?%'f/bX-k1C/2W.ZOc*Vb|k:'1cT7J`|׷ 3ַ, /%R'Bo>C_R>sA @e|"eBI(dy#oE_ F?kJ bF?Ԗ56/>ggJ6y6ix)&?Ps! R?O(dD@?SJi%Ob7Jv=`8~3b9@2 /_K`sgJ~#k(]|ׂ5'cԯҔ|"(ݪї?OR區R}~#q*s2ת?K}q4 ϙv6\]+;YՄ]=;۷ؽe=۷l޹S6'c_ìٷo=/]ת.|\1/6qm7۷cn(nnщOp+ZΝù ЦЦЧG7`TGT:.%\yV1{_Qʲܐ~Nd l l ^8AWlLPLg>̻ VbJU:w ʧO9:2XG'\&aV̫EtqG(<⭯k*Q;QE 2VN_gm1(g&u(OFEZ0~7U-\:N+IfJ퓞m7+źmz٥vV ?#Zp:~&o%˯_WK;T.C}:ISuGkL M]d@nuVث|&*4p8۞j#N@(pZm[նr1;7"7x^m`qyvo;dI4)jn -VlsdҸULg=޸̩qO^өSjXœ#tgy+Os>LH\hw88vcWȦɾfcD (8vB>.m爓E׮\mٔ,g.CZ-Bb_>[woV ݑ+*/u$#CGMKԤmIl%IEǎzՠۇ!tӞ(~)ϮV!2_˟JVtүmY6] 5?}Œz2+S~kcexF6g8D]ѓNJ:⡾XJ͈hkN[lTwv6LBCt~uKfcŜvUjoћuN}P,tSBۀ4=s'']WvRuTNg$Sr'1@;z%եE^2%$na|zz}dNʧ;#!m_.:=սFhT^(*Dn:Dlz2toFՆ*NUNg7w ,˫rYi9g;AUfd]7<qZJU)ϻ]#^ܓrvRNWSajtXG:.$ӱa+9d^8=cgHUmmmGoP WeVՀ uY_dڲQwܲT`+(c+: 8i8N>IB5i6r9۠p.d:7 \>Og|ӱ;7~`m'{H2 dTfrd&}7ߑ;Խڽ6S yn*{)Q٠ TЌ]8FCm.Gij}o JvT5\`:{$]y+[axӣ=J*nPtw m<Yj72ڗby,(/iBl@5aCr^_>lRo/մ_q8y>T^j-khQ?ؤjF_?:F(P,T r>hS:FFWmxɟ],(;ٌs(mf<@K_g n |P62dֱ*٪v||;}wi=1ǪPRJ/_8 t9eˁΈY+5v-o6og60p RS?(ݢigal@~98P/deU<"J R"Ke|Pw VVy@F?/Q~+:srXu;./Ĺ뇒|/*E8w}SgHa@˾Rx' k7~#7Z[o0J|֯7SzF_Ir~[)#˧}Oe*= o~&lQz}My̟gU?C},ȈRF_:F\?O,oiΈX~}&4GM+OS,'1Ng(V/? |='X(<_%(bKe\eJ_eyPJPYP5b71QSz̀2?|#@_?| ?+?J;,o?/_VJ*FPq'~ʲ~OK 0~ vƋ|O~J3~-WOU|O,odj~Pc??Qjx>_B8s'dy _ jF_Bd ayS}.-hKZ}_:~3$g*!3D1{O240*D}!tF_7 Ip~f|:[/gA\h6/gS,Zb~_ F?TPP]B}YBL7Ji Mom,~6(*:XPʷ:@__?ہ?[?Dž+Nu >TЕ}eGu ]FFm~(f'/R-cECX(jZ CKۀ_(ImHËE (k<WBvS ? P8=G5˼/ngB\)CW*١4>O`矔u2,)4R Sec @6@_4!ŕ7Q5)~|.<ߢs1/SC/~1c@F}ǸM/o^QZ,_ (}&[(oՀRs4/Ӵ?/{X~#?^ͼCp>],RG/ zF߽X> P)ݭoB@M,?P}&MpGG>k}~rPzkUWޣP髵4ązFTX>P)}Q/Q Y>P ?q (P,@_FT4ϲ|5j/B5sɼB\$c~|5忯S0Oz4Ro8*G(}|ߤ}CM1/y4@_?߇ـbqQe/UG11'X^ÈOHF_I>Y> Pg_?K/}K0b$XP&@_? FX~&?#go}gM? (9&F4焥_m# W܀DWz/<}45b !OrA `_? xɟpsӁǸ*a3 /yw0?5j^/eBU\>hFi{{!0"|F_$FEsE< /i#> ;R|~Ch. _򩌲~\k%odE @i?ڏ{5bb}W, (i c\5<(/tP//~c ا gR@OJ/ YPebrMpRV5Y eLQѡs%}ɿ[?`W\mM<>0>JGY>PRG 섏ey=ؿ)~z>|#ߍ`<_@ī0FE,(nJEOe_LOTSkK(B/~&#oRJ/2}_d]Vt5^z@wRW-S|$|׳|:j끟(K(sC&\? (n3{d@ہofGjRoS f{0'}o~JOPNO`_(SU<@],({5w? | P~{~}`?( %?[?aO?D agPgRF_ w[>=2>>/?_]۾E7//8 WY>P%@_9p*0GXP JG_̈oR6WJV&}w?A}_^SE|[xϵ7 J|˸^ sv+鷔~?/WiodGyٌ?¿??qا!gۀU ~!?)G'4^q2c"1OeGh1g}jX>Q'Tد F8_2Nb@_db؟ (S4A?Oc'W7"fGfq |٣o`)Bal@#.؟|竐?>E(,`J@#ŞIsjd ˧}`)V~+e~P%_DVS9G/Wg#g| bLMp>R?oawYP꿉v>τogJ/[4v_?A7~]\S"{b^!(]z)Vyx?rF_FN=,(w%3!~о#r52zؾ)\~6PO'Ҭ*?GnBO4R8JvQS/o~>G^D%,_(b%71Sz%˗JxE~K PVx~Wqn^FPכ~!ƿ7{y>rp>QOcy}*G,"|~(A})a[;)=>/wCh~!Pz~ub3bW(Z'#_~:߁|>(}YK_f^k|$U}'([K^3Qw ePb4F9O3I?xɟȓ,(Geǔ`@? З~!#/YPbO}d,2/‹>qJ~oUѪ#=P5 Oݚsy/'<+ν}aI~>YT$#bG?Rw\U]݃~ ?>ô|Mt.r/%J^770_TXI<>׉eOfcĩ6jcT@)}ߦ*|qf(=yg诃;EWS[Nӯ-ۘy Wx;y a6o志ۙW̿y_1_?Gayjl^!֠^tFOhRyہh>у/~ (1V1LQ)bDF.'3h0?Pwa~wwKX끟̈^'.z~E^yW!7[^7?Q?BVxT/o~6_UiG_4C|Gߗ3rr}װ|26𓁗8NSPdE ,Uߞ }TM}['3IQ,| /MӀ)Ol/uLS}粪KtG*W8'+;4ܖ=vmݽ|5_klߕ.#?8qm7۷cnAR7/p-w.ܜ֬o۳wJ[/KԯWoٳ{~MFaf-ZEڱm cdm߲􋛠5h]W˦@ژjTVAP*< zp zx0 vʶ_iaT]Ɍ}-o.?۽}(Ne2v^uZtj1TkKxʭDZOruJ#*v)V̨o\o[w?8v. =ûgz3l/]uˎ:!v_c5GbCP;ٻZ[)ųfi8Ûgz(|CĚ>Sn ުQ|V;YNְ.Mr ; Z{ OЧuR]>gvqǗiӢǔ:{'o:*©N؞ Q'>7@,~GXmkltx;X92[lǫ\01x<0}cȶz6oŽ;ͱ[aƸ: mxs6]1Nc#QNSpFt>?8ةw;Gg)PzNl=}ailѦkT1YL2bCtCM%x՗M9vPRϱ3d`s})8fTv4 ]줝pvNrR mrS&;*h.&AHGEIeٕ8@^)VINQW޼SOl7K*,s/JP^>NJt'Ww:T>gs\\7wxNgɼCe+NgM*0մP̫SNo: VT^᫕YP{!e)N)[6I"LwLgͤޜJ9©Z6ȼT gm:W>hT^(*Dn:Dlz2toFՆ*NUNg7w ,Se;9 4{'ڽ9dGRBL}dtPt}1xtRzziu31IR=z"ʊT4:UQt*6*Rw84P׿*5 wd0E %ז5[LTE]\IhIStI1O{A8s$}Թi8|:șӝ`ܹy#|[|.Qɥ |GHH&}!-Seu݂;%*TwQa@]8FCmv'jl$~[C)*4u)'Sn"jk# X *j/&8YN˽FjT?uC6A4)Xm<;d7hQ#p}__ mεAQ*={@e`ay#qC͏C6A]Ns`v92QHTtVT.ëVy%[l"ч"N4f,RuT:a])NiC6U~kP*ffr2Dݯ+^,s;=ЋLhF1fT8SatO9SpÁ`۠ോÎk7{3Ͷ{7=ط#NVU71zrGSNu%TcrzrPէ9ny  IW.p54[$3|d'l\><7>JРí%'~.0V-UA<o"} DX6 tֿtWa 5nl-jRw:}tH2pM)4,to;I JD,pPsoYy$b܈gRI_dTѿ (pdY'/*w #O٨{.GocGiHr*8 b"3lV.+soN1=vfh'SNE2iN…]6y~@ϑ!8Pd&\{:J E*sAd FstSӾLr}vuTEQڰ%T}n;͙- >nplգn.wdW]s0+VX}gulQH y,|g"aDCF1>3czLqd갺yL}+VtE3F<%<ʹw.F\ kw{696~CJ,Rm(҈Ȫo ʳٮd:Pg d]/H^ 5O6RDO &Q5p8۞˶ɬc$pmoЏSaJUr;+KdEP%iX8;ވz+^QZ?G2ݴ.Mb|;_LcJrv&I*. I= ӘʆE-1KNW6bzem[+fZoK=*қqb8%*Yky^d<lIUI:Ffu骉G2>Yy;)f&as/& 3^ TP&Cy.+q§Rq109T4B>H++s( =AOk%LbEy#-bahcHyQw[5,~ Ovra M,ϖ~ZU|AD[1gIn+-7=et'vp!^Gm [Rhg$[y FiⒺ<(XaO6Jyy'Z͏`_av1P#nk{ªe7| ~7u,f\Dei?A;.C7⵺;VT|f`k YmW]U&N[(O)n|qJrя 1 wn4Jڿ YluX[NݸWT57HUZ wz*Of\̟qnИ RKRngkU>Ⲗٿ e==.u}=u;rh A3p-6/ܤq5#iVXuf){%e<84lYGq7kn9w4y;j@ބ*PO!W[SxLR.'%[k7뙮RsG_6|K($v9Q)Gq1zʹ`NmXƇ _ī*U&۱뒝;]hayJ8\3W1R,> v"1IU2ѝe;ݙDw&3ѝLtg;ݙt8_Ȕ+tbY5PH= 1 JSk>՜|q Qiڻ=v˯?UюbHJnA}e|& B*B|hkӸq93_+4 ;s^!uNBQOdv ٓ?i'8&i7vkzv¾LZ x0 }!ۗvGrDuoSd;[`f=C6FI<̬ xOcLsTi LXSusZ +_&c/K zhU̷ 4.h  yV:R|?,y5f@'pk`^Bvo[RxjCVe<<>/6^?TpFyHӻ́é96@Hi_2Җ96~YHFϼ?cZК&5eڐ hM@kZZڡ`Znr̀ĎVOcmfmѕt.,^b^u1 L f1@b&3 L w,.=b}="7/W^\yW[ȓ<9/i*ŕ,;oŕpZe^\ܔ[i^f1x/È3x/+9^6x/2W~yq hM@kZКv0&5 hM@k(WFL f@b&3 L f1@b&]̋+j^\1/n/Vk$d2O+cJCr ŕ\+/_岿2oQ- V4OK/3OfslaęfslN6fslJ氙+?^~YȼbZ&5 hM@;M@kZК&5ik,#p&L f1@b&3 L f1.ڊY5V5XNXN{<̓c˘WF̋+~˼ŸU=s>s>x/eqx/eS|3x/ᴒ9^ʏ_2/2 hM@kZqК&5 hM@{1; ,@b&3 L f1@bbyqU͋+ŕŕx}Ъ|O&29t2//ךW,J妤2/gUOϸ29^6xF9^6x_9^6xy8du업̋+&L@kZКy&5 hM@kZОFm̼2g1b&3 L f1@b&3袭e^\aUyqt{q!z*_#yȓ<9/i* A^\ʀ\xyż}6﯌ʪy2SfslNqslN)ƌ9e6ٜ2?Ny hM@kZКv0&5 hM@k(WFL f@b&3 L f1@b&]+j_1ﯜn|׬ʷI>dW4G|墓xż2~e\ ޕ[i^f1|ٜ/È3|ٜ/+9_6|ٜ/2W~Ηys hM@kZКv0&5 hM@k(7WFL f@b&3 L f1@b&]̛+j\1ono|wHd7W4G|se oOךV,Jf2/gUO|ϸ2|9V6XF9V6X_̱9V6Xy8du업 +&L@kZКy&5 hM@kZОFm̼2g1b&3 L f1@b&3袭e^XaUŠyat{a<*_'yr _XU򅕭JymżRl^[S>)slr.esl5c.Z.eڊ hM@kZК&5m@M@kZК'm̼2g1b&3 L f1@b&3袭e^[aUڊymt{mg<~cUD3O&29trmż2~e^X ތ[i^f1X+È3X++9V6X+̱2W~ya hM@kZКv0&5 hM@k(VFL f@b&3 L f1@b&] +j^X1/n/zU2%ޓ<9/i* ʅT"齤`kZ(7YSgÉopJeezپp>>eFן k"]-o8z,^2gUOԸ29}7F9}7_9}7}8dN߹u{=&L@kZКy&5 hM@kZОFmY|'RYQ#v1 U2e1@b&3 L f1@ tV2ygzo 'U78>- X ``` ```{r} a = 1 ``` lintr/tests/testthat/test-redundant_equals_linter.R0000644000176200001440000000213614577052532022433 0ustar liggesuserstest_that("redundant_equals_linter skips allowed usages", { # comparisons to non-logical constants expect_lint("x == 1", NULL, redundant_equals_linter()) # comparison to TRUE as a string expect_lint("x != 'TRUE'", NULL, redundant_equals_linter()) }) test_that("multiple lints return correct custom messages", { expect_lint( "list(x == TRUE, y != TRUE)", list( "Using == on a logical vector", "Using != on a logical vector" ), redundant_equals_linter() ) }) test_that("Order doesn't matter", { expect_lint("TRUE == x", rex::rex("Using == on a logical vector is redundant."), redundant_equals_linter()) }) patrick::with_parameters_test_that( "redundant_equals_linter blocks simple disallowed usages", { lint_msg <- rex::rex(paste("Using", op, "on a logical vector is redundant.")) code <- paste("x", op, bool) expect_lint(code, lint_msg, redundant_equals_linter()) }, .cases = tibble::tribble( ~.test_name, ~op, ~bool, "==, TRUE", "==", "TRUE", "==, FALSE", "==", "FALSE", "!=, TRUE", "!=", "TRUE", "!=, FALSE", "!=", "FALSE" ) ) lintr/tests/testthat/dummy_projects/0000755000176200001440000000000014250050336017446 5ustar liggesuserslintr/tests/testthat/dummy_projects/project/0000755000176200001440000000000014250050336021114 5ustar liggesuserslintr/tests/testthat/dummy_projects/project/cp1252.R0000644000176200001440000000011414250050336022147 0ustar liggesusers# This file is encoded in Cp-1252 # Comment containing non-ASCII <- 42 lintr/tests/testthat/dummy_projects/project/cp1252_parseable.R0000644000176200001440000000011414250050336024165 0ustar liggesusers# This file is encoded in Cp-1252 # Comment containing non-ASCII a <- 42 lintr/tests/testthat/dummy_projects/project/one_start_no_end.R0000644000176200001440000000003014250050336024550 0ustar liggesusers #nolint start c(1,2) lintr/tests/testthat/dummy_projects/project/partially_matched_exclusions.R0000644000176200001440000000040014250050336027173 0ustar liggesusers# nolint start: assign. a = 2 # nolint end # nolint start: s. warn (and lint) because of non-unique identifier x <- 42; y <- 2 # nolint end # nolint start: bogus_linter. warn because of nonexistent identifier # nolint end # nolint: hocus_pocus, bogus. lintr/tests/testthat/dummy_projects/project/project.Rproj0000644000176200001440000000040614250050336023600 0ustar liggesusersVersion: 1.0 RestoreWorkspace: Default SaveWorkspace: Default AlwaysSaveHistory: Default EnableCodeIndexing: Yes UseSpacesForTab: Yes NumSpacesForTab: 2 Encoding: ISO8859-1 RnwWeave: Sweave LaTeX: pdfLaTeX AutoAppendNewline: Yes StripTrailingWhitespace: Yes lintr/tests/testthat/dummy_projects/project/default_linter_testcode.R0000644000176200001440000000144614250050336026137 0ustar liggesusers# Each of the default linters should throw at least one lint on this file # assignment # function_left_parentheses # closed_curly # commas # paren_brace f = function (x,y = 1){} # commented_code # some <- commented("out code") # cyclocomp # equals_na # infix_spaces # line_length # object_length # object_name # object_usage # open_curly # T_and_F_symbol someComplicatedFunctionWithALongCamelCaseName <- function(x) { y <- 1 if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else {F} } # no_tab # pipe_continuation # seq_linter # spaces_inside x <- 1:10 x[ 2] 1:length(x) %>% lapply(function(x) x*2) %>% head() # single_quotes message('single_quotes') # spaces_left_parentheses # trailing_whitespace # semicolon x <- 42; y <- 2 +(1:10) # trailing_blank_lines lintr/tests/testthat/dummy_projects/project/mismatched_starts_ends.R0000644000176200001440000000011614250050336025764 0ustar liggesusers#nolint end #nolint start c(1,2) #nolint start #nolint end #nolint start lintr/tests/testthat/test-function_return_linter.R0000644000176200001440000000266114577052532022324 0ustar liggesuserstest_that("function_return_linter skips allowed usages", { lines_simple <- trim_some(" foo <- function(x) { x <- x + 1 return(x) } ") expect_lint(lines_simple, NULL, function_return_linter()) # arguably an expression as complicated as this should also be assigned, # but for now that's out of the scope of this linter lines_subassignment <- trim_some(" foo <- function(x) { return(x[, { col <- col + 1 .(grp, col) }]) } ") expect_lint(lines_subassignment, NULL, function_return_linter()) }) test_that("function_return_linter blocks simple disallowed usages", { linter <- function_return_linter() lint_msg <- rex::rex("Move the assignment outside of the return() clause") expect_lint( trim_some(" foo <- function(x) { return(x <- x + 1) } "), lint_msg, linter ) expect_lint( trim_some(" foo <- function(x) { return(x <<- x + 1) } "), lint_msg, linter ) expect_lint( trim_some(" foo <- function(x) { return(x + 1 ->> x) } "), lint_msg, linter ) expect_lint( trim_some(" foo <- function(x) { return(x + 1 -> x) } "), lint_msg, linter ) side_effect_lines <- expect_lint( trim_some(" e <- new.env() foo <- function(x) { return(e$val <- x + 1) } "), lint_msg, linter ) }) lintr/tests/testthat/test-consecutive_assertion_linter.R0000644000176200001440000000554114577055664023527 0ustar liggesuserstest_that("consecutive_assertion_linter skips allowed usages", { linter <- consecutive_assertion_linter() expect_lint("stopifnot(x)", NULL, linter) expect_lint("stopifnot(x, y, z)", NULL, linter) # intervening expression expect_lint("stopifnot(x); y; stopifnot(z)", NULL, linter) # inline or potentially with gaps don't matter expect_lint( trim_some(" stopifnot(x) y stopifnot(z) "), NULL, linter ) }) test_that("consecutive_assertion_linter blocks simple disallowed usages", { linter <- consecutive_assertion_linter() lint_msg <- rex::rex("Unify consecutive calls to stopifnot().") # one test of inline usage expect_lint( "stopifnot(x); stopifnot(y)", lint_msg, linter ) expect_lint( trim_some(" stopifnot(x) stopifnot(y, z) "), lint_msg, linter ) expect_lint( trim_some(" stopifnot(x) stopifnot(y) "), lint_msg, linter ) expect_lint( trim_some(" stopifnot(x) # a comment on y stopifnot(y) "), lint_msg, linter ) }) test_that("assert_that usages are handled correctly too", { linter <- consecutive_assertion_linter() lint_msg <- rex::rex("Unify consecutive calls to assert_that().") expect_lint("assert_that(x)", NULL, linter) expect_lint("assertthat::assert_that(x, y, z)", NULL, linter) # if msg= is used, can't necessarily combine lines <- trim_some(" assert_that(x, msg = 'bad x') assert_that(y, msg = 'bad y') ") expect_lint(lines, NULL, linter) # one test of inline usage expect_lint( "assert_that(x); assert_that(y)", lint_msg, linter ) lines_gap <- trim_some(" assert_that(x) assertthat::assert_that(y, z) ") expect_lint(lines_gap, lint_msg, linter) }) test_that("Mixing test functions is fine", { expect_lint( trim_some(" assert_that(x) stopifnot(y) "), NULL, consecutive_assertion_linter() ) }) test_that("lints vectorize", { expect_lint( trim_some("{ stopifnot(A) stopifnot(B) assert_that(C) assert_that(D) }"), list( list("stopifnot", line_number = 2L), list("assert_that", line_number = 4L) ), consecutive_assertion_linter() ) }) test_that("old name consecutive_stopifnot_linter() is deprecated", { expect_warning( { old_linter <- consecutive_stopifnot_linter() }, "Use consecutive_assertion_linter instead", fixed = TRUE ) expect_lint("stopifnot(x); y; stopifnot(z)", NULL, old_linter) expect_lint("stopifnot(x); stopifnot(y)", "Unify consecutive calls", old_linter) }) test_that("interceding = assignments aren't linted", { expect_lint( trim_some("{ stopifnot(A) x = 1 stopifnot(B) assert_that(C) z = 3 assert_that(D) }"), NULL, consecutive_assertion_linter() ) }) lintr/tests/testthat/test-expect_identical_linter.R0000644000176200001440000000455414577052532022407 0ustar liggesuserstest_that("expect_identical_linter skips allowed usages", { linter <- expect_identical_linter() # expect_type doesn't have an inverted version expect_lint("expect_true(identical(x, y) || identical(y, z))", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(identical(x, y) || identical(y, z))", NULL, linter) # expect_equal calls with explicit tolerance= are OK expect_lint("expect_equal(x, y, tolerance = 1e-6)", NULL, linter) # ditto if the argument is passed quoted expect_lint("expect_equal(x, y, 'tolerance' = 1e-6)", NULL, linter) # ditto for check.attributes = FALSE expect_lint("expect_equal(x, y, check.attributes = FALSE)", NULL, linter) }) test_that("expect_identical_linter blocks simple disallowed usages", { linter <- expect_identical_linter() lint_msg <- rex::rex("Use expect_identical(x, y) by default; resort to expect_equal() only when needed") expect_lint("expect_equal(x, y)", lint_msg, linter) # different usage to redirect to expect_identical expect_lint("expect_true(identical(x, y))", lint_msg, linter) }) test_that("expect_identical_linter skips cases likely testing numeric equality", { linter <- expect_identical_linter() lint_msg <- rex::rex("Use expect_identical(x, y) by default; resort to expect_equal() only when needed") expect_lint("expect_equal(x, 1.034)", NULL, linter) expect_lint("expect_equal(x, c(1.01, 1.02))", NULL, linter) # whole numbers with explicit decimals are OK, even in mixed scenarios expect_lint("expect_equal(x, c(1.0, 2))", NULL, linter) # plain numbers are still caught, however expect_lint("expect_equal(x, 1L)", lint_msg, linter) expect_lint("expect_equal(x, 1)", lint_msg, linter) # NB: TRUE is a NUM_CONST so we want test matching it, even though this test is # also a violation of expect_true_false_linter() expect_lint("expect_equal(x, TRUE)", lint_msg, linter) }) test_that("expect_identical_linter skips 3e cases needing expect_equal", { expect_lint("expect_equal(x, y, ignore_attr = 'names')", NULL, expect_identical_linter()) }) # this arose where a helper function was wrapping expect_equal() and # some of the "allowed" arguments were being passed --> false positive test_that("expect_identical_linter skips calls using ...", { expect_lint("expect_equal(x, y, ...)", NULL, expect_identical_linter()) }) lintr/tests/testthat/test-extraction_operator_linter.R0000644000176200001440000000210014577052532023157 0ustar liggesuserstest_that("extraction_operator_linter skips allowed usages", { linter <- extraction_operator_linter() expect_lint("x[[1]]", NULL, linter) expect_lint("x[-1]", NULL, linter) expect_lint("x[1, 'a']", NULL, linter) expect_lint("self$a", NULL, linter) expect_lint(".self $\na", NULL, linter) expect_lint("x[NULL]", NULL, linter) }) test_that("extraction_operator_linter blocks disallowed usages", { linter <- extraction_operator_linter() msg_b <- rex::escape("Use `[[` instead of `[` to extract an element.") msg_d <- rex::escape("Use `[[` instead of `$` to extract an element.") expect_lint("x$a", list(message = msg_d, line_number = 1L, column_number = 2L), linter) expect_lint("x $\na", list(message = msg_d, line_number = 1L, column_number = 3L), linter) expect_lint("x[++ + 3]", list(message = msg_b, line_number = 1L, column_number = 2L), linter) expect_lint( "c(x['a'], x [ 1 ])", list( list(message = msg_b, line_number = 1L, column_number = 4L), list(message = msg_b, line_number = 1L, column_number = 13L) ), linter ) }) lintr/tests/testthat/test-redundant_ifelse_linter.R0000644000176200001440000001027414577052532022412 0ustar liggesuserstest_that("redundant_ifelse_linter skips allowed usages", { linter <- redundant_ifelse_linter() expect_lint("ifelse(x > 5, 0, 2)", NULL, linter) expect_lint("ifelse(x > 5, TRUE, NA)", NULL, linter) expect_lint("ifelse(x > 5, FALSE, NA)", NULL, linter) expect_lint("ifelse(x > 5, TRUE, TRUE)", NULL, linter) expect_lint("ifelse(x > 5, 0L, 2L)", NULL, linter) expect_lint("ifelse(x > 5, 0L, 10L)", NULL, linter) }) test_that("redundant_ifelse_linter blocks simple disallowed usages", { linter <- redundant_ifelse_linter() expect_lint( "ifelse(x > 5, TRUE, FALSE)", rex::rex("Just use the logical condition (or its negation) directly"), linter ) expect_lint( "ifelse(x > 5, FALSE, TRUE)", rex::rex("Just use the logical condition (or its negation) directly"), linter ) # other ifelse equivalents from common packages expect_lint( "if_else(x > 5, TRUE, FALSE)", rex::rex("Just use the logical condition (or its negation) directly"), linter ) expect_lint( "fifelse(x > 5, FALSE, TRUE)", rex::rex("Just use the logical condition (or its negation) directly"), linter ) }) test_that("redundant_ifelse_linter blocks usages equivalent to as.numeric, optionally", { linter <- redundant_ifelse_linter() expect_lint( "ifelse(x > 5, 1L, 0L)", rex::rex("Prefer as.integer(x) to ifelse(x, 1L, 0L)"), linter ) expect_lint( "ifelse(x > 5, 0L, 1L)", rex::rex("Prefer as.integer(!x) to ifelse(x, 0L, 1L)"), linter ) expect_lint( "ifelse(x > 5, 1, 0)", rex::rex("Prefer as.numeric(x) to ifelse(x, 1, 0)"), linter ) expect_lint( "ifelse(x > 5, 0, 1)", rex::rex("Prefer as.numeric(!x) to ifelse(x, 0, 1)"), linter ) # mixing int and num expect_lint( "ifelse(x > 5, 0, 1L)", rex::rex("Prefer as.numeric(!x) to ifelse(x, 0, 1L)"), linter ) expect_lint( "ifelse(x > 5, 0L, 1)", rex::rex("Prefer as.numeric(!x) to ifelse(x, 0L, 1)"), linter ) expect_lint( "ifelse(x > 5, 1, 0L)", rex::rex("Prefer as.numeric(x) to ifelse(x, 1, 0L)"), linter ) expect_lint( "ifelse(x > 5, 1L, 0)", rex::rex("Prefer as.numeric(x) to ifelse(x, 1L, 0)"), linter ) # data.table/dplyr equivalents expect_lint( "dplyr::if_else(x > 5, 1L, 0L)", rex::rex("Prefer as.integer(x) to if_else(x, 1L, 0L)"), linter ) expect_lint( "data.table::fifelse(x > 5, 0L, 1L)", rex::rex("Prefer as.integer(!x) to fifelse(x, 0L, 1L)"), linter ) expect_lint( "if_else(x > 5, 1, 0)", rex::rex("Prefer as.numeric(x) to if_else(x, 1, 0)"), linter ) expect_lint( "fifelse(x > 5, 0, 1)", rex::rex("Prefer as.numeric(!x) to fifelse(x, 0, 1)"), linter ) }) test_that("allow10 works as intended", { linter <- redundant_ifelse_linter(allow10 = TRUE) expect_lint("ifelse(x > 5, 1L, 0L)", NULL, linter) expect_lint("ifelse(x > 5, 0L, 1L)", NULL, linter) expect_lint("ifelse(x > 5, 1, 0)", NULL, linter) expect_lint("ifelse(x > 5, 0, 1)", NULL, linter) expect_lint("dplyr::if_else(x > 5, 1L, 0L)", NULL, linter) expect_lint("data.table::fifelse(x > 5, 0L, 1L)", NULL, linter) expect_lint("if_else(x > 5, 1, 0)", NULL, linter) expect_lint("fifelse(x > 5, 0, 1)", NULL, linter) }) test_that("ifelse(missing = ) gives correct lints", { linter <- redundant_ifelse_linter() expect_lint("if_else(x > 5, TRUE, FALSE, NA)", rex::rex("Just use the logical condition"), linter) expect_lint("if_else(x > 5, TRUE, FALSE, TRUE)", NULL, linter) expect_lint("if_else(x > 5, TRUE, FALSE, 5L)", NULL, linter) expect_lint("if_else(x > 5, 1L, 0L, NA_integer_)", rex::rex("Prefer as.integer(x)"), linter) expect_lint("if_else(x > 5, 1L, 0L, 2L)", NULL, linter) expect_lint("if_else(x > 5, 1L, 0L, 5)", NULL, linter) expect_lint("if_else(x > 5, 1, 0, NA_real_)", rex::rex("Prefer as.numeric(x)"), linter) expect_lint("if_else(x > 5, 1, 0, 2)", NULL, linter) expect_lint("if_else(x > 5, 1, 0, '5')", NULL, linter) # TRUE/FALSE must be found in yes/no, not missing= expect_lint("if_else(x > 5, 'a', TRUE, FALSE)", NULL, linter) expect_lint("if_else(x > 5, 'a', 0L, 1L)", NULL, linter) expect_lint("if_else(x > 5, 'a', 1, 0)", NULL, linter) }) lintr/tests/testthat/test-duplicate_argument_linter.R0000644000176200001440000001145214577203416022751 0ustar liggesuserstest_that("duplicate_argument_linter doesn't block allowed usages", { linter <- duplicate_argument_linter() expect_lint("fun(arg = 1)", NULL, linter) expect_lint("fun('arg' = 1)", NULL, linter) expect_lint("fun(`arg` = 1)", NULL, linter) expect_lint("'fun'(arg = 1)", NULL, linter) expect_lint("(function(x, y) x + y)(x = 1)", NULL, linter) expect_lint("dt[i = 1]", NULL, linter) }) test_that("duplicate_argument_linter blocks disallowed usages", { linter <- duplicate_argument_linter() lint_msg <- rex::rex("Avoid duplicate arguments in function calls.") expect_lint("fun(arg = 1, arg = 2)", lint_msg, linter) expect_lint("fun(arg = 1, 'arg' = 2)", lint_msg, linter) expect_lint("fun(arg = 1, `arg` = 2)", lint_msg, linter) expect_lint("'fun'(arg = 1, arg = 2)", lint_msg, linter) expect_lint("(function(x, y) x + y)(x = 1, x = 2)", lint_msg, linter) expect_lint("dt[i = 1, i = 2]", lint_msg, linter) expect_lint( trim_some(" list( var = 1, var = 2 ) "), lint_msg, linter ) }) test_that("duplicate_argument_linter respects except argument", { expect_lint( "list( var = 1, var = 2 )", NULL, duplicate_argument_linter(except = "list") ) expect_lint( "(function(x, y) x + y)(x = 1) list(var = 1, var = 2)", NULL, duplicate_argument_linter(except = "list") ) expect_lint( "fun(` ` = 1, ` ` = 2)", rex::rex("Avoid duplicate arguments in function calls."), duplicate_argument_linter(except = character()) ) expect_lint( "function(arg = 1, arg = 1) {}", rex::rex("Repeated formal argument 'arg'."), duplicate_argument_linter(except = character()) ) }) test_that("doesn't lint duplicated arguments in allowed functions", { linter <- duplicate_argument_linter() expect_lint( "x %>% dplyr::mutate( col = a + b, col = col + d )", NULL, linter ) expect_lint( "x %>% dplyr::transmute( col = a + b, col = col / 2.5 )", NULL, linter ) skip_if_not_r_version("4.1.0") expect_lint( "x |> dplyr::mutate( col = col |> str_replace('t', '') |> str_replace('\\\\s+$', 'xxx') )", NULL, linter ) }) test_that("interceding comments don't trip up logic", { linter <- duplicate_argument_linter() lint_msg <- rex::rex("Avoid duplicate arguments") # comment before the EQ_SUB # actually this case "just works" even before #2402 since # get_r_string() returns NA for both argument names expect_lint( trim_some(" fun( arg # xxx = 1, arg # yyy = 2 ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg # xxx = 1, arg = 2 ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg = 1, arg # yyy = 2 ) "), list(lint_msg, line_number = 3L), linter ) # comment after the EQ_SUB expect_lint( trim_some(" fun( arg = # xxx 1, arg = # yyy 2 ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg = # xxx 1, arg = 2 ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg = 1, arg = # yyy 2 ) "), list(lint_msg, line_number = 3L), linter ) # comment after the arg value expect_lint( trim_some(" fun( arg = 1 # xxx , arg = 2 # yyy ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg = 1 # xxx , arg = 2 ) "), list(lint_msg, line_number = 4L), linter ) expect_lint( trim_some(" fun( arg = 1, arg = 2 # yyy ) "), list(lint_msg, line_number = 3L), linter ) # comment after the OP-COMMA expect_lint( trim_some(" fun( arg = 1, # xxx arg = 2 # yyy ) "), list(lint_msg, line_number = 3L), linter ) expect_lint( trim_some(" fun( arg = 1, # xxx arg = 2 ) "), list(lint_msg, line_number = 3L), linter ) }) test_that("lints vectorize", { lint_msg <- rex::rex("Avoid duplicate arguments") expect_lint( trim_some("{ c(a = 1, a = 2, a = 3) list(b = 1, b = 2, b = 3) }"), list( list(lint_msg, line_number = 2L, column_number = 12L), list(lint_msg, line_number = 2L, column_number = 19L), list(lint_msg, line_number = 3L, column_number = 15L), list(lint_msg, line_number = 3L, column_number = 22L) ), duplicate_argument_linter() ) }) lintr/tests/testthat/test-xp_utils.R0000644000176200001440000000156514510656223017366 0ustar liggesuserstest_that("xp_call_name works", { xml_from_code <- function(str) { xml2::read_xml(xmlparsedata::xml_parse_data(parse(text = str, keep.source = TRUE))) } xml <- xml_from_code("sum(1:10)") expect_identical(xp_call_name(xml, depth = 2L), "sum") expect_identical(xp_call_name(xml2::xml_find_first(xml, "expr")), "sum") xml <- xml_from_code(c("sum(1:10)", "sd(1:10)")) expect_identical(xp_call_name(xml, depth = 2L, condition = "text() = 'sum'"), "sum") }) test_that("xp_call_name input validation works", { expect_error(xp_call_name(2L), "Expected an xml_nodeset", fixed = TRUE) xml <- xml2::read_xml("
    ") expect_error(xp_call_name(xml, depth = -1L), "depth >= 0", fixed = TRUE) expect_error(xp_call_name(xml, depth = "1"), "is.numeric(depth)", fixed = TRUE) expect_error(xp_call_name(xml, condition = 1L), "is.character(condition)", fixed = TRUE) }) lintr/tests/testthat/test-expect_lint.R0000644000176200001440000000661314577052532020042 0ustar liggesusers# testthat's expect_success() and expect_failure() can only handle the first expectation and are # thus less than ideal to test expect_lint(), which can process multiple lints. If you want to test # for failure, always put the lint check or lint field that must fail first. linter <- assignment_linter() lint_msg <- "Use <-, not =" test_that("no checks", { expect_success(expect_lint("a", NULL, linter)) expect_success(expect_lint("a=1", NULL, list())) expect_failure(expect_lint("a=1", NULL, linter)) }) test_that("single check", { expect_failure(expect_lint(character(), lint_msg, linter)) expect_failure(expect_lint("", lint_msg, linter)) expect_success(expect_lint(content = "a=1", checks = lint_msg, linters = linter)) expect_success(expect_lint("a=1", lint_msg, linter)) expect_failure(expect_lint("a=1", "asdf", linter)) expect_success(expect_lint("a=1", c(message = lint_msg), linter)) expect_failure(expect_lint("a=1", c(message = NULL), linter)) expect_success(expect_lint("a=1", c(message = lint_msg, line_number = 1L), linter)) expect_failure(expect_lint("a=1", c(line_number = 2L, message = lint_msg), linter)) expect_error(expect_lint("a=1", c(message = lint_msg, lineXXX = 1L), linter), "invalid field") expect_failure(expect_lint("foo ()", list(ranges = list(c(2L, 2L))), function_left_parentheses_linter())) expect_success(expect_lint("\t1", list(ranges = list(c(1L, 1L))), whitespace_linter())) expect_success(expect_lint("a=1", list(message = lint_msg, line_number = 1L), linter)) expect_failure(expect_lint("a=1", list(2L, lint_msg), linter)) expect_error(expect_lint("1:nrow(x)", "(group)", seq_linter()), "Invalid regex result", fixed = TRUE) }) test_that("multiple checks", { expect_success( expect_lint(file = "exclusions-test", checks = as.list(rep(lint_msg, 9L)), linters = linter, parse_settings = FALSE) ) expect_success(expect_lint("a=1; b=2", list(lint_msg, lint_msg), linter)) expect_success(expect_lint("a=1; b=2", list(c(message = lint_msg), c(message = lint_msg)), linter)) expect_success(expect_lint("a=1; b=2", list(c(line_number = 1L), c(linter = "assignment_linter")), linter)) expect_success(expect_lint("a=1; b=2", list(lint_msg, c(line = "a=1; b=2", type = "warning")), linter)) expect_success(expect_lint(c("a=1", "b=2"), list(c(line_number = 1L), c(line_number = 2L)), linter)) expect_failure(expect_lint(c("a=1", "b=2"), list(c(line_number = 2L), c(line_number = 2L)), linter)) expect_success(expect_lint("a=1; b=2", list(list(line_number = 1L), list(line_number = 2L)), linter)) expect_failure(expect_lint("a=1; b=2", list(list(line_number = 2L), list(line_number = 2L)), linter)) expect_success( expect_lint("\t1\n\t2", list("tabs", list(column_number = 1L, ranges = list(c(1L, 1L)))), whitespace_linter()) ) }) test_that("expect_lint_free works", { withr::local_options( lintr.rstudio_source_markers = FALSE, lintr.linter_file = "lintr_test_config" ) withr::local_envvar(c(NOT_CRAN = "true", R_COVR = "false")) expect_lint_free(test_path("dummy_packages", "clean")) expect_lint_free(test_path("dummy_packages", "clean_subdir", "r")) expect_failure(expect_lint_free(test_path("dummy_packages", "package"))) }) test_that("expect_lint doesn't change language", { withr::with_envvar(c(LANGUAGE = "mr"), { expect_success(expect_lint("a=1", lint_msg, linter)) expect_identical(Sys.getenv("LANGUAGE"), "mr") }) }) lintr/tests/testthat/test-line_length_linter.R0000644000176200001440000000260614577052532021367 0ustar liggesuserstest_that("line_length_linter skips allowed usages", { linter <- line_length_linter(80L) expect_lint("blah", NULL, linter) expect_lint(strrep("x", 80L), NULL, linter) }) test_that("line_length_linter blocks disallowed usages", { linter <- line_length_linter(80L) lint_msg <- rex::rex("Lines should not be more than 80 characters. This line is 81 characters.") expect_lint( strrep("x", 81L), list( message = lint_msg, column_number = 81L ), linter ) expect_lint( paste(rep(strrep("x", 81L), 2L), collapse = "\n"), list( list( message = lint_msg, column_number = 81L ), list( message = lint_msg, column_number = 81L ) ), linter ) linter <- line_length_linter(20L) lint_msg <- rex::rex("Lines should not be more than 20 characters. This line is 22 characters.") expect_lint(strrep("a", 20L), NULL, linter) expect_lint( strrep("a", 22L), list( message = lint_msg, column_number = 21L ), linter ) # Don't duplicate lints expect_length( lint( "x <- 2 # ------------\n", linters = linter, parse_settings = FALSE ), 1L ) }) test_that("Multiple lints give custom messages", { expect_lint( trim_some("{ abcdefg hijklmnop }"), list("9 characters", "11 characters"), line_length_linter(5L) ) }) lintr/tests/testthat/test-sprintf_linter.R0000644000176200001440000001052714577052532020565 0ustar liggesuserspatrick::with_parameters_test_that( "sprintf_linter skips allowed usages", { linter <- sprintf_linter() # NB: using paste0, not sprintf, to avoid escaping '%d' in sprint fmt= expect_lint(paste0(call_name, "('hello')"), NULL, linter) expect_lint(paste0(call_name, "('hello %d', 1)"), NULL, linter) expect_lint(paste0(call_name, "('hello %d', x)"), NULL, linter) expect_lint(paste0(call_name, "('hello %d', x + 1)"), NULL, linter) expect_lint(paste0(call_name, "('hello %d', f(x))"), NULL, linter) expect_lint(paste0(call_name, "('hello %1$s %1$s', x)"), NULL, linter) expect_lint(paste0(call_name, "('hello %1$s %1$s %2$d', x, y)"), NULL, linter) expect_lint(paste0(call_name, "('hello %1$s %1$s %2$d %3$s', x, y, 1.5)"), NULL, linter) }, .test_name = c("sprintf", "gettextf"), call_name = c("sprintf", "gettextf") ) patrick::with_parameters_test_that( "sprintf_linter blocks disallowed usages", { linter <- sprintf_linter() unused_arg_msg <- if (getRversion() >= "4.1.0") "one argument not used by format" else NULL expect_lint(paste0(call_name, "('hello', 1)"), unused_arg_msg, linter) expect_lint( paste0(call_name, "('hello %d', 'a')"), rex::rex("invalid format '%d'; use format %s for character objects"), linter ) expect_lint( paste0(call_name, "('hello %d', 1.5)"), rex::rex("invalid format '%d'; use format %f, %e, %g or %a for numeric objects"), linter ) expect_lint( paste0(call_name, "('hello %d',)"), rex::rex("argument is missing, with no default"), linter ) expect_lint(paste0(call_name, "('hello %1$s %s', 'a', 'b')"), unused_arg_msg, linter) expect_lint(paste0(call_name, "('hello %1$s %1$s', x, y)"), unused_arg_msg, linter) expect_lint( paste0(call_name, "('hello %1$s %1$s %3$d', x, y)"), rex::rex("reference to non-existent argument 3"), linter ) expect_lint( paste0(call_name, "('hello %1$s %1$s %2$d %3$d', x, y, 1.5)"), rex::rex("invalid format '%d'; use format %f, %e, %g or %a for numeric objects"), linter ) }, .test_name = c("sprintf", "gettextf"), call_name = c("sprintf", "gettextf") ) test_that("edge cases are detected correctly", { linter <- sprintf_linter() # works with multi-line sprintf and comments expect_lint( trim_some(" sprintf( 'test fmt %s', # this is a comment 2 ) "), NULL, linter ) # dots expect_lint("sprintf('%d %d, %d', id, ...)", NULL, linter) # TODO(#1265) extend ... detection to at least test for too many arguments. # named argument fmt expect_lint("sprintf(x, fmt = 'hello %1$s %1$s')", NULL, linter) expect_lint( "sprintf(x, fmt = 'hello %1$s %1$s %3$d', y)", list(message = rex::rex("reference to non-existent argument 3")), linter ) # #2131: xml2lang stripped necessary whitespace expect_lint("sprintf('%s', if (A) '' else y)", NULL, linter) }) local({ linter <- sprintf_linter() unused_fmt_msg <- "too few arguments" unused_arg_msg <- "one argument not used by format" pipes <- pipes(exclude = "%$%") patrick::with_parameters_test_that( "piping into sprintf works", { expect_lint(paste("x", pipe, "sprintf(fmt = '%s')"), NULL, linter) # no fmt= specified -> this is just 'sprintf("%s", "%s%s")', which won't lint expect_lint(paste('"%s"', pipe, 'sprintf("%s%s")'), NULL, linter) expect_lint(paste("x", pipe, "sprintf(fmt = '%s%s')"), unused_fmt_msg, linter) # Cannot evaluate statically --> skip expect_lint(paste("x", pipe, 'sprintf("a")'), NULL, linter) # Nested pipes expect_lint( paste("'%%sb'", pipe, "sprintf('%s')", pipe, "sprintf('a')"), if (getRversion() >= "4.1.0") list(column_number = nchar(paste("'%%sb'", pipe, "x")), message = unused_arg_msg), linter ) expect_lint( paste("x", pipe, 'sprintf(fmt = "%s")', pipe, 'sprintf(fmt = "%s%s")'), list(column_number = nchar(paste("x", pipe, 'sprintf(fmt = "%s")', pipe, "x")), message = unused_fmt_msg), linter ) expect_lint( paste("x", pipe, 'sprintf(fmt = "%s%s")', pipe, 'sprintf(fmt = "%s")'), list(column_number = nchar(paste("x", pipe, "x")), message = unused_fmt_msg), linter ) }, pipe = pipes, .test_name = names(pipes) ) }) lintr/tests/testthat/test-scalar_in_linter.R0000644000176200001440000000225114577052532021026 0ustar liggesuserstest_that("scalar_in_linter skips allowed usages", { linter <- scalar_in_linter() expect_lint("x %in% y", NULL, linter) expect_lint("y %in% c('a', 'b')", NULL, linter) expect_lint("c('a', 'b') %chin% x", NULL, linter) expect_lint("z %in% 1:3", NULL, linter) # scalars on LHS are fine (often used as `"col" %in% names(DF)`) expect_lint("3L %in% x", NULL, linter) # this should be is.na(x), but it more directly uses the "always TRUE/FALSE, _not_ NA" # aspect of %in%, so we delegate linting here to equals_na_linter() expect_lint("x %in% NA", NULL, linter) expect_lint("x %in% NA_character_", NULL, linter) }) test_that("scalar_in_linter blocks simple disallowed usages", { linter <- scalar_in_linter() lint_in_msg <- rex::rex("Use == to match length-1 scalars, not %in%.") lint_chin_msg <- rex::rex("Use == to match length-1 scalars, not %chin%.") expect_lint("x %in% 1", lint_in_msg, linter) expect_lint("x %chin% 'a'", lint_chin_msg, linter) }) test_that("multiple lints are generated correctly", { linter <- scalar_in_linter() expect_lint( trim_some('{ x %in% 1 y %chin% "a" }'), list("%in%", "%chin%"), linter ) }) lintr/tests/testthat/test-knitr_formats.R0000644000176200001440000001271314577052532020404 0ustar liggesusersregexes <- list( assign = rex::rex("Use <-, not =, for assignment."), local_var = rex::rex("local variable"), quotes = rex::rex("Only use double-quotes."), trailing = rex::rex("Trailing blank lines are superfluous."), trailws = rex::rex("Trailing whitespace is superfluous."), indent = rex::rex("Indentation should be") ) test_that("it handles dir", { file_pattern <- rex::rex(".R", one_of("html", "md", "nw", "rst", "tex", "txt")) lints <- lint_dir(path = "knitr_formats", pattern = file_pattern, parse_settings = FALSE) # For every file there should be at least 1 lint expect_identical( sort(unique(names(lints))), sort(list.files(test_path("knitr_formats"), pattern = file_pattern)) ) }) test_that("it handles markdown", { expect_lint( file = test_path("knitr_formats", "test.Rmd"), checks = list( list(regexes[["assign"]], line_number = 9L), list(regexes[["local_var"]], line_number = 22L), list(regexes[["assign"]], line_number = 22L), list(regexes[["trailing"]], line_number = 24L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles quarto", { expect_lint( file = test_path("knitr_formats", "test.qmd"), checks = list( list(regexes[["assign"]], line_number = 9L), list(regexes[["local_var"]], line_number = 22L), list(regexes[["assign"]], line_number = 22L), list(regexes[["trailing"]], line_number = 24L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles Sweave", { expect_lint( file = test_path("knitr_formats", "test.Rnw"), checks = list( list(regexes[["assign"]], line_number = 12L), list(regexes[["local_var"]], line_number = 24L), list(regexes[["assign"]], line_number = 24L), list(regexes[["trailing"]], line_number = 26L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles reStructuredText", { expect_lint( file = test_path("knitr_formats", "test.Rrst"), checks = list( list(regexes[["assign"]], line_number = 10L), list(regexes[["local_var"]], line_number = 23L), list(regexes[["assign"]], line_number = 23L), list(regexes[["trailing"]], line_number = 25L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles HTML", { expect_lint( file = test_path("knitr_formats", "test.Rhtml"), checks = list( list(regexes[["assign"]], line_number = 15L), list(regexes[["local_var"]], line_number = 27L), list(regexes[["assign"]], line_number = 27L), list(regexes[["trailing"]], line_number = 29L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles tex", { expect_lint( file = test_path("knitr_formats", "test.Rtex"), checks = list( list(regexes[["indent"]], line_number = 11L), # TODO(AshesITR): # masking the Rtex escape char by whitespace causes false-positive indentation lints list(regexes[["assign"]], line_number = 11L), list(regexes[["indent"]], line_number = 22L), list(regexes[["local_var"]], line_number = 23L), list(regexes[["assign"]], line_number = 23L), list(regexes[["trailing"]], line_number = 25L), list(regexes[["trailws"]], line_number = 25L) # TODO(AshesITR): #1043 # file_lines contains a whitespace on the final line for Rtex, because that is used to mark the Rtex escape char # "%" as well. # cf. get_source_expressions("tests/testthat/knitr_formats/test.Rtex")$lines[[25]] ), linters = default_linters, parse_settings = FALSE ) }) test_that("it handles asciidoc", { expect_lint( file = test_path("knitr_formats", "test.Rtxt"), checks = list( list(regexes[["assign"]], line_number = 9L), list(regexes[["local_var"]], line_number = 22L), list(regexes[["assign"]], line_number = 22L), list(regexes[["trailing"]], line_number = 24L) ), linters = default_linters, parse_settings = FALSE ) }) test_that("it does _not_ handle brew", { expect_lint("'<% a %>'\n", checks = list( regexes[["quotes"]], regexes[["trailing"]] ), default_linters ) }) test_that("it does _not_ error with inline \\Sexpr", { expect_lint( "#' text \\Sexpr{1 + 1} more text", NULL, default_linters ) }) test_that("it does lint .Rmd or .qmd file with malformed input", { expect_lint( file = test_path("knitr_malformed", "incomplete_r_block.Rmd"), checks = "Missing chunk end", linters = default_linters, parse_settings = FALSE ) expect_lint( file = test_path("knitr_malformed", "incomplete_r_block.qmd"), checks = "Missing chunk end", linters = default_linters, parse_settings = FALSE ) contents <- c( trim_some(" ```{r chunk} lm(x ~ y) # some text ``` bash some_script.sh ``` "), trim_some(" ```{r chunk-1} code <- 42 # A heading Some text ```{r chunk-2} some_more_code <- 42 ``` "), trim_some(" ```{r chunk-1} code <- 42 ``` # A heading Some text ```{r chunk-2} some_more_code <- 42 ") ) expected <- list( NULL, # This test case would require parsing all chunk fences, not just r chunks. "maybe starting at line 1", "maybe starting at line 8" ) for (i in seq_along(contents)) { expect_lint(contents[[i]], expected[[i]], linters = list()) } }) lintr/tests/testthat/test-numeric_leading_zero_linter.R0000644000176200001440000000205314577052532023257 0ustar liggesuserstest_that("numeric_leading_zero_linter skips allowed usages", { linter <- numeric_leading_zero_linter() expect_lint("a <- 0.1", NULL, linter) expect_lint("b <- -0.2", NULL, linter) expect_lint("c <- 3.0", NULL, linter) expect_lint("d <- 4L", NULL, linter) expect_lint("e <- TRUE", NULL, linter) expect_lint("f <- 0.5e6", NULL, linter) expect_lint("g <- 0x78", NULL, linter) expect_lint("h <- 0.9 + 0.1i", NULL, linter) expect_lint("h <- 0.9+0.1i", NULL, linter) expect_lint("h <- 0.9 - 0.1i", NULL, linter) expect_lint("i <- 2L + 3.4i", NULL, linter) }) test_that("numeric_leading_zero_linter blocks simple disallowed usages", { linter <- numeric_leading_zero_linter() lint_msg <- rex::rex("Include the leading zero for fractional numeric constants.") expect_lint("a <- .1", lint_msg, linter) expect_lint("b <- -.2", lint_msg, linter) expect_lint("c <- .3 + 4.5i", lint_msg, linter) expect_lint("d <- 6.7 + .8i", lint_msg, linter) expect_lint("d <- 6.7+.8i", lint_msg, linter) expect_lint("e <- .9e10", lint_msg, linter) }) lintr/tests/testthat/test-length_test_linter.R0000644000176200001440000000211414577052532021411 0ustar liggesuserstest_that("skips allowed usages", { linter <- length_test_linter() expect_lint("length(x) > 0", NULL, linter) expect_lint("length(DF[key == val, cols])", NULL, linter) }) test_that("blocks simple disallowed usages", { linter <- length_test_linter() lint_msg_stub <- rex::rex("Checking the length of a logical vector is likely a mistake. Did you mean ") expect_lint("length(x == 0)", rex::rex(lint_msg_stub, "`length(x) == 0`?"), linter) expect_lint("length(x == y)", rex::rex(lint_msg_stub, "`length(x) == y`?"), linter) expect_lint("length(x + y == 2)", rex::rex(lint_msg_stub, "`length(x+y) == 2`?"), linter) }) local({ ops <- c(lt = "<", lte = "<=", gt = ">", gte = ">=", eq = "==", neq = "!=") linter <- length_test_linter() lint_msg_stub <- rex::rex("Checking the length of a logical vector is likely a mistake. Did you mean ") patrick::with_parameters_test_that( "all logical operators detected", expect_lint( paste("length(x", op, "y)"), rex::rex("`length(x) ", op, " y`?"), linter ), op = ops, .test_name = names(ops) ) }) lintr/tests/testthat/test-object_name_linter.R0000644000176200001440000003041714577052532021346 0ustar liggesusers# styler: off test_that("styles are correctly identified", { do_style_check <- function(nms) lapply(unname(style_regexes), lintr:::check_style, nms = nms) # symbl UpC lowC snake SNAKE dot alllow ALLUP expect_identical(do_style_check("x"), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) expect_identical(do_style_check(".x"), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) expect_identical(do_style_check("X"), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) expect_identical(do_style_check("x."), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("X."), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("x_"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("X_"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("xy"), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) expect_identical(do_style_check("xY"), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("Xy"), list(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("XY"), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) expect_identical(do_style_check("x1"), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) expect_identical(do_style_check("X1"), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) expect_identical(do_style_check("x_y"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("X_Y"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("X.Y"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("x_2"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("X_2"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("x.2"), list(FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) expect_identical(do_style_check("X.2"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) # symbl UpC lowC snake SNAKE dot alllow ALLUP expect_identical(do_style_check("IHave1Cat"), list(FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("iHave1Cat"), list(FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("i_have_1_cat"), list(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("I_HAVE_1_CAT"), list(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("i.have.1.cat"), list(FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE)) expect_identical(do_style_check("ihave1cat"), list(FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE)) expect_identical(do_style_check("IHAVE1CAT"), list(FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)) expect_identical(do_style_check("I.HAVE_ONECAT"), list(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("."), list(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) expect_identical(do_style_check("%^%"), list(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)) }) # styler: on test_that("linter ignores some objects", { # names for which style check is ignored expect_lint("`%X%` <- t", NULL, object_name_linter("SNAKE_CASE")) # operator expect_lint("`%x%` <- t", NULL, object_name_linter("snake_case")) # operator expect_lint("`t.test` <- t", NULL, object_name_linter("UPPERCASE")) # std pkg expect_lint(".Deprecated('x')", NULL, object_name_linter("lowercase")) # std pkg expect_lint("print.foo <- t", NULL, object_name_linter("CamelCase")) # S3 generic expect_lint("names.foo <- t", NULL, object_name_linter("CamelCase")) # int generic expect_lint("sapply(x,f,USE.NAMES=T)", NULL, object_name_linter("snake_case")) # defined elsewhere expect_lint(".onLoad <- function(...) TRUE", NULL, object_name_linter("snake_case")) # namespace hooks, #500 expect_lint(".First <- function(...) TRUE", NULL, object_name_linter("snake_case")) # namespace hooks expect_lint("`%++%` <- `+`", NULL, object_name_linter("symbols")) # all-symbol operator expect_lint("`%<-%` <- `+`", NULL, object_name_linter("symbols")) # all-symbol operator #495 # S3 group generic, #1841 expect_lint( "`==.snake_case` <- function(a, b) unclass(a) == unclass(b)", NULL, object_name_linter("snake_case") ) }) test_that("linter returns correct linting", { lint_msg <- "Variable and function name style should match camelCase." linter <- object_name_linter("camelCase") expect_lint("myObject <- 123", NULL, linter) expect_lint("`myObject` <- 123", NULL, linter) expect_lint("my.confused_NAME <- 1;", list(message = lint_msg, line_number = 1L, column_number = 1L), linter) expect_lint("1 ->> read.data.frame;", list(message = lint_msg, line_number = 1L, column_number = 7L), linter) expect_lint( "object_name_linter <- function(...) {}", list(message = lint_msg, line_number = 1L, column_number = 1L), linter ) expect_lint( "Z = sapply('function', function(x=function(x){1}, b.a.z=F, ...){identity(b.a.z)}, USE.NAMES=TRUE)", list( list(message = lint_msg, line_number = 1L, column_number = 1L), list(message = lint_msg, line_number = 1L, column_number = 51L) ), linter ) expect_lint("blah", NULL, linter) expect_lint("invokeRestartInteractively", NULL, linter) expect_lint("camelCase", NULL, linter) expect_lint("camelCase()", NULL, linter) expect_lint("pack::camelCase", NULL, linter) expect_lint("pack:::camelCase", NULL, linter) expect_lint("a(camelCase = 1)", NULL, linter) expect_lint("a$b <- 1", NULL, linter) }) test_that("linter accepts vector of styles", { lint_msg <- "Variable and function name style should match camelCase or dotted.case." linter <- object_name_linter(styles = c("camelCase", "dotted.case")) expect_lint( c("var.one <- 1", "varTwo <- 2", "var_three <- 3"), list(message = lint_msg, line_number = 3L, column_number = 1L), linter ) }) test_that("dollar subsetting only lints the first expression", { # Regression test for #582 linter <- object_name_linter() lint_msg <- rex::rex("Variable and function name style should match snake_case or symbols.") expect_lint("my_var$MY_COL <- 42L", NULL, linter) expect_lint("MY_VAR$MY_COL <- 42L", lint_msg, linter) expect_lint("my_var@MY_SUB <- 42L", NULL, linter) expect_lint("MY_VAR@MY_SUB <- 42L", lint_msg, linter) }) patrick::with_parameters_test_that( "nested extraction only lints on the first symbol", expect_lint( sprintf("%s%sMY_SUB%sMY_COL <- 42L", if (should_lint) "MY_VAR" else "my_var", op1, op2), if (should_lint) rex::rex("Variable and function name style should match snake_case or symbols."), object_name_linter() ), .cases = within( expand.grid(should_lint = c(TRUE, FALSE), op1 = c("$", "@"), op2 = c("$", "@"), stringsAsFactors = FALSE), { .test_name <- sprintf("(should lint? %s, op1=%s, op2=%s)", should_lint, op1, op2) } ) ) test_that("assignment targets of compound lhs are correctly identified", { linter <- object_name_linter() lint_msg <- "Variable and function name style should match snake_case or symbols." # (recursive) [, $, and [[ subsetting expect_lint("good_name[badName] <- badName2", NULL, linter) expect_lint("good_name[1L][badName] <- badName2", NULL, linter) expect_lint("good_name[[badName]] <- badName2", NULL, linter) expect_lint("good_name[[1L]][[badName]] <- badName2", NULL, linter) expect_lint("good_name[[fun(badName)]] <- badName2", NULL, linter) expect_lint("good_name[[badName]]$badName2 <- badName3", NULL, linter) expect_lint("good_name$badName[[badName2]][badName3]$badName4 <- badName5", NULL, linter) expect_lint("badName[badName] <- badName2", lint_msg, linter) expect_lint("badName[1L][badName] <- badName2", lint_msg, linter) expect_lint("badName[[badName]] <- badName2", lint_msg, linter) expect_lint("badName[[1L]][[badName]] <- badName2", lint_msg, linter) expect_lint("badName[[fun(badName)]] <- badName2", lint_msg, linter) expect_lint("badName[[badName]]$badName2 <- badName3", lint_msg, linter) expect_lint("badName$badName[[badName2]][badName3]$badName4 <- badName5", lint_msg, linter) # setters expect_lint("setter(badName) <- good_name", lint_msg, linter) expect_lint("setter(good_name[[badName]]) <- good_name2", NULL, linter) # quotation expect_lint("\"good_name\" <- 42", NULL, linter) expect_lint("\"badName\" <- 42", lint_msg, linter) expect_lint("'good_name' <- 42", NULL, linter) expect_lint("'badName' <- 42", lint_msg, linter) expect_lint("`good_name` <- 42", NULL, linter) expect_lint("`badName` <- 42", lint_msg, linter) # subsetting with quotation expect_lint("good_name$\"badName\" <- 42", NULL, linter) expect_lint("good_name$'badName' <- 42", NULL, linter) expect_lint("badName$\"good_name\" <- 42", lint_msg, linter) expect_lint("badName$'good_name' <- 42", lint_msg, linter) expect_lint("`badName`$\"good_name\" <- 42", lint_msg, linter) expect_lint("`badName`$'good_name' <- 42", lint_msg, linter) }) test_that("object_name_linter won't fail if an imported namespace is unavailable", { expect_length( lint_package(test_path("dummy_packages", "missing_dep"), linters = object_name_linter(), parse_settings = FALSE), 3L ) }) test_that("object_name_linter supports custom regexes", { # disables default styles linter <- object_name_linter( regexes = c(shinyModule = rex::rex(start, lower, zero_or_more(alnum), "UI" %or% "Server", end)) ) msg <- rex::rex("Variable and function name style should match shinyModule.") linter2 <- object_name_linter( styles = c("snake_case", "symbols"), regexes = c(shinyModule = rex::rex(start, lower, zero_or_more(alnum), "UI" %or% "Server", end)) ) msg2 <- rex::rex("Variable and function name style should match snake_case, symbols or shinyModule.") # Can't allow 0 styles expect_error( object_name_linter(NULL), rex::rex("At least one style must be specified using `styles` or `regexes`.") ) expect_lint( trim_some(' snake_case <- 42L "%+%" <- function(...) ..1 + ..2 myModuleUI <- function(id) { # blah } myModuleServer <- function(id) { # blah } myBadName <- 20L '), list( list(line_number = 1L, message = msg), list(line_number = 2L, message = msg), # argument "id" is linted if we only allow shinyModule names list(line_number = 4L, column_number = 24L, message = msg), list(line_number = 8L, column_number = 28L, message = msg), list(line_number = 12L, message = msg) ), linter ) expect_lint( trim_some(' snake_case <- 42L "%+%" <- function(...) ..1 + ..2 myModuleUI <- function(id) { # blah } myModuleServer <- function(id) { # blah } myBadName <- 20L '), list(line_number = 12L, message = msg2), linter2 ) # Default regex naming works expect_lint( trim_some(" a <- 42L b <- 1L c <- 2L "), list(line_number = 3L, message = rex::rex("Variable and function name style should match /^a$/ or /^b$/.")), object_name_linter(regexes = c("^a$", "^b$")) ) expect_lint( trim_some(" a <- 42L b <- 1L c <- 2L "), list(line_number = 3L, message = rex::rex("Variable and function name style should match a or /^b$/.")), object_name_linter(regexes = c(a = "^a$", "^b$")) ) }) test_that("complex LHS of := doesn't cause false positive", { # "_l" would be included under previous logic which tried ancestor::expr[ASSIGN] for STR_CONST, # but only parent::expr[ASSIGN] is needed for strings. expect_lint('dplyr::mutate(df, !!paste0(v, "_l") := df$a * 2)', NULL, object_name_linter()) }) test_that("function shorthand also lints", { skip_if_not_r_version("4.1.0") expect_lint("aBc <- \\() NULL", "function name style", object_name_linter()) }) lintr/tests/testthat/test-for_loop_index_linter.R0000644000176200001440000000174314577052532022106 0ustar liggesuserstest_that("for_loop_index_linter skips allowed usages", { linter <- for_loop_index_linter() expect_lint("for (xi in x) {}", NULL, linter) # this is OK, so not every symbol is problematic expect_lint("for (col in DF$col) {}", NULL, linter) expect_lint("for (col in S4@col) {}", NULL, linter) expect_lint("for (col in DT[, col]) {}", NULL, linter) # make sure symbol check is scoped expect_lint( trim_some(" { for (i in 1:10) { 42L } i <- 7L } "), NULL, linter ) }) test_that("for_loop_index_linter blocks simple disallowed usages", { linter <- for_loop_index_linter() lint_msg <- "Don't re-use any sequence symbols as the index symbol in a for loop" expect_lint("for (x in x) {}", lint_msg, linter) # these also overwrite a variable in calling scope expect_lint("for (x in foo(x)) {}", lint_msg, linter) # arbitrary nesting expect_lint("for (x in foo(bar(y, baz(2, x)))) {}", lint_msg, linter) }) lintr/tests/testthat/test-expect_comparison_linter.R0000644000176200001440000000311114577055450022613 0ustar liggesuserstest_that("expect_comparison_linter skips allowed usages", { linter <- expect_comparison_linter() # there's no expect_ne() for this operator expect_lint("expect_true(x != y)", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(x != y)", NULL, linter) # multiple comparisons are OK expect_lint("expect_true(x > y || x > z)", NULL, linter) # expect_gt() and friends don't have an info= argument expect_lint("expect_true(x > y, info = 'x is bigger than y yo')", NULL, linter) # expect_true() used incorrectly, and as executed the first argument is not a lint expect_lint("expect_true(is.count(n_draws), n_draws > 1)", NULL, linter) }) test_that("expect_comparison_linter blocks simple disallowed usages", { expect_lint( "expect_true(x > y)", rex::rex("expect_gt(x, y) is better than expect_true(x > y)."), expect_comparison_linter() ) # namespace qualification is irrelevant expect_lint( "testthat::expect_true(x < y)", rex::rex("expect_lt(x, y) is better than expect_true(x < y)."), expect_comparison_linter() ) expect_lint( "expect_true(foo(x) >= y[[2]])", rex::rex("expect_gte(x, y) is better than expect_true(x >= y)."), expect_comparison_linter() ) expect_lint( "expect_true(x <= y)", rex::rex("expect_lte(x, y) is better than expect_true(x <= y)."), expect_comparison_linter() ) expect_lint( "expect_true(x == (y == 2))", rex::rex("expect_identical(x, y) is better than expect_true(x == y)."), expect_comparison_linter() ) }) lintr/tests/testthat/test-sort_linter.R0000644000176200001440000000645214577052532020071 0ustar liggesuserstest_that("sort_linter skips allowed usages", { linter <- sort_linter() expect_lint("order(y)", NULL, linter) expect_lint("y[order(x)]", NULL, linter) # If another function is intercalated, don't fail expect_lint("x[c(order(x))]", NULL, linter) expect_lint("x[order(y, x)]", NULL, linter) expect_lint("x[order(x, y)]", NULL, linter) # pretty sure this never makes sense, but test anyway expect_lint("x[order(y, na.last = x)]", NULL, linter) }) test_that("sort_linter blocks simple disallowed usages", { linter <- sort_linter() lint_message <- rex::rex("sort(", anything, ") is better than") expect_lint("x[order(x)]", lint_message, linter) # Works with extra args in order() expect_lint("x[order(x, decreasing = TRUE)]", lint_message, linter) # ...even in disorder expect_lint("x[order(decreasing = TRUE, x)]", lint_message, linter) }) test_that("sort_linter produces customized warning message", { linter <- sort_linter() expect_lint( "y[order(y)]", rex::rex("sort(y, na.last = TRUE) is better than y[order(y)]."), linter ) # We capture the correct variable symbol expect_lint( "y + x[order(x)]", rex::rex("sort(x, na.last = TRUE) is better than x[order(x)]."), linter ) # Default na.last = TRUE is overwritten if na.last is already provided expect_lint( "x[order(x, na.last = FALSE)]", rex::rex("sort(x, na.last = FALSE) is better than x[order(x, na.last = FALSE)]."), linter ) expect_lint( "x[order(x, decreasing = FALSE)]", rex::rex("sort(x, decreasing = FALSE, na.last = TRUE) is better than x[order(x, decreasing = FALSE)]."), linter ) expect_lint( "f()[order(f())]", rex::rex("sort(f(), na.last = TRUE) is better than f()[order(f())]"), linter ) }) test_that("sort_linter works with multiple lints in a single expression", { linter <- sort_linter() expect_lint( "c( x[order(x)], y[order(y, decreasing = TRUE, na.last = FALSE)] )", list( rex::rex("sort(x, na.last = TRUE) is better than x[order(x)]."), rex::rex( "sort(y, decreasing = TRUE, na.last = FALSE)", anything, "y[order(y, decreasing = TRUE, na.last = FALSE)]." ) ), linter ) }) test_that("sort_linter skips usages calling sort arguments", { linter <- sort_linter() # any arguments to sort --> not compatible expect_lint("sort(x, decreasing = TRUE) == x", NULL, linter) expect_lint("sort(x, na.last = TRUE) != x", NULL, linter) expect_lint("sort(x, method_arg = TRUE) == x", NULL, linter) }) test_that("sort_linter skips when inputs don't match", { linter <- sort_linter() expect_lint("sort(x) == y", NULL, linter) expect_lint("sort(x) == foo(x)", NULL, linter) expect_lint("sort(foo(x)) == x", NULL, linter) }) test_that("sort_linter blocks simple disallowed usages", { linter <- sort_linter() unsorted_msg <- rex::rex("Use is.unsorted(x) to test the unsortedness of a vector.") sorted_msg <- rex::rex("Use !is.unsorted(x) to test the sortedness of a vector.") expect_lint("sort(x) == x", sorted_msg, linter) # argument order doesn't matter expect_lint("x == sort(x)", sorted_msg, linter) # inverted version expect_lint("sort(x) != x", unsorted_msg, linter) # expression matching expect_lint("sort(foo(x)) == foo(x)", sorted_msg, linter) }) lintr/tests/testthat/test-outer_negation_linter.R0000644000176200001440000000332714577052532022122 0ustar liggesuserstest_that("outer_negation_linter skips allowed usages", { linter <- outer_negation_linter() expect_lint("x <- any(y)", NULL, linter) expect_lint("y <- all(z)", NULL, linter) # extended usage of any is not covered expect_lint("any(!a & b)", NULL, linter) expect_lint("all(a | !b)", NULL, linter) expect_lint("any(a, b)", NULL, linter) expect_lint("all(b, c)", NULL, linter) expect_lint("any(!a, b)", NULL, linter) expect_lint("all(a, !b)", NULL, linter) expect_lint("any(a, !b, na.rm = TRUE)", NULL, linter) # ditto when na.rm is passed quoted expect_lint("any(a, !b, 'na.rm' = TRUE)", NULL, linter) }) test_that("outer_negation_linter blocks simple disallowed usages", { linter <- outer_negation_linter() expect_lint( "any(!x)", rex::rex("!all(x) is better than any(!x)"), linter ) expect_lint( "all(!foo(x))", rex::rex("!any(x) is better than all(!x)"), linter ) # na.rm doesn't change the recommendation expect_lint( "any(!x, na.rm = TRUE)", rex::rex("!all(x) is better than any(!x)"), linter ) # also catch nested usage expect_lint( "all(!(x + y))", rex::rex("!any(x) is better than all(!x)"), linter ) # catch when all inputs are negated expect_lint( "any(!x, !y)", rex::rex("!all(x) is better than any(!x)"), linter ) expect_lint( "all(!x, !y, na.rm = TRUE)", rex::rex("!any(x) is better than all(!x)"), linter ) }) test_that("outer_negation_linter doesn't trigger on empty calls", { # minimal version of issue expect_lint("any()", NULL, outer_negation_linter()) # closer to what was is practically relevant, as another regression test expect_lint("x %>% any()", NULL, outer_negation_linter()) }) lintr/tests/testthat/lints0000644000176200001440000005374314122042072015466 0ustar liggesusers[R/AES.R:30:34:](https://github.com///blob//R/AES.R#L30) *style:* **Put spaces around all infix operators.** ```r for (i in seq_len(len/16)) { ~^~ ``` [R/AES.R:31:26:](https://github.com///blob//R/AES.R#L31) *style:* **Put spaces around all infix operators.** ```r ind <- (i-1)*16 + 1:16 ~^~ ``` [R/AES.R:31:29:](https://github.com///blob//R/AES.R#L31) *style:* **Put spaces around all infix operators.** ```r ind <- (i-1)*16 + 1:16 ~^~ ``` [R/AES.R:39:29:](https://github.com///blob//R/AES.R#L39) *style:* **Put spaces around all infix operators.** ```r result <- raw(16*blocks) ~^~ ``` [R/AES.R:42:26:](https://github.com///blob//R/AES.R#L42) *style:* **Put spaces around all infix operators.** ```r result[16*(i-1)+1:16] <- IV ~^~ ``` [R/AES.R:42:27:](https://github.com///blob//R/AES.R#L42) *style:* **Place a space before left parenthesis, except in a function call.** ```r result[16*(i-1)+1:16] <- IV ^ ``` [R/AES.R:42:29:](https://github.com///blob//R/AES.R#L42) *style:* **Put spaces around all infix operators.** ```r result[16*(i-1)+1:16] <- IV ~^~ ``` [R/AES.R:42:32:](https://github.com///blob//R/AES.R#L42) *style:* **Put spaces around all infix operators.** ```r result[16*(i-1)+1:16] <- IV ~^~ ``` [R/AES.R:64:34:](https://github.com///blob//R/AES.R#L64) *style:* **Put spaces around all infix operators.** ```r for (i in seq_len(len/16)) { ~^~ ``` [R/AES.R:65:26:](https://github.com///blob//R/AES.R#L65) *style:* **Put spaces around all infix operators.** ```r ind <- (i-1)*16 + 1:16 ~^~ ``` [R/AES.R:65:29:](https://github.com///blob//R/AES.R#L65) *style:* **Put spaces around all infix operators.** ```r ind <- (i-1)*16 + 1:16 ~^~ ``` [R/AES.R:72:1:](https://github.com///blob//R/AES.R#L72) *style:* **Trailing whitespace is superfluous.** ```r ^~~~~~~~ ``` [R/AES.R:89:1:](https://github.com///blob//R/AES.R#L89) *style:* **Trailing blank lines are superfluous.** ```r ^ ``` [R/digest.R:54:1:](https://github.com///blob//R/digest.R#L54) *style:* **lines should not be more than 80 characters.** ```r stop("Argument object must be of type character or raw vector if serialize is FALSE") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [R/digest.R:71:27:](https://github.com///blob//R/digest.R#L71) *style:* **Put spaces around all infix operators.** ```r algoint <- algoint+100 ~^~ ``` [R/hmac.R:4:15:](https://github.com///blob//R/hmac.R#L4) *style:* **Only use double-quotes.** ```r UseMethod('makeRaw') ^~~~~~~~~ ``` [R/hmac.R:13:33:](https://github.com///blob//R/hmac.R#L13) *style:* **Opening curly braces should never go on their own line and should always be followed by a new line.** ```r function(i) { substr(x, i, i + 1) }) ^ ``` [R/hmac.R:13:55:](https://github.com///blob//R/hmac.R#L13) *style:* **Closing curly-braces should always be on their own line, unless it's followed by an else.** ```r function(i) { substr(x, i, i + 1) }) ^ ``` [R/hmac.R:29:20:](https://github.com///blob//R/hmac.R#L29) *style:* **Trailing whitespace is superfluous.** ```r blocksize <- 64 ^ ``` [R/hmac.R:33:31:](https://github.com///blob//R/hmac.R#L33) *style:* **Opening curly braces should never go on their own line and should always be followed by a new line.** ```r if(length(k) > blocksize) {# not while() ^ ``` [R/hmac.R:33:45:](https://github.com///blob//R/hmac.R#L33) *style:* **Trailing whitespace is superfluous.** ```r if(length(k) > blocksize) {# not while() ^ ``` [R/hmac.R:34:11:](https://github.com///blob//R/hmac.R#L34) *style:* **Put spaces around all infix operators.** ```r k <-digest(k, algo=algo, serialize=FALSE,raw=TRUE) ^~~ ``` [R/hmac.R:47:5:](https://github.com///blob//R/hmac.R#L47) *style:* **Words within variable and function names should be separated by '_' rather than '.'.** ```r i.xored.key <- xor(padded.key, makeRaw(0x36)) ^~~~~~~~~~~ ``` [R/hmac.R:48:34:](https://github.com///blob//R/hmac.R#L48) *style:* **Words within variable and function names should be separated by '_' rather than '.'.** ```r character.digest <- digest(c(i.xored.key, makeRaw(object)), ^~~~~~~~~~~ ``` [R/hmac.R:51:5:](https://github.com///blob//R/hmac.R#L51) *style:* **Words within variable and function names should be separated by '_' rather than '.'.** ```r o.xored.key <- xor(padded.key, makeRaw(0x5c)) ^~~~~~~~~~~ ``` [R/hmac.R:52:1:](https://github.com///blob//R/hmac.R#L52) *style:* **lines should not be more than 80 characters.** ```r result <- digest(c(o.xored.key, raw.digest), algo=algo, serialize=serialize, ...) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [R/hmac.R:52:24:](https://github.com///blob//R/hmac.R#L52) *style:* **Words within variable and function names should be separated by '_' rather than '.'.** ```r result <- digest(c(o.xored.key, raw.digest), algo=algo, serialize=serialize, ...) ^~~~~~~~~~~ ``` [tests/AESTest.R:8:1:](https://github.com///blob//tests/AESTest.R#L8) *style:* **Variable and function names should be all lowercase.** ```r hextextToRaw <- function(text) { ^~~~~~~~~~~~ ``` [tests/AESTest.R:9:1:](https://github.com///blob//tests/AESTest.R#L9) *style:* **lines should not be more than 80 characters.** ```r vals <- matrix(as.integer(as.hexmode(strsplit(text, "")[[1]])), ncol=2, byrow=TRUE) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/AESTest.R:14:20:](https://github.com///blob//tests/AESTest.R#L14) *style:* **Variable and function names should be all lowercase.** ```r plaintext <- hextextToRaw("00112233445566778899aabbccddeeff") ^~~~~~~~~~~~ ``` [tests/AESTest.R:16:20:](https://github.com///blob//tests/AESTest.R#L16) *style:* **Variable and function names should be all lowercase.** ```r aes128key <- hextextToRaw("000102030405060708090a0b0c0d0e0f") ^~~~~~~~~~~~ ``` [tests/AESTest.R:17:20:](https://github.com///blob//tests/AESTest.R#L17) *style:* **Variable and function names should be all lowercase.** ```r aes128output <- hextextToRaw("69c4e0d86a7b0430d8cdb78070b4c55a") ^~~~~~~~~~~~ ``` [tests/AESTest.R:24:1:](https://github.com///blob//tests/AESTest.R#L24) *style:* **lines should not be more than 80 characters.** ```r aes192key <- hextextToRaw("000102030405060708090a0b0c0d0e0f1011121314151617") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/AESTest.R:24:20:](https://github.com///blob//tests/AESTest.R#L24) *style:* **Variable and function names should be all lowercase.** ```r aes192key <- hextextToRaw("000102030405060708090a0b0c0d0e0f1011121314151617") ^~~~~~~~~~~~ ``` [tests/AESTest.R:25:20:](https://github.com///blob//tests/AESTest.R#L25) *style:* **Variable and function names should be all lowercase.** ```r aes192output <- hextextToRaw("dda97ca4864cdfe06eaf70a0ec0d7191") ^~~~~~~~~~~~ ``` [tests/AESTest.R:32:1:](https://github.com///blob//tests/AESTest.R#L32) *style:* **lines should not be more than 80 characters.** ```r aes256key <- hextextToRaw("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/AESTest.R:32:20:](https://github.com///blob//tests/AESTest.R#L32) *style:* **Variable and function names should be all lowercase.** ```r aes256key <- hextextToRaw("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") ^~~~~~~~~~~~ ``` [tests/AESTest.R:33:21:](https://github.com///blob//tests/AESTest.R#L33) *style:* **Variable and function names should be all lowercase.** ```r aes256output <- hextextToRaw("8ea2b7ca516745bfeafc49904b496089") ^~~~~~~~~~~~ ``` [tests/AESTest.R:42:14:](https://github.com///blob//tests/AESTest.R#L42) *style:* **Variable and function names should be all lowercase.** ```r plaintext <- hextextToRaw(paste("6bc1bee22e409f96e93d7e117393172a", ^~~~~~~~~~~~ ``` [tests/AESTest.R:46:8:](https://github.com///blob//tests/AESTest.R#L46) *style:* **Variable and function names should be all lowercase.** ```r key <- hextextToRaw("2b7e151628aed2a6abf7158809cf4f3c") ^~~~~~~~~~~~ ``` [tests/AESTest.R:48:17:](https://github.com///blob//tests/AESTest.R#L48) *style:* **Variable and function names should be all lowercase.** ```r ecb128output <- hextextToRaw(paste("3ad77bb40d7a3660a89ecaf32466ef97", ^~~~~~~~~~~~ ``` [tests/AESTest.R:58:17:](https://github.com///blob//tests/AESTest.R#L58) *style:* **Variable and function names should be all lowercase.** ```r cbc128output <- hextextToRaw(paste("7649abac8119b246cee98e9b12e9197d", ^~~~~~~~~~~~ ``` [tests/AESTest.R:62:7:](https://github.com///blob//tests/AESTest.R#L62) *style:* **Variable and function names should be all lowercase.** ```r iv <- hextextToRaw("000102030405060708090a0b0c0d0e0f") ^~~~~~~~~~~~ ``` [tests/AESTest.R:70:17:](https://github.com///blob//tests/AESTest.R#L70) *style:* **Variable and function names should be all lowercase.** ```r ctr128output <- hextextToRaw(paste("874d6191b620e3261bef6864990db6ce", ^~~~~~~~~~~~ ``` [tests/AESTest.R:74:7:](https://github.com///blob//tests/AESTest.R#L74) *style:* **Variable and function names should be all lowercase.** ```r iv <- hextextToRaw("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") ^~~~~~~~~~~~ ``` [tests/digestTest.R:67:13:](https://github.com///blob//tests/digestTest.R#L67) *style:* **Put spaces around all infix operators.** ```r sha512Input <-c( ^~~ ``` [tests/digestTest.R:72:1:](https://github.com///blob//tests/digestTest.R#L72) *style:* **lines should not be more than 80 characters.** ```r "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/digestTest.R:73:1:](https://github.com///blob//tests/digestTest.R#L73) *style:* **lines should not be more than 80 characters.** ```r "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:7:17:](https://github.com///blob//tests/hmacTest.R#L7) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "md5") ^~~~~~ ``` [tests/hmacTest.R:7:25:](https://github.com///blob//tests/hmacTest.R#L7) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "md5") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:8:11:](https://github.com///blob//tests/hmacTest.R#L8) *style:* **Only use double-quotes.** ```r target <- '750c783e6ab0b503eaa86e310a5db738' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:12:17:](https://github.com///blob//tests/hmacTest.R#L12) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "md5") ^~~~~~ ``` [tests/hmacTest.R:12:25:](https://github.com///blob//tests/hmacTest.R#L12) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "md5") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:13:11:](https://github.com///blob//tests/hmacTest.R#L13) *style:* **Only use double-quotes.** ```r target <- '750c783e6ab0b503eaa86e310a5db738' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:17:32:](https://github.com///blob//tests/hmacTest.R#L17) *style:* **Only use double-quotes.** ```r current <- hmac(rep(0x0b, 16), 'Hi There', "md5") ^~~~~~~~~~ ``` [tests/hmacTest.R:18:11:](https://github.com///blob//tests/hmacTest.R#L18) *style:* **Only use double-quotes.** ```r target <- '9294727a3638bb1c13f48ef8158bfc9d' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:23:11:](https://github.com///blob//tests/hmacTest.R#L23) *style:* **Only use double-quotes.** ```r target <- '56be34521d144c88dbb8c733f0e8b3f6' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:29:1:](https://github.com///blob//tests/hmacTest.R#L29) *style:* **lines should not be more than 80 characters.** ```r current <- hmac('ThisKeyIsLongerThan64BytesAndThereforeLongerThanTheHashDigestByFour', 'what do ya want for nothing?', "md5") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:29:17:](https://github.com///blob//tests/hmacTest.R#L29) *style:* **Only use double-quotes.** ```r current <- hmac('ThisKeyIsLongerThan64BytesAndThereforeLongerThanTheHashDigestByFour', 'what do ya want for nothing?', "md5") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:29:88:](https://github.com///blob//tests/hmacTest.R#L29) *style:* **Only use double-quotes.** ```r current <- hmac('ThisKeyIsLongerThan64BytesAndThereforeLongerThanTheHashDigestByFour', 'what do ya want for nothing?', "md5") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:30:11:](https://github.com///blob//tests/hmacTest.R#L30) *style:* **Only use double-quotes.** ```r target <- '109af8ddd9cccbf8927d354da8be892b' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:36:17:](https://github.com///blob//tests/hmacTest.R#L36) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "sha1") ^~~~~~ ``` [tests/hmacTest.R:36:25:](https://github.com///blob//tests/hmacTest.R#L36) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "sha1") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:37:11:](https://github.com///blob//tests/hmacTest.R#L37) *style:* **Only use double-quotes.** ```r target <- 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:41:32:](https://github.com///blob//tests/hmacTest.R#L41) *style:* **Only use double-quotes.** ```r current <- hmac(rep(0x0b, 16), 'Hi There', "sha1") ^~~~~~~~~~ ``` [tests/hmacTest.R:42:11:](https://github.com///blob//tests/hmacTest.R#L42) *style:* **Only use double-quotes.** ```r target <- '675b0b3a1b4ddf4e124872da6c2f632bfed957e9' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:47:11:](https://github.com///blob//tests/hmacTest.R#L47) *style:* **Only use double-quotes.** ```r target <- 'd730594d167e35d5956fd8003d0db3d3f46dc7bb' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:52:17:](https://github.com///blob//tests/hmacTest.R#L52) *style:* **Only use double-quotes.** ```r current <- hmac('key', 'The quick brown fox jumps over the lazy dog', 'sha256') ^~~~~ ``` [tests/hmacTest.R:52:24:](https://github.com///blob//tests/hmacTest.R#L52) *style:* **Only use double-quotes.** ```r current <- hmac('key', 'The quick brown fox jumps over the lazy dog', 'sha256') ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:52:71:](https://github.com///blob//tests/hmacTest.R#L52) *style:* **Only use double-quotes.** ```r current <- hmac('key', 'The quick brown fox jumps over the lazy dog', 'sha256') ^~~~~~~~ ``` [tests/hmacTest.R:53:11:](https://github.com///blob//tests/hmacTest.R#L53) *style:* **Only use double-quotes.** ```r target <- 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:63:17:](https://github.com///blob//tests/hmacTest.R#L63) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "sha512") ^~~~~~ ``` [tests/hmacTest.R:63:25:](https://github.com///blob//tests/hmacTest.R#L63) *style:* **Only use double-quotes.** ```r current <- hmac('Jefe', 'what do ya want for nothing?', "sha512") ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:64:1:](https://github.com///blob//tests/hmacTest.R#L64) *style:* **lines should not be more than 80 characters.** ```r target <- '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:64:11:](https://github.com///blob//tests/hmacTest.R#L64) *style:* **Only use double-quotes.** ```r target <- '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:68:17:](https://github.com///blob//tests/hmacTest.R#L68) *style:* **Only use double-quotes.** ```r current <- hmac('love', 'message', 'sha512') ^~~~~~ ``` [tests/hmacTest.R:68:25:](https://github.com///blob//tests/hmacTest.R#L68) *style:* **Only use double-quotes.** ```r current <- hmac('love', 'message', 'sha512') ^~~~~~~~~ ``` [tests/hmacTest.R:68:36:](https://github.com///blob//tests/hmacTest.R#L68) *style:* **Only use double-quotes.** ```r current <- hmac('love', 'message', 'sha512') ^~~~~~~~ ``` [tests/hmacTest.R:69:1:](https://github.com///blob//tests/hmacTest.R#L69) *style:* **lines should not be more than 80 characters.** ```r target <- 'f955821b3f6161673eb20985c677e3dc101860cafef3321ee31641acad9fcd85d9c7d3481ed3e4e1f4fa7af41d7e6ea9606d51e9bc7205d0091a4ee87d90fb9c' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:69:11:](https://github.com///blob//tests/hmacTest.R#L69) *style:* **Only use double-quotes.** ```r target <- 'f955821b3f6161673eb20985c677e3dc101860cafef3321ee31641acad9fcd85d9c7d3481ed3e4e1f4fa7af41d7e6ea9606d51e9bc7205d0091a4ee87d90fb9c' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:74:1:](https://github.com///blob//tests/hmacTest.R#L74) *style:* **lines should not be more than 80 characters.** ```r current <- hmac('verylongkeyinfactlongerthanthedigestandthereforehashedbyitselfonlyifwegettothislengthsoonpleasepleasebutiddoesnotlooksoogoodahthereweare', 'message', 'sha512') ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:74:17:](https://github.com///blob//tests/hmacTest.R#L74) *style:* **Only use double-quotes.** ```r current <- hmac('verylongkeyinfactlongerthanthedigestandthereforehashedbyitselfonlyifwegettothislengthsoonpleasepleasebutiddoesnotlooksoogoodahthereweare', 'message', 'sha512') ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:74:157:](https://github.com///blob//tests/hmacTest.R#L74) *style:* **Only use double-quotes.** ```r current <- hmac('verylongkeyinfactlongerthanthedigestandthereforehashedbyitselfonlyifwegettothislengthsoonpleasepleasebutiddoesnotlooksoogoodahthereweare', 'message', 'sha512') ^~~~~~~~~ ``` [tests/hmacTest.R:74:168:](https://github.com///blob//tests/hmacTest.R#L74) *style:* **Only use double-quotes.** ```r current <- hmac('verylongkeyinfactlongerthanthedigestandthereforehashedbyitselfonlyifwegettothislengthsoonpleasepleasebutiddoesnotlooksoogoodahthereweare', 'message', 'sha512') ^~~~~~~~ ``` [tests/hmacTest.R:75:1:](https://github.com///blob//tests/hmacTest.R#L75) *style:* **lines should not be more than 80 characters.** ```r target <- '54b274484a8b8a371c8ae898f453d37c90d488b0c88c89dd705de06b18263d50c28069f1f778c8a997a61a4d53a06bc8e8719ad11a553c49140bb93a4636882e' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:75:11:](https://github.com///blob//tests/hmacTest.R#L75) *style:* **Only use double-quotes.** ```r target <- '54b274484a8b8a371c8ae898f453d37c90d488b0c88c89dd705de06b18263d50c28069f1f778c8a997a61a4d53a06bc8e8719ad11a553c49140bb93a4636882e' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` [tests/hmacTest.R:78:1:](https://github.com///blob//tests/hmacTest.R#L78) *style:* **Trailing blank lines are superfluous.** ```r ^ ``` lintr/tests/testthat/test-parse_exclusions.R0000644000176200001440000000742314457657444021124 0ustar liggesuserswithr::local_options(list( lintr.exclude = "#TeSt_NoLiNt", lintr.exclude_start = "#TeSt_NoLiNt_StArT", lintr.exclude_end = "#TeSt_NoLiNt_EnD" )) test_that("it returns an empty list if there are no exclusions", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = trim_some(" this is a test ")) expect_identical(lintr:::parse_exclusions(t1), list()) }) test_that("it returns the line if one line is excluded", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = trim_some(" this is #TeSt_NoLiNt a test ")) expect_identical(lintr:::parse_exclusions(t1), list(2L)) t2 <- withr::local_tempfile(lines = trim_some(" this is #TeSt_NoLiNt a test #TeSt_NoLiNt ")) expect_identical(lintr:::parse_exclusions(t2), list(c(2L, 4L))) }) test_that("it supports specific linter exclusions", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = trim_some(" this is #TeSt_NoLiNt: my_linter. a test ")) expect_identical(lintr:::parse_exclusions(t1), list(my_linter = 2L)) t2 <- withr::local_tempfile(lines = trim_some(" this is #TeSt_NoLiNt: my_linter. a test #TeSt_NoLiNt: my_linter2. ")) expect_identical(lintr:::parse_exclusions(t2), list(my_linter = 2L, my_linter2 = 4L)) t3 <- withr::local_tempfile(lines = trim_some(" this is #TeSt_NoLiNt: my_linter. another useful #TeSt_NoLiNt: my_linter. test testing #TeSt_NoLiNt: my_linter2. ")) expect_identical(lintr:::parse_exclusions(t3), list(my_linter = c(2L, 4L), my_linter2 = 6L)) }) test_that("it supports multiple linter exclusions", { t1 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt is #TeSt_NoLiNt: a, b. a thorough #TeSt_NoLiNt_StArT: a, b, c. test #TeSt_NoLiNt of #TeSt_NoLiNt_EnD all features #TeSt_NoLiNt_StArT a, b, c interleaved #TeSt_NoLiNt a, b with each #TeSt_NoLiNt_EnD other ")) expect_identical(lintr:::parse_exclusions(t1), list( a = c(2L, 4L:6L), b = c(2L, 4L:6L), c = 4L:6L, c(1L, 5L, 8L:11L) )) }) test_that("it supports overlapping exclusion ranges", { t1 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt_StArT: a. is a #TeSt_NoLiNt_StArT: b. test with #TeSt_NoLiNt_EnD overlapping #TeSt_NoLiNt_EnD ranges ")) expect_identical(lintr:::parse_exclusions(t1), list( a = 1L:5L, b = 3L:6L )) }) test_that("it returns all lines between start and end", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt_StArT is a #TeSt_NoLiNt_EnD test ")) expect_identical(lintr:::parse_exclusions(t1), list(c(1L, 2L, 3L))) t2 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt_StArT is a #TeSt_NoLiNt_EnD test of the #TeSt_NoLiNt_StArT emergency #TeSt_NoLiNt_EnD broadcast system ")) expect_identical(lintr:::parse_exclusions(t2), list(c(1L, 2L, 3L, 6L, 7L))) }) test_that("it ignores exclude coverage lines within start and end", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = c( "this #TeSt_NoLiNt_StArT", "is #TeSt_NoLiNt", "a #TeSt_NoLiNt_EnD", "test" )) expect_identical(lintr:::parse_exclusions(t1), list(c(1L, 2L, 3L))) }) test_that("it throws an error if start and end are unpaired", { lintr:::read_settings(NULL) t1 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt_StArT is #TeSt_NoLiNt a test ")) expect_error(lintr:::parse_exclusions(t1), "but only") t2 <- withr::local_tempfile(lines = trim_some(" this #TeSt_NoLiNt_StArT is #TeSt_NoLiNt_EnD a #TeSt_NoLiNt_EnD test ")) expect_error(lintr:::parse_exclusions(t2), "but only") }) lintr/tests/testthat/test-sarif_output.R0000644000176200001440000000231014517602346020234 0ustar liggesuserstest_that("`sarif_output` produces expected error", { skip_if_not_installed("jsonlite") l <- lint(text = "x = 1", linters = assignment_linter()) expect_error(sarif_output(l), "Package path needs to be a relative path", fixed = TRUE) }) test_that("`sarif_output` writes expected files", { skip_if_not_installed("jsonlite") l <- lint_package( test_path("dummy_packages", "missing_dep"), linters = object_length_linter(), parse_settings = FALSE ) withr::with_tempdir({ sarif_output(l) expect_true(file.exists("lintr_results.sarif")) }) withr::with_tempdir({ sarif_output(l, filename = "myfile.sarif") expect_true(file.exists("myfile.sarif")) }) }) test_that("`sarif_output` produces valid files", { skip_if_not_installed("jsonlite") l <- lint_package( test_path("dummy_packages", "clean"), linters = default_linters, parse_settings = FALSE ) withr::with_tempdir({ sarif <- sarif_output(l) sarif <- jsonlite::fromJSON( "lintr_results.sarif", simplifyVector = TRUE, simplifyDataFrame = FALSE, simplifyMatrix = FALSE ) expect_false(is.null(sarif$runs)) expect_false(is.null(sarif$runs[[1L]]$results)) }) }) lintr/tests/testthat/test-expect_true_false_linter.R0000644000176200001440000000157514577052532022604 0ustar liggesuserstest_that("expect_true_false_linter skips allowed usages", { # expect_true is a scalar test; testing logical vectors with expect_equal is OK expect_lint("expect_equal(x, c(TRUE, FALSE))", NULL, expect_true_false_linter()) }) test_that("expect_true_false_linter blocks simple disallowed usages", { expect_lint( "expect_equal(foo(x), TRUE)", rex::rex("expect_true(x) is better than expect_equal(x, TRUE)"), expect_true_false_linter() ) # expect_identical is treated the same as expect_equal expect_lint( "testthat::expect_identical(x, FALSE)", rex::rex("expect_false(x) is better than expect_identical(x, FALSE)"), expect_true_false_linter() ) # also caught when TRUE/FALSE is the first argument expect_lint( "expect_equal(TRUE, foo(x))", rex::rex("expect_true(x) is better than expect_equal(x, TRUE)"), expect_true_false_linter() ) }) lintr/tests/testthat/test-paren_brace_linter.R0000644000176200001440000000217414577052532021340 0ustar liggesuserstest_that("returns the correct linting", { expect_warning( { linter <- paren_brace_linter() }, "Linter paren_brace_linter was deprecated", fixed = TRUE ) lint_msg <- rex::rex("There should be a space between right parenthesis and an opening curly brace.") expect_lint("blah", NULL, linter) expect_lint("blah <- function() {}", NULL, linter) expect_lint("blah <- function() {\n}", NULL, linter) expect_lint( "blah <- function(){}", list( message = lint_msg, column_number = 19L ), linter ) expect_lint( "\nblah <- function(){\n\n\n}", list( message = lint_msg, column_number = 19L ), linter ) # paren_brace_linter should ignore strings and comments, as in regexes: expect_lint("grepl('(iss){2}', 'Mississippi')", NULL, linter) expect_lint( "x <- 123 # don't flag (paren){brace} if inside a comment", NULL, linter ) # paren-brace lint should not be thrown when the brace lies on subsequent line expect_lint( paste( "x <- function()", " {2}", sep = "\n" ), NULL, linter ) }) lintr/tests/testthat/test-spaces_left_parentheses_linter.R0000644000176200001440000000633714577052532023775 0ustar liggesuserstest_that("spaces_left_parentheses_linter skips allowed usages", { linter <- spaces_left_parentheses_linter() expect_lint("blah", NULL, linter) expect_lint("print(blah)", NULL, linter) expect_lint("base::print(blah)", NULL, linter) expect_lint("base::print(blah, fun(1))", NULL, linter) expect_lint("blah <- function(blah) { }", NULL, linter) expect_lint("(1 + 1)", NULL, linter) expect_lint("(1 + 1)", NULL, linter) expect_lint("( (1 + 1) )", NULL, linter) expect_lint("if (blah) { }", NULL, linter) expect_lint("for (i in j) { }", NULL, linter) expect_lint("1 * (1 + 1)", NULL, linter) expect_lint("!(1 == 1)", NULL, linter) expect_lint("(2 - 1):(3 - 1)", NULL, linter) expect_lint("c(1, 2, 3)[(2 - 1)]", NULL, linter) expect_lint("list(1, 2, 3)[[(2 - 1)]]", NULL, linter) expect_lint("range(10)[(2 - 1):(10 - 1)]", NULL, linter) expect_lint("function(){function(){}}()()", NULL, linter) expect_lint("c(function(){})[1]()", NULL, linter) expect_lint("\"test <- function(x) { if(1 + 1) 'hi' }\"", NULL, linter) expect_lint("res <- c((mat - 1L) %*% combs + 1L)", NULL, linter) expect_lint("if (!(foo && bar || baz)) { foo }", NULL, linter) expect_lint("x^(y + z)", NULL, linter) expect_lint("x**(y + z)", NULL, linter) expect_lint("a <- -(b)", NULL, linter) expect_lint("(3^(3 + 2))", NULL, linter) expect_lint("-(!!!symb)", NULL, linter) expect_lint("'[[<-.data.frame'(object, y)", NULL, linter) expect_lint("object@data@get('input')", NULL, linter) expect_lint("x <- ~(. + y)", NULL, linter) # the internal newline is required to trigger the lint expect_lint("if (x > 1)\n x <- x[-(i)]", NULL, linter) # these don't violate the linter, even if they are strange coding practice expect_lint("for (ii in 1:10) next()", NULL, linter) expect_lint("for (ii in 1:10) break()", NULL, linter) }) test_that("spaces_left_parentheses_linter blocks disallowed usages", { linter <- spaces_left_parentheses_linter() lint_msg <- rex::rex("Place a space before left parenthesis, except in a function call.") expect_lint("if(blah) { }", lint_msg, linter) expect_lint("for(i in j) { }", lint_msg, linter) expect_lint("1*(1 + 1)", lint_msg, linter) expect_lint("test <- function(x) { if(1 + 1) 'hi' }", lint_msg, linter) expect_lint("test <- function(x) { if(`+`(1, 1)) 'hi' }", lint_msg, linter) # more complicated cases for parse tree expect_lint("y1<-(abs(yn)>90)*1", lint_msg, linter) expect_lint("c(a,(a+b))", lint_msg, linter) expect_lint("if (x>y) 1 else(2)", lint_msg, linter) expect_lint("y~(x+z)", lint_msg, linter) expect_lint("if (x>y) {(x+y) / (x-y)}", lint_msg, linter) expect_lint("for (ii in(1:10)) { }", lint_msg, linter) expect_lint("x = 1;(x + 2)*3", lint_msg, linter) expect_lint("foo <- function(x=(1+2)) { }", lint_msg, linter) }) test_that("doesn't produce a warning", { # complexity.R contains a function with nested if-statements where the conditional includes a unary minus. # This specific constellation with another if-statement at the same nesting level on the other enclosing if-branch # caused a warning in spaces_left_parentheses_linter (e.g. 84bc3a is broken) expect_silent( lint( system.file("example/complexity.R", package = "lintr") ) ) }) lintr/tests/testthat/test-yoda_test_linter.R0000644000176200001440000000550114577052532021067 0ustar liggesuserstest_that("yoda_test_linter skips allowed usages", { expect_lint("expect_equal(x, 2)", NULL, yoda_test_linter()) # namespace qualification doesn't matter expect_lint("testthat::expect_identical(x, 'a')", NULL, yoda_test_linter()) # two variables can't be distinguished which is expected/actual (without # playing quixotic games trying to parse that out from variable names) expect_lint("expect_equal(x, y)", NULL, yoda_test_linter()) }) test_that("yoda_test_linter blocks simple disallowed usages", { expect_lint( "expect_equal(2, x)", rex::rex("Tests should compare objects in the order 'actual', 'expected'"), yoda_test_linter() ) expect_lint( "testthat::expect_identical('a', x)", rex::rex("Tests should compare objects in the order 'actual', 'expected'"), yoda_test_linter() ) expect_lint( "expect_setequal(2, x)", rex::rex("Tests should compare objects in the order 'actual', 'expected'"), yoda_test_linter() ) # complex literals are slightly odd expect_lint( "expect_equal(2 + 1i, x)", rex::rex("Tests should compare objects in the order 'actual', 'expected'"), yoda_test_linter() ) }) test_that("yoda_test_linter ignores strings in $ expressions", { # the "key" here shows up at the same level of the parse tree as plain "key" normally would expect_lint('expect_equal(x$"key", 2)', NULL, yoda_test_linter()) expect_lint('expect_equal(x@"key", 2)', NULL, yoda_test_linter()) }) # if we only inspect the first argument & ignore context, get false positives local({ pipes <- pipes(exclude = c("%<>%", "%$%")) linter <- yoda_test_linter() patrick::with_parameters_test_that( "yoda_test_linter ignores usage in pipelines", expect_lint(sprintf("foo() %s expect_identical(2)", pipe), NULL, linter), pipe = pipes, .test_name = names(pipes) ) }) test_that("yoda_test_linter throws a special message for placeholder tests", { expect_lint( "expect_equal(1, 1)", rex::rex("Avoid storing placeholder tests like expect_equal(1, 1)"), yoda_test_linter() ) }) # TODO(michaelchirico): Should this be extended to RUnit tests? It seems yes, # but the argument names in RUnit (inherited from base all.equal()) are a bit # confusing, e.g. `checkEqual(target=, current=)`. From the name, one might # reasonably conclude 'expected' comes first, and 'actual' comes second. # TODO(michaelchirico): What sorts of combinations of literals can be included? # e.g. expect_equal(c(1, 2), x) is a yoda test; is expect_equal(c(x, 1), y)? # clearly it's not true for general f() besides c(). What about other # constructors of literals? data.frame(), data.table(), tibble(), ...? # TODO(michaelchirico): The logic could also be extended to "tests" inside regular # code, not just test suites, e.g. `if (2 == x)`, `while(3 <= x)`, # `stopifnot('a' == foo(y))`. lintr/tests/testthat/test-expect_s4_class.R0000644000176200001440000000207014577052532020600 0ustar liggesuserstest_that("expect_s4_class_linter skips allowed usages", { linter <- expect_s4_class_linter() # expect_s4_class doesn't have an inverted version expect_lint("expect_true(!is(x, 'class'))", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_s3_class(!is(x, 'class'))", NULL, linter) # expect_s4_class() doesn't have info= or label= arguments expect_lint("expect_true(is(x, 'SpatialPoly'), info = 'x should be SpatialPoly')", NULL, linter) expect_lint("expect_true(is(x, 'SpatialPoly'), label = 'x inheritance')", NULL, linter) }) test_that("expect_s4_class blocks simple disallowed usages", { expect_lint( "expect_true(is(x, 'data.frame'))", rex::rex("expect_s4_class(x, k) is better than expect_true(is(x, k))"), expect_s4_class_linter() ) # namespace qualification is irrelevant expect_lint( "testthat::expect_true(methods::is(x, 'SpatialPolygonsDataFrame'))", rex::rex("expect_s4_class(x, k) is better than expect_true(is(x, k))"), expect_s4_class_linter() ) }) lintr/tests/testthat/test-package_hooks_linter.R0000644000176200001440000002120414577052532021670 0ustar liggesuserstest_that("package_hooks_linter skips allowed usages of packageStartupMessage() & library.dynam()", { # allowed in .onAttach, not .onLoad expect_lint( ".onAttach <- function(lib, pkg) packageStartupMessage('hi')", NULL, package_hooks_linter() ) # allowed in .onLoad, not .onAttach expect_lint( ".onLoad <- function(lib, pkg) library.dynam()", NULL, package_hooks_linter() ) }) test_that("package_hooks_linter blocks simple disallowed usages of packageStartupMessage() & library.dynam()", { # inline version expect_lint( ".onLoad <- function(lib, pkg) packageStartupMessage('hi')", rex::rex("Put packageStartupMessage() calls in .onAttach()"), package_hooks_linter() ) # multiline version expect_lint( trim_some(" .onAttach <- function(libname, pkgname) { library.dynam() } "), rex::rex("Put library.dynam() calls in .onLoad, not .onAttach()."), package_hooks_linter() ) # found at deeper nesting too expect_lint( trim_some(" .onLoad <- function(libname, pkgname) { foo(bar(baz(packageStartupMessage('hi')))) } "), rex::rex("Put packageStartupMessage() calls in .onAttach()"), package_hooks_linter() ) }) test_that("package_hooks_linter blocks simple disallowed usages of other blocked messaging functions", { # inline version expect_lint( ".onLoad <- function(lib, pkg) cat('hi')", rex::rex("Don't use cat() in .onLoad()"), package_hooks_linter() ) # multiline version expect_lint( trim_some(" .onAttach <- function(libname, pkgname) { writeLines('hi') } "), rex::rex("Don't use writeLines() in .onAttach()"), package_hooks_linter() ) expect_lint( trim_some(" .onLoad <- function(libname, pkgname) { print('hi') } "), rex::rex("Don't use print() in .onLoad()"), package_hooks_linter() ) # found at deeper nesting too expect_lint( trim_some(" .onAttach <- function(libname, pkgname) { foo(bar(baz(message('hi')))) } "), rex::rex("Don't use message() in .onAttach()"), package_hooks_linter() ) }) test_that("package_hooks_linter skips valid .onLoad() and .onAttach() arguments", { expect_lint(".onAttach <- function(lib, pkg) { }", NULL, package_hooks_linter()) expect_lint(".onLoad <- function(lib, pkg) { }", NULL, package_hooks_linter()) # args only need to start with those characters expect_lint(".onAttach <- function(libname, pkgpath) { }", NULL, package_hooks_linter()) expect_lint(".onLoad <- function(libXXXX, pkgYYYY) { }", NULL, package_hooks_linter()) }) test_that("package_hooks_linter blocks invalid .onLoad() / .onAttach() arguments", { expect_lint( ".onAttach <- function(xxx, pkg) { }", rex::rex(".onAttach() should take two arguments"), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, yyy) { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) # only one lint if both are wrong expect_lint( ".onLoad <- function(xxx, yyy) { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) # exactly two arguments required. # NB: QC.R allows ... arguments to be passed, but disallow this flexibility in the linter. expect_lint( ".onLoad <- function() { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib) { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, pkg, third) { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, ...) { }", rex::rex(".onLoad() should take two arguments"), package_hooks_linter() ) }) test_that("package_hooks_linter skips valid namespace loading", { expect_lint(".onAttach <- function(lib, pkg) { requireNamespace('foo') }", NULL, package_hooks_linter()) expect_lint(".onLoad <- function(lib, pkg) { requireNamespace('foo') }", NULL, package_hooks_linter()) }) test_that("package_hooks_linter blocks attaching namespaces", { expect_lint( ".onAttach <- function(lib, pkg) { require(foo) }", rex::rex("Don't alter the search() path in .onAttach() by calling require()."), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, pkg) { library(foo) }", rex::rex("Don't alter the search() path in .onLoad() by calling library()."), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, pkg) { installed.packages() }", rex::rex("Don't slow down package load by running installed.packages() in .onLoad()."), package_hooks_linter() ) # find at further nesting too expect_lint( ".onAttach <- function(lib, pkg) { a(b(c(require(foo)))) }", rex::rex("Don't alter the search() path in .onAttach() by calling require()."), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, pkg) { d(e(f(library(foo)))) }", rex::rex("Don't alter the search() path in .onLoad() by calling library()."), package_hooks_linter() ) expect_lint( ".onLoad <- function(lib, pkg) { g(h(i(installed.packages()))) }", rex::rex("Don't slow down package load by running installed.packages() in .onLoad()."), package_hooks_linter() ) # also find when used as names expect_lint( ".onAttach <- function(lib, pkg) { sapply(c('a', 'b', 'c'), require, character.only = TRUE) }", rex::rex("Don't alter the search() path in .onAttach() by calling require()."), package_hooks_linter() ) expect_lint( ".onAttach <- function(lib, pkg) { lapply(c('a', 'b', 'c'), library, character.only = TRUE) }", rex::rex("Don't alter the search() path in .onAttach() by calling library()"), package_hooks_linter() ) }) test_that("package_hooks_linter skips valid .onDetach() and .Last.lib()", { expect_lint(".onDetach <- function(lib) { }", NULL, package_hooks_linter()) expect_lint(".onDetach <- function(libname) { }", NULL, package_hooks_linter()) expect_lint(".Last.lib <- function(lib) { }", NULL, package_hooks_linter()) expect_lint(".Last.lib <- function(libname) { }", NULL, package_hooks_linter()) }) test_that("package_hooks_linter catches usage of library.dynam.unload()", { expect_lint( ".onDetach <- function(lib) { library.dynam.unload() }", rex::rex("Use library.dynam.unload() calls in .onUnload(), not .onDetach()."), package_hooks_linter() ) expect_lint( ".Last.lib <- function(lib) { library.dynam.unload() }", rex::rex("Use library.dynam.unload() calls in .onUnload(), not .Last.lib()."), package_hooks_linter() ) # expected usage is in .onUnload expect_lint( ".onUnload <- function(lib) { library.dynam.unload() }", NULL, package_hooks_linter() ) }) test_that("package_hooks_linter detects bad argument names in .onDetach()/.Last.lib()", { expect_lint( ".onDetach <- function(xxx) { }", rex::rex(".onDetach() should take one argument starting with 'lib'."), package_hooks_linter() ) expect_lint( ".Last.lib <- function(yyy) { }", rex::rex(".Last.lib() should take one argument starting with 'lib'."), package_hooks_linter() ) # exactly one argument required. # NB: QC.R allows ... arguments to be passed, but disallow this flexibility in the linter. expect_lint( ".onDetach <- function() { }", rex::rex(".onDetach() should take one argument starting with 'lib'."), package_hooks_linter() ) expect_lint( ".Last.lib <- function(lib, pkg) { }", rex::rex(".Last.lib() should take one argument starting with 'lib'."), package_hooks_linter() ) expect_lint( ".onDetach <- function(...) { }", rex::rex(".onDetach() should take one argument starting with 'lib'."), package_hooks_linter() ) }) test_that("function shorthand is handled", { skip_if_not_r_version("4.1.0") linter <- package_hooks_linter() expect_lint( ".onLoad <- \\(lib, pkg) packageStartupMessage('hi')", rex::rex("Put packageStartupMessage() calls in .onAttach()"), linter ) expect_lint( ".onAttach <- \\(xxx, pkg) { }", rex::rex(".onAttach() should take two arguments"), linter ) expect_lint( ".onAttach <- \\(lib, pkg) { require(foo) }", rex::rex("Don't alter the search() path in .onAttach() by calling require()."), linter ) expect_lint( ".onDetach <- \\(lib) { library.dynam.unload() }", rex::rex("Use library.dynam.unload() calls in .onUnload(), not .onDetach()."), linter ) expect_lint( ".onDetach <- \\(xxx) { }", rex::rex(".onDetach() should take one argument starting with 'lib'."), linter ) }) lintr/tests/testthat/test-assignment_linter.R0000644000176200001440000001166614577052532021255 0ustar liggesuserstest_that("assignment_linter skips allowed usages", { linter <- assignment_linter() expect_lint("blah", NULL, linter) expect_lint("blah <- 1", NULL, linter) expect_lint("blah<-1", NULL, linter) expect_lint("fun(blah=1)", NULL, linter) }) test_that("assignment_linter blocks disallowed usages", { linter <- assignment_linter() lint_msg <- rex::rex("Use <-, not =, for assignment.") expect_lint("blah=1", lint_msg, linter) expect_lint("blah = 1", lint_msg, linter) expect_lint("blah = fun(1)", lint_msg, linter) expect_lint("fun((blah = fun(1)))", lint_msg, linter) expect_lint( "blah = fun(1) {", list( lint_msg, c(type = "error", "unexpected") ), linter ) }) test_that("arguments handle <<- and ->/->> correctly", { expect_lint("1 -> blah", rex::rex("Use <-, not ->, for assignment."), assignment_linter()) expect_lint("1 ->> blah", rex::rex("->> can have hard-to-predict behavior;"), assignment_linter()) # <<- is only blocked optionally expect_lint("1 <<- blah", NULL, assignment_linter()) expect_lint( "1 <<- blah", rex::rex("<<- can have hard-to-predict behavior;"), assignment_linter(allow_cascading_assign = FALSE) ) # blocking -> can be disabled expect_lint("1 -> blah", NULL, assignment_linter(allow_right_assign = TRUE)) expect_lint("1 ->> blah", NULL, assignment_linter(allow_right_assign = TRUE)) # blocked under cascading assign but not under right assign --> blocked expect_lint( "1 ->> blah", rex::rex("->> can have hard-to-predict behavior;"), assignment_linter(allow_cascading_assign = FALSE, allow_right_assign = TRUE) ) }) test_that("arguments handle trailing assignment operators correctly", { expect_lint("x <- y", NULL, assignment_linter(allow_trailing = FALSE)) expect_lint("foo(bar = 1)", NULL, assignment_linter(allow_trailing = FALSE)) expect_lint( "foo(bar =\n1)", rex::rex("= should not be trailing at the end of a line."), assignment_linter(allow_trailing = FALSE) ) expect_lint( "x <<-\ny", rex::rex("<<- should not be trailing"), assignment_linter(allow_trailing = FALSE) ) expect_lint( "x <<-\ny", rex::rex("<<- can have hard-to-predict behavior"), assignment_linter(allow_trailing = FALSE, allow_cascading_assign = FALSE) ) expect_lint( "x <- #Test \ny", rex::rex("<- should not be trailing"), assignment_linter(allow_trailing = FALSE) ) expect_lint( "is_long <-\nis %>%\ngather(measure, value, -Species) %>%\narrange(-value)", NULL, assignment_linter() ) expect_lint( "is_long <-\nis %>%\ngather(measure, value, -Species) %>%\narrange(-value)", rex::rex("<- should not be trailing"), assignment_linter(allow_trailing = FALSE) ) expect_lint( "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long", rex::rex("Use <-, not ->"), assignment_linter() ) expect_lint( "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long", rex::rex("Use <-, not ->"), assignment_linter(allow_trailing = FALSE) ) expect_lint( "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long", rex::rex("-> should not be trailing"), assignment_linter(allow_right_assign = TRUE, allow_trailing = FALSE) ) expect_lint( "\n\nblah=\n42\nblh2<-\n54", list( list(message = "=", line_number = 3L, column_number = 5L), list(message = "<-", line_number = 5L, column_number = 5L) ), assignment_linter(allow_trailing = FALSE) ) }) test_that("allow_trailing interacts correctly with comments in braced expressions", { linter <- assignment_linter(allow_trailing = FALSE) expect_lint( trim_some(" { x <- 1 # blah y <- 2 } "), NULL, linter ) expect_lint( trim_some(" { x <- '#x' y <- '#y' } "), NULL, linter ) expect_lint( trim_some(" { x <- # blah 'x' } "), list(message = "<-", line_number = 2L), linter ) expect_lint( trim_some(" { x <- ' a string with an assignment <- at the end of the line ' } "), NULL, linter ) }) test_that("%<>% throws a lint", { expect_lint("x %<>% sum()", "Avoid the assignment pipe %<>%", assignment_linter()) expect_lint("x %<>% sum()", NULL, assignment_linter(allow_pipe_assign = TRUE)) # interaction with allow_trailing expect_lint("x %<>%\n sum()", "Assignment %<>% should not be trailing", assignment_linter(allow_trailing = FALSE)) }) test_that("multiple lints throw correct messages", { expect_lint( "{ x <<- 1; y ->> 2; z -> 3; x %<>% as.character() }", list( list(message = "<<- can have hard-to-predict behavior"), list(message = "->> can have hard-to-predict behavior"), list(message = "Use <-, not ->"), list(message = "Avoid the assignment pipe %<>%") ), assignment_linter(allow_cascading_assign = FALSE) ) }) lintr/tests/testthat/test-closed_curly_linter.R0000644000176200001440000000261214577052532021563 0ustar liggesuserstest_that("returns the correct linting", { closed_curly_message_regex <- rex::rex( paste( "Closing curly-braces should always be on their own line,", "unless they are followed by an else." ) ) expect_warning( { linter <- closed_curly_linter() }, "Linter closed_curly_linter was deprecated", fixed = TRUE ) expect_lint("blah", NULL, linter) expect_lint("a <- function() {\n}", NULL, linter) expect_lint( "a <- function() { 1 }", closed_curly_message_regex, linter ) expect_lint( "a <- function() { 1 }", closed_curly_message_regex, linter ) expect_lint( "a <- function() { 1 }", NULL, suppressWarnings(closed_curly_linter(allow_single_line = TRUE)) ) expect_lint( "a <- if(1) {\n 1} else {\n 2\n}", closed_curly_message_regex, linter ) expect_lint( "a <- if(1) {\n 1\n} else {\n 2}", closed_curly_message_regex, linter ) expect_lint( "a <- if(1) {\n 1} else {\n 2}", list( closed_curly_message_regex, closed_curly_message_regex ), linter ) expect_lint( "eval(bquote({...}))", NULL, linter ) expect_lint( "fun({\n statements\n}, param)", NULL, linter ) expect_lint( "out <- lapply(stuff, function(i) {\n do_something(i)\n}) %>% unlist", NULL, linter ) expect_lint("{{x}}", NULL, linter) }) lintr/tests/testthat/test-indentation_linter.R0000644000176200001440000003512214510656223021404 0ustar liggesuserstest_that("indentation linter flags unindented expressions", { linter <- indentation_linter(indent = 2L) expect_lint( trim_some(" lapply(1:10, function(i) { i %% 2 }) "), NULL, linter ) expect_lint( trim_some(" lapply(1:10, function(i) { i %% 2 # indentation is only 1 character }) "), "Indentation", linter ) expect_lint( trim_some(" lapply(1:10, function(i) { # indentation is only 1 character i %% 2 }) "), "Indentation", linter ) # no double-block indents even if the indentation-starting tokens are immediately next to each other expect_lint( trim_some(" local({ # no lint }) local({ # must lint }) "), list(line_number = 6L, message = "Indentation"), linter ) expect_lint( trim_some(" lapply(1:10, function(i) { i %% 2 }) "), NULL, indentation_linter(indent = 4L) ) expect_lint( trim_some(" lapply(1:10, function(i) { i %% 2 # indentation is only 2 characters }) "), "Indentation", indentation_linter(indent = 4L) ) # ugly code, but still correctly indented expect_lint( trim_some(" list( 1, 2) "), NULL, linter ) # comments do not trigger hanging indent rule expect_lint( trim_some(" list( # comment ok ) "), NULL, linter ) # comments do not suppress block indents (#1751) expect_lint( trim_some(" a <- # comment 42L "), NULL, linter ) # assignment triggers indent expect_lint( trim_some(" a <- expr( 42 ) "), NULL, linter ) expect_lint( trim_some(" if (cond) code if (cond) code else code2 if (cond) { code } else code if (cond) { code } else { code } "), NULL, linter ) }) test_that("indentation linter flags improper closing curly braces", { linter <- indentation_linter(indent = 2L) expect_lint( trim_some(" lapply(1:10, function(i) { { i %% 2 } }) "), NULL, linter ) expect_lint( trim_some(" lapply(1:10, function(i) { i %% 2 } # closing curly doesn't return to parent indentation ) "), "Indentation", linter ) }) test_that("function argument indentation works in tidyverse-style", { linter <- indentation_linter() expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), NULL, linter ) # new style (#1754) expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), NULL, linter ) expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), "Indentation should be 4", linter ) # Hanging is only allowed if there is an argument next to "(" expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), "Indentation should be 4", linter ) # Block is only allowed if there is no argument next to ")" expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), "Indentation should be 4", linter ) expect_lint( trim_some(" function( a = 1L, b = 2L ) { a + b } "), NULL, linter ) # anchor is correctly found with assignments as well expect_lint( trim_some(" test <- function(a = 1L, b = 2L) { a + b } "), NULL, linter ) expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), "Hanging", linter ) # This is a case for brace_linter expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), NULL, linter ) }) test_that("function argument indentation works in always-hanging-style", { linter <- indentation_linter(hanging_indent_style = "always") expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), NULL, linter ) expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), "Hanging", linter ) expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), NULL, linter ) # Block is only allowed if there is no argument next to ")" expect_lint( trim_some(" function( a = 1L, b = 2L) { a + b } "), "Hanging", linter ) expect_lint( trim_some(" function( a = 1L, b = 2L ) { a + b } "), NULL, linter ) # anchor is correctly found with assignments as well expect_lint( trim_some(" test <- function(a = 1L, b = 2L) { a + b } "), NULL, linter ) expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), "Hanging", linter ) # This is a case for brace_linter expect_lint( trim_some(" function(a = 1L, b = 2L) { a + b } "), NULL, linter ) }) test_that("indentation with operators works", { linter <- indentation_linter() expect_lint( trim_some(" a %>% b() "), NULL, linter ) expect_lint( trim_some(" (a + b + c) / (d + e + f) / (g + h + i) "), NULL, linter ) expect_lint( trim_some(" a %>% b() "), "Indentation", linter ) expect_lint( trim_some(" a + b() "), "Indentation", linter ) expect_lint( trim_some(" abc$ def$ ghi "), NULL, linter ) }) test_that("indentation with bracket works", { linter <- indentation_linter() expect_lint( trim_some(" dt[ , col := 42L ][ , ok ] bla[hanging, also_ok] "), NULL, linter ) expect_lint( trim_some(" abc[[ 'elem' ]] def[[a, b]] "), NULL, linter ) }) test_that("indentation works with control flow statements", { linter <- indentation_linter() expect_lint( trim_some(" if (TRUE) { do_something } else { do_other_thing } "), NULL, linter ) expect_lint( trim_some(" while (1 > 2) { do_something } "), "Indentation", linter ) expect_lint( trim_some(" if (FALSE) { do_something } else { do_other_thing } "), "Indentation", linter ) }) test_that("indentation lint messages are dynamic", { linter <- indentation_linter() expect_lint( trim_some(" local({ # should be 2 }) "), rex::rex("Indentation should be 2 spaces but is 4 spaces."), linter ) expect_lint( trim_some(" fun( 3) # should be 4 "), rex::rex("Hanging indent should be 4 spaces but is 2 spaces."), linter ) }) test_that("indentation within string constants is ignored", { expect_lint( trim_some(" x <- ' an indented string ' "), NULL, indentation_linter() ) expect_lint( trim_some(" x <- ' an indented string with 3 spaces indentation ' "), NULL, indentation_linter() ) }) test_that("combined hanging and block indent works", { linter <- indentation_linter() expect_lint( trim_some(" func(hang, and, block( combined )) "), NULL, linter ) expect_lint( trim_some(" func(ha, func2(ab, block( indented ))) "), NULL, linter ) expect_lint( trim_some(" func(func2( a = 42 )) "), NULL, linter ) # Adapted from cli R/ansi.R L231-234 expect_lint( trim_some(" stopifnot(is.character(style) && length(style) == 1 || is_rgb_matrix(style) && ncol(style) == 1, is.logical(bg) && length(bg) == 1, is.numeric(colors) && length(colors) == 1) "), NULL, linter ) # Adapted from cli inst/scripts/up.R L26-37 expect_lint( trim_some(" http_head(url, ...)$ then(function(res) { if (res$status_code < 300) { cli_alert_success() } else { cli_alert_danger() } })$ catch(error = function(err) { e <- if (grepl('timed out', err$message)) 'timed out' else 'error' cli_alert_danger() }) "), NULL, linter ) }) test_that("hanging_indent_stlye works", { code_block_multi_line <- "map(x, f,\n extra_arg = 42\n)" code_hanging_multi_line <- "map(x, f,\n extra_arg = 42\n)" code_block_same_line <- "map(x, f,\n extra_arg = 42)" code_hanging_same_line <- "map(x, f,\n extra_arg = 42)" tidy_linter <- indentation_linter() hanging_linter <- indentation_linter(hanging_indent_style = "always") non_hanging_linter <- indentation_linter(hanging_indent_style = "never") expect_lint(code_block_multi_line, NULL, tidy_linter) expect_lint(code_block_multi_line, "Hanging indent", hanging_linter) expect_lint(code_block_multi_line, NULL, non_hanging_linter) expect_lint(code_hanging_multi_line, "Indent", tidy_linter) expect_lint(code_hanging_multi_line, NULL, hanging_linter) expect_lint(code_hanging_multi_line, "Indent", non_hanging_linter) expect_lint(code_block_same_line, "Hanging indent", tidy_linter) expect_lint(code_block_same_line, "Hanging indent", hanging_linter) expect_lint(code_block_same_line, NULL, non_hanging_linter) expect_lint(code_hanging_same_line, NULL, tidy_linter) expect_lint(code_hanging_same_line, NULL, hanging_linter) expect_lint(code_hanging_same_line, "Indent", non_hanging_linter) # regression test for #1898 expect_lint( trim_some(" outer_fun(inner_fun(x, one_indent = 42L )) "), NULL, tidy_linter ) expect_lint( trim_some(" outer_fun(inner_fun(x, # this is first arg one_indent = 42L # this is second arg )) "), NULL, tidy_linter ) expect_lint( trim_some(" outer_fun(inner_fun( x, one_indent = 42L )) "), NULL, tidy_linter ) expect_lint( trim_some(" outer_fun( inner_fun( x, one_indent = 42L ) ) "), NULL, tidy_linter ) }) test_that("assignment_as_infix works", { # test function call restorator and LEFT_ASSIGN suppressor code_infix <- trim_some(" ok_code <- var1 + f( var2 + var3 ) + var4 ") # test that innermost ancestor token decides the indentation code_infix_2 <- trim_some(" lapply(x, function(e) { temp_var <- e + 42 } ) ") # test brace restorator code_infix_3 <- trim_some(" ok_code <- if (condition) { a + b } else { c + d } + e ") # test EQ_ASSIGN, EQ_SUB and EQ_FORMALS suppressors code_infix_4 <- trim_some(" # EQ_ASSIGN ok_code = a + b # EQ_SUB f( a = b + c ) # EQ_FORMALS f <- function( a = b + c ) { NULL } ") code_no_infix <- trim_some(" ok_code <- var1 + f( var2 + var3 ) + var4 ") tidy_linter <- indentation_linter() no_infix_linter <- indentation_linter(assignment_as_infix = FALSE) expect_lint(code_infix, NULL, tidy_linter) expect_lint(code_infix_2, NULL, tidy_linter) expect_lint(code_infix_3, NULL, tidy_linter) expect_lint(code_infix_4, NULL, tidy_linter) expect_lint(code_no_infix, rex::rex("Indentation should be 2 spaces but is 4 spaces."), tidy_linter) expect_lint(code_infix, rex::rex("Indentation should be 4 spaces but is 2 spaces."), no_infix_linter) expect_lint(code_infix_2, rex::rex("Indentation should be 8 spaces but is 6 spaces."), no_infix_linter) expect_lint(code_infix_3, rex::rex("Indentation should be 4 spaces but is 2 spaces."), no_infix_linter) expect_lint(code_infix_4, list( list(line_number = 4L, rex::rex("Indentation should be 4 spaces but is 2 spaces.")), list(line_number = 10L, rex::rex("Indentation should be 6 spaces but is 4 spaces.")), list(line_number = 17L, rex::rex("Indentation should be 6 spaces but is 4 spaces.")) ), no_infix_linter) expect_lint(code_no_infix, NULL, no_infix_linter) }) test_that("consecutive same-level lints are suppressed", { bad_code <- trim_some(" ok_code <- 42 wrong_hanging <- fun(a, b, c, d, e %>% f()) wrong_block <- function() { a + b c + d if (a == 24) boo } wrong_hanging_args <- function(a = 1, b = 2, c = 3, d = 4, e = 5, f = 6) { a + b + c + d + e + f } ") expect_lint( bad_code, list( list(line_number = 4L, message = "Hanging indent"), list(line_number = 8L, message = "Indentation"), list(line_number = 15L, message = "Hanging indent") ), indentation_linter() ) }) test_that("native pipe is supported", { skip_if_not_r_version("4.1.0") linter <- indentation_linter() expect_lint( trim_some(" a |> foo() "), NULL, linter ) expect_lint( trim_some(" b <- a |> foo() "), NULL, linter ) }) test_that("it doesn't error on invalid code", { # Part of #1427 expect_lint("function() {)", list(linter = "error", message = rex::rex("unexpected ')'")), indentation_linter()) }) test_that("function shorthand is handled", { skip_if_not_r_version("4.1.0") linter <- indentation_linter() expect_lint( trim_some(" lapply(1:10, \\(i) { i %% 2 }) "), NULL, linter ) expect_lint( trim_some(" lapply(1:10, \\(i) { i %% 2 # indentation is only 1 character }) "), "Indentation", linter ) expect_lint( trim_some(" \\( a = 1L, b = 2L) { a + b } "), NULL, linter ) }) lintr/tests/testthat/test-error.R0000644000176200001440000000347514510656222016651 0ustar liggesuserstest_that("returns the correct linting", { msg_escape_char <- rex::rex("is an unrecognized escape in character string") expect_lint('"\\R"', msg_escape_char) expect_lint('"\\A"', msg_escape_char) expect_lint('"\\z"', msg_escape_char) placeholder_linter <- function(...) NULL class(placeholder_linter) <- "linter" attr(placeholder_linter, "name") <- "null" expect_lint( "a <- 1 function() { b", rex::rex("unexpected end of input"), placeholder_linter ) linter <- equals_na_linter() expect_lint("x=", rex::rex("unexpected end of input"), linter) expect_lint("x += 1", rex::rex("unexpected '='"), linter) expect_lint("{x = }", rex::rex("unexpected '}'"), linter) expect_lint("x = ;", rex::rex("unexpected ';'"), linter) # no parsing error is expected for the equals-assignment in this code expect_lint("purrr::partial(list, 1, ... = , 2)", NULL, linter) # trigger error with base only, and extract it to match against # what comes out from expect_lint. get_base_message <- function(e) { rex::re_substitutes( data = conditionMessage(e), pattern = rex::rex( list(start, ":", any_digits, ":", any_digits, ": ") %or% list(newline, anything, newline, anything, end) ), replacement = "", global = TRUE ) } expected_message <- tryCatch(parse(text = "\\"), error = get_base_message) expect_lint("\\", rex::rex(expected_message)) msg_zero_length_var <- rex::rex("attempt to use zero-length variable name") expect_lint("``", msg_zero_length_var) expect_lint("``()", msg_zero_length_var) expect_lint("''()", msg_zero_length_var) expect_lint('""()', msg_zero_length_var) expect_lint("fun(''=42)", msg_zero_length_var) expect_lint('fun(""=42)', msg_zero_length_var) expect_lint('fun(a=1,""=42)', msg_zero_length_var) }) lintr/tests/testthat/test-conjunct_test_linter.R0000644000176200001440000001242714577052532021763 0ustar liggesuserstest_that("conjunct_test_linter skips allowed usages of expect_true", { expect_lint("expect_true(x)", NULL, conjunct_test_linter()) expect_lint("testthat::expect_true(x, y, z)", NULL, conjunct_test_linter()) # more complicated expression expect_lint("expect_true(x || (y && z))", NULL, conjunct_test_linter()) # the same by operator precedence, though not obvious a priori expect_lint("expect_true(x || y && z)", NULL, conjunct_test_linter()) expect_lint("expect_true(x && y || z)", NULL, conjunct_test_linter()) }) test_that("conjunct_test_linter skips allowed usages of expect_true", { expect_lint("expect_false(x)", NULL, conjunct_test_linter()) expect_lint("testthat::expect_false(x, y, z)", NULL, conjunct_test_linter()) # more complicated expression # (NB: xx && yy || zz and xx || yy && zz both parse with || first) expect_lint("expect_false(x && (y || z))", NULL, conjunct_test_linter()) }) test_that("conjunct_test_linter blocks && conditions with expect_true()", { expect_lint( "expect_true(x && y)", rex::rex("Instead of expect_true(A && B), write multiple expectations"), conjunct_test_linter() ) expect_lint( "expect_true(x && y && z)", rex::rex("Instead of expect_true(A && B), write multiple expectations"), conjunct_test_linter() ) }) test_that("conjunct_test_linter blocks || conditions with expect_false()", { linter <- conjunct_test_linter() lint_msg <- rex::rex("Instead of expect_false(A || B), write multiple expectations") expect_lint("expect_false(x || y)", lint_msg, linter) expect_lint("expect_false(x || y || z)", lint_msg, linter) # these lint because `||` is always outer by operator precedence expect_lint("expect_false(x || y && z)", lint_msg, linter) expect_lint("expect_false(x && y || z)", lint_msg, linter) }) test_that("conjunct_test_linter skips allowed stopifnot() and assert_that() usages", { linter <- conjunct_test_linter() expect_lint("stopifnot(x)", NULL, linter) expect_lint("assert_that(x, y, z)", NULL, linter) # more complicated expression expect_lint("stopifnot(x || (y && z))", NULL, linter) # the same by operator precedence, though not obvious a priori expect_lint("stopifnot(x || y && z)", NULL, linter) expect_lint("assertthat::assert_that(x && y || z)", NULL, linter) }) test_that("conjunct_test_linter blocks simple disallowed usages of stopifnot() and assert_that()", { linter <- conjunct_test_linter() lint_msg <- function(fun) rex::rex("Instead of ", fun, "(A && B), write multiple conditions") expect_lint("stopifnot(x && y)", lint_msg("stopifnot"), linter) expect_lint("stopifnot(x && y && z)", lint_msg("stopifnot"), linter) # assert_that() versions expect_lint("assert_that(x && y)", lint_msg("assert_that"), linter) expect_lint("assertthat::assert_that(x && y && z)", lint_msg("assert_that"), linter) }) test_that("conjunct_test_linter's allow_named_stopifnot argument works", { # allowed by default expect_lint( "stopifnot('x must be a logical scalar' = length(x) == 1 && is.logical(x) && !is.na(x))", NULL, conjunct_test_linter() ) expect_lint( "stopifnot('x is a logical scalar' = length(x) == 1 && is.logical(x) && !is.na(x))", rex::rex("Instead of stopifnot(A && B), write multiple conditions"), conjunct_test_linter(allow_named_stopifnot = FALSE) ) }) test_that("conjunct_test_linter skips allowed usages", { linter <- conjunct_test_linter() expect_lint("dplyr::filter(DF, A, B)", NULL, linter) expect_lint("dplyr::filter(DF, !(A & B))", NULL, linter) # | is the "top-level" operator here expect_lint("dplyr::filter(DF, A & B | C)", NULL, linter) expect_lint("dplyr::filter(DF, A | B & C)", NULL, linter) }) test_that("conjunct_test_linter blocks simple disallowed usages", { linter <- conjunct_test_linter() lint_msg <- rex::rex("Use dplyr::filter(DF, A, B) instead of dplyr::filter(DF, A & B)") expect_lint("dplyr::filter(DF, A & B)", lint_msg, linter) expect_lint("dplyr::filter(DF, A & B & C)", lint_msg, linter) # more common usage, in pipes expect_lint("DF %>% dplyr::filter(A & B)", lint_msg, linter) }) test_that("conjunct_test_linter respects its allow_filter argument", { linter_always <- conjunct_test_linter(allow_filter = "always") linter_dplyr <- conjunct_test_linter(allow_filter = "not_dplyr") lint_msg <- rex::rex("Use dplyr::filter(DF, A, B) instead of dplyr::filter(DF, A & B)") expect_lint("dplyr::filter(DF, A & B)", NULL, linter_always) expect_lint("dplyr::filter(DF, A & B & C)", NULL, linter_always) expect_lint("DF %>% dplyr::filter(A & B)", NULL, linter_always) expect_lint("dplyr::filter(DF, A & B)", lint_msg, linter_dplyr) expect_lint("dplyr::filter(DF, A & B & C)", lint_msg, linter_dplyr) expect_lint("DF %>% dplyr::filter(A & B)", lint_msg, linter_dplyr) expect_lint("filter(DF, A & B)", NULL, linter_dplyr) expect_lint("filter(DF, A & B & C)", NULL, linter_dplyr) expect_lint("DF %>% filter(A & B)", NULL, linter_dplyr) }) test_that("filter() is assumed to be dplyr::filter() by default, unless o/w specified", { linter <- conjunct_test_linter() expect_lint("stats::filter(A & B)", NULL, linter) expect_lint("ns::filter(A & B)", NULL, linter) expect_lint( "DF %>% filter(A & B)", rex::rex("Use dplyr::filter(DF, A, B) instead of dplyr::filter(DF, A & B)"), linter ) }) lintr/tests/testthat/test-Lint-builder.R0000644000176200001440000000176514510650642020052 0ustar liggesuserstest_that("Lint() errors on invalid input", { dummy_line <- "abc" expect_error( Lint("dummy.R", line = dummy_line, column_number = NA_integer_), rex::rex("`column_number` must be an integer between 0 and nchar(line) + 1 (4). It was NA.") ) expect_error( Lint("dummy.R", line = dummy_line, line_number = 0L), rex::rex("`line_number` must be a positive integer. It was 0.") ) expect_error( Lint("dummy.R", ranges = c(1L, 3L)), rex::rex("`ranges` must be NULL or a list.") ) expect_error( Lint("dummy.R", ranges = list(1L)), rex::rex("`ranges` must only contain length 2 integer vectors without NAs.") ) expect_error( Lint("dummy.R", ranges = list(c(1L, NA_integer_))), rex::rex("`ranges` must only contain length 2 integer vectors without NAs.") ) expect_error( Lint("dummy.R", line = dummy_line, ranges = list(c(1L, 2L), c(1L, 5L))), rex::rex("All entries in `ranges` must satisfy 0 <= range[1L] <= range[2L] <= nchar(line) + 1 (4).") ) }) lintr/tests/testthat/test-expect_length_linter.R0000644000176200001440000000352614577052532021732 0ustar liggesuserstest_that("expect_length_linter skips allowed usages", { expect_lint("expect_equal(nrow(x), 4L)", NULL, expect_length_linter()) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_equal(nrow(x), 4L)", NULL, expect_length_linter()) # only check the first argument. yoda tests in the second argument will be # missed, but there are legitimate uses of length() in argument 2 expect_lint("expect_equal(nrow(x), length(y))", NULL, expect_length_linter()) # expect_length() doesn't have info= or label= arguments expect_lint("expect_equal(length(x), n, info = 'x should have size n')", NULL, expect_length_linter()) expect_lint("expect_equal(length(x), n, label = 'x size')", NULL, expect_length_linter()) expect_lint("expect_equal(length(x), n, expected.label = 'target size')", NULL, expect_length_linter()) }) test_that("expect_length_linter blocks simple disallowed usages", { expect_lint( "expect_equal(length(x), 2L)", rex::rex("expect_length(x, n) is better than expect_equal(length(x), n)"), expect_length_linter() ) expect_lint( "testthat::expect_equal(length(DF), length(old))", rex::rex("expect_length(x, n) is better than expect_equal(length(x), n)"), expect_length_linter() ) # yoda test cases expect_lint( "expect_equal(2, length(x))", rex::rex("expect_length(x, n) is better than expect_equal(length(x), n)"), expect_length_linter() ) expect_lint( "expect_equal(2L, length(x))", rex::rex("expect_length(x, n) is better than expect_equal(length(x), n)"), expect_length_linter() ) }) test_that("expect_length_linter blocks expect_identical usage as well", { expect_lint( "expect_identical(length(x), 2L)", rex::rex("expect_length(x, n) is better than expect_identical(length(x), n)"), expect_length_linter() ) }) lintr/tests/testthat/test-missing_package_linter.R0000644000176200001440000000373314577052532022225 0ustar liggesuserstest_that("missing_package_linter skips allowed usages", { linter <- missing_package_linter() lint_msg <- list(message = rex::rex("Package 'statts' is not installed.")) expect_lint("library(stats)", NULL, linter) expect_lint('library("stats")', NULL, linter) expect_lint("library('stats')", NULL, linter) expect_lint("library(`stats`)", NULL, linter) expect_lint("library(stats, quietly)", NULL, linter) expect_lint("library(stats, quietly = TRUE)", NULL, linter) expect_lint("require(stats)", NULL, linter) expect_lint("require(stats, quietly = TRUE)", NULL, linter) expect_lint('loadNamespace("stats")', NULL, linter) expect_lint('requireNamespace("stats")', NULL, linter) }) test_that("missing_package_linter blocks disallowed usages", { linter <- missing_package_linter() lint_msg <- list(message = rex::rex("Package 'statts' is not installed.")) expect_lint("require(statts)", lint_msg, linter) expect_lint("library(statts, quietly = TRUE)", lint_msg, linter) expect_lint("library(statts, quietly = TRUE)", lint_msg, linter) expect_lint('loadNamespace("statts")', lint_msg, linter) expect_lint('requireNamespace("statts")', lint_msg, linter) expect_lint( trim_some(" library(utils) library(statts) "), list(line = "library(statts)"), linter ) }) test_that("loadNamespace and requireNamespace allow plain symbols", { expect_lint("loadNamespace(mypkg)", NULL, missing_package_linter()) expect_lint("requireNamespace(mypkg)", NULL, missing_package_linter()) }) test_that("character.only=TRUE case is handled", { expect_lint("library(statts, character.only = TRUE)", NULL, missing_package_linter()) expect_lint("require(statts, character.only = TRUE)", NULL, missing_package_linter()) expect_lint('library("stats", character.only = TRUE)', NULL, missing_package_linter()) expect_lint( 'library("statts", character.only = TRUE)', rex::rex("Package 'statts' is not installed."), missing_package_linter() ) }) lintr/tests/testthat/default_linter_testcode.R0000644000176200001440000000167214577052532021442 0ustar liggesusers# Each of the default linters should throw at least one lint on this file # assignment # function_left_parentheses # brace_linter # commas # paren_brace f = function (x,y = 1){} # commented_code # some <- commented("out code") # cyclocomp # equals_na # brace_linter # indentation # infix_spaces # line_length # object_length # object_name # object_usage # open_curly # T_and_F_symbol someComplicatedFunctionWithALongCamelCaseName <- function(x) { y <- 1 if (1 > 2 && 2 > 3 && 3 > 4 && 4 > 5 && 5*10 > 6 && 5 > 6 && 6 > 7 && x == NA) {T} else F } # vector_logic if (1 & 2) FALSE else TRUE # function_brace my_metric <- function(x) sum(x) + prod(x) # no_tab # pipe_continuation # seq_linter # spaces_inside # indentation x <- 1:10 x[ 2] 1:length(x) %>% lapply(function(x) x*2) %>% head() # single_quotes message('single_quotes') # spaces_left_parentheses # trailing_whitespace # semicolon x <- 42; y <- 2 +(1:10) # trailing_blank_lines lintr/tests/testthat/test-unnecessary_nested_if_linter.R0000644000176200001440000000660414577052532023460 0ustar liggesuserstest_that("unnecessary_nested_if_linter skips allowed usages", { linter <- unnecessary_nested_if_linter() expect_lint( trim_some(" if (x && y) { 1L }"), NULL, linter ) expect_lint( trim_some(" for (x in 1:3) { if (x && y) { 1L } }"), NULL, linter ) expect_lint( trim_some(" if (x) { 1L } else if (y) { 2L }"), NULL, linter ) expect_lint( trim_some(" if (x) { 1L } else { if (y) { 2L } }"), NULL, linter ) expect_lint( trim_some(" if (if (x) TRUE else FALSE) { 1L }"), NULL, linter ) expect_lint( trim_some(" if (x) { y <- x + 1L if (y) { 1L } }"), NULL, linter ) expect_lint( trim_some(" if ((x && y) || (if (x) TRUE else FALSE)) { 1L }"), NULL, linter ) # if there is any additional code between the inner and outer scopes, no lint expect_lint( trim_some(" if (x && a) { y <- x + 1L if (y || b) { 1L } }"), NULL, linter ) expect_lint( trim_some(" if (x) { if (y) { 1L } y <- x + 1L }"), NULL, linter ) expect_lint( trim_some(" if (x) { y <- x + 1L if (y) { 1L } y <- x }"), NULL, linter ) expect_lint( trim_some(" if (x) { y <- x + 1L { if (y) { 1L } } }"), NULL, linter ) expect_lint( trim_some(" if (x) { { y <- x + 1L if (y) { 1L } } }"), NULL, linter ) expect_lint( trim_some(" if (x) { { if (y) { 1L } } y <- x + 1L }"), NULL, linter ) expect_lint( trim_some(" if (x) { { y <- x + 1L { if (y) { 1L } } } }"), NULL, linter ) }) test_that("unnecessary_nested_if_linter blocks disallowed usages", { lint_message <- rex::rex("Don't use nested `if` statements") linter <- unnecessary_nested_if_linter() expect_lint( trim_some(" if (x) { if (y) { 1L } }"), lint_message, linter ) expect_lint( trim_some(" if (x) { if (y) 1L }"), lint_message, linter ) expect_lint( trim_some(" if (x && a) { if (y || b) { 1L } }"), lint_message, linter ) expect_lint( trim_some(" if (if (x) TRUE else FALSE) { if (y) { 1L } }"), lint_message, linter ) expect_lint( "if (x) if (y) 1L", lint_message, linter ) expect_lint( trim_some(" for (x in 1:3) { if (x) if (y) 1L }"), lint_message, linter ) expect_lint( trim_some(" if (x) { if (y) { if (z) { 1L } } }"), list( list(message = lint_message, line_number = 2L, column_number = 3L), list(message = lint_message, line_number = 3L, column_number = 5L) ), linter ) }) lintr/tests/testthat/test-string_boundary_linter.R0000644000176200001440000001271014577052532022305 0ustar liggesuserstest_that("string_boundary_linter skips allowed grepl() usages", { linter <- string_boundary_linter() # no known start/end anchor --> no lint expect_lint("grepl(p1, x)", NULL, linter) # no start/end anchor --> no lint expect_lint("grepl('abc', x)", NULL, linter) # regex pattern --> no lint expect_lint("grepl('^[a-z]', x)", NULL, linter) expect_lint("grepl('[a-z]$', x)", NULL, linter) # ignore.case --> no lint expect_lint("grepl('^abc', x, ignore.case = TRUE)", NULL, linter) expect_lint("grepl('^abc', x, ignore.case = ignore.case)", NULL, linter) # fixed --> no lint expect_lint("grepl('^abc', x, fixed = TRUE)", NULL, linter) expect_lint("grepl('^abc', x, fixed = fixed)", NULL, linter) }) test_that("string_boundary_linter skips allowed str_detect() usages", { linter <- string_boundary_linter() # no known start/end anchor --> no lint expect_lint("str_detect(x, p1)", NULL, linter) # no start/end anchor --> no lint expect_lint("str_detect(x, 'abc')", NULL, linter) # regex pattern --> no lint expect_lint("str_detect(x, '^[a-z]')", NULL, linter) expect_lint("str_detect(x, '[a-z]$')", NULL, linter) }) test_that("string_boundary_linter skips allowed substr()/substring() usages", { linter <- string_boundary_linter() # no comparison operator --> no lint expect_lint("substr(x, start, end)", NULL, linter) # unknown indices --> no lint expect_lint("substr(x, start, end) == 'a'", NULL, linter) expect_lint("substring(x, start, end) == 'a'", NULL, linter) # using foo(nchar(.)) expect_lint("substring(x, nchar(x) - 4, nchar(x) - 1) == 'abc'", NULL, linter) # using nchar(), but not of the input expect_lint("substring(x, nchar(y) - 4, nchar(y)) == 'abcd'", NULL, linter) # using x in nchar(), but on foo(input) expect_lint("substring(x, nchar(foo(x)) - 4, nchar(foo(x))) == 'abcd'", NULL, linter) # _close_ to equivalent, but not so in general -- e.g. # substring(s <- "abcdefg", 2L) == "efg" is not TRUE, but endsWith(s, "efg") # is. And if `s` contains strings of varying lengths, there's no equivalent. expect_lint("substring(x, 2L)", NULL, linter) }) test_that("string_boundary_linter blocks simple disallowed grepl() usages", { linter <- string_boundary_linter() starts_message <- rex::rex("Use !is.na(x) & startsWith(x, string) to detect a fixed initial substring,") ends_message <- rex::rex("Use !is.na(x) & endsWith(x, string) to detect a fixed terminal substring,") expect_lint("grepl('^a', x)", starts_message, linter) # non-trivially equivalent (but still same as startsWith()) expect_lint("grepl('^[.]', x)", starts_message, linter) expect_lint("grepl('a$', x)", ends_message, linter) # also get negation for free expect_lint("!grepl('a$', x)", ends_message, linter) # perl = TRUE doesn't matter expect_lint("grepl('^a', x, perl = TRUE)", starts_message, linter) # explicit FALSE (i.e., an explicit default) is ignored expect_lint("grepl('^a', x, fixed = FALSE)", starts_message, linter) expect_lint("grepl('^a', x, fixed = F)", starts_message, linter) }) test_that("string_boundary_linter blocks simple disallowed str_detect() usages", { expect_lint( "str_detect(x, '^a')", rex::rex("Use startsWith() to detect a fixed initial substring."), string_boundary_linter() ) expect_lint( "str_detect(x, 'a$')", rex::rex("Use endsWith() to detect a fixed terminal substring."), string_boundary_linter() ) }) test_that("string_boundary_linter blocks disallowed substr()/substring() usage", { expect_lint( "substr(x, 1L, 2L) == 'ab'", rex::rex("Use startsWith() to detect an initial substring."), string_boundary_linter() ) # end doesn't matter, just anchoring to 1L expect_lint( "substr(x, 1L, end) == 'ab'", rex::rex("Use startsWith() to detect an initial substring."), string_boundary_linter() ) expect_lint( "substring(x, nchar(x) - 4L, nchar(x)) == 'abcde'", rex::rex("Use endsWith() to detect a terminal substring."), string_boundary_linter() ) # start doesn't matter, just anchoring to nchar(x) expect_lint( "substring(x, start, nchar(x)) == 'abcde'", rex::rex("Use endsWith() to detect a terminal substring."), string_boundary_linter() ) # more complicated expressions expect_lint( "substring(colnames(x), start, nchar(colnames(x))) == 'abc'", rex::rex("Use endsWith() to detect a terminal substring."), string_boundary_linter() ) }) test_that("plain ^ or $ are skipped", { expect_lint('grepl("^", x)', NULL, string_boundary_linter()) expect_lint('grepl("$", x)', NULL, string_boundary_linter()) }) test_that("substr inverted tests are caught as well", { expect_lint( "substr(x, 1L, 2L) != 'ab'", rex::rex("Use startsWith() to detect an initial substring."), string_boundary_linter() ) expect_lint( "substring(x, nchar(x) - 4L, nchar(x)) != 'abcde'", rex::rex("Use endsWith() to detect a terminal substring."), string_boundary_linter() ) }) test_that("R>=4 raw strings are detected", { skip_if_not_r_version("4.0.0") expect_lint('grepl(R"(^.{3})", x)', NULL, string_boundary_linter()) expect_lint( 'grepl(R"(^abc)", x)', rex::rex("Use !is.na(x) & startsWith(x, string) to detect a fixed initial substring,"), string_boundary_linter() ) }) test_that("grepl() can optionally be ignored", { expect_lint("grepl('^abc', x)", NULL, string_boundary_linter(allow_grepl = TRUE)) expect_lint("grepl('xyz$', x)", NULL, string_boundary_linter(allow_grepl = TRUE)) }) lintr/tests/testthat/test-undesirable_function_linter.R0000644000176200001440000000534314577052532023302 0ustar liggesuserstest_that("linter returns correct linting", { linter <- undesirable_function_linter(fun = c(return = NA, log10 = "use log()")) msg_return <- "Function \"return\" is undesirable.$" msg_log10 <- "Function \"log10\" is undesirable. As an alternative, use log\\(\\)." expect_lint("x <- options()", NULL, linter) expect_lint("cat(\"Try to return\")", NULL, linter) expect_lint("lapply(x, log10)", list(message = msg_log10, line_number = 1L, column_number = 11L), linter) expect_lint("return()", list(message = msg_return, line_number = 1L, column_number = 1L), linter) expect_lint( trim_some(" function(x) { print(options()) y <- log10(x) return(y) }"), list( list(message = msg_log10, line_number = 3L, column_number = 8L), list(message = msg_return, line_number = 4L, column_number = 3L) ), linter ) # regression test for #1050 expect_lint("df$return <- 1", NULL, linter) expect_lint("df@return <- 1", NULL, linter) }) test_that("it's possible to NOT lint symbols", { linter <- undesirable_function_linter( fun = c(dir = NA, log10 = "use log()"), symbol_is_undesirable = FALSE ) expect_lint("dir <- 'path/to/a/directory'", NULL, linter) expect_lint("lapply(x, log10)", NULL, linter) }) test_that("undesirable_function_linter doesn't lint library and require calls", { linter <- undesirable_function_linter(fun = c(foo = NA)) expect_lint("test::foo()", "undesirable", linter) expect_lint("foo::test()", NULL, linter) expect_lint("library(foo)", NULL, linter) expect_lint("require(foo)", NULL, linter) linter <- undesirable_function_linter(fun = c(foo = NA, bar = NA)) expect_lint("library(foo)", NULL, linter) linter <- undesirable_function_linter(fun = c(foo = NA, bar = NA), symbol_is_undesirable = FALSE) expect_lint("library(foo)", NULL, linter) }) # regression test for #866 test_that("Line numbers are extracted correctly", { lines <- c(rep(letters, 10L), "tmp <- tempdir()") expect_lint(paste(lines, collapse = "\n"), "undesirable", undesirable_function_linter(c(tempdir = NA))) }) test_that("invalid inputs fail correctly", { error_msg <- "'fun' should be a non-empty named character vector" expect_error( undesirable_function_linter("***"), error_msg, fixed = TRUE ) expect_error( undesirable_function_linter(c("***" = NA, NA)), error_msg, fixed = TRUE ) expect_error( undesirable_function_linter(fun = NULL), error_msg, fixed = TRUE ) expect_error( undesirable_function_linter(fun = character(0L)), error_msg, fixed = TRUE ) expect_error( undesirable_function_linter(symbol_is_undesirable = 1.0), "is.logical(symbol_is_undesirable) is not TRUE", fixed = TRUE ) }) lintr/tests/testthat/helper.R0000644000176200001440000000450314506330025016006 0ustar liggesusers# Helpers for lintr tests single_quote <- function(x) paste0("'", x, "'") double_quote <- function(x) paste0('"', x, '"') #' Trim some leading whitespace #' #' Trim `num` characters from the start of every line in `x`, or auto-detect to remove the maximum number whitespace #' from all lines while preserving relative indentation #' #' @param x a string containing newlines #' @param num number of characters to strip from the start of each line. #' `NULL` will auto-detect this based on the minimum number of leading spaces greater than one. #' #' @return A modified version of `x` with `num` characters removed from the start of every line and with a possible #' leading and trailing blank line removed. #' #' @examples #' my_var <- local({ #' out <- trim_some(" #' This will be the content #' of the file where #' only the following line #' is indented by two spaces. #' ") #' }) #' #' stopifnot(identical( #' my_var, #' "This will be the content\nof the file where\nonly the following line\n is indented by two spaces." #' )) trim_some <- function(x, num = NULL) { x <- rex::re_substitutes( x, rex::rex(list(start, any_blanks, newline) %or% list(newline, any_blanks, end)), replacement = "", global = TRUE ) if (is.null(num)) { ms <- rex::re_matches(x, "^\\s+", locations = TRUE, global = TRUE, options = "multi-line")[[1L]] num <- min(ms$end - ms$start) + 1L } rex::re_substitutes(x, rex::rex(start, n_times(any, num)), "", global = TRUE, options = "multi-line") } local_config <- function(config_dir, contents, filename = ".lintr", .local_envir = parent.frame()) { config_path <- file.path(config_dir, filename) writeLines(contents, config_path) withr::defer(unlink(config_path), envir = .local_envir) config_path } skip_if_not_r_version <- function(min_version) { if (getRversion() < min_version) { testthat::skip(paste("R version at least", min_version, "is required")) } } skip_if_not_utf8_locale <- function() { testthat::skip_if_not(l10n_info()[["UTF-8"]], "Not a UTF-8 locale") } pipes <- function(exclude = NULL) { if (getRversion() < "4.1.0") exclude <- unique(c(exclude, "|>")) all_pipes <- c( standard = "%>%", greedy = "%!>%", tee = "%T>%", assignment = "%<>%", extraction = "%$%", native = "|>" ) all_pipes[!all_pipes %in% exclude] } lintr/tests/testthat/test-rstudio_markers.R0000644000176200001440000000767614577052532020753 0ustar liggesuserstest_that("it returns markers which match lints", { skip_if_not_installed("mockery") mockery::stub(rstudio_source_markers, "rstudioapi::callFun", function(...) list(...)) mockery::stub(rstudio_source_markers, "rstudioapi::executeCommand", function(...) NULL) lint1 <- list(Lint( filename = "test_file", line_number = 1L, column_number = 2L, type = "error", line = "a line", message = "hi" )) class(lint1) <- "lints" lint1[[1L]]$linter <- "linter_name" marker1 <- rstudio_source_markers(lint1) expect_identical(marker1$name, "lintr") expect_identical(marker1$markers[[1L]]$type, lint1[[1L]]$type) expect_identical(marker1$markers[[1L]]$file, lint1[[1L]]$filename) expect_identical(marker1$markers[[1L]]$line, lint1[[1L]]$line_number) expect_identical(marker1$markers[[1L]]$column, lint1[[1L]]$column_number) expect_identical(marker1$markers[[1L]]$message, paste0("[", lint1[[1L]]$linter, "] ", lint1[[1L]]$message)) lint2 <- list( Lint( filename = "test_file", line_number = 1L, column_number = 2L, type = "error", line = "a line", message = "hi" ), Lint( filename = "test_file2", line_number = 10L, column_number = 1L, type = "warning", message = "test a message" ) ) class(lint2) <- "lints" lint2[[1L]]$linter <- "linter_name" lint2[[2L]]$linter <- "linter_name" marker2 <- rstudio_source_markers(lint2) expect_identical(marker2$name, "lintr") expect_identical(marker2$markers[[1L]]$type, lint2[[1L]]$type) expect_identical(marker2$markers[[1L]]$file, lint2[[1L]]$filename) expect_identical(marker2$markers[[1L]]$line, lint2[[1L]]$line_number) expect_identical(marker2$markers[[1L]]$column, lint2[[1L]]$column_number) expect_identical(marker2$markers[[1L]]$message, paste0("[", lint2[[1L]]$linter, "] ", lint2[[1L]]$message)) }) test_that("it prepends the package path if it exists", { skip_if_not_installed("mockery") mockery::stub(rstudio_source_markers, "rstudioapi::callFun", function(...) list(...)) mockery::stub(rstudio_source_markers, "rstudioapi::executeCommand", function(...) NULL) lint3 <- list(Lint( filename = "test_file", line_number = 1L, column_number = 2L, type = "error", line = "a line", message = "hi" )) class(lint3) <- "lints" attr(lint3, "path") <- "test" lint3[[1L]]$linter <- "linter_name" marker3 <- rstudio_source_markers(lint3) expect_identical(marker3$name, "lintr") expect_identical(marker3$basePath, "test") expect_identical(marker3$markers[[1L]]$type, lint3[[1L]]$type) expect_identical(marker3$markers[[1L]]$file, file.path("test", lint3[[1L]]$filename)) expect_identical(marker3$markers[[1L]]$line, lint3[[1L]]$line_number) expect_identical(marker3$markers[[1L]]$column, lint3[[1L]]$column_number) expect_identical(marker3$markers[[1L]]$message, paste0("[", lint3[[1L]]$linter, "] ", lint3[[1L]]$message)) }) test_that("it returns an empty list of markers if there are no lints", { skip_if_not_installed("mockery") mockery::stub(rstudio_source_markers, "rstudioapi::callFun", function(...) list(...)) mockery::stub(rstudio_source_markers, "rstudioapi::executeCommand", function(...) NULL) lint4 <- `class<-`(list(), "lints") marker4 <- rstudio_source_markers(lint4) expect_identical(marker4$name, "lintr") expect_identical(marker4$markers, list()) }) test_that("rstudio_source_markers apply to print within rstudio", { skip_if_not_installed("mockery") withr::local_options(lintr.rstudio_source_markers = TRUE) tmp <- withr::local_tempfile(lines = "1:ncol(x)") empty <- withr::local_tempfile(lines = character(0L)) mockery::stub(print.lints, "rstudioapi::hasFun", function(x, ...) TRUE) mockery::stub(print.lints, "rstudio_source_markers", function(x) cat("matched\n")) l <- lint(tmp, seq_linter()) expect_output(print(l), "matched", fixed = TRUE) l <- lint(empty, seq_linter()) expect_output(print(l), "matched", fixed = TRUE) }) lintr/tests/testthat/test-pipe_call_linter.R0000644000176200001440000000374214506330025021015 0ustar liggesuserstest_that("pipe_call_linter skips allowed usages", { linter <- pipe_call_linter() expect_lint("a %>% foo()", NULL, linter) expect_lint("a %>% foo(x)", NULL, linter) expect_lint("b %>% { foo(., ., .) }", NULL, linter) expect_lint("a %>% foo() %>% bar()", NULL, linter) # ensure it works across lines too lines <- trim_some(" a %>% foo() %>% bar() ") expect_lint(lines, NULL, linter) # symbol extraction is OK (don't force extract2(), e.g.) expect_lint("a %>% .$y %>% mean()", NULL, linter) # more complicated expressions don't pick up on nested symbols lines <- trim_some(" x %>% { tmp <- . bla <- foo %>% unrelated_stuff(tmp) my_combination_fun(tmp, bla) } ") expect_lint(lines, NULL, linter) # extraction pipe uses RHS symbols expect_lint("a %$% b", NULL, linter) }) test_that("pipe_call_linter blocks simple disallowed usages", { expect_lint( "x %>% foo", "Use explicit calls in magrittr pipes", pipe_call_linter() ) expect_lint( "x %>% foo() %>% bar", "Use explicit calls in magrittr pipes", pipe_call_linter() ) expect_lint( "x %>% foo %>% bar()", "Use explicit calls in magrittr pipes", pipe_call_linter() ) lines <- trim_some(" a %>% foo %>% bar() ") expect_lint( lines, "Use explicit calls in magrittr pipes", pipe_call_linter() ) }) local({ pipes <- pipes(exclude = c("%$%", "|>")) linter <- pipe_call_linter() patrick::with_parameters_test_that( "All pipe operators are caught", { expect_lint(sprintf("a %s foo()", pipe), NULL, linter) expect_lint(sprintf("a %s foo", pipe), sprintf("`a %s foo`", pipe), linter) }, pipe = pipes, .test_name = names(pipes) ) }) test_that("Multiple lints give custom messages", { expect_lint( trim_some(" a %>% b c %T>% d "), list( list(message = "%>%", line_number = 1L), list(message = "%T>%", line_number = 2L) ), pipe_call_linter() ) }) lintr/tests/testthat/test-absolute_path_linter.R0000644000176200001440000001162314577052532021730 0ustar liggesusers# styler: off test_that("is_root_path", { f <- lintr:::is_root_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("", "foo", "http://rseek.org/", "./", " /", "/foo", "'/'") y <- c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) expect_identical(f(x), y) x <- c("/", "//") y <- c(TRUE, FALSE) expect_identical(f(x), y) x <- c("~", "~/", "~//", "~bob2", "~foo_bar/") y <- c(TRUE, TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) x <- c("c:", "C:\\", "D:/", "C:\\\\", "D://") y <- c(TRUE, TRUE, TRUE, FALSE, FALSE) expect_identical(f(x), y) x <- c("\\\\", "\\\\localhost", "\\\\localhost\\") y <- c(TRUE, TRUE, TRUE) expect_identical(f(x), y) }) test_that("is_absolute_path", { f <- lintr:::is_absolute_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("/", "//", "/foo", "/foo/") y <- c(TRUE, FALSE, TRUE, TRUE) expect_identical(f(x), y) x <- c("~", "~/foo", "~/foo/", "~'") y <- c(TRUE, TRUE, TRUE, FALSE) expect_identical(f(x), y) x <- c("c:", "C:\\foo\\", "C:/foo/") y <- c(TRUE, TRUE, TRUE) expect_identical(f(x), y) x <- c("\\\\", "\\\\localhost", "\\\\localhost\\c$", "\\\\localhost\\c$\\foo") y <- c(TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) }) test_that("is_relative_path", { f <- lintr:::is_relative_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("/", "c:\\", "~/", "foo", "http://rseek.org/", "'./'") y <- c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) expect_identical(f(x), y) x <- c("/foo", "foo/", "foo/bar", "foo//bar", "./foo", "../foo") y <- c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) x <- c("\\\\", "\\foo", "foo\\", "foo\\bar", ".\\foo", "..\\foo", ".", "..", "../") y <- c(FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) }) test_that("is_path", { f <- lintr:::is_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("", "foo", "http://rseek.org/", "foo\nbar", "'foo/bar'", "'/'") y <- c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE) expect_identical(f(x), y) x <- c("c:", "..", "foo/bar", "foo\\bar", "~", "\\\\localhost") y <- c(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) }) test_that("is_valid_path", { f <- lintr:::is_valid_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("C:/asdf", "C:/asd*f", "a\\s:df", "a\\\nsdf") y <- c(TRUE, FALSE, FALSE, FALSE) expect_identical(f(x), y) x <- c("C:/asdf", "C:/asd*f", "a\\s:df", "a\\\nsdf") y <- c(TRUE, FALSE, FALSE, FALSE) expect_identical(f(x, lax = TRUE), y) x <- c("/asdf", "/asd*f", "/as:df", "/a\nsdf") y <- c(TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) x <- c("/asdf", "/asd*f", "/as:df", "/a\nsdf") y <- c(TRUE, FALSE, FALSE, FALSE) expect_identical(f(x, lax = TRUE), y) }) test_that("is_long_path", { f <- lintr:::is_long_path x <- character() y <- logical() expect_identical(f(x), y) x <- c("foo/", "/foo", "n/a", "Z:\\foo", "foo/bar", "~/foo", "../foo") y <- c(FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE) expect_identical(f(x), y) }) # styler: on test_that("returns the correct linting", { lint_msg <- rex::escape("Do not use absolute paths.") # strict mode linter <- absolute_path_linter(lax = FALSE) non_absolute_path_strings <- c( "..", "./blah", encodeString("blah\\file.txt") ) for (path in non_absolute_path_strings) { expect_lint(single_quote(path), NULL, linter) expect_lint(double_quote(path), NULL, linter) } expect_lint("\"'/'\"", NULL, linter) # nested quotes absolute_path_strings <- c( "/", "/blah/file.txt", encodeString("d:\\"), "E:/blah/file.txt", encodeString("\\\\"), encodeString("\\\\server\\path"), "~", "~james.hester/blah/file.txt", encodeString("/a\nsdf"), "/as:df" ) for (path in absolute_path_strings) { expect_lint(single_quote(path), lint_msg, linter) expect_lint(double_quote(path), lint_msg, linter) } # lax mode: no check for strings that are likely not paths (too short or with special characters) linter <- absolute_path_linter(lax = TRUE) unlikely_path_strings <- c( "/", encodeString("/a\nsdf/bar"), "/as:df/bar" ) for (path in unlikely_path_strings) { expect_lint(single_quote(path), NULL, linter) expect_lint(double_quote(path), NULL, linter) } }) test_that("raw strings are handled correctly", { skip_if_not_r_version("4.0.0") expect_lint('R"(./blah)"', NULL, absolute_path_linter(lax = FALSE)) expect_lint( "R'--[/blah/file.txt]--'", rex::rex("Do not use absolute paths."), absolute_path_linter(lax = FALSE) ) }) lintr/tests/testthat/checkstyle.xml0000644000176200001440000000054214457657444017311 0ustar liggesusers lintr/tests/testthat/test-brace_linter.R0000644000176200001440000002604214577052532020153 0ustar liggesuserstest_that("brace_linter lints braces correctly", { open_curly_msg <- rex::rex( "Opening curly braces should never go on their own line" ) closed_curly_msg <- rex::rex( "Closing curly-braces should always be on their own line, ", "unless they are followed by an else." ) linter <- brace_linter() expect_lint("blah", NULL, linter) expect_lint("a <- function() {\n}", NULL, linter) expect_lint("a <- function() { \n}", NULL, linter) expect_lint("a <- function() { 1 }", list(open_curly_msg, closed_curly_msg), linter) # allowed by allow_single_line expect_lint("a <- function() { 1 }", NULL, brace_linter(allow_single_line = TRUE)) expect_lint( trim_some(" a <- if(1) { 1} else { 2 } "), closed_curly_msg, linter ) expect_lint( trim_some(" a <- if(1) { 1 } else { 2} "), closed_curly_msg, linter ) expect_lint( trim_some(" a <- if(1) { 1} else { 2} "), list( closed_curly_msg, closed_curly_msg ), linter ) # }) is allowed expect_lint("eval(bquote({\n...\n}))", NULL, linter) # }] is too expect_lint("df[, {\n...\n}]", NULL, linter) # }, is allowed expect_lint( trim_some(" fun({ statements }, param)"), NULL, linter ) expect_lint( trim_some(" fun(function(a) { statements }, param)"), NULL, linter ) # ,<\n>{ is allowed expect_lint( trim_some(" switch( x, 'a' = do_something(x), 'b' = do_another(x), { do_first(x) do_second(x) } ) "), NULL, linter ) # a comment before ,<\n>{ is allowed expect_lint( trim_some(" switch( x, 'a' = do_something(x), 'b' = do_another(x), # comment { do_first(x) do_second(x) } ) "), NULL, linter ) # a comment before <\n>{ is allowed expect_lint( trim_some(" switch(stat, o = { x <- 0.01 }, # else { x <- 2 } ) "), NULL, linter ) expect_lint( trim_some(" fun( 'This is very very very long text.', { message('This is the code.') message('It\\'s stupid, but proves my point.') } ) "), NULL, linter ) # (\n{ is allowed optionally expect_lint( trim_some(" tryCatch( { print(1) }, error = function(err) { } ) "), NULL, linter ) # {{ }} is allowed expect_lint("{{ x }}", NULL, linter) expect_lint( trim_some(" pkg_name <- function(path = find_package()) { if (is.null(path)) { return(NULL) } else { read.dcf(file.path(path, \"DESCRIPTION\"), fields = \"Package\")[1] } } "), NULL, linter ) expect_lint( "a <- function() # comment { 1 }", open_curly_msg, linter ) expect_lint("a <- function()\n{\n 1 \n}", open_curly_msg, linter) expect_lint("a <- function()\n {\n 1 \n}", open_curly_msg, linter) expect_lint("a <- function()\n\t{\n 1 \n}", open_curly_msg, linter) # trailing comments are allowed expect_lint( trim_some(' if ("P" != "NP") { # what most people expect print("Cryptomania is possible") } '), NULL, linter ) }) test_that("brace_linter lints spaces before open braces", { linter <- brace_linter() lint_msg <- rex::rex("There should be a space before an opening curly brace.") expect_lint( "blah <- function(){\n}", list( message = lint_msg, column_number = 19L ), linter ) expect_lint( "\nblah <- function(){\n\n\n}", list( message = lint_msg, column_number = 19L ), linter ) # should also lint if/else expect_lint( "a <- if (a){\n} else{\n}", list( list(message = lint_msg, line_number = 1L, column_number = 12L), list(message = lint_msg, line_number = 2L, column_number = 7L) ), linter ) # should lint repeat{ expect_lint( "repeat{\nblah\n}", list(message = lint_msg, line_number = 1L, column_number = 7L), linter ) # should ignore strings and comments, as in regexes: expect_lint("grepl('(iss){2}', 'Mississippi')", NULL, linter) expect_lint( "x <- 123 # don't flag (paren){brace} if inside a comment", NULL, linter ) # should not be thrown when the brace lies on subsequent line expect_lint( trim_some(" x <- function() {2} "), list( rex::rex("Opening curly braces should never go on their own line"), rex::rex("Closing curly-braces should always be on their own line") ), # , but not lint_msg linter ) }) test_that("brace_linter lints else correctly", { linter <- brace_linter() expect_lint("if (TRUE) 1 else 2", NULL, linter) expect_lint("if (TRUE) 1", NULL, linter) lines_brace <- trim_some(" if (TRUE) { 1 } else { 2 } ") expect_lint(lines_brace, NULL, linter) # such usage is also not allowed by the style guide, but test anyway lines_unbrace <- trim_some(" foo <- function(x) { if (TRUE) 1 else 2 } ") expect_lint(lines_unbrace, NULL, linter) lines <- trim_some(" foo <- function(x) { if (x) { 1 } else { 2 } } ") expect_lint( lines, rex::rex("`else` should come on the same line as the previous `}`."), linter ) }) test_that("brace_linter lints function expressions correctly", { linter <- brace_linter() expect_lint("function(x) 4", NULL, linter) lines <- trim_some(" function(x) { x + 4 } ") expect_lint(lines, NULL, linter) lines <- trim_some(" function(x) x+4 ") expect_lint( lines, rex::rex("Any function spanning multiple lines should use curly braces."), linter ) }) test_that("brace_linter lints if/else matching braces correctly", { linter <- brace_linter() expect_lint("if (TRUE) 1 else 2", NULL, linter) expect_lint("if (TRUE) 1", NULL, linter) lines_brace <- trim_some(" if (TRUE) { 1 } else { 2 } ") expect_lint(lines_brace, NULL, linter) # such usage is also not allowed by the style guide, but test anyway lines_unbrace <- trim_some(" foo <- function(x) { if (TRUE) 1 else 2 } ") expect_lint(lines_unbrace, NULL, linter) # else if is OK lines_else_if <- trim_some(" if (x) { 1 } else if (y) { 2 } else { 3 } ") expect_lint(lines_else_if, NULL, linter) lines_if <- trim_some(" foo <- function(x) { if (x) { 1 } else 2 } ") expect_lint( lines_if, rex::rex("Either both or neither branch in `if`/`else` should use curly braces."), linter ) lines_else <- trim_some(" foo <- function(x) { if (x) 1 else { 2 } } ") expect_lint( lines_else, rex::rex("Either both or neither branch in `if`/`else` should use curly braces."), linter ) }) # Keep up to date with https://github.com/tidyverse/style/issues/191 test_that("empty brace expressions are always allowed inline", { expect_lint("while (FALSE) {}", NULL, brace_linter()) expect_lint("while (FALSE) { }", NULL, brace_linter()) # only applies when `{` is "attached" to the preceding token on the same line expect_lint("while (FALSE)\n{}", rex::rex("Opening curly braces"), brace_linter()) expect_lint("while (FALSE)\n{ }", rex::rex("Opening curly braces"), brace_linter()) expect_lint("while (FALSE) {}", NULL, brace_linter(allow_single_line = TRUE)) expect_lint("while (FALSE) { }", NULL, brace_linter(allow_single_line = TRUE)) }) test_that("formula syntax is linted properly", { linter <- brace_linter() lint_msg_open <- rex::rex("Opening curly braces should never go on their own line") lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line") expect_lint( trim_some(" map( .x = 1:4, .f = ~ { .x + 1 } )"), NULL, linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ {.x + 1} )"), list( list(message = lint_msg_open, line_number = 3L, column_number = 10L), list(message = lint_msg_closed, line_number = 3L, column_number = 17L) ), linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ { .x + 1 } )"), list( list(message = lint_msg_open, line_number = 3L, column_number = 10L) ), linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ { .x + 1} )"), list( list(message = lint_msg_closed, line_number = 4L, column_number = 17L) ), linter ) }) test_that("code with pipes is handled correctly", { linter <- brace_linter() lint_msg_open <- rex::rex("Opening curly braces should never go on their own line") lint_msg_closed <- rex::rex("Closing curly-braces should always be on their own line") expect_lint( trim_some(" out <- lapply(stuff, function(i) { do_something(i) }) %>% unlist "), NULL, linter ) expect_lint( trim_some(" 1:4 %!>% { sum(.) } "), NULL, linter ) # %>%\n{ is allowed expect_lint( trim_some(" 1:4 %T>% { sum(.) } "), NULL, linter ) expect_lint( trim_some(" xx %<>% { sum(.) } "), list( list(message = lint_msg_open, line_number = 1L, column_number = 9L) ), linter ) expect_lint( trim_some(" x %>% { uvwxyz } "), list( list(message = lint_msg_closed, line_number = 3L, column_number = 12L) ), linter ) expect_lint( trim_some(" 1:4 %>% { sum(.) } "), list( list(message = lint_msg_closed, line_number = 2L, column_number = 12L) ), linter ) expect_lint( "1:4 %>% { sum(.) }", list( list(message = lint_msg_open, line_number = 1L, column_number = 9L), list(message = lint_msg_closed, line_number = 1L, column_number = 18L) ), linter ) skip_if_not_r_version("4.1.0") expect_lint( trim_some(" out <- lapply(stuff, function(i) { do_something(i) }) |> unlist() "), NULL, linter ) expect_lint( "local({ 1:4 |> sum() })", list( list(message = lint_msg_open, line_number = 1L, column_number = 7L) ), linter ) }) test_that("function shorthand is treated like 'full' function", { skip_if_not_r_version("4.1.0") linter <- brace_linter() expect_lint("a <- \\() { \n}", NULL, linter) expect_lint( trim_some(" x <- \\() {2} "), list( rex::rex("Opening curly braces should never go on their own line"), rex::rex("Closing curly-braces should always be on their own line") ), linter ) }) lintr/tests/testthat/test-function_left_parentheses_linter.R0000644000176200001440000001176014510656222024331 0ustar liggesuserstest_that("function_left_parentheses_linter skips allowed usages", { linter <- function_left_parentheses_linter() expect_lint("blah", NULL, linter) expect_lint("print(blah)", NULL, linter) expect_lint('"print"(blah)', NULL, linter) expect_lint("base::print(blah)", NULL, linter) expect_lint('base::"print"(blah)', NULL, linter) expect_lint("base::print(blah, fun(1))", NULL, linter) expect_lint("blah <- function(blah) { }", NULL, linter) expect_lint("(1 + 1)", NULL, linter) expect_lint("( (1 + 1) )", NULL, linter) expect_lint("if (blah) { }", NULL, linter) expect_lint("for (i in j) { }", NULL, linter) expect_lint("1 * (1 + 1)", NULL, linter) expect_lint("!(1 == 1)", NULL, linter) expect_lint("(2 - 1):(3 - 1)", NULL, linter) expect_lint("c(1, 2, 3)[(2 - 1)]", NULL, linter) expect_lint("list(1, 2, 3)[[(2 - 1)]]", NULL, linter) expect_lint("range(10)[(2 - 1):(10 - 1)]", NULL, linter) expect_lint("function(){function(){}}()()", NULL, linter) expect_lint("c(function(){})[1]()", NULL, linter) expect_lint("function(x) (mean(x) + 3)", NULL, linter) expect_lint("\"blah (1)\"", NULL, linter) }) test_that("function_left_parentheses_linter blocks disallowed usages", { linter <- function_left_parentheses_linter() fun_lint_msg <- rex::rex("Remove spaces before the left parenthesis in a function definition.") call_lint_msg <- rex::rex("Remove spaces before the left parenthesis in a function call.") expect_lint("blah (1)", call_lint_msg, linter) expect_lint("base::print (blah)", call_lint_msg, linter) expect_lint("base::print(blah, f (1))", call_lint_msg, linter) expect_lint("`+` (1, 1)", call_lint_msg, linter) expect_lint("test <- function (x) { }", fun_lint_msg, linter) expect_lint( "blah (1)", list(message = call_lint_msg, column_number = 5L, ranges = list(c(5L, 6L))), linter ) expect_lint( "test <- function (x) { }", list(message = fun_lint_msg, column_number = 17L, ranges = list(c(17L, 18L))), linter ) }) test_that("multi-line cases are handled correctly", { linter <- function_left_parentheses_linter() call_lint_msg <- rex::rex("Left parenthesis should be on the same line as the function's symbol.") fun_lint_msg <- rex::rex("Left parenthesis should be on the same line as the 'function' symbol.") expect_lint( trim_some(" foo <- function ( param ) { param + 1 } "), fun_lint_msg, linter ) # edge case where '(' is in the right place on the wrong line expect_lint( trim_some(" foo <- function ( param ) { param + 1 } "), fun_lint_msg, linter ) # ditto for function calls expect_lint( trim_some(" if ( y > sum ( x ) ) { TRUE } "), call_lint_msg, linter ) expect_lint( trim_some(" if ( y > sum ( x ) ) { TRUE } "), call_lint_msg, linter ) }) test_that("multi-lint case works", { expect_lint( trim_some(" if ( y > sum ( x ) && z > mean ( x ) && a > sd (x) && b > var (x) ) { TRUE } "), list( list( message = "Left parenthesis should be on the same line as the function's symbol.", line_number = 2L, ranges = list(c(7L, 9L)) ), list( message = "Left parenthesis should be on the same line as the function's symbol.", line_number = 6L, ranges = list(c(7L, 10L)) ), list( message = "Remove spaces before the left parenthesis in a function call.", line_number = 10L, ranges = list(c(9L, 9L)) ), list( message = "Remove spaces before the left parenthesis in a function call.", line_number = 11L, ranges = list(10L:11L) ) ), function_left_parentheses_linter() ) }) test_that("it doesn't produce invalid lints", { # Part of #1427 expect_no_warning( expect_lint( "function() {)", list(linter = "error"), function_left_parentheses_linter() ) ) }) test_that("newline in character string doesn't trigger false positive (#1963)", { linter <- function_left_parentheses_linter() expect_lint('foo("\n")$bar()', NULL, linter) # also corrected the lint metadata for similar cases expect_lint( trim_some(' ( foo(" ")$bar () ) '), # attach to 'b' in '$bar' list(line_number = 3L, column_number = 6L), linter ) }) test_that("shorthand functions are handled", { skip_if_not_r_version("4.1.0") linter <- function_left_parentheses_linter() fun_lint_msg <- rex::rex("Remove spaces before the left parenthesis in a function definition.") expect_lint("blah <- \\(blah) { }", NULL, linter) expect_lint("\\(){\\(){}}()()", NULL, linter) expect_lint("test <- \\ (x) { }", fun_lint_msg, linter) }) lintr/tests/testthat/test-cyclocomp_linter.R0000644000176200001440000000131014577052532021056 0ustar liggesuserstest_that("returns the correct linting", { cc_linter_1 <- cyclocomp_linter(1L) cc_linter_2 <- cyclocomp_linter(2L) lint_msg <- rex::rex("Functions should have cyclomatic complexity") expect_lint("if (TRUE) 1 else 2", NULL, cc_linter_2) expect_lint("if (TRUE) 1 else 2", lint_msg, cc_linter_1) expect_lint( "function(x) {not parsing}", "unexpected symbol", cc_linter_2 ) complexity <- readLines( system.file("example/complexity.R", package = "lintr") ) expect_lint(complexity, lint_msg, cc_linter_2) expect_lint( complexity, "should have cyclomatic complexity of less than 2, this has 10", cc_linter_2 ) expect_lint(complexity, NULL, cyclocomp_linter(10L)) }) lintr/tests/testthat/test-class_equals_linter.R0000644000176200001440000000314614577052532021556 0ustar liggesuserstest_that("class_equals_linter skips allowed usages", { linter <- class_equals_linter() expect_lint("class(x) <- 'character'", NULL, linter) expect_lint("class(x) = 'character'", NULL, linter) # proper way to test exact class expect_lint("identical(class(x), c('glue', 'character'))", NULL, linter) expect_lint("is_lm <- inherits(x, 'lm')", NULL, linter) }) test_that("class_equals_linter blocks simple disallowed usages", { linter <- class_equals_linter() lint_msg <- rex::rex("Instead of comparing class(x) with ==") expect_lint("if (class(x) == 'character') stop('no')", lint_msg, linter) expect_lint("is_regression <- class(x) == 'lm'", lint_msg, linter) expect_lint("is_regression <- 'lm' == class(x)", lint_msg, linter) }) test_that("class_equals_linter blocks usage of %in% for checking class", { linter <- class_equals_linter() lint_msg <- rex::rex("Instead of comparing class(x) with %in%") expect_lint("if ('character' %in% class(x)) stop('no')", lint_msg, linter) expect_lint("if (class(x) %in% 'character') stop('no')", lint_msg, linter) }) test_that("class_equals_linter blocks class(x) != 'klass'", { expect_lint( "if (class(x) != 'character') TRUE", rex::rex("Instead of comparing class(x) with !="), class_equals_linter() ) }) # as seen, e.g. in base R test_that("class_equals_linter skips usage for subsetting", { linter <- class_equals_linter() expect_lint("class(x)[class(x) == 'foo']", NULL, linter) # but not further nesting expect_lint( "x[if (class(x) == 'foo') 1 else 2]", rex::rex("Instead of comparing class(x) with =="), linter ) }) lintr/tests/testthat/knitr_malformed/0000755000176200001440000000000014450447225017570 5ustar liggesuserslintr/tests/testthat/knitr_malformed/incomplete_r_block.Rmd0000644000176200001440000000115114250050336024053 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. lintr/tests/testthat/knitr_malformed/incomplete_r_block.qmd0000644000176200001440000000115114450447225024123 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. lintr/tests/testthat/test-infix_spaces_linter.R0000644000176200001440000001504314577052532021551 0ustar liggesuserstest_that("returns the correct linting", { ops <- c( "+", "-", "~", "=", "==", "!=", "<=", ">=", "<-", ":=", "<<-", "<", ">", "->", "->>", "%%", "/", "*", "|", "||", "&", "&&", "%>%", "%Anything%", "%+%", NULL ) linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") expect_lint("blah", NULL, linter) for (op in ops) { expect_lint(paste0("1 ", op, " 2"), NULL, linter) expect_lint(paste0("1 ", op, "\n2"), NULL, linter) expect_lint(paste0("1 ", op, "\n 2"), NULL, linter) expect_lint(paste0("1", op, "2"), lint_msg, linter) # unary plus and minus can have no space before them if (!op %in% ops[1L:2L]) { expect_lint(paste0("1 ", op, "2"), lint_msg, linter) } expect_lint(paste0("1", op, " 2"), lint_msg, linter) } expect_lint("b <- 2E+4", NULL, linter) expect_lint("a <- 1e-3", NULL, linter) expect_lint("a[-1]", NULL, linter) expect_lint("a[-1 + 1]", NULL, linter) expect_lint("a[1 + -1]", NULL, linter) expect_lint("fun(a=1)", lint_msg, linter) }) test_that("The three `=` are all linted", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") # EQ_ASSIGN in the parse data expect_lint("a=1", lint_msg, linter) # EQ_FORMALS in the parse data expect_lint("foo <- function(x=1) {}", lint_msg, linter) # EQ_SUB in the parse data expect_lint("foo(x=1)", lint_msg, linter) }) test_that("exclude_operators works", { lint_msg <- rex::rex("Put spaces around all infix operators.") expect_lint("a+b", NULL, infix_spaces_linter(exclude_operators = "+")) expect_lint( trim_some(" a+b a-b "), NULL, infix_spaces_linter(exclude_operators = c("+", "-")) ) # operators match on text, not hidden node expect_lint("a<<-1", lint_msg, infix_spaces_linter(exclude_operators = "<-")) expect_lint("a<<-1", NULL, infix_spaces_linter(exclude_operators = "<<-")) expect_lint("a:=1", lint_msg, infix_spaces_linter(exclude_operators = "<-")) expect_lint("a:=1", NULL, infix_spaces_linter(exclude_operators = ":=")) expect_lint("a->>1", lint_msg, infix_spaces_linter(exclude_operators = "->")) expect_lint("a->>1", NULL, infix_spaces_linter(exclude_operators = "->>")) expect_lint("a%any%1", NULL, infix_spaces_linter(exclude_operators = "%%")) expect_lint("function(a=1) { }", NULL, infix_spaces_linter(exclude_operators = "=")) expect_lint("foo(a=1)", NULL, infix_spaces_linter(exclude_operators = "=")) }) # more tests specifically for assignment test_that("assignment cases return the correct linting", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") expect_lint("fun(blah = 1)", NULL, linter) expect_lint("blah <- 1", NULL, linter) expect_lint("blah = 1", NULL, linter) expect_lint("\"my = variable\" <- 42.0", NULL, linter) expect_lint("if (0 < 1) x <- 42L", NULL, linter) expect_lint( trim_some(" if (0 < 1) { x <- 42L }"), NULL, linter ) expect_lint("my = bad = variable = name <- 2.0", NULL, linter) expect_lint("blah<- 1", lint_msg, linter) expect_lint("blah <-1", lint_msg, linter) expect_lint("blah= 1", lint_msg, linter) expect_lint("blah =1", lint_msg, linter) }) test_that("infix_spaces_linter can allow >1 spaces optionally", { expect_lint( "x ~ 1", rex::rex("Put exactly one space on each side of infix operators."), infix_spaces_linter(allow_multiple_spaces = FALSE) ) expect_lint( "x - 1", rex::rex("Put exactly one space on each side of infix operators."), infix_spaces_linter(allow_multiple_spaces = FALSE) ) expect_lint( "x / 1", rex::rex("Put exactly one space on each side of infix operators."), infix_spaces_linter(allow_multiple_spaces = FALSE) ) }) test_that("exception for box::use()", { linter <- infix_spaces_linter() expect_lint("box::use(a/b)", NULL, linter) expect_lint("box::use(./a/b)", NULL, linter) expect_lint( trim_some(" box::use( a, a/b, ../a, alias = a/b/c[xyz = abc, ...], ) "), NULL, linter ) }) test_that("multi-line, multi-expression case is caught", { expect_lint( trim_some(" x + y+ z "), rex::rex("Put spaces around all infix operators."), infix_spaces_linter() ) }) test_that("Rules around missing arguments are respected", { linter <- infix_spaces_linter() lint_msg <- rex::rex("Put spaces around all infix operators.") expect_lint("switch(a = , b = 2)", NULL, linter) expect_lint("alist(missing_arg = )", NULL, linter) expect_lint("switch(a =, b = 2)", lint_msg, linter) expect_lint("alist(missing_arg =)", lint_msg, linter) }) test_that("native pipe is supported", { skip_if_not_r_version("4.1.0") linter <- infix_spaces_linter() expect_lint("a |> foo()", NULL, linter) expect_lint("a|>foo()", rex::rex("Put spaces around all infix operators."), linter) }) test_that("mixed unary & binary operators aren't mis-lint", { expect_lint( "-1-1", list( message = rex::rex("Put spaces around all infix operators."), column_number = 3L ), infix_spaces_linter() ) }) test_that("parse tags are accepted by exclude_operators", { expect_lint("sum(x, na.rm=TRUE)", NULL, infix_spaces_linter(exclude_operators = "EQ_SUB")) expect_lint("function(x, na.rm=TRUE) { }", NULL, infix_spaces_linter(exclude_operators = "EQ_FORMALS")) expect_lint("x=1", NULL, infix_spaces_linter(exclude_operators = "EQ_ASSIGN")) # uses parse_tag expect_lint("1+1", NULL, infix_spaces_linter(exclude_operators = "'+'")) # mixing text <- "x=function(a=foo(bar=1)) { }" col_assign <- list(column_number = 2L) col_formals <- list(column_number = 13L) col_sub <- list(column_number = 21L) expect_lint(text, NULL, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS", "EQ_ASSIGN"))) expect_lint(text, col_assign, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS"))) expect_lint(text, col_formals, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_ASSIGN"))) expect_lint(text, col_sub, infix_spaces_linter(exclude_operators = c("EQ_FORMALS", "EQ_ASSIGN"))) expect_lint(text, list(col_assign, col_formals), infix_spaces_linter(exclude_operators = "EQ_SUB")) expect_lint(text, list(col_assign, col_sub), infix_spaces_linter(exclude_operators = "EQ_FORMALS")) expect_lint(text, list(col_formals, col_sub), infix_spaces_linter(exclude_operators = "EQ_ASSIGN")) }) lintr/tests/testthat/test-lint_dir.R0000644000176200001440000001031714577052532017324 0ustar liggesuserstest_that("lint all files in a directory", { # NB: not using .lintr in the test packages because # R CMD check doesn't like hidden files in any subdirectory withr::local_options(lintr.linter_file = "lintr_test_config") the_dir <- test_path("dummy_packages", "package", "vignettes") files <- list.files(the_dir) lints <- lint_dir(the_dir, parse_settings = FALSE) linted_files <- unique(names(lints)) expect_s3_class(lints, "lints") expect_identical(sort(linted_files), sort(files)) expect_output( lint_dir(the_dir, parse_settings = FALSE, show_progress = TRUE), "======", fixed = TRUE ) }) test_that("lint all relevant directories in a package", { withr::local_options(lintr.linter_file = "lintr_test_config") the_pkg <- test_path("dummy_packages", "package") files <- setdiff( list.files(the_pkg, recursive = TRUE), c("package.Rproj", "DESCRIPTION", "NAMESPACE", "lintr_test_config") ) lintr:::read_settings(NULL) lints <- lint_package(the_pkg, parse_settings = FALSE) linted_files <- unique(names(lints)) # lintr paths contain backslash on windows, list.files uses forward slash. linted_files <- gsub("\\", "/", linted_files, fixed = TRUE) expect_s3_class(lints, "lints") expect_identical(sort(linted_files), sort(files)) # Code coverage is not detected for default_linters. # We want to ensure that object_name_linter uses namespace_imports correctly. # assignment_linter is needed to cause a lint in all vignettes. linters <- list(assignment_linter(), object_name_linter()) lintr:::read_settings(NULL) lints <- lint_package(the_pkg, linters = linters, parse_settings = FALSE) linted_files <- unique(names(lints)) # lintr paths contain backslash on windows, list.files uses forward slash. linted_files <- gsub("\\", "/", linted_files, fixed = TRUE) expect_s3_class(lints, "lints") expect_identical(sort(linted_files), sort(files)) }) test_that("respects directory exclusions", { the_dir <- withr::local_tempdir() the_excluded_dir <- file.path(the_dir, "exclude-me") dir.create(the_excluded_dir) defaults_path <- test_path("default_linter_testcode.R") file.copy(defaults_path, the_dir) file.copy(defaults_path, the_excluded_dir) file.copy(defaults_path, file.path(the_excluded_dir, "bad2.R")) lints <- lint_dir(the_dir, exclusions = "exclude-me") linted_files <- unique(names(lints)) expect_length(linted_files, 1L) expect_identical(linted_files, "default_linter_testcode.R") lints_norm <- lint_dir(the_dir, exclusions = "exclude-me", relative_path = FALSE) linted_files <- unique(names(lints_norm)) expect_length(linted_files, 1L) expect_identical(linted_files, normalizePath(file.path(the_dir, "default_linter_testcode.R"))) }) test_that("respect directory exclusions from settings", { the_dir <- withr::local_tempdir() the_excluded_dir <- file.path(the_dir, "exclude-me") dir.create(the_excluded_dir) defaults_path <- test_path("default_linter_testcode.R") file.copy(defaults_path, the_dir) file.copy(defaults_path, the_excluded_dir) file.copy(defaults_path, file.path(the_excluded_dir, "bad2.R")) cat("exclusions:\n 'exclude-me'\n", file = file.path(the_dir, ".lintr")) lints <- lint_dir(the_dir) linted_files <- unique(names(lints)) expect_length(linted_files, 1L) }) test_that("lint_dir works with specific linters without specifying other arguments", { withr::local_options(lintr.linter_file = "lintr_test_config") the_dir <- test_path("dummy_packages", "package", "vignettes") expect_length(lint_dir(the_dir, assignment_linter(), parse_settings = FALSE), 14L) expect_length(lint_dir(the_dir, commented_code_linter(), parse_settings = FALSE), 0L) }) test_that("lint_dir errors for relative_path= in 2nd positional argument", { the_dir <- test_path("dummy_packages", "package", "vignettes") expect_error( lint_dir(the_dir, FALSE), "'relative_path' is no longer available as a positional argument", fixed = TRUE ) }) test_that("typo in argument name gives helpful error", { expect_error(lint_dir(litners = identity), "Found unknown arguments in [.][.][.].*[?]lint_dir ") }) test_that("linting empty directory passes", { expect_length(lint_dir(withr::local_tempdir(), any_duplicated_linter()), 0L) }) lintr/tests/testthat/test-make_linter_from_xpath.R0000644000176200001440000000230114510650642022224 0ustar liggesuserstest_that("basic usage works", { linter <- make_linter_from_xpath("//NUM_CONST", "Number") expect_type(linter, "closure") expect_lint("1", list("Number", type = "warning"), linter()) expect_lint("'a'", "Letter", make_linter_from_xpath("//STR_CONST", "Letter")()) expect_lint("'a'", "Letter", make_linter_from_xpath("//STR_CONST", "Letter", level = "file")()) expect_lint("'a'", list("Letter", type = "style"), make_linter_from_xpath("//STR_CONST", "Letter", type = "style")()) }) test_that("input validation works", { expect_error( make_linter_from_xpath("//NUM_CONST", "Number", type = "x"), 'one of "warning", "style", "error"', fixed = TRUE ) expect_error( make_linter_from_xpath("//NUM_CONST", "Number", level = "x"), 'one of "expression", "file"', fixed = TRUE ) err_msg <- if (getRversion() < "4.0.0") "is.character(xpath)" else "xpath should be a character string" expect_error(make_linter_from_xpath(FALSE), err_msg, fixed = TRUE) expect_error(make_linter_from_xpath(letters), err_msg, fixed = TRUE) expect_error(make_linter_from_xpath(NA_character_), err_msg, fixed = TRUE) expect_error(make_linter_from_xpath(character()), err_msg, fixed = TRUE) }) lintr/tests/testthat/test-unnecessary_placeholder_linter.R0000644000176200001440000000234014577052532023773 0ustar liggesuserslinter <- unnecessary_placeholder_linter() pipes <- pipes(exclude = "|>") patrick::with_parameters_test_that( "unnecessary_placeholder_linter skips allowed usages", { # . used in position other than first --> ok expect_lint(sprintf("x %s foo(y, .)", pipe), NULL, linter) # ditto for nested usage expect_lint(sprintf("x %s foo(y, bar(.))", pipe), NULL, linter) # . used twice --> ok expect_lint(sprintf("x %s foo(., .)", pipe), NULL, linter) # . used as a keyword argument --> ok expect_lint(sprintf("x %s foo(arg = .)", pipe), NULL, linter) # . used inside a scope --> ok expect_lint(sprintf("x %s { foo(arg = .) }", pipe), NULL, linter) }, .test_name = names(pipes), pipe = pipes ) patrick::with_parameters_test_that( "unnecessary_placeholder_linter blocks simple disallowed usages", { expect_lint( sprintf("x %s sum(.)", pipe), rex::rex("Don't use the placeholder (`.`) when it's not needed"), unnecessary_placeholder_linter() ) expect_lint( sprintf("x %s y(.) %s sum()", pipe, pipe), rex::rex("Don't use the placeholder (`.`) when it's not needed"), unnecessary_placeholder_linter() ) }, .test_name = names(pipes), pipe = pipes ) lintr/tests/testthat/test-methods.R0000644000176200001440000001131214577052532017157 0ustar liggesuserstest_that("it returns the input if less than the max", { expect_identical(lintr:::trim_output(character()), character()) expect_identical(lintr:::trim_output("test", max = 10L), "test") }) test_that("it returns the input trimmed strictly to max if no lints found", { expect_identical(lintr:::trim_output("testing a longer non_lint string", max = 7L), "testing") }) test_that("it returns the input trimmed to the last full lint if one exists within the max", { t1 <- readChar(test_path("lints"), file.size(test_path("lints"))) if (.Platform$OS.type == "windows") { # Magic numbers expect newlines to be 1 character t1 <- gsub("\r\n", "\n", t1, fixed = TRUE) } expect_identical(lintr:::trim_output(t1, max = 200L), substr(t1, 1L, 195L)) expect_identical(lintr:::trim_output(t1, max = 400L), substr(t1, 1L, 380L)) expect_identical(lintr:::trim_output(t1, max = 2000L), substr(t1, 1L, 1930L)) }) test_that("as.data.frame.lints", { l1 <- Lint( "dummy.R", line_number = 1L, type = "style", message = "", line = "" ) # A minimum lint expect_s3_class(l1, "lint") expect_type(l1, "list") # A larger lint l2 <- Lint( "dummy.R", line_number = 2L, column_number = 6L, type = "error", message = "Under no circumstances is the use of foobar allowed.", line = "a <- 1", ranges = list(c(1L, 2L), c(6L, 7L)) ) expect_s3_class(l2, "lint") expect_warning( Lint("dummy.R", linter = "deprecated"), regexp = "deprecated", fixed = TRUE ) # Convert lints to data.frame lints <- list(l1, l2) class(lints) <- "lints" df <- as.data.frame(lints) expect_s3_class(df, "data.frame") exp <- data.frame( filename = rep("dummy.R", 2L), line_number = c(1L, 2L), column_number = c(1L, 6L), type = c("style", "error"), message = c("", "Under no circumstances is the use of foobar allowed."), line = c("", "a <- 1"), linter = c(NA_character_, NA_character_), # These are assigned in lint() now. stringsAsFactors = FALSE ) expect_identical(df, exp) }) test_that("summary.lints() works (no lints)", { no_lints <- lint("x <- 1\n", linters = assignment_linter()) no_lint_summary <- summary(no_lints) expect_s3_class(no_lint_summary, "data.frame") expect_identical(nrow(no_lint_summary), 0L) }) test_that("summary.lints() works (lints found)", { has_lints <- lint("x = 1\n", linters = assignment_linter()) has_lint_summary <- summary(has_lints) expect_s3_class(has_lint_summary, "data.frame") expect_identical(nrow(has_lint_summary), 1L) expect_gt(has_lint_summary$style, 0L) expect_identical(has_lint_summary$warning, 0L) expect_identical(has_lint_summary$error, 0L) }) test_that("print.lint works", { # don't treat \t as width-1, #528 l <- Lint( filename = "tmp", line_number = 1L, column_number = 3L, type = "warning", message = "this is a lint", line = c(`1` = "\t\t1:length(x)"), ranges = list(c(3L, 3L)) ) expect_output(print(l), " 1:length(x)", fixed = TRUE) }) test_that("print.lint works for inline data, even in RStudio", { skip_if_not_installed("mockery") l <- lint("x = 1\n") # Make sure lints print to console. # The full output is: # # > :1:3: style: Use <-, not =, for assignment. # > x = 1 # > ^ withr::with_options( list(lintr.rstudio_source_markers = FALSE), expect_output(print(l), "not =") ) mockery::stub(print.lints, "rstudioapi::hasFun", function(...) FALSE) withr::with_options( list(lintr.rstudio_source_markers = TRUE), expect_output(print(l), "not =") ) }) test_that("print.lints works", { withr::local_options(lintr.rstudio_source_markers = FALSE) tmp <- withr::local_tempfile() stopifnot(file.create(tmp)) expect_invisible(print(lint(tmp))) }) test_that("split.lint works as intended", { tmp <- withr::local_tempfile(lines = c("1:nrow(x)", "1:ncol(x)")) l <- lint(tmp, seq_linter()) expect_true(all(vapply(split(l), inherits, logical(1L), "lints"))) }) test_that("within.list is dispatched", { l <- lint(text = "a=1\nb=2", linters = infix_spaces_linter()) expect_silent({ # nolint next: implicit_assignment_linter. Workaround is pretty horrid. l <- lapply(l, within, line_number <- line_number + 1L) }) expect_identical(vapply(l, `[[`, integer(1L), "line_number"), 2L:3L) }) test_that("as_tibble.list is _not_ dispatched directly", { skip_if_not_installed("tibble") lints <- lint(text = "a = 1", linters = assignment_linter()) expect_identical(nrow(tibble::as_tibble(lints)), 1L) }) test_that("as.data.table.list is _not_ dispatched directly", { skip_if_not_installed("data.table") lints <- lint(text = "a = 1", linters = assignment_linter()) expect_identical(nrow(data.table::as.data.table(lints)), 1L) }) lintr/tests/testthat/test-literal_coercion_linter.R0000644000176200001440000001120314577052532022405 0ustar liggesuserstest_that("literal_coercion_linter skips allowed usages", { linter <- line_length_linter() # naive xpath includes the "_f0" here as a literal expect_lint('as.numeric(x$"_f0")', NULL, linter) expect_lint('as.numeric(x@"_f0")', NULL, linter) # only examine the first method for as. methods expect_lint("as.character(as.Date(x), '%Y%m%d')", NULL, linter) # we are as yet agnostic on whether to prefer literals over coerced vectors expect_lint("as.integer(c(1, 2, 3))", NULL, linter) # even more ambiguous for character vectors like here, where quotes are much # more awkward to type than a sequence of numbers expect_lint("as.character(c(1, 2, 3))", NULL, linter) # not possible to declare raw literals expect_lint("as.raw(c(1, 2, 3))", NULL, linter) # also not taking a stand on as.complex(0) vs. 0 + 0i expect_lint("as.complex(0)", NULL, linter) # ditto for as.integer(1e6) vs. 1000000L expect_lint("as.integer(1e6)", NULL, linter) # ditto for as.numeric(1:3) vs. c(1, 2, 3) expect_lint("as.numeric(1:3)", NULL, linter) }) test_that("literal_coercion_linter skips allowed rlang usages", { linter <- line_length_linter() expect_lint("int(1, 2.0, 3)", NULL, linter) expect_lint("chr('e', 'ab', 'xyz')", NULL, linter) expect_lint("lgl(0, 1)", NULL, linter) expect_lint("lgl(0L, 1)", NULL, linter) expect_lint("dbl(1.2, 1e5, 3L, 2E4)", NULL, linter) # make sure using namespace (`rlang::`) doesn't create problems expect_lint("rlang::int(1, 2, 3)", NULL, linter) # even if scalar, carve out exceptions for the following expect_lint("int(1.0e6)", NULL, linter) }) test_that("literal_coercion_linter skips quoted keyword arguments", { expect_lint("as.numeric(foo('a' = 1))", NULL, literal_coercion_linter()) }) skip_if_not_installed("tibble") patrick::with_parameters_test_that( "literal_coercion_linter blocks simple disallowed usages", expect_lint( sprintf("as.%s(%s)", out_type, input), lint_msg, literal_coercion_linter() ), .cases = tibble::tribble( ~.test_name, ~out_type, ~input, ~lint_msg, "lgl, from int", "logical", "1L", rex::rex("Use TRUE instead of as.logical(1L)"), "lgl, from num", "logical", "1", rex::rex("Use TRUE instead of as.logical(1)"), "lgl, from chr", "logical", '"true"', rex::rex('Use TRUE instead of as.logical("true")'), "int, from num", "integer", "1", rex::rex("Use 1L instead of as.integer(1)"), "num, from num", "numeric", "1", rex::rex("Use 1 instead of as.numeric(1)"), "dbl, from num", "double", "1", rex::rex("Use 1 instead of as.double(1)"), "chr, from num", "character", "1", rex::rex('Use "1" instead of as.character(1)'), "chr, from chr", "character", '"e"', rex::rex('Use "e" instead of as.character("e")'), "chr, from chr", "character", '"E"', rex::rex('Use "E" instead of as.character("E")'), # affirmatively lint as.(NA) should be NA__ "int, from NA", "integer", "NA", rex::rex("Use NA_integer_ instead of as.integer(NA)"), "num, from NA", "numeric", "NA", rex::rex("Use NA_real_ instead of as.numeric(NA)"), "dbl, from NA", "double", "NA", rex::rex("Use NA_real_ instead of as.double(NA)"), "chr, from NA", "character", "NA", rex::rex("Use NA_character_ instead of as.character(NA)") ) ) skip_if_not_installed("rlang") test_that("multiple lints return custom messages", { expect_lint( "c(as.integer(1), lgl(1L))", list( rex::rex("Use 1L instead of as.integer(1)"), rex::rex("Use TRUE instead of lgl(1L)") ), literal_coercion_linter() ) }) patrick::with_parameters_test_that( "literal_coercion_linter blocks rlang disallowed usages", expect_lint( sprintf("%s(%s)", out_type, input), lint_msg, literal_coercion_linter() ), # even if `as.character(1)` works, `chr(1)` doesn't, so no corresponding test case .cases = tibble::tribble( ~.test_name, ~out_type, ~input, ~lint_msg, "rlang::lgl", "lgl", "1L", rex::rex("Use TRUE instead of lgl(1L)"), "rlang::lgl[ns]", "rlang::lgl", "1L", rex::rex("Use TRUE instead of rlang::lgl(1L)"), "rlang::int", "int", "1.0", rex::rex("Use 1L instead of int(1.0)"), "rlang::dbl", "dbl", "1L", rex::rex("Use 1 instead of dbl(1L)"), "rlang::chr", "chr", '"e"', rex::rex('Use "e" instead of chr("e")'), "rlang::chr", "chr", '"E"', rex::rex('Use "E" instead of chr("E")') ) ) test_that("literal_coercion_linter blocks scalar rlang list2 construction", { expect_lint( "int(1, )", rex::rex("Use 1L instead of int(1,)"), literal_coercion_linter() ) }) lintr/tests/testthat/test-unneeded_concatenation_linter.R0000644000176200001440000000031514506330025023552 0ustar liggesuserstest_that("unneeded_concatenation_linter generates deprecation warning", { expect_warning( unneeded_concatenation_linter(), rex::rex("Linter unneeded_concatenation_linter was deprecated") ) }) lintr/tests/testthat/test-object_usage_linter.R0000644000176200001440000004231714510656223021526 0ustar liggesuserstest_that("returns the correct linting", { linter <- object_usage_linter() local_var_msg <- rex::rex("local variable", anything, "assigned but may not be used") expect_lint("blah", NULL, linter) expect_lint( trim_some(" function() { a <- 1 a } "), NULL, linter ) expect_lint( trim_some(" fun <- function(x) { fun(1) } fun2 <- function(x) { fun2(2) } "), NULL, linter ) expect_lint( trim_some(" fun <- function() { a <- 1 } "), local_var_msg, linter ) expect_lint( trim_some(" fun <- function() { a <- 1 1 } "), local_var_msg, linter ) expect_lint( trim_some(" fun <- function() { a <- 1 } "), local_var_msg, linter ) # same, using = for assignment expect_lint( trim_some(" fun = function() { a = 1 } "), local_var_msg, linter ) expect_lint( trim_some(" fun <- function() { a2 <- 1 a3 } "), list( local_var_msg, rex::rex("no visible binding for global variable ", anything) ), linter ) expect_lint( trim_some(" fun <- function() { fnu(1) } "), rex::rex("no visible global function definition for ", anything), linter ) # earlier we used n(1) but this might conflict with dplyr::n(), # so switch to use an obscure symbol expect_lint( trim_some(" fun <- function(x) { `__lintr_obj`(1) } "), rex::rex("no visible global function definition for ", anything), linter ) # setMethod and assign also checked expect_lint( trim_some(" assign('fun', function() { a <- 1 1 }) "), local_var_msg, linter ) expect_lint( trim_some(" setMethod('plot', 'numeric', function() { a <- 1 1 }) "), local_var_msg, linter ) }) test_that("replace_functions_stripped", { expect_lint( trim_some(" fun <- function(x) { `__lintr_obj`(x) = 1 } "), rex::rex("no visible global function definition for ", anything), object_usage_linter() ) expect_lint( trim_some(" fun <- function(x) { `__lintr_obj`(x) <- 1 } "), rex::rex("no visible global function definition for ", anything), object_usage_linter() ) }) test_that("eval errors are ignored", { expect_lint( trim_some(' setMethod("[[<-", c("stampedEnv", "character", "missing"), function(x) { x }) '), NULL, object_usage_linter() ) }) test_that("calls with top level function definitions are ignored", { expect_lint( 'tryCatch("foo", error = function(e) e)', NULL, object_usage_linter() ) }) test_that("object-usage line-numbers are relative to start-of-file", { expect_lint( trim_some(" a <- function(y) { y ** 2 } b <- function() { x } "), list(line_number = 5L), object_usage_linter() ) }) test_that("used symbols are detected correctly", { # From #666 expect_lint( trim_some(' foo <- data.frame(0) foo$bar <- 1 zero <- function() { file.info("/dev/null")$size } message(zero()) '), NULL, object_usage_linter() ) expect_lint( trim_some(" foo$bar <- 1 zero <- function() { foo } message(zero()) "), list("foo"), object_usage_linter() ) # Also test deeper nesting expect_lint( trim_some(' foo <- list(0) foo$bar$baz$goo <- 1 zero <- function() { file.info("/dev/null")$size foo$bar foo$bar$baz foo$bar$baz$goo } message(zero()) '), NULL, object_usage_linter() ) # Test alternative assignment and access methods expect_lint( trim_some(' foo <- list(0) foo[["bar"]][["baz"]][["goo"]] <- 1 zero <- function() { file.info("/dev/null")$size foo$bar foo$bar$baz foo$bar$baz$goo foo[["bar"]] foo[[c("bar", "baz")]] foo[["bar"]]$baz$goo } message(zero()) '), NULL, object_usage_linter() ) # regression #1322 expect_silent(expect_lint("assign('x', 42)", NULL, object_usage_linter())) }) test_that("object_usage_linter finds lints spanning multiple lines", { # Regression test for #507 expect_lint( trim_some(" foo <- function() { if (unknown_function()) NULL if (unknown_function()) { NULL } } "), list( list(message = "unknown_function", line_number = 2L), list(message = "unknown_function", line_number = 4L) ), object_usage_linter() ) # Linted symbol is not on the first line of the usage warning expect_lint( trim_some(" foo <- function(x) { with( x, unknown_symbol ) } "), list(message = "unknown_symbol", line_number = 4L, column_number = 5L), object_usage_linter(skip_with = FALSE) ) # Even ugly names are found expect_lint( trim_some(" foo <- function(x) { with( x, `\u2019regex_kill` ) } "), list(line_number = 4L, column_number = 5L), object_usage_linter(skip_with = FALSE) ) }) test_that("global variable detection works", { old_globals <- utils::globalVariables(package = globalenv()) utils::globalVariables("global_function", package = globalenv()) on.exit(utils::globalVariables(old_globals, package = globalenv(), add = FALSE)) expect_lint( trim_some(" foo <- function() { if (global_function()) NULL if (global_function()) { NULL } } "), NULL, object_usage_linter() ) }) test_that("package detection works", { expect_length( lint_package(test_path("dummy_packages", "package"), linters = object_usage_linter(), parse_settings = FALSE), 10L ) }) test_that("robust against errors", { expect_lint( 'assign("x", unknown_function)', NULL, object_usage_linter() ) }) test_that("interprets glue expressions", { linter <- object_usage_linter() expect_lint(trim_some(" fun <- function() { local_var <- 42 glue::glue('The answer is {local_var}.') } "), NULL, linter) # no need for namespace-qualification expect_lint(trim_some(" glue <- glue::glue # imitate this being an @import fun <- function() { local_var <- 42 glue('The answer is {local_var}.') } "), NULL, linter) # multiple variables in different interpolations expect_lint(trim_some(" fun <- function() { local_key <- 'a' local_value <- 123 glue::glue('Key-value pair: {local_key}={local_value}.') } "), NULL, linter) # multiple variables in single interpolation expect_lint(trim_some(" fun <- function() { local_str1 <- 'a' local_str2 <- 'b' glue::glue('With our powers combined: {paste(local_str1, local_str2)}.') } "), NULL, linter) # Check non-standard .open and .close expect_lint(trim_some(" fun <- function() { local_var <- 42 glue::glue('The answer is $[local_var].', .open = '$[', .close = ']') } "), NULL, linter) # Steer clear of custom .transformer and .envir constructs expect_lint(trim_some(" fun <- function() { local_var <- 42 glue::glue('The answer is {local_var}.', .transformer = glue::identity_transformer) } "), "local_var", linter) expect_lint(trim_some(" fun <- function() { local_var <- 42 e <- new.env() glue::glue('The answer is {local_var}.', .envir = e) } "), "local_var", linter) # unused is caught, glue-used is not expect_lint(trim_some(" fun <- function() { local_var <- 42 unused_var <- 3 glue::glue('The answer is {local_var}.') } "), "unused_var", linter) # glue-only is caught with option off expect_lint(trim_some(" fun <- function() { local_var <- 42 glue::glue('The answer is {local_var}.') } "), "local_var", object_usage_linter(interpret_glue = FALSE)) # call in glue is caught expect_lint( trim_some(" fun <- function() { local_call <- identity local_unused_call <- identity glue::glue('{local_call(1)}') } "), "local_unused_call", linter ) # ditto infix operator expect_lint(trim_some(" glue <- glue::glue # imitate this being an @import foo <- function() { `%++%` <- `+` glue('{x %++% y}') } "), NULL, linter) }) test_that("errors/edge cases in glue syntax don't fail lint()", { linter <- object_usage_linter() # no lint & no error, despite glue error expect_warning( expect_lint( trim_some(" fun <- function() { a <- 2 a + 1 glue::glue('The answer is {a') } "), NULL, linter ), "Evaluating glue expression.*failed: Expecting '\\}'.*Please ensure correct glue syntax" ) # generates a lint because the "usage" inside glue() is not detected expect_warning( expect_lint( trim_some(" fun <- function() { a <- 2 glue::glue('The answer is {a') } "), "local variable 'a'", linter ), "Evaluating glue expression.*failed: Expecting '\\}'" ) # complete {...}, but syntax error in code -> ignore expect_lint( trim_some(" fun <- function() { a <- 2 glue::glue('The answer is {a + }') } "), "local variable 'a'", linter ) # empty glue expression {} expect_lint( trim_some(" fun <- function() { a <- 2 glue::glue('The answer is {}: {a}') } "), NULL, linter ) # comment inside glue range (#1919) expect_lint( trim_some(" fun <- function() { a <- 2 glue::glue( 'The answer is {}: {a}' # show the answer ) } "), NULL, linter ) }) test_that("backtick'd names in glue are handled", { expect_lint( trim_some(" fun <- function() { `w` <- 2 x <- 3 y <- -4 `\\`y` <- 4 z <- -5 `z\\`` <- 5 glue::glue('{w}{`x`}{y}{z}') } "), list( rex::rex("local variable '`y'"), rex::rex("local variable 'z`'") ), object_usage_linter() ) }) # reported as #1088 test_that("definitions below top level are ignored (for now)", { expect_lint( trim_some(" local({ x <- 1 f <- function() { x } }) "), NULL, object_usage_linter() ) }) # reported as #1127 test_that("package imports are detected if present in file", { skip_if("package:xml2" %in% search()) expect_lint( trim_some(" dog <- function(url) { a <- read_xml(url) a } "), rex::rex("no visible global function definition for ", anything, "read_xml"), object_usage_linter() ) expect_lint( trim_some(" library(xml2) dog <- function(url) { a <- read_xml(url) a } "), NULL, object_usage_linter() ) }) test_that("fallback works", { expect_lint( trim_some(" f <- function() { `non_existing_assign<-`(1, 2) } "), list( message = rex::rex("no visible global function definition for ", anything, "non_existing_assign<-"), line_number = 2L, column_number = 3L ), object_usage_linter() ) }) test_that("unknown infix operators give good lint metadata", { expect_lint( trim_some(" foo <- function(x) { x %unknown-operator% 1 } "), list( message = rex::rex("no visible global function definition for '%unknown-operator%'"), line_number = 2L, column_number = 5L ), object_usage_linter() ) skip_if(any(c("package:rlang", "package:data.table") %in% search())) expect_lint( trim_some(' foo <- function(x) { x[, "new_col" := 2L] } '), list( message = rex::rex("no visible global function definition for ':='"), line_number = 2L, column_number = 17L ), object_usage_linter() ) }) test_that("respects `skip_with` argument for `with()` expressions", { f <- withr::local_tempfile( lines = c( "test_fun <- function(df) {", " with(df, first_var + second_var)", "}" ) ) expect_length(lint(f, object_usage_linter(skip_with = TRUE)), 0L) expect_length(lint(f, object_usage_linter(skip_with = FALSE)), 2L) }) test_that("missing libraries don't cause issue", { expect_lint( trim_some(" library(a.a.a.z.z.z) foo <- function() { a <- 1 a } "), NULL, object_usage_linter() ) }) test_that("messages without a quoted name are caught", { # regression test for #1714 expect_lint( trim_some(" foo <- function() { a <- ... a } "), list( message = "... may be used in an incorrect context", line_number = 2L ), object_usage_linter() ) }) # See #1914 test_that("symbols in formulas aren't treated as 'undefined global'", { expect_lint( trim_some(" foo <- function(x) { lm( y ~ z, data = x[!is.na(y)] ) } "), list( message = "no visible binding for global variable 'y'", line_number = 4L, column_number = 21L ), object_usage_linter() ) # neither on the RHS expect_lint( trim_some(" foo <- function(x) { lm( z ~ y, data = x[!is.na(y)] ) } "), list( message = "no visible binding for global variable 'y'", line_number = 4L, column_number = 21L ), object_usage_linter() ) # nor in nested expressions expect_lint( trim_some(" foo <- function(x) { lm( log(log(y)) ~ z, data = x[!is.na(y)] ) } "), list( message = "no visible binding for global variable 'y'", line_number = 4L, column_number = 21L ), object_usage_linter() ) # nor as a call # NB: I wanted this to be s(), as in mgcv::s(), but that # doesn't work in this test suite because it resolves to # rex::s() since we attach that in testthat.R expect_lint( trim_some(" foo <- function(x) { lm( y(w) ~ z, data = x[!is.na(y)] ) } "), list( message = "no visible binding for global variable 'y'", line_number = 4L, column_number = 21L ), object_usage_linter() ) }) test_that("NSE-ish symbols after $/@ are ignored as sources for lints", { expect_lint( trim_some(" foo <- function(x) { ggplot2::ggplot( x[!is.na(x$column), ], ggplot2::aes(x = column, fill = factor(x$grp)) ) } "), list( message = "no visible binding for global variable 'column'", line_number = 4L, column_number = 22L ), object_usage_linter() ) expect_lint( trim_some(" foo <- function(x) { ggplot2::ggplot( x[!is.na(x@column), ], ggplot2::aes(x = column, fill = factor(x$grp)) ) } "), list( message = "no visible binding for global variable 'column'", line_number = 4L, column_number = 22L ), object_usage_linter() ) }) test_that("functional lambda definitions are also caught", { skip_if_not_r_version("4.1.0") expect_lint( trim_some(" fun <- \\() { a <- 1 } "), rex::rex("local variable", anything, "assigned but may not be used"), object_usage_linter() ) }) test_that("messages without location info are repaired", { # regression test for #1986 expect_lint( trim_some(" foo <- function() no_fun() "), list( message = rex::rex("no visible global function definition for", anything), line_number = 1L, column_number = 19L ), object_usage_linter() ) expect_lint( trim_some(" foo <- function(a = no_fun()) a "), list( message = rex::rex("no visible global function definition for", anything), line_number = 1L, column_number = 21L ), object_usage_linter() ) expect_lint( trim_some(" foo <- function() no_global "), list( message = rex::rex("no visible binding for global variable", anything), line_number = 1L, column_number = 19L ), object_usage_linter() ) expect_lint( trim_some(" foo <- function() unused_local <- 42L "), list( message = rex::rex("local variable", anything, "assigned but may not be used"), line_number = 1L, column_number = 19L ), object_usage_linter() ) # More complex case with two lints and missing location info expect_lint( trim_some(" foo <- function() a <- bar() "), list( list( message = rex::rex("local variable", anything, "assigned but may not be used"), line_number = 1L, column_number = 19L ), list( message = rex::rex("no visible global function definition for", anything), line_number = 2L, column_number = 3L ) ), object_usage_linter() ) }) lintr/tests/testthat/test-is_numeric_linter.R0000644000176200001440000000473414577052532021240 0ustar liggesuserstest_that("is_numeric_linter skips allowed usages involving ||", { expect_lint("is.numeric(x) || is.integer(y)", NULL, is_numeric_linter()) # x is used, but not identically expect_lint("is.numeric(x) || is.integer(foo(x))", NULL, is_numeric_linter()) # not totally crazy, e.g. if input accepts a vector or a list expect_lint("is.numeric(x) || is.integer(x[[1]])", NULL, is_numeric_linter()) }) test_that("is_numeric_linter skips allowed usages involving %in%", { # false positives for class(x) %in% c('integer', 'numeric') style expect_lint("class(x) %in% 1:10", NULL, is_numeric_linter()) expect_lint("class(x) %in% 'numeric'", NULL, is_numeric_linter()) expect_lint("class(x) %in% c('numeric', 'integer', 'factor')", NULL, is_numeric_linter()) expect_lint("class(x) %in% c('numeric', 'integer', y)", NULL, is_numeric_linter()) }) test_that("is_numeric_linter blocks disallowed usages involving ||", { linter <- is_numeric_linter() lint_msg <- rex::rex("same as is.numeric(x) || is.integer(x)") expect_lint("is.numeric(x) || is.integer(x)", lint_msg, linter) # order doesn't matter expect_lint("is.integer(x) || is.numeric(x)", lint_msg, linter) # identical expressions match too expect_lint("is.integer(DT$x) || is.numeric(DT$x)", lint_msg, linter) # line breaks don't matter lines <- trim_some(" if ( is.integer(x) || is.numeric(x) ) TRUE ") expect_lint(lines, lint_msg, linter) # caught when nesting expect_lint("all(y > 5) && (is.integer(x) || is.numeric(x))", lint_msg, linter) # implicit nesting expect_lint("is.integer(x) || is.numeric(x) || is.logical(x)", lint_msg, linter) }) test_that("is_numeric_linter blocks disallowed usages involving %in%", { linter <- is_numeric_linter() lint_msg <- rex::rex('same as class(x) %in% c("integer", "numeric")') expect_lint("class(x) %in% c('integer', 'numeric')", lint_msg, linter) expect_lint('class(x) %in% c("numeric", "integer")', lint_msg, linter) }) test_that("raw strings are handled properly when testing in class", { skip_if_not_r_version("4.0.0") linter <- is_numeric_linter() lint_msg <- rex::rex('same as class(x) %in% c("integer", "numeric")') expect_lint("class(x) %in% c(R'(numeric)', 'integer', 'factor')", NULL, linter) expect_lint("class(x) %in% c('numeric', R'--(integer)--', y)", NULL, linter) expect_lint("class(x) %in% c(R'(integer)', 'numeric')", lint_msg, linter) expect_lint('class(x) %in% c("numeric", R"--[integer]--")', lint_msg, linter) }) lintr/tests/testthat/test-boolean_arithmetic_linter.R0000644000176200001440000000216414577052532022726 0ustar liggesuserstest_that("boolean_arithmetic_linter doesn't block allowed usages", { linter <- boolean_arithmetic_linter() expect_lint("!any(x == y)", NULL, linter) expect_lint("!any(grepl(pattern, x))", NULL, linter) }) test_that("boolean_arithmetic_linter requires use of any() or !any()", { linter <- boolean_arithmetic_linter() lint_msg <- rex::rex("Use any() to express logical aggregations.") expect_lint("length(which(x == y)) == 0", lint_msg, linter) # anything passed to which() can be assumed to be logical expect_lint("length(which(is_treatment)) == 0L", lint_msg, linter) # regex version expect_lint("length(grep(pattern, x)) == 0", lint_msg, linter) # sum version expect_lint("sum(x == y) == 0L", lint_msg, linter) expect_lint("sum(grepl(pattern, x)) == 0", lint_msg, linter) # non-== comparisons expect_lint("length(which(x == y)) > 0L", lint_msg, linter) expect_lint("length(which(is_treatment)) < 1", lint_msg, linter) expect_lint("length(grep(pattern, x)) >= 1L", lint_msg, linter) expect_lint("sum(x == y) != 0", lint_msg, linter) expect_lint("sum(grepl(pattern, x)) > 0L", lint_msg, linter) }) lintr/tests/testthat/test-if_not_else_linter.R0000644000176200001440000000542314577052532021365 0ustar liggesuserstest_that("if_not_else_linter skips allowed usages", { linter <- if_not_else_linter() # simple if/else statement is fine expect_lint("if (A) x else y", NULL, linter) # not plain negation --> OK expect_lint("if (!A || B) x else y", NULL, linter) # no else clause --> OK expect_lint("if (!A) x", NULL, linter) # nested statements are also OK expect_lint("if (!A) x else if (B) y", NULL, linter) expect_lint("if (!A) x else if (B) y else z", NULL, linter) expect_lint("if (A) x else if (B) y else if (!C) z", NULL, linter) # ! picked up in the evaluation statements is skipped expect_lint("if (A) !x else y", NULL, linter) expect_lint("if (A) x else !y", NULL, linter) }) test_that("if_not_else_linter blocks simple disallowed usages", { linter <- if_not_else_linter() lint_msg <- rex::rex("In a simple if/else statement, prefer `if (A) x else y`") expect_lint("if (!A) x else y", lint_msg, linter) expect_lint("if (!A) x else if (!B) y else z", lint_msg, linter) # ditto for more complicated expressions where ! is still the outer operator expect_lint("if (!x %in% 1:10) y else z", lint_msg, linter) }) patrick::with_parameters_test_that( "if_not_else_linter blocks usages in ifelse() and friends as well", { linter <- if_not_else_linter() expect_lint(sprintf("%s(!A | B, x, y)", ifelse_fun), NULL, linter) expect_lint(sprintf("%s(A, !x, y)", ifelse_fun), NULL, linter) expect_lint( sprintf("%s(!A, x, y)", ifelse_fun), sprintf("Prefer `%s[(]A, x, y[)]` to the less-readable", ifelse_fun), linter ) # particularly relevant for if_else() expect_lint(sprintf("%s(!!A, x, y)", ifelse_fun), NULL, linter) }, .test_name = c("ifelse", "fifelse", "if_else"), ifelse_fun = c("ifelse", "fifelse", "if_else") ) test_that("if_not_else_linter skips negated calls to is.null & similar", { linter <- if_not_else_linter() expect_lint("if (!is.null(x)) x else y", NULL, linter) expect_lint("if (!is.na(x)) x else y", NULL, linter) expect_lint("if (!missing(x)) x else y", NULL, linter) expect_lint("ifelse(!is.na(x), x, y)", NULL, linter) }) test_that("multiple lints are generated correctly", { expect_lint( trim_some("{ if (!A) x else B ifelse(!A, x, y) fifelse(!A, x, y) if_else(!A, x, y) }"), list( "In a simple if/else statement", "Prefer `ifelse", "Prefer `fifelse", "Prefer `if_else" ), if_not_else_linter() ) }) test_that("exceptions= argument works", { expect_lint( "if (!is.null(x)) x else y", "In a simple if/else statement", if_not_else_linter(exceptions = character()) ) expect_lint("if (!foo(x)) y else z", NULL, if_not_else_linter(exceptions = "foo")) }) # TODO(michaelchirico): should if (A != B) be considered as well? lintr/tests/testthat/test-expect_s3_class_linter.R0000644000176200001440000000635214577052532022163 0ustar liggesuserstest_that("expect_s3_class_linter skips allowed usages", { linter <- expect_s3_class_linter() # expect_s3_class doesn't have an inverted version expect_lint("expect_true(!inherits(x, 'class'))", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(!inherits(x, 'class'))", NULL, linter) # other is. calls are not suitable for expect_s3_class in particular expect_lint("expect_true(is.na(x))", NULL, linter) # case where expect_s3_class() *could* be used but we don't enforce expect_lint("expect_true(is.data.table(x))", NULL, linter) # expect_s3_class() doesn't have info= or label= arguments expect_lint("expect_equal(class(x), k, info = 'x should have class k')", NULL, linter) expect_lint("expect_equal(class(x), k, label = 'x class')", NULL, linter) expect_lint("expect_equal(class(x), k, expected.label = 'target class')", NULL, linter) expect_lint("expect_true(is.data.frame(x), info = 'x should be a data.frame')", NULL, linter) }) test_that("expect_s3_class_linter blocks simple disallowed usages", { expect_lint( "expect_equal(class(x), 'data.frame')", rex::rex("expect_s3_class(x, k) is better than expect_equal(class(x), k)"), expect_s3_class_linter() ) # works when testing against a sequence of classes too expect_lint( "expect_equal(class(x), c('data.table', 'data.frame'))", rex::rex("expect_s3_class(x, k) is better than expect_equal(class(x), k)"), expect_s3_class_linter() ) # expect_identical is treated the same as expect_equal expect_lint( "testthat::expect_identical(class(x), 'lm')", rex::rex("expect_s3_class(x, k) is better than expect_identical(class(x), k)"), expect_s3_class_linter() ) # yoda test with string literal in first arg also caught expect_lint( "expect_equal('data.frame', class(x))", rex::rex("expect_s3_class(x, k) is better than expect_equal(class(x), k)"), expect_s3_class_linter() ) # different equivalent usages expect_lint( "expect_true(is.table(foo(x)))", rex::rex("expect_s3_class(x, k) is better than expect_true(is.(x))"), expect_s3_class_linter() ) expect_lint( "expect_true(inherits(x, 'table'))", rex::rex("expect_s3_class(x, k) is better than expect_true(is.(x))"), expect_s3_class_linter() ) # TODO(michaelchirico): consider more carefully which sorts of class(x) %in% . and # . %in% class(x) calls should be linted #> expect_lint( #> "expect_true('lm' %in% class(x))", #> "expect_s3_class\\(x, k\\) is better than expect_equal\\(class\\(x\\), k", #> expect_s3_class_linter #> ) }) local({ # test for lint errors appropriately raised for all is. calls is_classes <- c( "data.frame", "factor", "numeric_version", "ordered", "package_version", "qr", "table", "relistable", "raster", "tclObj", "tkwin", "grob", "unit", "mts", "stepfun", "ts", "tskernel" ) patrick::with_parameters_test_that( "expect_true(is.) is caught", expect_lint( sprintf("expect_true(is.%s(x))", is_class), rex::rex("expect_s3_class(x, k) is better than expect_true(is.(x))"), expect_s3_class_linter() ), .test_name = is_classes, is_class = is_classes ) }) lintr/tests/testthat/test-lint.R0000644000176200001440000001620414577052532016467 0ustar liggesusers# The lints for a given file should be the same regardless of the working # directory test_that("lint() results do not depend on the working directory", { # Helper function: run assignment_linter on a given file lint_assignments <- function(filename) { lint(filename, linters = list(assignment_linter())) } # a dummy package for use in the test pkg_path <- test_path("dummy_packages", "assignmentLinter") # put a .lintr in the package root that excludes the first line of `R/jkl.R` local_config(pkg_path, "exclusions: list('R/jkl.R' = 1)") # linting the `R/jkl.R` should identify the following assignment lint on the # second line of the file expected_lines <- "mno = 789" # lint the file from: # - outside the package # - at the package root # - in the package's R/ directory lints_from_outside <- lint_assignments( file.path(pkg_path, "R", "jkl.R") ) lints_from_pkg_root <- withr::with_dir( pkg_path, lint_assignments(file.path("R", "jkl.R")) ) lints_from_a_subdir <- withr::with_dir( file.path(pkg_path, "R"), lint_assignments("jkl.R") ) expect_identical( as.data.frame(lints_from_pkg_root)[["line"]], expected_lines ) expect_identical( as.data.frame(lints_from_outside), as.data.frame(lints_from_pkg_root) ) expect_identical( as.data.frame(lints_from_a_subdir), as.data.frame(lints_from_pkg_root) ) }) # The lints for a given file should be the same regardless of where the .lintr # file is positioned (file-exclusions in the .lintr should be relative to the # directory containing the .lintr) test_that("lint() results do not depend on the position of the .lintr", { # .lintr config files for lint(filepath) are looked for in: # - the same directory as filepath # - the project directory # - the user's home directory lint_with_config <- function(config_dir, config_string, filename) { local_config(config_dir, config_string) lint(filename, linters = assignment_linter()) } # a dummy package for use in the test pkg_path <- test_path("dummy_packages", "assignmentLinter") # we lint the file /R/jkl.R using the pkg-root as working directory # and # - 1) a .lintr config in the package root, # - 2) a .lintr config in the source directory R/ # The second line of jkl.R contains the following assignment lint: expected_lines <- "mno = 789" lints_with_config_at_pkg_root <- withr::with_dir( pkg_path, lint_with_config( config_dir = ".", config_string = "exclusions: list('R/jkl.R' = 1)", filename = file.path("R", "jkl.R") ) ) lints_with_config_in_r_dir <- withr::with_dir( pkg_path, lint_with_config( config_dir = "R", config_string = "exclusions: list('jkl.R' = 1)", filename = file.path("R", "jkl.R") ) ) expect_identical( as.data.frame(lints_with_config_at_pkg_root)[["line"]], expected_lines ) expect_identical( as.data.frame(lints_with_config_at_pkg_root), as.data.frame(lints_with_config_in_r_dir), info = paste( "lints for a source file should be independent of whether the .lintr", "file is in the project-root or the source-file-directory" ) ) }) test_that("lint uses linter names", { expect_lint("a = 2", list(linter = "bla"), linters = list(bla = assignment_linter()), parse_settings = FALSE) }) test_that("lint() results from file or text should be consistent", { linters <- list(assignment_linter(), infix_spaces_linter()) lines <- c("x<-1", "x+1") file <- withr::local_tempfile(lines = lines) text <- paste0(lines, collapse = "\n") file <- normalizePath(file) lint_from_file <- lint(file, linters = linters) lint_from_lines <- lint(linters = linters, text = lines) lint_from_text <- lint(linters = linters, text = text) # Remove file before linting to ensure that lint works and do not # assume that file exists when both filename and text are supplied. expect_identical(unlink(file), 0L) lint_from_text2 <- lint(file, linters = linters, text = text) expect_length(lint_from_file, 2L) expect_length(lint_from_lines, 2L) expect_length(lint_from_text, 2L) expect_length(lint_from_text2, 2L) expect_identical(lint_from_file, lint_from_text2) for (i in seq_along(lint_from_lines)) { lint_from_file[[i]]$filename <- "" lint_from_lines[[i]]$filename <- "" lint_from_text[[i]]$filename <- "" } expect_identical(lint_from_file, lint_from_lines) expect_identical(lint_from_file, lint_from_text) }) test_that("exclusions work with custom linter names", { expect_lint( "a = 2 # nolint: bla.", NULL, linters = list(bla = assignment_linter()), parse_settings = FALSE ) }) test_that("compatibility warnings work", { expect_warning( expect_lint( "a == NA", "Use is.na", linters = equals_na_linter ), regexp = "Passing linters as variables", fixed = TRUE ) expect_warning( expect_lint( "a = 42", "Use <-", linters = assignment_linter ), regexp = "Passing linters as variables", fixed = TRUE ) # Also within `linters_with_defaults()` (#1725) expect_warning( expect_lint( "a = 42", "Use <-", linters = linters_with_defaults(assignment_linter) ), regexp = "Passing linters as variables", fixed = TRUE ) expect_warning( expect_lint( "a == NA", "Use is.na", linters = unclass(equals_na_linter()) ), regexp = "The use of linters of class 'function'", fixed = TRUE ) # Trigger compatibility in auto_names() expect_warning( expect_lint( "a == NA", "Use is.na", linters = list(unclass(equals_na_linter())) ), "The use of linters of class 'function'", fixed = TRUE ) expect_error( expect_warning( lint("a <- 1\n", linters = function(two, arguments) NULL), regexp = "The use of linters of class 'function'", fixed = TRUE ), regexp = "`fun` must be a function taking exactly one argument", fixed = TRUE ) expect_error( lint("a <- 1\n", linters = "equals_na_linter"), regexp = rex::rex("Expected '", anything, "' to be a function of class 'linter'") ) }) test_that("Deprecated positional usage of cache= errors", { expect_error( lint("a = 2\n", FALSE, linters = assignment_linter()), "'cache' is no longer available as a positional argument", fixed = TRUE ) }) test_that("Linters throwing an error give a helpful error", { tmp_file <- withr::local_tempfile(lines = "a <- 1") linter <- function() Linter(function(source_expression) stop("a broken linter")) # NB: Some systems/setups may use e.g. symlinked files when creating under tempfile(); # we don't care much about that, so just check basename() expect_error( lint(tmp_file, linter()), rex::rex("Linter 'linter' failed in ", anything, basename(tmp_file), ": a broken linter") ) expect_error( lint(tmp_file, list(broken_linter = linter())), rex::rex("Linter 'broken_linter' failed in ", anything, basename(tmp_file), ": a broken linter") ) }) test_that("typo in argument name gives helpful error", { expect_error(lint("xxx", litners = identity), "Found unknown arguments in [.][.][.].*[?]lint ") }) lintr/tests/testthat/test-unused_import_linter.R0000644000176200001440000000541414577052532021774 0ustar liggesuserstest_that("unused_import_linter lints as expected", { linter <- unused_import_linter() expect_lint("library(dplyr)\ntibble(a = 1)", NULL, linter) # SYMBOL_FUNCTION_CALL usage is detected expect_lint("library(tidyverse)\ntibble(a = 1)", NULL, linter) # SYMBOL usage is detected expect_lint("library(dplyr)\ndo.call(tibble, args = list(a = 1))", NULL, linter) # SPECIAL usage is detected expect_lint("library(magrittr)\n1:3 %>% mean()", NULL, linter) # dataset is detected expect_lint("library(dplyr)\nstarwars", NULL, linter) expect_lint("library(datasets)\nstate.center", NULL, linter) # Missing packages are ignored expect_lint("library(not.a.package)\ntibble(a = 1)", NULL, linter) # SYMBOL calls with character.only = TRUE are ignored, even if the argument is a package name expect_lint("library(dplyr, character.only = TRUE)\n1 + 1", NULL, linter) lint_msg <- rex::rex("Package 'dplyr' is attached but never used") msg_ns <- rex::rex("Package 'dplyr' is only used by namespace") expect_lint("library(dplyr)\n1 + 1", lint_msg, linter) expect_lint("require(dplyr)\n1 + 1", lint_msg, linter) expect_lint("library('dplyr')\n1 + 1", lint_msg, linter) expect_lint("library('dplyr', character.only = TRUE)\n1 + 1", lint_msg, linter) # ignore namespaced usages by default, but provide custom lint message expect_lint("library(dplyr)\ndplyr::tibble(a = 1)", msg_ns, linter) expect_lint("library(dplyr)\ndplyr::tibble(a = 1)", NULL, unused_import_linter(allow_ns_usage = TRUE)) # ignore packages in except_packages expect_lint("library(data.table)\n1 + 1", NULL, linter) expect_lint("library(dplyr)\n1 + 1", NULL, unused_import_linter(except_packages = "dplyr")) }) test_that("unused_import_linter handles message vectorization", { skip_if_not_installed("crayon") expect_lint( trim_some(" library(crayon) library(xmlparsedata) xmlparsedata::xml_parse_data(parse(text = 'a')) "), list( rex::rex("Package 'crayon' is attached but never used."), rex::rex("Package 'xmlparsedata' is only used by namespace") ), unused_import_linter() ) }) test_that("unused_import_linter lints packages with exports like pkg::pkg", { # glue::glue is an export, so don't get thrown off by the 'glue' symbol in library() expect_lint( trim_some(" library(glue) 1 + 1 "), rex::rex("Package 'glue' is attached but never used."), unused_import_linter() ) }) test_that("glue usages are seen", { lint_msg <- rex::rex("Package 'xmlparsedata' is attached but never used.") lines <- trim_some(" library(glue) library(xmlparsedata) glue('{ xml_parse_data() }') ") expect_lint(lines, NULL, unused_import_linter()) expect_lint(lines, lint_msg, unused_import_linter(interpret_glue = FALSE)) }) lintr/tests/testthat/test-implicit_integer_linter.R0000644000176200001440000000737614577052532022437 0ustar liggesusers# styler: off skip_if_not_installed("tibble") local({ # Note: cases indicated by "*" are whole numbers, but don't lint because the user has # effectively declared "this is a double" much as adding '.0' is otherwise accepted. cases <- tibble::tribble( ~num_value_str, ~should_lint, "Inf", FALSE, "NaN", FALSE, "TRUE", FALSE, "FALSE", FALSE, "NA", FALSE, "NA_character_", FALSE, "2.000", FALSE, "2.", FALSE, "2L", FALSE, "2.0", FALSE, "2.1", FALSE, "2", TRUE, "1e3", TRUE, "1e3L", FALSE, "1.0e3L", FALSE, "1.2e3", FALSE, # * ( = 1200) "1.2e-3", FALSE, "1e-3", FALSE, "1e-33", FALSE, "1.2e0", FALSE, "0x1p+0", FALSE, # * ( = 1) "0x1.ecp+6L", FALSE, "0x1.ecp+6", FALSE, # * ( = 123) "0x1.ec66666666666p+6", FALSE, "8i", FALSE, "8.0i", FALSE ) # for convenience of coercing these to string (since tribble doesn't support auto-conversion) int_max <- .Machine[["integer.max"]] # largest number that R can represent as an integer cases_int_max <- tibble::tribble( ~num_value_str, ~should_lint, -int_max - 1.0, FALSE, -int_max, TRUE, int_max, TRUE, int_max + 1.0, FALSE ) cases_int_max$num_value_str <- as.character(cases_int_max$num_value_str) cases <- rbind(cases, cases_int_max) cases$.test_name <- sprintf("num_value_str=%s, should_lint=%s", cases$num_value_str, cases$should_lint) linter <- implicit_integer_linter() patrick::with_parameters_test_that( "single numerical constants are properly identified ", expect_lint(num_value_str, if (should_lint) "Integers should not be implicit", linter), .cases = cases ) }) # styler: on test_that("linter returns the correct linting", { lint_msg <- "Integers should not be implicit. Use the form 1L for integers or 1.0 for doubles." linter <- implicit_integer_linter() expect_lint("x <<- 1L", NULL, linter) expect_lint("1.0/-Inf -> y", NULL, linter) expect_lint( "y <- 1+i", list(message = lint_msg, line_number = 1L, column_number = 7L), linter ) expect_lint( "z <- 1e5", list(message = lint_msg, line_number = 1L, column_number = 9L), linter ) expect_lint( "cat(1:n)", list(message = lint_msg, line_number = 1L, column_number = 6L), linter ) expect_lint( "552^9", list( list(message = lint_msg, line_number = 1L, column_number = 4L), list(message = lint_msg, line_number = 1L, column_number = 6L) ), linter ) }) skip_if_not_installed("tibble") patrick::with_parameters_test_that( "numbers in a:b input are optionally not linted", expect_lint( paste0(left, ":", right), if (n_lints > 0L) rep(list("Integers should not be implicit"), n_lints), implicit_integer_linter(allow_colon = allow_colon) ), .cases = tibble::tribble( ~left, ~right, ~n_lints, ~allow_colon, ~.test_name, "1", "1", 2L, FALSE, "1:1, !allow_colon", "1", "1", 0L, TRUE, "1:1, allow_colon", "1", "1L", 1L, FALSE, "1:1L, !allow_colon", "1", "1L", 0L, TRUE, "1:1L, allow_colon", "1L", "1", 1L, FALSE, "1L:1, !allow_colon", "1L", "1", 0L, TRUE, "1L:1, allow_colon", "1L", "1L", 0L, FALSE, "1L:1L, !allow_colon", "1L", "1L", 0L, TRUE, "1L:1L, allow_colon" ) ) lintr/tests/testthat/test-condition_message_linter.R0000644000176200001440000001142114577052532022564 0ustar liggesuserstest_that("condition_message_linter skips allowed usages", { linter <- condition_message_linter() expect_lint("stop('a string', 'another')", NULL, linter) expect_lint("warning('a string', 'another')", NULL, linter) expect_lint("message('a string', 'another')", NULL, linter) # extracted calls likely don't obey base::stop() semantics expect_lint("ctx$stop(paste('a', 'b'))", NULL, linter) expect_lint("ctx@stop(paste('a', 'b'))", NULL, linter) # sprintf is OK -- gettextf() enforcement is left to other linters expect_lint("stop(sprintf('A %s!', 'string'))", NULL, linter) # get multiple sep= in one expression expect_lint( trim_some(" tryCatch( foo(x), error = function(e) stop(paste(a, b, sep = '-')), warning = function(w) warning(paste(a, b, sep = '--')), ) "), NULL, linter ) }) skip_if_not_installed("tibble") patrick::with_parameters_test_that( "paste/paste0 allowed by condition_message_linter when using other seps and/or collapse", expect_lint( sprintf("%s(%s(x, %s = '%s'))", condition, fun, parameter, arg), NULL, condition_message_linter() ), .cases = tibble::tribble( ~.test_name, ~condition, ~fun, ~parameter, ~arg, "stop, paste and collapse = ''", "stop", "paste", "collapse", "", "warning, paste and collapse = '\n'", "warning", "paste", "collapse", "\n", "message, paste and collapse = '|'", "message", "paste", "collapse", "|", "stop, paste0 and collapse = ''", "stop", "paste0", "collapse", "", "warning, paste0 and collapse = '\n'", "warning", "paste0", "collapse", "\n", "message, paste0 and collapse = '|'", "message", "paste0", "collapse", "|", "stop, paste and sep = '-'", "stop", "paste", "sep", "-", "warning, paste and sep = '\n'", "warning", "paste", "sep", "\n", "message, paste and sep = '|'", "message", "paste", "sep", "|" ) ) test_that("condition_message_linter blocks simple disallowed usages", { expect_lint( "stop(paste('a string', 'another'))", rex::rex("Don't use paste to build stop strings."), condition_message_linter() ) expect_lint( "warning(paste0('a string ', 'another'))", rex::rex("Don't use paste0 to build warning strings."), condition_message_linter() ) # `sep` argument allowed, but only if it is different from default expect_lint( "stop(paste(x, sep = ' '))", rex::rex("Don't use paste to build stop strings."), condition_message_linter() ) # not thrown off by named arguments expect_lint( "stop(paste('a', 'b'), call. = FALSE)", rex::rex("Don't use paste to build stop strings."), condition_message_linter() ) expect_lint( "warning(paste0('a', 'b'), immediate. = TRUE)", rex::rex("Don't use paste0 to build warning strings."), condition_message_linter() ) expect_lint( trim_some(" tryCatch( foo(x), error = function(e) stop(paste(a, b)), warning = function(w) warning(paste(a, b, sep = '--')), ) "), rex::rex("Don't use paste to build stop strings."), condition_message_linter() ) # one with no sep, one with linted sep expect_lint( trim_some(" tryCatch( foo(x), error = function(e) stop(paste(a, b)), warning = function(w) warning(paste(a, b, sep = '')), ) "), list( list(message = rex::rex("Don't use paste to build stop strings.")), list(message = rex::rex("Don't use paste to build warning strings")) ), condition_message_linter() ) }) test_that("packageStartupMessage usages are also matched", { expect_lint( "packageStartupMessage(paste('a string', 'another'))", rex::rex("Don't use paste to build packageStartupMessage strings."), condition_message_linter() ) expect_lint( "packageStartupMessage(paste0('a string ', 'another'))", rex::rex("Don't use paste0 to build packageStartupMessage strings."), condition_message_linter() ) }) test_that("R>=4.0.0 raw strings are handled", { skip_if_not_r_version("4.0.0") expect_lint( "warning(paste(a, b, sep = R'( )'))", rex::rex("Don't use paste to build warning strings."), condition_message_linter() ) expect_lint( "warning(paste(a, b, sep = R'---[ ]---'))", rex::rex("Don't use paste to build warning strings."), condition_message_linter() ) }) test_that("message vectorization works", { expect_lint( trim_some(" foo <- function() { warning(paste('uh oh!', 'spaghettios')) stop(paste0('look out ', 'below!')) } "), list( rex::rex("Don't use paste to build warning strings"), rex::rex("Don't use paste0 to build stop strings") ), condition_message_linter() ) }) lintr/tests/testthat/test-open_curly_linter.R0000644000176200001440000000402514577052532021253 0ustar liggesuserstest_that("returns the correct linting", { expect_warning( { linter <- open_curly_linter() }, "Linter open_curly_linter was deprecated", fixed = TRUE ) expect_lint("blah", NULL, linter) expect_lint( trim_some(" a <- function() { } "), NULL, linter ) expect_lint( trim_some(' pkg_name <- function(path = find_package()) { if (is.null(path)) { return(NULL) } else { read.dcf(file.path(path, "DESCRIPTION"), fields = "Package")[1] } } '), NULL, linter ) expect_lint( trim_some(" a <- function() { 1 } "), rex::rex("Opening curly braces should never go on their own line."), linter ) expect_lint( trim_some(" a <- function() { 1 } "), rex::rex("Opening curly braces should never go on their own line."), linter ) expect_lint( trim_some(" a <- function() \t{ 1 } "), rex::rex("Opening curly braces should never go on their own line"), linter ) # trailing whitespace _doesn't_ trigger a lint (it used to; leave that to trailing_whitespace_linter now) expect_lint("a <- function() { \n}", NULL, linter) expect_lint( "a <- function() { 1 }", rex::rex("Opening curly braces should always be followed by a new line"), linter ) expect_lint( trim_some(' if ("P" != "NP") { # what most people expect print("Cryptomania is possible") } '), NULL, linter ) expect_lint("{{x}}", NULL, linter) }) test_that("allow_single_line=TRUE works", { expect_warning( { linter <- open_curly_linter(allow_single_line = TRUE) }, "Linter open_curly_linter was deprecated", fixed = TRUE ) expect_lint("a <- function() { 1 }", NULL, linter) expect_lint( trim_some(" a <- function() { 1 2 } "), rex::rex("Opening curly braces should always be followed by a new line unless"), linter ) }) lintr/tests/testthat/test-with.R0000644000176200001440000001220114577052532016465 0ustar liggesuserstest_that("modify_defaults produces error with missing or incorrect defaults", { lint_msg <- "`defaults` must be a named list." expect_error(modify_defaults(), lint_msg, fixed = TRUE) expect_error(modify_defaults("assignment_linter"), lint_msg, fixed = TRUE) }) test_that("linters_with_tags produces error with incorrect tags", { expect_error(linters_with_tags(1L:4L), "`tags` must be a character vector, or NULL.", fixed = TRUE) }) test_that("linters_with_defaults works as expected with unnamed args", { # assignment_linter is in defaults, so output doesn't change expect_named(linters_with_defaults(assignment_linter), names(linters_with_defaults())) }) test_that("linters_with_defaults warns on unused NULLs", { expect_warning(linters_with_defaults(not_a_default = NULL), rex::rex("which is not in `defaults`.")) expect_warning( linters_with_defaults(not_a_default = NULL, also_not_default = NULL), rex::rex("which are not in `defaults`.") ) }) test_that("linters_with_tags() verifies the output of available_linters()", { skip_if_not_installed("mockery") mockery::stub( linters_with_tags, "available_linters", data.frame(linter = c("fake_linter", "very_fake_linter"), package = "lintr", tags = "", stringsAsFactors = FALSE) ) expect_error( linters_with_tags(NULL), "'fake_linter' and 'very_fake_linter'" ) }) test_that("all default linters are tagged default", { expect_named(linters_with_defaults(), available_linters(tags = "default")$linter) skip_if_not_installed("waldo", "0.4.0") # needs waldo#133 # covr modifies package functions causing differing deparse() results even for identical anonymous functions. # This happens because default_linters is generated at build time and thus not modifiable by covr, whereas # linters_with_tags() constructs the linters at runtime. skip_on_covr() expect_identical(linters_with_tags("default"), linters_with_defaults()) expect_length(linters_with_tags(NULL, exclude_tags = available_tags()), 0L) # Check that above test also trips on default arguments. skip_if_not_r_version("4.1.0") # Desired all.equal behavior only available in >= 4.1 expect_identical( all.equal(linters_with_tags("default"), linters_with_defaults(line_length_linter(120L))), c( 'Component "line_length_linter": Component "general_msg": 1 string mismatch', 'Component "line_length_linter": Component "length": Mean relative difference: 0.5' ) ) }) test_that("can instantiate all linters without arguments", { all_linters <- linters_with_tags(tags = NULL) expect_type(all_linters, "list") expect_length(all_linters, nrow(available_linters())) really_all_linters <- suppressWarnings(linters_with_tags(tags = NULL, exclude_tags = NULL)) expect_type(really_all_linters, "list") expect_length(really_all_linters, nrow(available_linters(exclude_tags = NULL))) }) test_that("with_defaults is supported with a deprecation warning", { defaults <- linters_with_defaults() expect_warning( { old_defaults <- with_defaults() }, rex::rex("Use linters_with_defaults or modify_defaults instead.") ) expect_identical(defaults, old_defaults) # linters_with_defaults only accepts `defaults = list()` to start from blank defaults <- linters_with_defaults(defaults = list(), whitespace_linter()) expect_warning( { old_defaults <- with_defaults(default = NULL, whitespace_linter()) }, rex::rex("Use linters_with_defaults or modify_defaults instead.") ) expect_identical(defaults, old_defaults) }) test_that("modify_defaults works", { my_default <- list(a = 1L, b = 2L, c = 3L) expect_identical(modify_defaults(defaults = my_default), my_default) expect_identical(modify_defaults(defaults = my_default, a = 2L), list(a = 2L, b = 2L, c = 3L)) expect_identical(modify_defaults(defaults = my_default, c = NULL), list(a = 1L, b = 2L)) # auto-sorts expect_identical(modify_defaults(defaults = list(b = 2L, a = 1L), c = 3L), my_default) }) test_that("linters_with_defaults(default = .) is supported with a deprecation warning", { expect_warning( { linters <- linters_with_defaults(default = list(), whitespace_linter()) }, "'default'" ) expect_named(linters, "whitespace_linter") # the same warning is not triggered in modify_defaults expect_silent({ linters <- modify_defaults(defaults = list(), default = list(), whitespace_linter()) }) expect_named(linters, c("default", "whitespace_linter")) # if default= is explicitly provided alongside defaults=, assume that was intentional default <- Linter(function(.) list()) expect_silent({ linters <- linters_with_defaults(defaults = list(), default = default) }) expect_named(linters, "default") }) test_that("all_linters contains all available linters", { all_linters <- all_linters(packages = "lintr") expect_identical(linters_with_tags(NULL, packages = "lintr"), all_linters) expect_length(all_linters, nrow(available_linters())) }) test_that("all_linters respects ellipsis argument", { expect_identical( linters_with_tags(tags = NULL, implicit_integer_linter = NULL), all_linters(packages = "lintr", implicit_integer_linter = NULL) ) }) lintr/tests/testthat/test-cache.R0000644000176200001440000003036214577052532016565 0ustar liggesusers# Fixtures fixtures <- list() fixtures$retrieve_lint <- function() { file_name <- "R/test.R" lines <- c("foobar1", "foobar2", "foobar3") lints <- list( Lint(file_name, 1L, line = "foobar1"), Lint(file_name, 2L, line = "foobar2"), Lint(file_name, 3L, line = "foobar3") ) expr <- list(content = paste(collapse = "\n", lines)) list( lines = lines, linters = list(), lints = lints, expr = expr ) } # Run tests with a temporary cache directory, so we don't leave files behind # after running withr::local_options(lintr.cache_directory = withr::local_tempdir()) # Helper functions fhash <- function(filename) { digest::digest(filename, algo = "sha1") } # Tests # `clear_cache` test_that("clear_cache deletes the file if a file is given", { skip_if_not_installed("mockery") mockery::stub(clear_cache, "read_settings", function(...) invisible(...)) mockery::stub(clear_cache, "unlink", function(...) list(...)) e1 <- new.env(parent = emptyenv()) d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" lintr:::save_cache(cache = e1, file = f1, path = d1) want <- list(file.path(d1, fhash("R/test.R")), recursive = TRUE) expect_identical(clear_cache(f1, d1), want) expect_identical(clear_cache(file = f1, path = d1), want) }) test_that("clear_cache deletes the directory if no file is given", { skip_if_not_installed("mockery") mockery::stub(clear_cache, "read_settings", function(...) invisible(...)) mockery::stub(clear_cache, "unlink", function(...) list(...)) expect_identical(clear_cache(file = NULL, path = "."), list(".", recursive = TRUE)) }) # `load_cache` test_that("load_cache loads the saved file in a new empty environment", { e1 <- new.env(parent = emptyenv()) e1[["x"]] <- "foobar" d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" lintr:::save_cache(cache = e1, file = f1, path = d1) e2 <- lintr:::load_cache(file = f1, path = d1) expect_identical(e2, e1) }) test_that("load_cache returns an empty environment if no cache file exists", { e1 <- new.env(parent = emptyenv()) d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" f2 <- "test.R" lintr:::save_cache(cache = e1, file = f1, path = d1) e2 <- lintr:::load_cache(file = f2, path = d1) expect_identical(e2, e1) }) test_that("load_cache returns an empty environment if reading cache file fails", { e1 <- new.env(parent = emptyenv()) e1[["x"]] <- "foobar" d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" lintr:::save_cache(cache = e1, file = f1, path = d1) cache_f1 <- file.path(d1, fhash(f1)) writeLines(character(), cache_f1) expect_warning( { e2 <- lintr:::load_cache(file = f1, path = d1) }, "Could not load cache file" ) saveRDS(e1, cache_f1) expect_warning( { e3 <- lintr:::load_cache(file = f1, path = d1) }, "Could not load cache file" ) expect_identical(ls(e2), character()) expect_identical(ls(e3), character()) }) # `save_cache` test_that("save_cache creates a directory if needed", { e1 <- new.env(parent = emptyenv()) d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" expect_false(file.exists(d1)) expect_false(file.exists(file.path(d1, fhash(f1)))) lintr:::save_cache(cache = e1, file = f1, path = d1) expect_true(file.exists(d1)) expect_true(file.info(d1)$isdir) expect_true(file.exists(file.path(d1, fhash(f1)))) }) test_that("save_cache uses unambiguous cache file names", { e1 <- new.env(parent = emptyenv()) d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" f2 <- "test.R" expect_false(file.exists(file.path(d1, fhash(f1)))) expect_false(file.exists(file.path(d1, fhash(f2)))) lintr:::save_cache(cache = e1, file = f1, path = d1) lintr:::save_cache(cache = e1, file = f2, path = d1) expect_true(fhash(f1) != fhash(f2)) expect_true(file.exists(file.path(d1, fhash(f1)))) expect_true(file.exists(file.path(d1, fhash(f2)))) }) test_that("save_cache saves all non-hidden objects from the environment", { e1 <- new.env(parent = emptyenv()) e1$t1 <- 1L e1$t2 <- 2L d1 <- withr::local_tempfile(pattern = "lintr_cache_") f1 <- "R/test.R" lintr:::save_cache(cache = e1, file = f1, path = d1) e2 <- new.env(parent = emptyenv()) load(file = file.path(d1, fhash(f1)), envir = e2) expect_identical(e2, e1) }) # `cache_file` test_that("cache_file generates the same cache with different lints", { e1 <- new.env(parent = emptyenv()) f1 <- withr::local_tempfile(lines = "foobar") lintr:::cache_file(e1, f1, list(), list()) lintr:::cache_file(e1, f1, list(), list(1L)) expect_length(ls(e1), 1L) }) test_that("cache_file generates different caches for different linters", { e1 <- new.env(parent = emptyenv()) f1 <- withr::local_tempfile(lines = "foobar") lintr:::cache_file(e1, f1, list(), list()) lintr:::cache_file(e1, f1, list(1L), list()) expect_length(ls(e1), 2L) }) # `retrieve_file` test_that("retrieve_file returns NULL if there is no cached result", { e1 <- new.env(parent = emptyenv()) f1 <- withr::local_tempfile(lines = "foobar") expect_null(lintr:::retrieve_file(e1, f1, list())) }) test_that("retrieve_file returns the cached result if found", { e1 <- new.env(parent = emptyenv()) f1 <- withr::local_tempfile(lines = "foobar") lintr:::cache_file(e1, f1, list(), list("foobar")) expect_identical(lintr:::retrieve_file(e1, f1, list()), list("foobar")) }) # `cache_lint` test_that("cache_lint generates the same cache with different lints", { e1 <- new.env(parent = emptyenv()) t1 <- list(content = "test") lintr:::cache_lint(e1, t1, list(), list()) lintr:::cache_lint(e1, t1, list(), list(1L)) expect_length(ls(e1), 1L) }) test_that("cache_lint generates different caches for different linters", { e1 <- new.env(parent = emptyenv()) t1 <- list(content = "test") lintr:::cache_lint(e1, t1, list(), list()) lintr:::cache_lint(e1, t1, list(1L), list()) expect_length(ls(e1), 2L) }) # `retrieve_lint` test_that("retrieve_lint returns the same lints if nothing has changed", { test_data <- fixtures$retrieve_lint() e1 <- new.env(parent = emptyenv()) lintr:::cache_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lints = test_data[["lints"]] ) t1 <- lintr:::retrieve_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lines = test_data[["lines"]] ) expect_identical(t1, test_data[["lints"]]) }) test_that( "retrieve_lint returns the same lints with fixed line numbers if lines added above", { test_data <- fixtures$retrieve_lint() e1 <- new.env(parent = emptyenv()) lines1 <- test_data[["lines"]] lines2 <- c("", lines1) lints <- test_data[["lints"]] lintr:::cache_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lints = lints ) t1 <- lintr:::retrieve_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lines = lines2 ) expect_identical(t1[[1L]]$line_number, lints[[1L]]$line_number + 1L) expect_identical(t1[[2L]]$line_number, lints[[2L]]$line_number + 1L) expect_identical(t1[[3L]]$line_number, lints[[3L]]$line_number + 1L) } ) test_that("retrieve_lint returns the same lints with lines added below", { test_data <- fixtures$retrieve_lint() e1 <- new.env(parent = emptyenv()) lines1 <- test_data[["lines"]] lines2 <- c(lines1, "") lintr:::cache_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lints = test_data[["lints"]] ) t1 <- lintr:::retrieve_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lines = lines2 ) expect_identical(t1, test_data[["lints"]]) }) test_that( "retrieve_lint returns the same lints with fixed line numbers if lines added between", { test_data <- fixtures$retrieve_lint() e1 <- new.env(parent = emptyenv()) lines1 <- test_data[["lines"]] lines2 <- c(lines1[1L], "", lines1[2L:3L], "") lints1 <- test_data[["lints"]] lintr:::cache_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lints = lints1 ) t1 <- lintr:::retrieve_lint( cache = e1, expr = test_data[["expr"]], linter = test_data[["linters"]], lines = lines2 ) expect_identical(t1[[1L]]$line_number, lints1[[1L]]$line_number) expect_identical(t1[[2L]]$line_number, lints1[[2L]]$line_number + 1L) expect_identical(t1[[3L]]$line_number, lints1[[3L]]$line_number + 1L) } ) # `has_lint` test_that("has_lint returns FALSE if there is no cached result", { e1 <- new.env(parent = emptyenv()) t1 <- list(content = "foobar") expect_false(lintr:::has_lint(e1, t1, list())) }) test_that("has_lint returns TRUE if there is a cached result", { e1 <- new.env(parent = emptyenv()) t1 <- list(content = "foobar") lintr:::cache_lint(e1, t1, list(), list()) expect_true(lintr:::has_lint(e1, t1, list())) }) test_that("has_lint distinguishes global expressions from line expression with same content", { e1 <- new.env(parent = emptyenv()) same_content <- "foobar" line_expr <- list(content = same_content, parsed_content = data.frame()) lintr:::cache_lint(e1, line_expr, list(), list()) global_expr <- list(content = same_content, file_lines = character()) expect_false(lintr:::has_lint(e1, global_expr, list())) }) # `find_new_line` test_that("find_new_line returns the same if the line is the same", { t1 <- c( "foobar1", "foobar2", "foobar3" ) expect_identical(lintr:::find_new_line(1L, "foobar1", t1), 1L) expect_identical(lintr:::find_new_line(2L, "foobar2", t1), 2L) expect_identical(lintr:::find_new_line(3L, "foobar3", t1), 3L) }) test_that("find_new_line returns the correct line if it is before the current line", { t1 <- c( "foobar1", "foobar2", "foobar3" ) expect_identical(lintr:::find_new_line(1L, "foobar1", t1), 1L) expect_identical(lintr:::find_new_line(2L, "foobar1", t1), 1L) expect_identical(lintr:::find_new_line(3L, "foobar1", t1), 1L) }) test_that("find_new_line returns the correct line if it is after the current line", { t1 <- c( "foobar1", "foobar2", "foobar3" ) expect_identical(lintr:::find_new_line(1L, "foobar3", t1), 3L) expect_identical(lintr:::find_new_line(2L, "foobar3", t1), 3L) expect_identical(lintr:::find_new_line(3L, "foobar3", t1), 3L) }) # test_that("lint with cache uses the provided relative cache directory", { path <- withr::local_tempdir("my_cache_dir") linter <- assignment_linter() # create the cache expect_lint("a <- 1", NULL, linter, cache = path) expect_true(dir.exists(path)) expect_length(list.files(file.path(path)), 1L) # read the cache expect_lint("a <- 1", NULL, linter, cache = path) expect_true(dir.exists(path)) }) test_that("it works outside of a package", { skip_if_not_installed("mockery") linter <- assignment_linter() mockery::stub(lintr:::find_default_encoding, "find_package", function(...) NULL) path <- withr::local_tempfile(pattern = "my_cache_dir_") expect_false(dir.exists(path)) expect_lint("a <- 1", NULL, linter, cache = path) expect_true(dir.exists(path)) expect_length(list.files(path), 1L) expect_lint("a <- 1", NULL, linter, cache = path) expect_true(dir.exists(path)) }) test_that("cache = TRUE workflow works", { # Need a test structure with a safe to load .lintr pkg <- "dummy_packages/package" files <- normalizePath(list.files(pkg, recursive = TRUE, full.names = TRUE)) # Manually clear cache (that function is exported) for (f in files) { clear_cache(file = f) } l1 <- lint_package(pkg, cache = TRUE) l2 <- lint_package(pkg, cache = TRUE) expect_identical(l1, l2) }) test_that("cache = TRUE works with nolint", { linters <- list(infix_spaces_linter()) file <- withr::local_tempfile() writeLines("1+1\n", file) expect_length(lint(file, linters, cache = TRUE), 1L) writeLines("1+1 # nolint\n", file) expect_length(lint(file, linters, cache = TRUE), 0L) writeLines("1+1\n", file) expect_length(lint(file, linters, cache = TRUE), 1L) writeLines("1+1 # nolint\n", file) expect_length(lint(file, linters, cache = TRUE), 0L) }) lintr/tests/testthat/test-missing_argument_linter.R0000644000176200001440000000716214577055717022464 0ustar liggesuserstest_that("missing_argument_linter skips allowed usages", { linter <- missing_argument_linter() expect_lint("fun(x, a = 1)", NULL, linter) expect_lint("fun(x = 1, a = 1)", NULL, linter) expect_lint("dt[, 1]", NULL, linter) expect_lint("dt[, 'col']", NULL, linter) expect_lint("array[, , 1]", NULL, linter) expect_lint("switch(a =, b =, c = 1, 0)", NULL, linter) expect_lint("alist(a =, b =, c = 1, 0)", NULL, linter) expect_lint("pairlist(path = quote(expr = ))", NULL, linter) #1889 # always allow this missing usage expect_lint("foo()", NULL, linter) expect_lint("test(a =, b =, c = 1, 0)", NULL, missing_argument_linter("test")) }) test_that("missing_argument_linter blocks disallowed usages", { linter <- missing_argument_linter() lint_msg <- rex::rex("Missing argument in function call.") expect_lint("fun(, a = 1)", list(message = lint_msg), linter) expect_lint("f <- function(x, y) x\nf(, y = 1)\n", list(line = "f(, y = 1)"), linter) expect_lint("fun(a = 1,, b = 2)", list(message = lint_msg), linter) expect_lint("fun(a = 1, b =)", list(message = lint_msg), linter) expect_lint("fun(a = 1,)", list(message = lint_msg), linter) expect_lint("fun(a = )", list(message = lint_msg), linter) expect_lint( trim_some(" list( a = 1, b = 2, ) "), list(message = lint_msg), linter ) expect_lint("stats::median(1:10, na.rm =)", list(message = lint_msg), linter) expect_lint("env$get(1:10, default =)", list(message = lint_msg), linter) # except list can be empty expect_lint("switch(a =, b = 1, 0)", list(message = lint_msg), missing_argument_linter(character())) expect_lint("alist(a =)", list(message = lint_msg), missing_argument_linter(character())) # allow_trailing can allow trailing empty args also for non-excepted functions expect_lint("fun(a = 1,)", NULL, missing_argument_linter(allow_trailing = TRUE)) expect_lint( trim_some(" fun( a = 1, # comment ) "), NULL, missing_argument_linter(allow_trailing = TRUE) ) # ... but not if the final argument is named expect_lint( "fun(a = 1, b = )", list(message = lint_msg), missing_argument_linter(allow_trailing = TRUE) ) # Fixes https://github.com/r-lib/lintr/issues/906 # Comments should be ignored so that missing arguments could be # properly identified in these cases. expect_lint( trim_some(" fun( 1, 2, # comment ) "), list(message = lint_msg), linter ) expect_lint( trim_some(" fun( # comment , 1 ) "), list(message = lint_msg), linter ) expect_lint( trim_some(" fun( a = # comment , 1 ) "), list(message = lint_msg), linter ) }) test_that("lints vectorize", { linter <- missing_argument_linter() linter_trailing <- missing_argument_linter(allow_trailing = TRUE) lint_msg <- rex::rex("Missing argument in function call.") expect_lint( "foo(,,)", list( list(lint_msg, column_number = 5L), list(lint_msg, column_number = 6L), list(lint_msg, column_number = 7L) ), linter ) expect_lint( "foo(,,)", list( list(lint_msg, column_number = 5L), list(lint_msg, column_number = 6L) ), linter_trailing ) expect_lint( trim_some("{ foo(1,) bar(,2) }"), list( list(lint_msg, line_number = 2L), list(lint_msg, line_number = 3L) ), linter ) expect_lint( trim_some("{ foo(1,) bar(,2) }"), list(lint_msg, line_number = 3L), linter_trailing ) }) lintr/tests/testthat/test-pipe-consistency-linter.R0000644000176200001440000001032414577052532022305 0ustar liggesuserstest_that("pipe_consistency skips allowed usage", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter() expect_lint("1:3 %>% mean() %>% as.character()", NULL, linter) expect_lint("1:3 |> mean() |> as.character()", NULL, linter) # With no pipes expect_lint("x <- 1:5", NULL, linter) # Across multiple lines expect_lint( trim_some(" 1:3 %>% mean() %>% as.character() "), NULL, linter ) }) test_that("pipe_consistency lints inconsistent usage", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter() expected_msg <- rex("Found 1 instances of %>% and 1 instances of |>. Stick to one pipe operator.") expect_lint( "1:3 |> mean() %>% as.character()", list( list(message = expected_msg, line_number = 1L, column_number = 5L), list(message = expected_msg, line_number = 1L, column_number = 15L) ), linter ) expect_lint( "1:3 %>% mean() |> as.character()", list( list(message = expected_msg, line_number = 1L, column_number = 5L), list(message = expected_msg, line_number = 1L, column_number = 16L) ), linter ) expect_lint( trim_some(" 1:3 %>% mean() |> as.character() "), list( list(message = expected_msg, line_number = 1L, column_number = 5L), list(message = expected_msg, line_number = 2L, column_number = 10L) ), linter ) expected_msg_multi <- rex("Found 1 instances of %>% and 2 instances of |>. Stick to one pipe operator.") expect_lint( "1:3 |> sort() |> mean() %>% as.character()", list( list(message = expected_msg_multi, line_number = 1L, column_number = 5L), list(message = expected_msg_multi, line_number = 1L, column_number = 15L), list(message = expected_msg_multi, line_number = 1L, column_number = 25L) ), linter ) }) test_that("pipe_consistency_linter works with |> argument", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter(pipe = "|>") expected_message <- rex("Use the |> pipe operator instead of the %>% pipe operator.") expect_lint( trim_some(" 1:3 %>% mean() %>% as.character() "), list( list(message = expected_message, line_number = 1L, column_number = 5L), list(message = expected_message, line_number = 2L, column_number = 10L) ), linter ) expect_lint( trim_some(" 1:3 |> mean() %>% as.character() "), list(message = expected_message, line_number = 2L, column_number = 10L), linter ) expect_lint( "1:3 |> mean() |> as.character()", NULL, linter ) expect_lint( trim_some(" 1:3 |> mean() %>% as.character() "), list(message = expected_message, line_number = 2L, column_number = 10L), linter ) }) test_that("pipe_consistency_linter works with %>% argument", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter(pipe = "%>%") expected_message <- rex("Use the %>% pipe operator instead of the |> pipe operator.") expect_lint( "1:3 |> mean() |> as.character()", list( list(message = expected_message, line_number = 1L, column_number = 5L), list(message = expected_message, line_number = 1L, column_number = 15L) ), linter ) expect_lint( "1:3 %>% mean() |> as.character()", list(message = expected_message, line_number = 1L, column_number = 16L), linter ) expect_lint( "1:3 %>% mean() %>% as.character()", NULL, linter ) expect_lint( trim_some(" 1:3 %>% mean() |> as.character() "), list(message = expected_message, line_number = 2L, column_number = 10L), linter ) }) test_that("pipe_consistency_linter works with other magrittr pipes", { skip_if_not_r_version("4.1.0") linter <- pipe_consistency_linter() expected_message <- rex("Found 1 instances of %>% and 1 instances of |>. Stick to one pipe operator.") expect_lint("1:3 %>% mean() %T% print()", NULL, linter) expect_lint( "1:3 |> mean() %T>% print()", list( list(message = expected_message, line_number = 1L, column_number = 5L), list(message = expected_message, line_number = 1L, column_number = 15L) ), linter ) }) lintr/tests/testthat/test-expect_null_linter.R0000644000176200001440000000270414577052532021420 0ustar liggesuserstest_that("expect_null_linter skips allowed usages", { linter <- expect_null_linter() # NB: this usage should be expect_false(), but this linter should # nevertheless apply (e.g. for maintainers not using expect_not_linter) expect_lint("expect_true(!is.null(x))", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(!is.null(x))", NULL, linter) # length-0 could be NULL, but could be integer() or list(), so let it pass expect_lint("expect_length(x, 0L)", NULL, linter) # no false positive for is.null() at the wrong positional argument expect_lint("expect_true(x, is.null(y))", NULL, linter) }) test_that("expect_null_linter blocks simple disallowed usages", { expect_lint( "expect_equal(x, NULL)", rex::rex("expect_null(x) is better than expect_equal(x, NULL)"), expect_null_linter() ) # expect_identical is treated the same as expect_equal expect_lint( "testthat::expect_identical(x, NULL)", rex::rex("expect_null(x) is better than expect_identical(x, NULL)"), expect_null_linter() ) # reverse order lints the same expect_lint( "expect_equal(NULL, x)", rex::rex("expect_null(x) is better than expect_equal(x, NULL)"), expect_null_linter() ) # different equivalent usage expect_lint( "expect_true(is.null(foo(x)))", rex::rex("expect_null(x) is better than expect_true(is.null(x))"), expect_null_linter() ) }) lintr/tests/testthat/test-normalize_exclusions.R0000644000176200001440000001077314577052532022002 0ustar liggesuserswithr::local_options(list( lintr.exclude = "#TeSt_NoLiNt", lintr.exclude_start = "#TeSt_NoLiNt_StArT", lintr.exclude_end = "#TeSt_NoLiNt_EnD" )) a <- withr::local_tempfile() b <- withr::local_tempfile() c <- withr::local_tempfile(tmpdir = ".") file.create(a, b, c) a <- normalizePath(a) b <- normalizePath(b) c <- normalizePath(c) test_that("it merges two NULL or empty objects as an empty list", { expect_identical(lintr:::normalize_exclusions(c(NULL, NULL)), list()) expect_identical(lintr:::normalize_exclusions(c(NULL, list())), list()) expect_identical(lintr:::normalize_exclusions(c(list(), NULL)), list()) expect_identical(lintr:::normalize_exclusions(c(list(), list())), list()) }) test_that("it returns the object if the other is NULL", { t1 <- list() t1[[a]] <- list(1L:10L) expect_identical(lintr:::normalize_exclusions(c(t1, NULL)), t1) expect_identical(lintr:::normalize_exclusions(c(NULL, t1)), t1) }) test_that("it returns the union of two non-overlapping lists", { t1 <- list() t1[[a]] <- list(1L:10L) t2 <- list() t2[[a]] <- list(20L:30L) res <- list() res[[a]] <- list(c(1L:10L, 20L:30L)) expect_identical(lintr:::normalize_exclusions(c(t1, t2)), res) }) test_that("it works with named lists", { t1 <- list() t1[[a]] <- list(1L:10L, my_linter = 1L:20L) t2 <- list() t2[[a]] <- list(11L:15L, my_linter = 21L:25L) res <- list() res[[a]] <- list(1L:15L, my_linter = 1L:25L) expect_identical(lintr:::normalize_exclusions(c(t1, t2)), res) }) test_that("it returns the union of two overlapping lists", { t1 <- list() t1[[a]] <- list(1L:10L) t2 <- list() t2[[a]] <- list(5L:15L) res <- list() res[[a]] <- list(1L:15L) expect_identical(lintr:::normalize_exclusions(c(t1, t2)), res) }) test_that("it adds names if needed", { t1 <- list() t1[[a]] <- list(1L:10L) t2 <- list() t2[[b]] <- list(5L:15L) res <- list() res[[a]] <- list(1L:10L) res[[b]] <- list(5L:15L) expect_identical(lintr:::normalize_exclusions(c(t1, t2)), res) }) test_that("it handles full file exclusions", { res <- list() res[[a]] <- list(Inf) expect_identical(lintr:::normalize_exclusions(list(a)), res) t1 <- list() t1[[1L]] <- a t1[[b]] <- 1L res <- list() res[[a]] <- list(Inf) res[[b]] <- list(1L) expect_identical(lintr:::normalize_exclusions(t1), res) }) test_that("it handles redundant lines", { t1 <- list() t1[[a]] <- list(c(1L, 1L, 1L:10L)) res <- list() res[[a]] <- list(1L:10L) expect_identical(lintr:::normalize_exclusions(t1), res) t1 <- list() t1[[a]] <- list(c(1L, 1L, 1L:10L)) t1[[b]] <- list(1L:10L) res <- list() res[[a]] <- list(1L:10L) res[[b]] <- list(1L:10L) expect_identical(lintr:::normalize_exclusions(t1), res) }) test_that("it handles redundant linters", { t1 <- list() t1[[a]] <- list(c(1L, 1L, 1L:10L), my_linter = c(1L, 1L, 1L, 2L), my_linter = 3L) res <- list() res[[a]] <- list(1L:10L, my_linter = 1L:3L) expect_identical(lintr:::normalize_exclusions(t1), res) t1 <- list() t1[[a]] <- list(c(1L, 1L, 1L:10L), my_linter = c(1L, 1L, 1L, 2L)) t1[[b]] <- list(1L:10L, my_linter = 1L:10L, my_linter = 11L:20L) res <- list() res[[a]] <- list(1L:10L, my_linter = 1L:2L) res[[b]] <- list(1L:10L, my_linter = 1L:20L) expect_identical(lintr:::normalize_exclusions(t1), res) }) test_that("it handles redundant files", { t1 <- list(1L:10L, 10L:20L) names(t1) <- c(a, a) res <- list() res[[a]] <- list(1L:20L) expect_identical(lintr:::normalize_exclusions(t1), res) }) test_that("it normalizes file paths, removing non-existing files", { t1 <- list() t1[[a]] <- 1L:10L t2 <- list() t2[["notafile"]] <- 5L:15L t3 <- list() t3[[c]] <- 5L:15L res <- list() res[[a]] <- list(1L:10L) res[[normalizePath(c)]] <- list(5L:15L) expect_identical(lintr:::normalize_exclusions(c(t1, t2, t3)), res) res <- list() res[[a]] <- list(1L:10L) res[["notafile"]] <- list(5L:15L) res[[c]] <- list(5L:15L) expect_identical(lintr:::normalize_exclusions(c(t1, t2, t3), normalize_path = FALSE), res) }) test_that("it errors for invalid specifications", { msg_full_files <- "Full file exclusions must be character vectors of length 1." expect_error(lintr:::normalize_exclusions(2L), msg_full_files) expect_error(lintr:::normalize_exclusions(list("a.R", 2L)), msg_full_files) expect_error(lintr:::normalize_exclusions(list(a.R = Inf, 2L)), msg_full_files) msg_full_lines <- "Full line exclusions must be numeric or integer vectors." expect_error(lintr:::normalize_exclusions(list(a.R = "Inf")), msg_full_lines) }) lintr/tests/testthat/test-any_is_na_linter.R0000644000176200001440000000172314577052532021036 0ustar liggesuserstest_that("any_is_na_linter skips allowed usages", { linter <- any_is_na_linter() expect_lint("x <- any(y)", NULL, linter) expect_lint("y <- is.na(z)", NULL, linter) # extended usage of ... arguments to any is not covered expect_lint("any(is.na(y), b)", NULL, linter) expect_lint("any(b, is.na(y))", NULL, linter) # negation shouldn't list expect_lint("any(!is.na(x))", NULL, linter) expect_lint("any(!is.na(foo(x)))", NULL, linter) }) test_that("any_is_na_linter blocks simple disallowed usages", { lint_message <- rex::rex("anyNA(x) is better than any(is.na(x)).") expect_lint("any(is.na(x))", lint_message, any_is_na_linter()) expect_lint("any(is.na(foo(x)))", lint_message, any_is_na_linter()) # na.rm doesn't really matter for this since is.na can't return NA expect_lint("any(is.na(x), na.rm = TRUE)", lint_message, any_is_na_linter()) # also catch nested usage expect_lint("foo(any(is.na(x)))", lint_message, any_is_na_linter()) }) lintr/tests/testthat/test-empty_assignment_linter.R0000644000176200001440000000203014577052532022454 0ustar liggesuserstest_that("empty_assignment_linter skips valid usage", { expect_lint("x <- { 3 + 4 }", NULL, empty_assignment_linter()) expect_lint("x <- if (x > 1) { 3 + 4 }", NULL, empty_assignment_linter()) # also triggers assignment_linter expect_lint("x = { 3 + 4 }", NULL, empty_assignment_linter()) }) test_that("empty_assignment_linter blocks disallowed usages", { linter <- empty_assignment_linter() lint_msg <- rex::rex("Assign NULL explicitly or, whenever possible, allocate the empty object") expect_lint("xrep <- {}", lint_msg, linter) # assignment with equal works as well, and white space doesn't matter expect_lint("x = { }", lint_msg, linter) # ditto right assignments expect_lint("{} -> x", lint_msg, linter) expect_lint("{} ->> x", lint_msg, linter) # ditto data.table-style walrus assignments expect_lint("x[, col := {}]", lint_msg, linter) # newlines also don't matter expect_lint("x <- {\n}", lint_msg, linter) # LHS of assignment doesn't matter expect_lint("env$obj <- {}", lint_msg, linter) }) lintr/tests/testthat/test-ci.R0000644000176200001440000000447714577052532016125 0ustar liggesuserstest_that("GitHub Actions functionality works", { # imitate being on GHA whether or not we are withr::local_envvar(list(GITHUB_ACTIONS = "true")) withr::local_options(lintr.rstudio_source_markers = FALSE) tmp <- withr::local_tempfile(lines = "x <- 1:nrow(y)") l <- lint(tmp) expect_output(print(l), "::warning file", fixed = TRUE) }) test_that("GitHub Actions functionality works in a subdirectory", { # imitate being on GHA whether or not we are pkg_path <- test_path("dummy_packages", "assignmentLinter") withr::local_envvar(list(GITHUB_ACTIONS = "true")) withr::local_options(lintr.rstudio_source_markers = FALSE, lintr.github_annotation_project_dir = pkg_path) lintr:::read_settings(NULL) l <- lint_package( pkg_path, linters = list(assignment_linter()), parse_settings = FALSE ) expect_output( print(l), paste0("::warning file=", file.path(pkg_path, "R(/|\\\\)abc\\.R")) ) }) test_that("GitHub Actions - linting on error works", { skip_if_not_installed("mockery") # imitate being on GHA whether or not we are withr::local_envvar(list(GITHUB_ACTIONS = "true", LINTR_ERROR_ON_LINT = "true")) withr::local_options(lintr.rstudio_source_markers = FALSE) tmp <- withr::local_tempfile(lines = "x <- 1:nrow(y)") l <- lint(tmp) mockery::stub(print.lints, "base::quit", function(...) cat("Tried to quit.\n")) expect_output(print(l), "::warning file", fixed = TRUE) }) test_that("Printing works for Travis", { skip_if_not_installed("mockery") withr::local_envvar(list(GITHUB_ACTIONS = "false", TRAVIS_REPO_SLUG = "test/repo", LINTR_COMMENT_BOT = "true")) withr::local_options(lintr.rstudio_source_markers = FALSE) tmp <- withr::local_tempfile(lines = "x <- 1:nrow(y)") l <- lint(tmp) mockery::stub(print.lints, "github_comment", function(x, ...) cat(x, "\n")) expect_output(print(l), "*warning:*", fixed = TRUE) }) test_that("Printing works for Wercker", { skip_if_not_installed("mockery") withr::local_envvar(list(GITHUB_ACTIONS = "false", WERCKER_GIT_BRANCH = "test/repo", LINTR_COMMENT_BOT = "true")) withr::local_options(lintr.rstudio_source_markers = FALSE) tmp <- withr::local_tempfile(lines = "x <- 1:nrow(y)") l <- lint(tmp) mockery::stub(print.lints, "github_comment", function(x, ...) cat(x, "\n")) expect_output(print(l), "*warning:*", fixed = TRUE) }) lintr/tests/testthat/test-commas_linter.R0000644000176200001440000000712314577052532020355 0ustar liggesuserstest_that("returns the correct linting (with default parameters)", { linter <- commas_linter() msg_after <- rex::rex("Commas should always have a space after.") msg_before <- rex::rex("Commas should never have a space before.") expect_lint("blah", NULL, linter) expect_lint("fun(1, 1)", NULL, linter) expect_lint("fun(1,\n 1)", NULL, linter) expect_lint("fun(1,\n1)", NULL, linter) expect_lint("fun(1\n,\n1)", NULL, linter) expect_lint("fun(1\n ,\n1)", NULL, linter) expect_lint("fun(1\n,1)", msg_after, linter) expect_lint("fun(1,1)", msg_after, linter) expect_lint("\nfun(1,1)", msg_after, linter) expect_lint("a(1,)", msg_after, linter) expect_lint("a[1,]", msg_after, linter) expect_lint("a[[1,]]", msg_after, linter) expect_lint( "fun(1 ,1)", list( msg_before, msg_after ), linter ) expect_lint("\"fun(1 ,1)\"", NULL, linter) expect_lint("a[1, , 2]", NULL, linter) expect_lint("a[1, , 2, , 3]", NULL, linter) expect_lint("switch(op, x = foo, y = bar)", NULL, linter) expect_lint("switch(op, x = , y = bar)", NULL, linter) expect_lint("switch(op, \"x\" = , y = bar)", NULL, linter) expect_lint("switch(op, x = ,\ny = bar)", NULL, linter) expect_lint("switch(op, x = foo , y = bar)", msg_before, linter) expect_lint("switch(op, x = foo , y = bar)", msg_before, linter) expect_lint("switch(op , x = foo, y = bar)", msg_before, linter) expect_lint("switch(op, x = foo, y = bar(a = 4 , b = 5))", msg_before, linter) expect_lint("fun(op, x = foo , y = switch(bar, a = 4, b = 5))", msg_before, linter) expect_lint( "fun(op ,bar)", list( list(message = msg_before, column_number = 7L, ranges = list(c(7L, 10L))), list(message = msg_after, column_number = 12L, ranges = list(c(12L, 12L))) ), linter ) }) test_that("returns the correct linting (with 'allow_trailing' set)", { linter <- commas_linter(allow_trailing = TRUE) msg_after <- rex::rex("Commas should always have a space after.") msg_before <- rex::rex("Commas should never have a space before.") expect_lint("blah", NULL, linter) expect_lint("fun(1, 1)", NULL, linter) expect_lint("fun(1,\n 1)", NULL, linter) expect_lint("fun(1,\n1)", NULL, linter) expect_lint("fun(1\n,\n1)", NULL, linter) expect_lint("fun(1\n ,\n1)", NULL, linter) expect_lint("a[1,]", NULL, linter) expect_lint("a(1,)", NULL, linter) expect_lint("fun(1\n,1)", msg_after, linter) expect_lint("fun(1,1)", msg_after, linter) expect_lint("\nfun(1,1)", msg_after, linter) expect_lint( "fun(1 ,1)", list( msg_before, msg_after ), linter ) expect_lint("\"fun(1 ,1)\"", NULL, linter) expect_lint("a[1, , 2]", NULL, linter) expect_lint("a[1, , 2, , 3]", NULL, linter) expect_lint("a[[1,]]", NULL, linter) expect_lint("switch(op, x = foo, y = bar)", NULL, linter) expect_lint("switch(op, x = , y = bar)", NULL, linter) expect_lint("switch(op, \"x\" = , y = bar)", NULL, linter) expect_lint("switch(op, x = ,\ny = bar)", NULL, linter) expect_lint("switch(op, x = foo , y = bar)", msg_before, linter) expect_lint("switch(op, x = foo , y = bar)", msg_before, linter) expect_lint("switch(op , x = foo, y = bar)", msg_before, linter) expect_lint("switch(op, x = foo, y = bar(a = 4 , b = 5))", msg_before, linter) expect_lint("fun(op, x = foo , y = switch(bar, a = 4, b = 5))", msg_before, linter) expect_lint( "fun(op ,bar)", list( list(message = msg_before, column_number = 7L, ranges = list(c(7L, 10L))), list(message = msg_after, column_number = 12L, ranges = list(c(12L, 12L))) ), linter ) }) lintr/tests/testthat/knitr_formats/0000755000176200001440000000000014450447225017275 5ustar liggesuserslintr/tests/testthat/knitr_formats/test.Rnw0000644000176200001440000000142513740716603020746 0ustar liggesusers\documentclass{article} \usepackage{mathpazo} \begin{document} \title{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. <>= a = 1 @ \subtitle{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. <<>>= b <- function(x) { d = 1 } @ <>= a=[] a[0]=1 @ lintr/tests/testthat/knitr_formats/test.qmd0000644000176200001440000000262114450447225020760 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 ``` Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} b <- function(x) { d = 1 } ``` ```{r engine="python"} a=[] a[0]=1 ``` ``` Plain code blocks can be written after three or more backticks - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2) ``` ```r # This is a non-evaluated block of R code for formatting in markdown. # It should not be linted abc = 123 ``` ```cpp // Some C++ code for formatting by markdown ``` Calls to a non-R knitr-engine using {engine_name} syntax. ```{python} # Python that looks like R a = list() b = {2} print(a) ``` ```{python} # Python that's definitely not R a = [] a.append(2) print(a) ``` The following are only supported by Quarto and shouldn't lint either. ```{.r} 1+1 ``` ```{{r}} 1+1 ``` ```{.python} # Python that's definitely not R a = [] a.append(2) print(a) ``` lintr/tests/testthat/knitr_formats/test.Rmd0000644000176200001440000000233014250050336020705 0ustar liggesusers# Test # Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} a = 1 ``` Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ```{r} b <- function(x) { d = 1 } ``` ```{r engine="python"} a=[] a[0]=1 ``` ``` Plain code blocks can be written after three or more backticks - R Markdown: The Definitive Guide. Xie, Allaire and Grolemund (2.5.2) ``` ```r # This is a non-evaluated block of R code for formatting in markdown. # It should not be linted abc = 123 ``` ```cpp // Some C++ code for formatting by markdown ``` Calls to a non-R knitr-engine using {engine_name} syntax. ```{python} # Python that looks like R a = list() b = {2} print(a) ``` ```{python} # Python that's definitely not R a = [] a.append(2) print(a) ``` lintr/tests/testthat/knitr_formats/test.Rhtml0000644000176200001440000000154013740716603021264 0ustar liggesusers Test

    Test

    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

    Test

    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. lintr/tests/testthat/knitr_formats/test.Rtxt0000644000176200001440000000140113740716603021133 0ustar liggesusers= Test = Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. //begin.rcode a = 1 //end.rcode Test ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. //begin.rcode trailing b <- function(x) { d = 1 } //end.rcode //begin.rcode engine="python" a=[] a[0]=1 //end.rcode lintr/tests/testthat/knitr_formats/test.Rrst0000644000176200001440000000134013740716603021126 0ustar liggesusersTest ==== Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. .. {r rst-example} a = 1 .. .. Test ---- Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. .. {r} b <- function(x) { d = 1 } .. .. .. {r engine = "python"} a=[] a[0]=1 .. .. lintr/tests/testthat/knitr_formats/test.Rtex0000644000176200001440000000151713740716603021124 0ustar liggesusers\documentclass{article} \usepackage{graphicx} \title{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. %% begin.rcode % a = 1 %% end.rcode \subtitle{Test} Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. %% begin.rcode test_chunk % b <- function(x) { % d = 1 % } % %% end.rcode %% begin.rcode engine="python" % a=[] % % a[0]=1 %% end.rcode lintr/tests/testthat/test-backport_linter.R0000644000176200001440000000427714577052532020712 0ustar liggesuserstest_that("backport_linter produces error when R version misspecified", { expect_error( lint(text = "numToBits(2)", linters = backport_linter(420L)), "`r_version` must be a R version number, returned by R_system_version(), or a string.", fixed = TRUE ) }) test_that("backport_linter detects backwards-incompatibility", { # default should be current R version; all of these are included on our dependency expect_lint(".getNamespaceInfo(dir.exists(lapply(x, toTitleCase)))", NULL, backport_linter()) expect_lint(".getNamespaceInfo(dir.exists(lapply(x, toTitleCase)))", NULL, backport_linter("release")) expect_lint(".getNamespaceInfo(dir.exists(lapply(x, toTitleCase)))", NULL, backport_linter("devel")) expect_lint( "numToBits(2)", rex::rex("numToBits (R 4.1.0) is not available for dependency R >= 4.0.0."), backport_linter("4.0.0") ) # symbols as well as calls expect_lint( "lapply(1:10, numToBits)", rex::rex("numToBits (R 4.1.0) is not available for dependency R >= 4.0.0."), backport_linter("4.0.0") ) expect_lint( "trimws(...names())", list( rex::rex("trimws (R 3.2.0) is not available for dependency R >= 3.0.0."), rex::rex("...names (R 4.1.0) is not available for dependency R >= 3.0.0.") ), backport_linter("3.0.0") ) # oldrel specification expect_lint( ".pretty(2)", rex::rex(".pretty (R 4.2.0) is not available for dependency R >= 4.1.3."), backport_linter("oldrel") ) expect_error(backport_linter("oldrel-99"), "`r_version` must be a version number or one of") expect_lint( "numToBits(2)", rex::rex("numToBits (R 4.1.0) is not available for dependency R >= 3.6.3."), backport_linter("oldrel-3") ) # except is honored expect_lint( trim_some(" numToBits(2) R_user_dir('mypkg') "), NULL, backport_linter("3.0.0", except = c("numToBits", "R_user_dir")) ) }) test_that("backport_linter generates expected warnings", { tmp <- withr::local_tempfile(lines = "x <- x + 1") expect_warning( { l <- lint(tmp, backport_linter("2.0.0")) }, "version older than 3.0.0", fixed = TRUE ) expect_identical(l, lint(tmp, backport_linter("3.0.0"))) }) lintr/tests/testthat/test-expect_type_linter.R0000644000176200001440000000467514577052532021440 0ustar liggesuserstest_that("expect_type_linter skips allowed usages", { linter <- expect_type_linter() # expect_type doesn't have an inverted version expect_lint("expect_true(!is.numeric(x))", NULL, linter) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(!is.numeric(x))", NULL, linter) # other is. calls are not suitable for expect_type in particular expect_lint("expect_true(is.data.frame(x))", NULL, linter) # expect_type(x, ...) cannot be cleanly used here: expect_lint("expect_true(typeof(x) %in% c('builtin', 'closure'))", NULL, linter) # expect_type() doesn't have info= or label= arguments expect_lint("expect_equal(typeof(x), t, info = 'x should have type t')", NULL, linter) expect_lint("expect_equal(typeof(x), t, label = 'x type')", NULL, linter) expect_lint("expect_equal(typeof(x), t, expected.label = 'type')", NULL, linter) expect_lint("expect_true(is.double(x), info = 'x should be double')", NULL, linter) }) test_that("expect_type_linter blocks simple disallowed usages", { linter <- expect_type_linter() expect_lint( "expect_equal(typeof(x), 'double')", rex::rex("expect_type(x, t) is better than expect_equal(typeof(x), t)"), linter ) # expect_identical is treated the same as expect_equal expect_lint( "testthat::expect_identical(typeof(x), 'language')", rex::rex("expect_type(x, t) is better than expect_identical(typeof(x), t)"), linter ) # different equivalent usage expect_lint( "expect_true(is.complex(foo(x)))", rex::rex("expect_type(x, t) is better than expect_true(is.(x))"), linter ) # yoda test with clear expect_type replacement expect_lint( "expect_equal('integer', typeof(x))", rex::rex("expect_type(x, t) is better than expect_equal(typeof(x), t)"), linter ) }) local({ # test for lint errors appropriately raised for all is. calls is_types <- c( "raw", "logical", "integer", "double", "complex", "character", "list", "numeric", "function", "primitive", "environment", "pairlist", "promise", "language", "call", "name", "symbol", "expression" ) patrick::with_parameters_test_that( "expect_type_linter catches expect_true(is.)", expect_lint( sprintf("expect_true(is.%s(x))", is_type), rex::rex("expect_type(x, t) is better than expect_true(is.(x))"), expect_type_linter() ), .test_name = is_types, is_type = is_types ) }) lintr/tests/testthat/test-unreachable_code_linter.R0000644000176200001440000003363714577054223022351 0ustar liggesuserstest_that("unreachable_code_linter works in simple function", { lines <- trim_some(" foo <- function(bar) { return(bar) } ") expect_lint(lines, NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter works in sub expressions", { linter <- unreachable_code_linter() msg <- rex::rex("Code and comments coming after a return() or stop()") lines <- trim_some(" foo <- function(bar) { if (bar) { return(bar) # Test comment while (bar) { return(bar) 5 + 3 repeat { return(bar) # Test comment } } } else if (bla) { # test return(5) # Test 2 } else { return(bar) # Test comment for(i in 1:3) { return(bar) 5 + 4 } } return(bar) 5 + 1 } ") expect_lint( lines, list( list(line_number = 4L, message = msg), list(line_number = 7L, message = msg), list(line_number = 10L, message = msg), list(line_number = 16L, message = msg), list(line_number = 19L, message = msg), list(line_number = 22L, message = msg), list(line_number = 26L, message = msg) ), linter ) lines <- trim_some(" foo <- function(bar) { if (bar) { return(bar) # Test comment } while (bar) { return(bar) # 5 + 3 } repeat { return(bar) # Test comment } } ") expect_lint(lines, NULL, linter) lines <- trim_some(" foo <- function(bar) { if (bar) { return(bar); x <- 2 } else { return(bar); x <- 3 } while (bar) { return(bar); 5 + 3 } repeat { return(bar); test() } for(i in 1:3) { return(bar); 5 + 4 } } ") expect_lint( lines, list( list(line_number = 3L, message = msg), list(line_number = 5L, message = msg), list(line_number = 8L, message = msg), list(line_number = 11L, message = msg), list(line_number = 14L, message = msg) ), linter ) }) test_that("unreachable_code_linter works with next and break in sub expressions", { linter <- unreachable_code_linter() msg <- rex::rex("Code and comments coming after a `next` or `break`") lines <- trim_some(" foo <- function(bar) { if (bar) { next # Test comment while (bar) { break 5 + 3 repeat { next # Test comment } } } else { next # test for(i in 1:3) { break 5 + 4 } } } ") expect_lint( lines, list( list(line_number = 4L, message = msg), list(line_number = 7L, message = msg), list(line_number = 10L, message = msg), list(line_number = 15L, message = msg), list(line_number = 18L, message = msg) ), linter ) lines <- trim_some(" foo <- function(bar) { if (bar) { break # Test comment } else { next # Test comment } while (bar) { next # 5 + 3 } repeat { next # Test comment } for(i in 1:3) { break # 5 + 4 } } ") expect_lint(lines, NULL, linter) lines <- trim_some(" foo <- function(bar) { if (bar) { next; x <- 2 } else { break; x <- 3 } while (bar) { break; 5 + 3 } repeat { next; test() } for(i in 1:3) { break; 5 + 4 } } ") expect_lint( lines, list( list(line_number = 3L, message = msg), list(line_number = 5L, message = msg), list(line_number = 8L, message = msg), list(line_number = 11L, message = msg), list(line_number = 14L, message = msg) ), linter ) }) test_that("unreachable_code_linter ignores expressions that aren't functions", { expect_lint("x + 1", NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter ignores anonymous/inline functions", { expect_lint("lapply(rnorm(10), function(x) x + 1)", NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter passes on multi-line functions", { lines <- trim_some(" oo <- function(x) { y <- x + 1 return(y) } ") expect_lint(lines, NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter ignores comments on the same expression", { lines <- trim_some(" foo <- function(x) { return( y^2 ) # y^3 } ") expect_lint(lines, NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter ignores comments on the same line", { lines <- trim_some(" foo <- function(x) { return(y^2) # y^3 } ") expect_lint(lines, NULL, unreachable_code_linter()) }) test_that("unreachable_code_linter identifies simple unreachable code", { lines <- trim_some(" foo <- function(bar) { return(bar) x + 3 } ") # testing the correct expression is linted (the first culprit line) expect_lint( lines, list( line_number = 3L, message = rex::rex("Code and comments coming after a return() or stop()") ), unreachable_code_linter() ) }) test_that("unreachable_code_linter finds unreachable comments", { lines <- trim_some(" foo <- function(x) { y <- x + 1 return(y^2) # y^3 } ") expect_lint( lines, rex::rex("Code and comments coming after a return() or stop()"), unreachable_code_linter() ) }) test_that("unreachable_code_linter finds expressions in the same line", { msg <- rex::rex("Code and comments coming after a return() or stop()") linter <- unreachable_code_linter() lines <- trim_some(" foo <- function(x) { return( y^2 ); 3 + 1 } ") expect_lint(lines, msg, linter) lines <- trim_some(" foo <- function(x) { return(y^2); 3 + 1 } ") expect_lint(lines, msg, linter) lines <- trim_some(" foo <- function(x) { return(y^2); 3 + 1 # Test } ") expect_lint(lines, msg, linter) }) test_that("unreachable_code_linter finds expressions and comments after comment in return line", { msg <- rex::rex("Code and comments coming after a return() or stop()") linter <- unreachable_code_linter() lines <- trim_some(" foo <- function(x) { return(y^2) #Test comment #Test comment 2 } ") expect_lint(lines, msg, linter) lines <- trim_some(" foo <- function(x) { return(y^2) # Test 3 + 1 } ") expect_lint(lines, msg, linter) }) test_that("unreachable_code_linter finds a double return", { lines <- trim_some(" foo <- function(x) { return(y^2) return(y^3) } ") expect_lint( lines, rex::rex("Code and comments coming after a return() or stop()"), unreachable_code_linter() ) }) test_that("unreachable_code_linter finds code after stop()", { lines <- trim_some(" foo <- function(x) { y <- x + 1 stop(y^2) # y^3 } ") expect_lint( lines, rex::rex("Code and comments coming after a return() or stop()"), unreachable_code_linter() ) }) test_that("unreachable_code_linter ignores code after foo$stop(), which might be stopping a subprocess, for example", { expect_lint( trim_some(" foo <- function(x) { bar <- get_process() bar$stop() TRUE } "), NULL, unreachable_code_linter() ) expect_lint( trim_some(" foo <- function(x) { bar <- get_process() bar@stop() TRUE } "), NULL, unreachable_code_linter() ) }) test_that("unreachable_code_linter ignores terminal nolint end comments", { withr::local_options(list( lintr.exclude_start = "#\\s*TestNoLintStart", lintr.exclude_end = "#\\s*TestNoLintEnd" )) expect_lint( trim_some(" foo <- function() { do_something # TestNoLintStart: one_linter. a = 42 return(a) # TestNoLintEnd } "), NULL, list(unreachable_code_linter(), one_linter = assignment_linter()) ) expect_lint( trim_some(" foo <- function() { do_something # nolint start: one_linter. a = 42 next # nolint end } "), NULL, unreachable_code_linter() ) }) test_that("unreachable_code_linter identifies unreachable code in conditional loops", { linter <- unreachable_code_linter() msg <- rex::rex("Code inside a conditional loop with a deterministically false condition should be removed.") lines <- trim_some(" foo <- function(bar) { if (FALSE) { x <- 3 } x + 3 } ") expect_lint(lines, list(line_number = 3L, message = msg), linter) lines <- trim_some(" foo <- function(bar) { if (FALSE) { # Unlinted comment x <- 3 } x + 3 } ") expect_lint(lines, list(line_number = 4L, message = msg), linter) lines <- trim_some(" foo <- function(bar) { if (bla) { x <- 3 } else if (FALSE) { # Unlinted comment y <- 3 } x + 3 } ") expect_lint(lines, list(line_number = 6L, message = msg), linter) lines <- trim_some(" foo <- function(bar) { while (FALSE) { x <- 3 } x + 3 } ") expect_lint(lines, list(line_number = 3L, message = msg), linter) lines <- trim_some(" foo <- function(bar) { while (FALSE) { # Unlinted comment x <- 3 } x + 3 } ") expect_lint(lines, list(line_number = 4L, message = msg), linter) lines <- "while (FALSE) x <- 3" expect_lint( lines, list(line_number = 1L, ranges = list(c(15L, 20L)), message = msg), linter ) lines <- "if (FALSE) x <- 3 # Test comment" expect_lint( lines, list(line_number = 1L, ranges = list(c(12L, 17L)), message = msg), linter ) }) test_that("unreachable_code_linter identifies unreachable code in conditional loops", { linter <- unreachable_code_linter() msg <- rex::rex("Code inside an else block after a deterministically true if condition should be removed.") lines <- trim_some(" foo <- function(bar) { if (TRUE) { x <- 3 } else { # Unlinted comment x + 3 } } ") expect_lint(lines, list(line_number = 6L, message = msg), linter) lines <- trim_some(" foo <- function(bar) { if (TRUE) { x <- 3 } else if (bar) { # Unlinted comment x + 3 } } ") expect_lint(lines, list(line_number = 4L, message = msg), linter) lines <- "if (TRUE) x <- 3 else if (bar) x + 3" expect_lint( lines, list(line_number = 1L, ranges = list(c(23L, 36L)), message = msg), linter ) }) test_that("unreachable_code_linter identifies unreachable code in mixed conditional loops", { linter <- unreachable_code_linter() msg <- rex::rex("Code inside a conditional loop with a deterministically false condition should be removed.") lines <- trim_some(" function (bla) { if (FALSE) { code + 4 } while (FALSE) { code == 3 } if (TRUE) { } else { code + bla } stop('.') code <- 1 } ") expect_lint( lines, list( list(line_number = 3L, message = msg), list(line_number = 6L, message = msg), list( line_number = 10L, message = rex::rex("Code inside an else block after a deterministically true if condition should be removed.") ), list( line_number = 13L, message = rex::rex("Code and comments coming after a return() or stop()") ) ), linter ) lines <- "if (FALSE) x <- 3 else if (TRUE) x + 3 else x + 4" expect_lint( lines, list( list(line_number = 1L, ranges = list(c(12L, 17L)), message = msg), list( line_number = 1L, ranges = list(c(45L, 49L)), message = rex::rex("Code inside an else block after a deterministically true if condition should be removed.") ) ), linter ) }) test_that("function shorthand is handled", { skip_if_not_r_version("4.1.0") expect_lint( trim_some(" foo <- \\(bar) { return(bar) x + 3 } "), list( line_number = 3L, message = rex::rex("Code and comments coming after a return() or stop()") ), unreachable_code_linter() ) }) test_that("Do not lint inline else after stop", { expect_lint( "if (x > 3L) stop() else x + 3", NULL, unreachable_code_linter() ) }) test_that("Do not lint inline else after stop in inline function", { expect_lint( "function(x) if (x > 3L) stop() else x + 3", NULL, unreachable_code_linter() ) expect_lint( "function(x) if (x > 3L) { stop() } else {x + 3}", NULL, unreachable_code_linter() ) }) test_that("Do not lint inline else after stop in inline lambda function", { skip_if_not_r_version("4.1.0") expect_lint( "\\(x) if (x > 3L) stop() else x + 3", NULL, unreachable_code_linter() ) }) test_that("Do not lint inline else after stop in lambda function", { skip_if_not_r_version("4.1.0") expect_lint( "\\(x){ if (x > 3L) stop() else x + 3 }", NULL, unreachable_code_linter() ) }) # nolint start: commented_code_linter. # TODO(michaelchirico): extend to work on switch() statements # test_that("unreachable_code_linter interacts with switch() as expected", { # unreachable_inside_switch_lines <- trim_some(" # foo <- function(x) { # switch(x, # a = { # return(x) # x + 1 # }, # b = { # return(x + 1) # } # ) # } # ") # expect_lint( # unreachable_inside_switch_lines, # rex::rex("Code and comments coming after a return() or stop()"), # unreachable_code_linter() # ) # }) # nolint end: commented_code_linter. # TODO(michaelchirico): This could also apply to cases without # explicit returns (where it can only apply to comments) lintr/tests/testthat/test-vector_logic_linter.R0000644000176200001440000000675614577055422021571 0ustar liggesuserstest_that("vector_logic_linter skips allowed usages", { linter <- vector_logic_linter() expect_lint("if (TRUE) 5 else if (TRUE) 2", NULL, linter) expect_lint("if (TRUE || FALSE) 1; while (TRUE && FALSE) 2", NULL, linter) # function calls and extractions may aggregate to scalars -- only catch # usages at the highest logical level expect_lint("if (agg_function(x & y)) 1", NULL, linter) expect_lint("if (DT[x | y, cond]) 1", NULL, linter) # don't match potentially OK usages nested within calls expect_lint("if (TRUE && any(TRUE | FALSE)) 4", NULL, linter) # even if the usage is nested in those calls (b/181915948) expect_lint("if (TRUE && any(TRUE | FALSE | TRUE)) 4", NULL, linter) # don't match potentially OK usages in the branch itself lines <- trim_some(" if (TRUE) { x | y } ") expect_lint(lines, NULL, linter) # valid nested usage within aggregator expect_lint("testthat::expect_false(any(TRUE | TRUE))", NULL, linter) }) test_that("vector_logic_linter blocks simple disallowed usages", { linter <- vector_logic_linter() lint_msg <- rex::rex("Conditional expressions require scalar logical operators") expect_lint("if (TRUE & FALSE) 1", lint_msg, linter) expect_lint("while (TRUE | TRUE) 2", lint_msg, linter) }) test_that("vector_logic_linter detects nested conditions", { linter <- vector_logic_linter() lint_msg <- rex::rex("Conditional expressions require scalar logical operators") expect_lint("if (TRUE & TRUE || FALSE) 4", lint_msg, linter) expect_lint("if (TRUE && (TRUE | FALSE)) 4", lint_msg, linter) }) test_that("vector_logic_linter catches usages in expect_true()/expect_false()", { linter <- vector_logic_linter() lint_msg <- rex::rex("Conditional expressions require scalar logical operators") expect_lint("expect_true(TRUE & FALSE)", lint_msg, linter) expect_lint("expect_false(TRUE | TRUE)", lint_msg, linter) # ditto with namespace qualification expect_lint("testthat::expect_true(TRUE & FALSE)", lint_msg, linter) expect_lint("testthat::expect_false(TRUE | TRUE)", lint_msg, linter) }) test_that("vector_logic_linter doesn't get mixed up from complex usage", { expect_lint( trim_some(" if (a) { expect_true(ok) x <- 2 a | b } "), NULL, vector_logic_linter() ) }) test_that("vector_logic_linter recognizes some false positves around bitwise &/|", { linter <- vector_logic_linter() expect_lint("if (info & as.raw(12)) { }", NULL, linter) expect_lint("if (as.raw(12) & info) { }", NULL, linter) expect_lint("if (info | as.raw(12)) { }", NULL, linter) expect_lint("if (info & as.octmode('100')) { }", NULL, linter) expect_lint("if (info | as.octmode('011')) { }", NULL, linter) expect_lint("if (info & as.hexmode('100')) { }", NULL, linter) expect_lint("if (info | as.hexmode('011')) { }", NULL, linter) # implicit as.octmode() coercion expect_lint("if (info & '100') { }", NULL, linter) expect_lint("if (info | '011') { }", NULL, linter) expect_lint("if ('011' | info) { }", NULL, linter) # further nesting expect_lint("if ((info & as.raw(12)) == as.raw(12)) { }", NULL, linter) expect_lint("if ((info | as.raw(12)) == as.raw(12)) { }", NULL, linter) expect_lint('if ((mode & "111") != as.octmode("111")) { }', NULL, linter) expect_lint('if ((mode | "111") != as.octmode("111")) { }', NULL, linter) expect_lint('if ((mode & "111") != as.hexmode("111")) { }', NULL, linter) expect_lint('if ((mode | "111") != as.hexmode("111")) { }', NULL, linter) }) lintr/tests/testthat/test-system_file_linter.R0000644000176200001440000000120314577052532021412 0ustar liggesuserstest_that("system_file_linter skips allowed usages", { expect_lint("system.file('a', 'b', 'c')", NULL, system_file_linter()) expect_lint("file.path('a', 'b', 'c')", NULL, system_file_linter()) }) test_that("system_file_linter blocks simple disallowed usages", { expect_lint( "system.file(file.path('path', 'to', 'data'), package = 'foo')", rex::rex("Use the `...` argument of system.file() to expand paths"), system_file_linter() ) expect_lint( "file.path(system.file(package = 'foo'), 'path', 'to', 'data')", rex::rex("Use the `...` argument of system.file() to expand paths"), system_file_linter() ) }) lintr/tests/testthat/test-expect_not_linter.R0000644000176200001440000000173114577052532021245 0ustar liggesuserstest_that("expect_not_linter skips allowed usages", { expect_lint("expect_true(x)", NULL, expect_not_linter()) # NB: also applies to tinytest, but it's sufficient to test testthat expect_lint("testthat::expect_true(x)", NULL, expect_not_linter()) expect_lint("expect_false(x)", NULL, expect_not_linter()) expect_lint("testthat::expect_false(x)", NULL, expect_not_linter()) # not a strict ban on ! ## (expect_false(x && y) is the same, but it's not clear which to prefer) expect_lint("expect_true(!x || !y)", NULL, expect_not_linter()) }) test_that("expect_not_linter blocks simple disallowed usages", { linter <- expect_not_linter() lint_msg <- rex::rex("expect_false(x) is better than expect_true(!x), and vice versa.") expect_lint("expect_true(!x)", lint_msg, linter) expect_lint("testthat::expect_true(!x)", lint_msg, linter) expect_lint("expect_false(!foo(x))", lint_msg, linter) expect_lint("testthat::expect_true(!(x && y))", lint_msg, linter) }) lintr/tests/testthat/test-todo_comment_linter.R0000644000176200001440000000144314577052532021564 0ustar liggesuserstest_that("returns the correct linting", { linter <- todo_comment_linter(todo = c("todo", "fixme")) lint_msg <- "TODO comments should be removed." expect_lint("a <- \"you#need#to#fixme\"", NULL, linter) expect_lint("# something todo", NULL, linter) expect_lint( "cat(x) ### fixme", list(message = lint_msg, line_number = 1L, column_number = 8L), linter ) expect_lint( "x <- \"1.0\n2.0 #FIXME\n3 #TODO 4\"; y <- 2; z <- 3 # todo later", list(message = lint_msg, line_number = 3L, column_number = 28L), linter ) expect_lint( "function() {\n# TODO\n function() {\n # fixme\n }\n}", list( list(message = lint_msg, line_number = 2L, column_number = 1L), list(message = lint_msg, line_number = 4L, column_number = 3L) ), linter ) }) lintr/tests/testthat/test-nonportable_path_linter.R0000644000176200001440000000322214577052532022431 0ustar liggesuserstest_that("nonportable_path_linter skips allowed usages", { linter <- nonportable_path_linter(lax = FALSE) # various strings non_path_strings <- c( "foo", "https://cran.r-project.org/web/packages/lintr/", encodeString("hello\nthere!") ) for (path in non_path_strings) { expect_lint(single_quote(path), NULL, linter) expect_lint(double_quote(path), NULL, linter) } expect_lint("\"'/foo'\"", NULL, linter) # nested quotes # system root root_path_strings <- c("/", "~", "c:", ".") for (path in root_path_strings) { expect_lint(single_quote(path), NULL, linter) expect_lint(double_quote(path), NULL, linter) } }) test_that("nonportable_path_linter blocks disallowed usages", { linter <- nonportable_path_linter(lax = FALSE) lint_msg <- rex::escape("Use file.path() to construct portable file paths.") # paths with (back)slashes slash_path_strings <- c( "~/", "c:/", encodeString("D:\\"), "../", "/foo", "foo/", "foo/bar", encodeString("foo\\bar"), "/as:df", encodeString("/a\nsdf") ) for (path in slash_path_strings) { expect_lint(single_quote(path), lint_msg, linter) expect_lint(double_quote(path), lint_msg, linter) } }) test_that("nonportable_path_linter's lax argument works", { # lax mode: no check for strings that are likely not paths (too short or with special characters) linter <- nonportable_path_linter(lax = TRUE) unlikely_path_strings <- c( "/foo", encodeString("/a\nsdf/bar"), "/as:df/bar" ) for (path in unlikely_path_strings) { expect_lint(single_quote(path), NULL, linter) expect_lint(double_quote(path), NULL, linter) } }) lintr/tests/testthat/test-implicit_assignment_linter.R0000644000176200001440000002466714577052532023154 0ustar liggesuserstest_that("implicit_assignment_linter skips allowed usages", { linter <- implicit_assignment_linter() expect_lint("x <- 1L", NULL, linter) expect_lint("1L -> x", NULL, linter) expect_lint("x <<- 1L", NULL, linter) expect_lint("1L ->> x", NULL, linter) expect_lint("y <- if (is.null(x)) z else x", NULL, linter) expect_lint("for (x in 1:10) x <- x + 1", NULL, linter) expect_lint("abc <- mean(1:4)", NULL, linter) expect_lint("mean(1:4) -> abc", NULL, linter) expect_lint( trim_some(" x <- 1:4 mean(x)"), NULL, linter ) expect_lint( trim_some(" x <- 1L if (x) TRUE"), NULL, linter ) expect_lint( trim_some(" 0L -> abc while (abc) { FALSE }"), NULL, linter ) expect_lint( trim_some(" if (x > 20L) { x <- x / 2.0 }"), NULL, linter ) expect_lint( trim_some(" i <- 1 while (i < 6L) { print(i) i <- i + 1 }"), NULL, linter ) expect_lint( trim_some(" foo <- function(x) { x <- x + 1 return(x) }"), NULL, linter ) expect_lint( trim_some(" f <- function() { p <- g() p <- if (is.null(p)) x else p }"), NULL, linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ { x <- .x + 1 x } )"), NULL, linter ) expect_lint( trim_some(" lapply(1:4, function(x) { x <- x + 1 x })"), NULL, linter ) skip_if_not_r_version("4.1.0") expect_lint( trim_some(" map(1:4, \\(x) { x <- x + 1 x })"), NULL, linter ) }) test_that("implicit_assignment_linter respects except argument", { expect_lint( "local({ a <- 1L })", NULL, implicit_assignment_linter(except = NULL) ) expect_lint( "local({ a <- 1L })", NULL, implicit_assignment_linter(except = character(0L)) ) expect_lint( "local(a <- 1L)", rex::rex("Avoid implicit assignments in function calls."), implicit_assignment_linter(except = character(0L)) ) expect_lint( "local(a <- 1L)", rex::rex("Avoid implicit assignments in function calls."), implicit_assignment_linter(except = NULL) ) expect_lint( "local(a <- 1L)", NULL, implicit_assignment_linter(except = "local") ) }) test_that("implicit_assignment_linter skips allowed usages with braces", { linter <- implicit_assignment_linter(except = character(0L)) expect_lint( trim_some(" foo({ a <- 1L }) "), NULL, linter ) expect_lint( trim_some(" output <- capture.output({ x <- f() }) "), NULL, linter ) expect_lint( trim_some(" quote({ a <- 1L }) "), NULL, linter ) expect_lint( trim_some(" bquote({ a <- 1L }) "), NULL, linter ) expect_lint( trim_some(" expression({ a <- 1L }) "), NULL, linter ) expect_lint( trim_some(" local({ a <- 1L }) "), NULL, linter ) }) test_that("implicit_assignment_linter makes exceptions for functions that capture side-effects", { linter <- implicit_assignment_linter() expect_lint( trim_some(" test_that('my test', { a <- 1L expect_equal(a, 1L) })"), NULL, linter ) # rlang expect_lint("expr(a <- 1L)", NULL, linter) expect_lint("quo(a <- 1L)", NULL, linter) expect_lint("quos(a <- 1L)", NULL, linter) }) test_that("implicit_assignment_linter blocks disallowed usages in simple conditional statements", { lint_message <- rex::rex("Avoid implicit assignments in function calls.") linter <- implicit_assignment_linter() expect_lint("if (x <- 1L) TRUE", lint_message, linter) expect_lint("if (1L -> x) TRUE", lint_message, linter) expect_lint("if (x <<- 1L) TRUE", lint_message, linter) expect_lint("if (1L ->> x) TRUE", lint_message, linter) expect_lint("while (x <- 0L) FALSE", lint_message, linter) expect_lint("while (0L -> x) FALSE", lint_message, linter) expect_lint("for (x in y <- 1:10) print(x)", lint_message, linter) expect_lint("for (x in 1:10 -> y) print(x)", lint_message, linter) }) test_that("implicit_assignment_linter blocks disallowed usages in nested conditional statements", { lint_message <- rex::rex("Avoid implicit assignments in function calls.") linter <- implicit_assignment_linter() expect_lint( trim_some(" while (x <- 1L) { if (0L -> y) FALSE }"), list( list(message = lint_message, line_number = 1L, column_number = 8L), list(message = lint_message, line_number = 2L, column_number = 7L) ), linter ) expect_lint( trim_some(" for (x in y <- 1:10) { if (0L -> y) print(x) }"), list( list(message = lint_message, line_number = 1L, column_number = 11L), list(message = lint_message, line_number = 2L, column_number = 7L) ), linter ) }) test_that("implicit_assignment_linter blocks disallowed usages in function calls", { lint_message <- rex::rex("Avoid implicit assignments in function calls.") linter <- implicit_assignment_linter() expect_lint("mean(x <- 1:4)", lint_message, linter) expect_lint( "mean(x <- (y <- 1:3) + 1L)", list(list(column_number = 6L), list(column_number = 12L)), linter ) expect_lint("y <- median(x <- 1:4)", lint_message, linter) expect_lint("lapply(x, function(x) return(x <- x + 1))", lint_message, linter) expect_lint("map(x, function(x) return(x <- x + 1))", lint_message, linter) expect_lint("expect_warning(out <- f(-1))", lint_message, linter) expect_lint("expect_message(out <- f(-1))", lint_message, linter) expect_lint("expect_error(out <- f(-1))", lint_message, linter) expect_lint("expect_condition(out <- f(-1))", lint_message, linter) expect_lint( trim_some(" foo <- function(x) { return(x <- x + 1) }"), lint_message, linter ) expect_lint( trim_some(" foo <- function(x) { if (x <- 1L) x <- 2L return(x <- x + 1) }"), list( list(message = lint_message, line_number = 2L, column_number = 7L), list(message = lint_message, line_number = 3L, column_number = 10L) ), linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ .x + 1 -> x )"), lint_message, linter ) expect_lint( trim_some(" map( .x = 1:4, .f = ~ (x <- .x + 1) )"), lint_message, linter ) expect_lint( "foo(a <- 1, b <- 2, c <- 3)", list(list(column_number = 5L), list(column_number = 13L), list(column_number = 21L)), linter ) }) test_that("implicit_assignment_linter works as expected with pipes and walrus operator", { linter <- implicit_assignment_linter() expect_lint("data %>% mutate(a := b)", NULL, linter) expect_lint("dt %>% .[, z := x + y]", NULL, linter) expect_lint("data %<>% mutate(a := b)", NULL, linter) expect_lint("DT[i, x := i]", NULL, linter) skip_if_not_r_version("4.1.0") expect_lint("data |> mutate(a := b)", NULL, linter) }) test_that("parenthetical assignments are caught", { linter <- implicit_assignment_linter() lint_message <- rex::rex("Avoid implicit assignments in function calls.") expect_lint("(x <- 1:10)", lint_message, linter) expect_lint("if (A && (B <- foo())) { }", lint_message, linter) }) test_that("allow_lazy lets lazy assignments through", { linter <- implicit_assignment_linter(allow_lazy = TRUE) lint_message <- rex::rex("Avoid implicit assignments in function calls.") expect_lint("A && (B <- foo(A))", NULL, linter) # || also admits laziness expect_lint("A || (B <- foo(A))", NULL, linter) # & and |, however, do not expect_lint("A & (B <- foo(A))", lint_message, linter) expect_lint("A | (B <- foo(A))", lint_message, linter) expect_lint("A && foo(bar(idx <- baz()))", NULL, linter) # LHS _is_ linted expect_lint("(A <- foo()) && B", lint_message, linter) # however we skip on _any_ RHS (even if it's later an LHS) # test on all &&/|| combinations to stress test operator precedence expect_lint("A && (B <- foo(A)) && C", NULL, linter) expect_lint("A && (B <- foo(A)) || C", NULL, linter) expect_lint("A || (B <- foo(A)) && C", NULL, linter) expect_lint("A || (B <- foo(A)) || C", NULL, linter) # &&/|| elsewhere in the tree don't matter expect_lint( trim_some(" A && B foo(C <- bar()) "), lint_message, linter ) }) test_that("allow_scoped skips scoped assignments", { linter <- implicit_assignment_linter(allow_scoped = TRUE) lint_message <- rex::rex("Avoid implicit assignments in function calls.") expect_lint( trim_some(" if (any(idx <- x < 0)) { stop('negative elements: ', toString(which(idx))) } "), NULL, linter ) expect_lint( trim_some(" if (any(idx <- x < 0)) { stop('negative elements: ', toString(which(idx))) } print(idx) "), lint_message, linter ) # only applies to the branch condition itself -- within the branch, still lint expect_lint( trim_some(" if (TRUE) { foo(idx <- bar()) } "), lint_message, linter ) expect_lint( trim_some(" obj <- letters while ((n <- length(obj)) > 0) obj <- obj[-n] "), NULL, linter ) expect_lint( trim_some(" obj <- letters while ((n <- length(obj)) > 0) obj <- obj[-n] if (TRUE) { print(n) } "), lint_message, linter ) # outside of branching, doesn't matter expect_lint("(idx <- foo()); bar()", lint_message, linter) expect_lint("foo(idx <- bar()); baz()", lint_message, linter) expect_lint("foo(x, idx <- bar()); baz()", lint_message, linter) }) test_that("interaction of allow_lazy and allow_scoped", { linter <- implicit_assignment_linter(allow_scoped = TRUE, allow_lazy = TRUE) expect_lint( trim_some(" if (any(idx <- foo()) && BB) { stop('Invalid foo() output: ', toString(idx)) } "), NULL, linter ) expect_lint( trim_some(" if (any(idx <- foo()) && BB) { stop('Invalid foo() output: ', toString(idx)) } print(format(idx)) "), rex::rex("Avoid implicit assignments in function calls."), linter ) expect_lint( trim_some(" if (AA && any(idx <- foo())) { stop('Invalid foo() output: ', toString(idx)) } print(format(idx)) # NB: bad code! idx may not exist. "), NULL, linter ) }) lintr/tests/testthat/test-with_id.R0000644000176200001440000000126614577052532017152 0ustar liggesuserstest_that("with_id works as expected", { source_expression <- get_source_expressions("tmp.R", "a <- 42L")$expressions[[1L]] ref <- with_id( source_expression = source_expression, ids_with_token(source_expression = source_expression, value = "expr") ) expect_identical(ref, source_expression$parsed_content[c(1L, 3L, 6L), ]) expect_identical(ref$token, rep_len("expr", nrow(ref))) # deprecated argument expect_warning( { old_arg <- with_id( source_file = source_expression, id = ids_with_token(source_expression = source_expression, value = "expr") ) }, "Argument source_file was deprecated" ) expect_identical(old_arg, ref) }) lintr/tests/testthat/test-T_and_F_symbol_linter.R0000644000176200001440000000350214506330025021736 0ustar liggesuserstest_that("T_and_F_symbol_linter skips allowed usages", { linter <- T_and_F_symbol_linter() expect_lint("FALSE", NULL, linter) expect_lint("TRUE", NULL, linter) expect_lint("x <- \"TRUE a vs FALSE b\"", NULL, linter) }) test_that("T_and_F_symbol_linter blocks disallowed usages", { linter <- T_and_F_symbol_linter() msg_true <- "Use TRUE instead of the symbol T." msg_false <- "Use FALSE instead of the symbol F." msg_variable_true <- "Don't use T as a variable name, as it can break code relying on T being TRUE." msg_variable_false <- "Don't use F as a variable name, as it can break code relying on F being FALSE." expect_lint("T", list(message = msg_true, line_number = 1L, column_number = 2L), linter) expect_lint("F", list(message = msg_false, line_number = 1L, column_number = 2L), linter) expect_lint("T = 42", list(message = msg_variable_true, line_number = 1L, column_number = 2L), linter) expect_lint("F = 42", list(message = msg_variable_false, line_number = 1L, column_number = 2L), linter) expect_lint( "for (i in 1:10) {x <- c(T, TRUE, F, FALSE)}", list( list(message = msg_true, line_number = 1L, column_number = 26L), list(message = msg_false, line_number = 1L, column_number = 35L) ), linter ) expect_lint("DF$bool <- T", msg_true, linter) expect_lint("S4@bool <- T", msg_true, linter) expect_lint("sum(x, na.rm = T)", msg_true, linter) # Regression test for #657 expect_lint( trim_some(" x <- list( T = 42L, F = 21L ) x$F <- 42L y@T <- 84L T <- \"foo\" F = \"foo2\" \"foo3\" -> T "), list( list(message = msg_variable_true, line_number = 9L), list(message = msg_variable_false, line_number = 10L), list(message = msg_variable_true, line_number = 11L) ), linter ) }) lintr/tests/testthat/test-use_lintr.R0000644000176200001440000000213714457657444017537 0ustar liggesuserstest_that("use_lintr works as expected", { tmp <- withr::local_tempdir() lintr_file <- use_lintr(path = tmp) expect_true(file.exists(lintr_file)) # check that newly created file is in the root directory expect_identical( normalizePath(lintr_file, winslash = "/"), file.path(normalizePath(tmp, winslash = "/"), ".lintr") ) # can't generate if a .lintr already exists expect_error(use_lintr(path = tmp), "Found an existing configuration") # check that `read_settings()` works with the generated file # this can be checked by checking lintr runs successfully lints <- lint_dir(tmp) expect_length(lints, 0L) }) test_that("use_lintr with type = full also works", { tmp <- withr::local_tempdir() # type = "full" also works with read_settings() lintr_file <- use_lintr(path = tmp, type = "full") expect_true(file.exists(lintr_file)) # check that newly created file is in the root directory expect_identical( normalizePath(lintr_file, winslash = "/"), file.path(normalizePath(tmp, winslash = "/"), ".lintr") ) lints <- lint_dir(tmp) expect_length(lints, 0L) }) lintr/tests/testthat.R0000644000176200001440000000024714457657444014556 0ustar liggesuserslibrary(testthat) library(lintr) # suppress printing environment name (noisy) invisible({ loadNamespace("patrick") loadNamespace("withr") }) test_check("lintr") lintr/vignettes/0000755000176200001440000000000014600122250013402 5ustar liggesuserslintr/vignettes/vim-syntastic.gif0000644000176200001440000265272014250050336016727 0ustar liggesusersGIF89a ش֪״ٹժصwcLֹNJueֳֺٻsltoSؒeijӵķ֮yu\F[dkWdh̤vkט۶ȷƩƪǷȨc^cƅkY؋rjifjZ^es]Ut]6{ոjtwƺYbZ[cdͶ㰧g^YrtjÝxv鷈|wɚ祆k̨yרQgllv[iuygWeieiieΉzֹjfd释ŚvhgʶҬxeekZiijwwxv{gkthcZǸԨsV^mȳƵ{ʨr]Jr\}뭳bxlwf}l]H寕eed\^ZmgNUbRΖym]47p o\eMͫoYعϑϮOc*ͰrלdXJ)m`KΒN}P &#"tQx!6B`S6ne**1e.kY$+Jf/#jf,i՝$jfhhp ! NETSCAPE2.0!, H@*4p@C >8a3V,xQƏ =)ɓ(K\Re˗0cIL8o̹$=y JѣA"]ʴS:tX30JJ@B!# |CpƝK\xЄC3?~@Lق~ :YKI -VxClMHÐ @s >{ԒР7okװc˞l5aHK[#5 `!B5i1%GC|#)b%K#Ǖ8r, 5ڭ[Ki#CCj# }W!ArԑP t  jVm($X4Mp%OÎ=M45\uę Z _R@yBUe=v r2[P'`F2ɇK&搳N;# 9#M]N;HIPeFIP{RAhM[&Rj饘[=/v[ uQt9 Cp RpD! XhA\AHc,.C &DJ#:AY0Ġ = e0jiXF/N:B^7I 7Gat^r+PKw̚ PI&kl&-*$xRni}xXmH'= p o qJQ >|!aT CTPQT*t\HJV`Pʨqiw*8_2pi9 ;99LHӭrTP)xTp SɹҬD^GGDC h"_ EBу4@!0M q2@t!Pq̪ǚiB;ɠo˽DjD;Vb+bp 4нYC84: ̈2Xc2@H@BAI!E1, uZ#8J C&IxBQJIas G8?M]áH*^*o("O !VaH2B0}(F3Fэp(9Qẉ=x I@L"iHF:c$'HJVҒĤ&7Nz (/9RҔO~ @J~MBjP: D'JъZ4hF7fz hHGJR(MJWҖԥ0L_:Ӛ8ͩNiS= P?:ԢHM*PԦ:PTJժZX]jVծz` XJֲhժZֶpZJ׹xͫ^Wկ `KְMb:d;YVֲŬf3@zz-\e鐋+ Qg5Ptb.Ѝ6e7Mion"l7QVV u'ۍlW}8w!#ycU@"`i$WLN< [ΰ7a wA<$FW0S<ۘ7αw?Ld HN&;PL*[ʝŲ.{F&*<2h&s6bL:wƳg>MBЈNFѐ'MJ[Ҙδ7N{ӠGgRԨNWVѯgMkXָεw^-bNf;ЎljWζn{MrNv7MzηW͉}q;! |!, \s H*\ȰÇ#JHŋ3jܨ1Ǐ CIɓ(0˗0cʜI-5sɳO ,ۀQć~*]ʴt-hA`ҁ@rʵׯ3j`Ӫ]=vaJݻxA@/S| ䷰Qbo7wKegÜ?gM郝O^MAְc =@۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\vw}9k&fkaf|2X7Vtixg-ig[d !x,Kb dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~ H*\ȰÇ#JHŋ3jȱGN!,Hp@A*\aC#JHŋ3BȱǏ 7Y =>`ڢAH.4HfL8sRDqR%2te(J7l :JڱTቢN?uX:Xq 7#Ѓ́I>IRɊR TS (ZU#fX"䱢蘢yay TpTaD$DŽDLH V- ~\fXVx$M:I jOYm|:D7£`׏F>U0d #@eP<IYCECpaQ Txa]` dPIj:%:apApWDG d.-`ԂC)eiTTv%rȗh&cpUk&\|矀*蠄&袌6衏F*餗Qj饘f v駠*ꦤ:ꩨꪬ꫰ư*무j뭦k민+k,&6FV;fv{[覫Ȳ뮹+[+plp' 70G,1Wlgw ,$lR*03l8!,H"\p`Tm>Q`;)'CA%rƚlT@` GF|4*+2G!T$=o\P vb(2Põ@ThAAYsScAr2R`g9' Uabፃ)Vē҄G!Qٲ@*$F>rHv.ͺ!, (`A*\xÇćϟ F!4T$BF:#'DAQ BTac` #z `U T=\(CmRI1tU9 )5(3"WШSTq'IPbTSNN{R2ֹC+% s\0 =Je2A$ёUDRx"d6Pp!J:AI(H32g$?,gxEѳE+ Wn!, Dx(` &\Á d%HbƏ ^dBNJ(tɲĐ.cʜI͛8sdRf'ƤHtaО/&YAMwIիXZscŒ )$-t`"te4DKm)N|bJ) :snÈ+IgDt-Z^D2eR,%P̫iI֘KqhV, Ӏ>?5ȉ8 *qhˇ\ȑ##8З/a$mۂ/ˆuu $I֥KHFEd9`_H]I>! p* A)5D'FvBFT@PFYa:V (3Hv5Pq%Py!)!as=+ETTc]!C! aN;EdB:ӎC 0D:ti:QIIAB*qh0_eF@p ڕD(H㥘ΨARRYaHrp vJ!q%TGQ)HfDҤ&D NijH|r iCFFED%7\DtB9̑ EnـcW"I@jcGX UPC dA%rT{JvKf$:伣pDD|OETAp]S 1QC!*jaoe-,4\`okU +QED'5tm$ e-qwnƀ BD4m9Xh M=)gD4ddOT 4pH G՘u!%8ꬷjѵIdtAbP DFDRkIAӫ4sG N: ,IGKG`P, Vz^ƒzifrek1h3QdN&e$ aV&f/y ظj2aH%4$etaʖ>|O"b*[GQQ$(H֒(Z  ba(D#z1F5ۢI{9a\aX-+!MX/8hF(#t̊'BI&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:vɳ~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢxFMjAT #TQȩZXͪVծz` XJ֬2r4e#ZjDаn}k[*ҵv+^󊳀!,"0 9ȰaBB$0 P!#? !2*KD(x /t,896BFG1oH%0s`OX\XH% e`JJ' (hA-S€'Ы(dY)é\Ui' 0*t Ftj0@Ns\hw`1yt4pŜLdȆ& (l0B@,~Y[t@?|ٳqtK*P5n rdӣ/!, XbXA!J|q",ch'Ŋ EOG,DPP8@x0Til&(4Tr@Jx'@J ᩲ*:V-p~#.TP݃'֞C t8©@m v<2qY@":\eG>!gO!eA%QCdpŧL{*BO(H?6`;r d,Lb6K &/<^ػGG~ W!,"8 \2$PbÉ|pJ>S>Mñ FeTA90)pOx2 C'T|PH T6D FHb>&ZTFh|TϞ=|HPPKWp:H0?e.? #K'eO):9:+ VccYS&hU! lF*>.ajVD2"cOi%XWs\Y0zبxK& DSU$"!rDp@IPP&mЂ˚㳵۷ x@82M$L JLVp.P*)TT4G>Eb X!U!*9 &xwĀ ib,Q !@a,yDűh c9#*D($F&h?"dL6M!, H*\ȰÇ#J@ȱǏ C4h$F'Sɲ˗05Yc̛8sdK5JhK + J0JJ!Z2]Yp]կ` tYJ#'[ CS`]jʥs_,HHϦ:twJlW0AqSʠC#\9ϢS-ZהC&is=CNIeQ_/UT,瓦;agyFEy1A'PeLa - |e0 y(dEHzh ֡4l>YY idBB)>d@Iv\ɲ˗0[IxacɳO4:0Br*]ʴG C A I NKcS$X,3"^+('v)˷_ Jft CN€1Kσ`5I-DтӨY&PΉ' QPohHsfh(蓕# 91J5kG]|sloW x<I1G,|a/hى&{4(D6dy(̊!9iXNCMv?yISP)$hd䃚f1.eougfx>'#j([z dDw0*m6!, Hp`*\РÆ#JHŋ3>ܨ#GAIdH'Q\ɲ˗0cʜ͛8kԹgϟ@ JgѣH])SPFJUjիVj5kׯ`ÊlYhϦ]۷pm;n]w݋߿}LxpaÇ'^XqǐFLc˘3k̹ϠCMӨ/^ͺװc˞۸sͻߵ >xȓWμПKNأg׾ËOѫ_={˟O_(W&2 B(RhfvX ($('"02h8<@)DIHJL6OF)PRiXf\V`)dih^p)tYx橧{矀Y衈&j.J!@@ED>h1B:\Ԋ(E` ( ?jBAb@hJD`GP.\>@`I Բ!/Xˑe F(@ F Jk02e0D '@'.Da%X@i>pI%PR+x-QD;/ؐ!sd},|9"Pщ14ZP (, d'vt(P t$  NQ"^ F (u)Y0BR4Cbvf,t! xRQ =@WT(Ka[*x 8QBL16 J|<V)DPCBx/-hP-4 F[,lB#@'.eI':.aA fm,hE. 2\ep9^Mw Q&bp0pANzNw+UZLC,8*(DċI5 xРQ#VuBKP@!,0 *7y_#Y%ʾA!, H)@Ç JؐbE3jqcǏC)ɓ(S\ɲd˗0cʜId8s9K? Jgѝ T@ӥJ,TCvd`T) &aBS:(ŐأpʝKݻxEN˷K2.]$j~U qa4G\Ɲ[IaKӦS^ͺeRO [JR jRfS4I˚hAf~TSN>ihY@ !ZIkmyJ08]36륍/?; %hv&  jUHfxjZl)fm)pA 4,!\p \`rrˎAG1>c x͌SN3ID8j4c8PQ9@P9SVVD@ +IpG%2vZ& f@@0UJ%?PTD?ZTjR@j*Ҩw  wH!T 9 S`*JD0A5 ,`9m*k.:j(+=ibR[G 22ZAjڧ^V90ڌ Zv׋k%I <* V[ &|Cks73@[o.yd(;ʾiDA6ԡs !Ct\bA QNv9j))"Ceh­XDм' b%AQ0-\IOkL : h+G2}VRt5{CO"Ykd>Bjcʄ_P!\ݝ4J] WnX wC'HPN 5r 180}'L =.w/@)+Daq;}y`|1cD~&:OUH B{ʑa@Q 8@+|gViX H:юP b@rK <LΨ0!:ȥd옌"Mz qS\"D@wN򕮄! @|TwDYY#VL2)^H;&XV,0hDa 'MY=P账Cr6%ԉpЀP缧>~d <Ϙ'%ӡ5 TR 45A7Ѓa kB52grþNъO| #)HЂ> XE2@D Zہ_21h@`=U F^aS̩FjħZ@ dB@Grn ) 7*Z'Pu-򄉺$ Bh/ʑ$M&`,AW9uF̈q H,ږ"ԣ%D]U 7i[@ZBE(P!Tt @ 1A#Gbڔ]YXnsRxxCQ :r{B"ޑ^])bv@%n +UXa'kvBe1+v%`\vK 6)"nLfޕy)x+V3cMxXf6"|\)B #TU@50GF`;HA208;ke~Y-lEp#R Z: Z bK-K lgԶmc@z#VgV|OALLNpZmXrV+_j׭ْ!6{]ӂTUUC 4A(=AU0;B;?f_q[8Mp\6'(qwtb}e$ |/9DN4\vBw8* LB2aئa HAabr2P0P+\S8DAw96D4G{ -S|X@2tȀ@dZ< nGg +\\ r0qԐ Ny?j='i)d _IЀYpC5|YE!hKkX)3*j+57tԁ A@ ! (w:Aq|AV`5)0Xj X3M`0 #CtR5 t/#| x#,P -`>PV V 2=G M0%M Fr|6jpPp mt[6A^Ȑ UpU  0 W[ t@ < :v 6 U`~b8(`5Q` 8@)~,3dsv $5U_5D q->Pg`Y79@ Pi]X+ R p2BP10Q2`&fᑡQ`gg \0Z}1i^/ Op , e .  @ň  Cg s1Mb>ae( .F`Cp}60 s@h} ٰ \ P WRsIxUNusN<}l,POvcQ$(y=-`lpuP-@ i1ǡF0 j+gg;+0; LX%}  '0q!pi~6 1[G* \`0 Xp G 瀤ueC r#pBzC&4@r(CӨA!X8Ґ^U*os;&@#R(zG. @:.4牺Jʠ Ț3sTw#0BOoʭj3f M0'u? pJgv?IP/|wS0j*0/0/ geaF@z`+@"jR'Ҁ},V- F@ x`++ A<[[SP5wF3@\!IcpIJ?Zp1Z=JBsPXdq # ;)5 R7+\h\Z!JMaw `eCifhi>p:UG kq$N#y)z9m,9H9 Z[j"Gӡ|۹˵~op"C0ˮĻʼ!g k 0Ĭ{Q@ ;e$%t HBxy ! lG[z IvvB&@jC5:D 1I M 0 ]7@ :d<ǐ7U10pDDX`\! qP FOI:@ B! UZp}pő yeT0Ľ[q,s$bzP0iQ `@_?1dv@rQ4 [a3 ŚE" <ORjU9<ǐ51QwMQ ~`?` L jim?,tiiGwz) *J  cPUP&TĔw{1| btet!IGt7 ЙXq;2z}@O L.C$$meJC@|p/My5$M53 Ӟ+5GQ%`pupZ@}uT>d#3WlUC+в0Wu&s'^\JXSB :p hm|"+N8(]՜+<ФVmL*C/"--r"* _!_fqObWn&Ƅm!mۮ, 2YaaXPV0`0c5Zq;,ZP6C="W̑b+K1+&@fwf:']dRI:hgU!pF ^u0D0?@E_nXAng= ci%\Լ sVm n f`^؁*ɧW HIO#K>TArUn>D4~Gngj~OnK14 d 0 O8ap0'tp m pO N~阞~>N~ꦞꥮ&d!ePn r1ސ[&cL1PJ>{! iPJ*{ i@": n ^~. &Pp(p>p;& N7>*PE Sh- ё L .g p !#`T pwȰ ڷ `=3pIw~HLJPR j@ 0@n P a;lpP s`N@ ~ z AT i  0 90 pa>9_jP8?: ΀ O?O/_Na` $_lp lCDkn̎.$ΎA#ק.B? ?_&eMf!?9<xgi)k TA 0TCa|&Û/3i=0 *4D0aΝhd:*ҪYj֯RÂt@hUӋVrtQ[ZOmIbtx| P& UhCPFTh +Ћb4hG=Q#)IMZғ*+YO4.>9SJS4&2eӜUC%*PzT&hJejST>5STzUfUVjWyz^%kqiӃrk}@P@sZzWU{k_WV%la {X"6ebwV.uWM!PeNrV%mcK;ӦvUmk]ZVmmm-oXԷ%.7-W)\\FW-nu{]fWnwݟW%oyk^gz݋ }oP=旾o_X&p |"X fp r vCϺRu+\aX"1MX+fq]bX3KMncBҨ#A6O1;(p$AP[H$`?H㴴;El@A;(.'˺+@,xb>x75"$6  蛃`3B.{`< ]&9ȀP2L P)($28 H3t9h,*8=۷>=};DD\DSDF,DlsGBl[`KlW79 S`Y[`l ZYFP5=: d7 $6UCAUXTN RL[E $@F[(`FN[X;Ą=@S@U>`,4 57Frl9F;9F:KLi$$X$`R(WeO`E|@,;߫O,@D;@hsXHb5džŦ+[#8P<&$G (/: >uc"!GC)H8`?LK2(<Ȃ9Ȅ? D#DID:˾7 L̷dDBj bܺ80";J8\Ă3 \DbHBb:>A ;+;D6_ôZ J0C`E4f\$qM؃Z0W@<qFLlN4Ok[Bbx{܃Wm[ЃeP5۽<==+L l8L6+ЁDPh64MO,ԃA Ň Cd sct^yQ( `<X2!.`//"p(KvRuR.mDlRwS/7BSn+2R74J:`u378uS:;<9KAu7KK5P iUkUS8L BxEYed [ڋ$IX?LOF IH`8|<9|=h[pN费=O?l4P@::BJ嫁§Qؔ$k]VÓKtsQ=HAX80W]LKXE`ADm5T׉L@1`;S>1у}M6*<<29,po;%hl%e K./%>S;=>=DݶSSDnӧZکӬZĔگګ-۱ڱeZ~I0U\FYV{7xLG0\E}\W빣?ZP7ApHs8JU,Eh(XSVr4]Fq}GVY4D`0( @qX `%XӅKЁS_$NO8ľ@EZViC8(ëٌ<0C_^_V#ъՃ&\B}8=5XC$6cK4 7=tKDR)&(!Y `Q('0E۵5[ᱵ᭥ AJԁ"6[ikEGX3$́WKH>$M(D[_ȵ۵o @ӽAP)F\7p?=+]Z5 hAdO4Ox[VS%Wh? L@b壄c8v,Ƽm-cOlSjAA J0E6AN>>ý_CWe[BhI)\(́+6~}8G(L_l:ɬVf9-&N=_c>[C=ĭGYCH>9R$fRÂ$X@.xG5@SA+Hx%@d+xM &[{@~6H&6Mߋ=&cd%^[&V;oJo /?S Ӛ*L4mNm^m7fõ\[敔$8C<.~6c "XFnLSnk9hDn~N?Nk@H`lFUFdlbA6SA^l1fmocEe[5WV_̥f5=E|bHVnp :Ivol>Wvl;=zEoiYxxlAr!ԶM_p`lor5ݎdnkk>hOr_28 k/^qwovwWp9wq9r>ۘ' s@s!B}5DD]tvt\sW53Jt-'ϴ]@4" :Q w3;>;0i6[0;K34=;@ سbAcГ[v]@n3XO-p&^[7JX]7XZXcC}!HP@w)S0 .0h(]5  #d="uYYuL (W;8˼.,#8x(ChY*Ȁ)Ѐ#`1'0u#*0x &8+;pLXx7.H5P=p+$y$]p92 0\Owɞ)hz_pv}>w6J RN ڸ>W |8 v*P<\zw#P<%(C'ͬ( .W7+S`[B (/8x8h CU#zwf_sVH|gx @O09 (g#`V p$=97RA-xO#-J@"`Q@p`_'1Hh`&`Q1h&k,pVZo/mi,k"(ggxN !,,6H*\JHŋ3jQC vIɓ(SB&)cʜI&F?Fɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊK٫Ϫ `۷pʝKݻx~˗&̾)pGb^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞MSn޽1oC~ !, +H*\ȰÇ#J(0@3jȱǏ CIɓ(S\ɲ@\ʜI͛8sɳό JѣH*]Z2(ӧPJJU^ʵׯ`bKٳhӪes۷pʝKݻxM ޿ &È+^̱-ǐ#Kx˘3կϠCUgѨS>9Nϫc˞-iڸsnr {Nμ9d s;NzֳkF *p`|ӫ`{Oj{ 7'*Z 68}3CGtY &wJ$H] &"m#0@)(㌣0@o?/ HL:'Hɯ̠g z~ !'(S0 D8̡w@ H"HLD-uP2!Xd'r1=5(2-fL76pH:x̣> F  MQYd#)?1bHYJd {BKL9a'AGYa%5p)?F@ Sy%Ktb%&YHV@TX}3$Ĭ‘δ A7b`⡓,HJ"g`DHʀQس<$'۴F U2P du p(BRצ ;@SB (* ujp ភj~,[ n0^9è&lk 501BxbXE!In n->Lw=l[ D p%%RdQr7tFB$` H*RN@A[~a<\5L&pAa o *R͹BbbQulEhF',<ԁ.haA A/ TkeXRT$YEX2HY` x@n R`d8zk5}4NE"(j&_/<_qWY,Ljz{j &=)}nȧ|vBu[CxM'3[>b g1'sω!Hܢ /8mݐo$ ! ,PpH A D0D Jύ3jȱǏd8ē˗0c2X0$MOtPϟ@.dhgFA*} S(s"]JƦvFjU-PjMlfXg9{`hۻJ5K@x4`sb=f^#ԫ2Pzf݄:$$30 qmZl"j.2/v>Je] FNVسT cXJǃTC(pP `*C !eڰP4`)17̡D"N D|hA шP@&fBňTN8DZ9!AQ'zc},qbǨX1eTiG=zЊG iDm>th<% ǓI  )=iJ)!5$*l#$14 _!,PbH(\Ç#JHpąPȱcǃHIG\򤅌 Pٲ͆ o)pO Z(ЂG^S?*O^0=Qѯai+,׳U J E֨^݋4 C ֮x +nŃR (|cLxF΄jݙt?~tװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($hh Bu@"IA%  55 QB 3Ȓ*HG(1XaAuDM>") C@_("KQɚ&!;XÐvމLɑYf)#:i~vfĥ9NF*i8XQLzH<jeDL 40IwDSN%B(S 8,r({A2ZFqr%ܐ GBAUQJ& ȡ/ */pK1Gk .׆umtl ǽBW/!,UH*\ȰaC#JHŋw Ǐ C2(ɓ(7˗@@͛8& )P@IУH*]rL&i@iJݚs̪ZbJٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 65B %0-QV|!?$ ~4&C0kx(F.a(MDK DLщԂ@<&ˑqK %!d'dji%ifp%N&uIP9,R$ D,` *o֩vn҉pbɐ":ѣkFZêz@ZaPBCk%@,D ;Q1 zCP-pKQAa2XpcJ [la !.-8 CLB-2.@r F IlXmJ !0JLë8&ud,G#F ?КrA(PP1-p!QFݱ&,F`r 8DеHQF Bη2 >,y +(Q*!Z('t  R7`i r+XpI% Q+.ozGQ  'm# lpG'Ik|N $ r+ d҄(Ln Zu\J0`Z'E P @kRE * G02~  *E! YWOc.H!U+Z*ۀV!@B p#!| .x!!F(EP0W=`*P` { n]^%|(VQBI[- |&X` "RAjؿ >q ^`x"@_%kXC-"7;( XI$Pde|:,J 2°aj:ЁlP V\@[V Q bG)p&p+>% XC-zYxCBA,h1  jX!jp^U hiQ% 6[ L.- !a A ^ǣ ]bȂ-Fч5be+ 4*>pKF8ۯ\XsUXU%^, ~ . yG( Lo #Z)Vw+DvAce \f_Z9H5vXՕ[]WkaWb!sU,H5+ZV%rz+$yŬ:Y`!zgQllEK[ elmw[!8,PqH`AE !7,DA XGy0G$5\0 xb!A,ɳgO@7J`P E*-h(ʥP:*Ϧ j9֪]8l?.XS:~@!mx>A&˖!x ah@ &.ImP f3u9*֮B;*3J?FM4(Ԡ_UrϦ4(fqHvYYs.}:꽯c =tޥΝ:y͹,zuZwcv'?~~aןw'x% ЇE%ĠP5FFe|jWYgFYH"}ϝވ*f27Tx♤ZC7`!gp!x_qYU ~t^L$hN}S6ٜZ~ȥ [jaee[V&ixީ'w I!iJ‚u*Ԁ8@y{&Q'V(CUiFziYzJҹj*D(J뫬bkzaZ'n3 ABI 0Zmx u:+ 4 *Vֱ6joflksKrΫ'&f-;qzJܫWpDWW(4,#*P͝(`&bC#E| dr$B9$ϑ; p~Z{^ ׷ 2nG/e *+@ބK/45 ;FJ4Lsɴ8xPz2a/hRzWc-kn+l飣&]謫,{wB7&Ҵg)2L%O.Z2A"/Ip* 8nrÞ-_>S~~/)ԽިB,!#7Kl-B!dQAFU=W $'^ȑ%cD61~ca VBy{ O8>ɰe& P|! P6Ta;9bCC/|*C6M.c܈FvbD_<nZc 8ȍ1|r4sd% D,Ld- b:Z.) HNKhp1bl-ӦR|byKR#Ƌs¬#10fP[f2K;jw%^ZUg~H@r||Ɖxꪝvci4M°VP=T?y/R b(DOeʄju_CϪ*j̵2ժWqQ:"׾ `ˁwjPw./7Ca'KZlR*٨6Re0#4V+UJah)w`c$Ƕlnqk͖FKo\孔ZYBޔ|:;i:ͮvz+qPt-x裣hU0 q Ld<-:'L+7@f!^(,GL}A8\f0g,bCMqi/4L̪8 ̅q#P, B8r*{`.rIh*a'm6}Y!u`-.> X@ʶF;Z}'MJҘδLvD*FGMROyWVհ;Zց5w^Y;b؂F 5MjoZζ3m{ǭ.NvMzη~NO;'N[ϸ7{ GN(OW0gN8Ϲw@ЇNHOҗ;PԧN[XϺַ{`NhOpNxϻOO;񐏼'O[ϼ7{GOқOWֻgOϽwOO;ЏO[Ͼ{yi w^jf媄CP#~"}7ٌv˞I 7%P4$K@ 8@$_ (۱5.pDGH&ȁ_0%Q@&?@C1Sp Du 3 h 3@t?/p^H0,(8`qo\OȃvxPcQ8@ `! Qa5_/hiv(uE  p B>؂]Hi1VW:Q!%4 xDHxupˈ@Xz p ؍f@8:3 A88s@ !Jq_AA IxE 0=؍v8fh>P p bp숁.0(%@X `b |:@` ¸H3p `$4`  p]聻=9z X(j)EWTvk% L)I~`pIA@IH p5p*(?p (X _"hzP`VЛ;èh)~^XB؍(ω 9@ dٕ~X^t8{I)&ha˹^U9ع 萹H)I~ȕꉜ]؂Ʌ9Ic)I-z)(ݙ<ڗ  9ɩ;:=-8=@)ޥMh XJ-j8f/Ɋ`dڣ/ *jgZP ^ >ZR WP ~#hZQ.R#q[(`CfZ%8dЩЈ@y #` 0fAZe"~1jMzD:p~0Sڙ/@ \ ,0Ad} %ePSo | #`@pw+p w M6P ǀ PTp w-`&a$XF%U/c:G%2kײ0Rh0d?ڑ>˳x&j0P81zp E@(W@khf\0Q Op1$`/``G`6PDd@8X0\Gm,0oK\ c+:{~*{+!\{"պeGA\b]A & SUq3.Z ?`3`њ [$q0`FH GF;f:zec\FP!4Pkqdd[8[ `Q l8 <8pKQ A F`Ck\fzv' qkH1xQš CEa[u@~G̘g[C *RaLa20˾v ` ZL W@ A8Z`g$Z2- ,@ wP|@ 2FP Mx  `, }k@JpKq:@ k`#І P`P` !K+a ~4@g5A j-l5#L. ,#XF~Wvϧ|h=*{Υ *|_YJelϲ;&ck!=!&}'+1Am5![)kcƿ\ba)  1d4#+LH=j*Aԕ!,  0*DÇ !JHň3j܈Ǐ C8ɓ(S\ɲ˗$aʜ$̛8sɳO?*tdM ,0RPꔠCvdP4mJTJL3* ۷pƝKݜG%TSg. $ӡ~t5R.mB)pǖwMhӨSIwHp {À~sOHqRM(EK7ji(C30ЂI8P}5S0dU5дi&4йq> _~/70)~P}k"}| &u ($X'2N)+L 9C =yx@ chrprAXpтd˔И@G1>c|ZK9 r91̣3%]N9&4f)4L-*F$&@NVGCN(駠*ӎrCN;3d_Fz~ 5p-$mC7`4e n1 D{  jb KOEN20  ,DQl)<'|N0"0Ra*%)8`D uC h. J  Ϻ.yݚUV+nHY9`*˹qB&[ &+&#dmhkl]У$kpqF"rAZ$6d32̉=3KBQ^: DiR*wm+`a*4iڸfI0=wn+6wI( \%4(4 wn43 n@F)n# } ; \܀Y@[<` s zG ΂68UңB$FC6MH?IJXjgm,Ѫ !4h ` !kXr,_RNV`!j$C,xJq@|A|鲙^wz 8d@,[Z1r9\wFǻ.{AdI&¢FG"R,Ȅ&82`$ ڒ 7`AP ^P*GE@R„Or!pr[%@ɬZmT"׍')SJUTbRP*ƐX 5z@L>S/)A;pˈ})B<=xg8ݭC6)7- XK@>Յ8:@+#(E9ȀZ Q[V- B"WY)4AzK;[a(U$^@•pVz{8򻶡zxm d?@r"kn,A x'Ch X@8 .hA[ g Ezn %@ J`Dv!4 N]7{C. hBfy%oŢv#d%w٬^ AɖD:(#~SP^}u_u~"d (, EЅPu%~|!yҞ?O JR~/QOͣE7h?VD$Q hQ` XS |}"B0U8#geS7O] oa A*( X>8TIID%iKAA <5[C 2>eFZ %r]Y$ 8 UW=bhl l%pbP ɢq0XIs`PVޣ E]F &cZpPP ֠   ְ08e# xZ 1P6@h{" ЌW@ 0 ^py1AB`Cp$ ƲMpAG 2ƅ3g_N_$R #H xF@qQp -G3zcj 20 V 0iI'nzq2'`fwBr )ք~bf͠Z0 UP'@] Ϡ@ Pb ` Q >-@% U Uj268+*P<*HHt:`Kh+e`50~v&f ff g@ fc:```1Pҗ9liPR @3ۢ04 ݃4p 7Gph0  ΰ A 6PjHB Ѱ @ Ĺ-bdٝ%` h CIvCJa?`PK#2 5(X Pllϕ@ XpJ%P 3, E ɟ 1:   &\"\n\0] Pl1](` Й p p R*D3G )an$ >b#..w P;;y@rBYo/7u:okx >$GfGӢwp9 D431ؒҀ d6Pu  ox G\PV]91`%,И]N~+8捍5Sh a0Oa@)A66!`*L45Pݚ7tFBmw :ڍC۾<NTNa|44q@^"Bs_TWjP,CU|J |LRm#ƭPYcЯsH1yM,}s3Ȩs xYvJyBËHgpP(TBz\,L*DuإJ9q)J|14P ]|OiFTJeܜMܭ\!aCpUZU&!Rq)[ P.{Wf@l6Jib6 ݸLIMW0 vІCq2ݒm>D\?B÷|[M$h FX?S1~M`sFvR\ٟ3m 1l`hzg u+0AUK.( .LbP,T 0aT$QR/`Xdƒ);&XIʓ aΤْM9]䉲Ϟ@ :QG*]iӧPNJϫUwf+ŽDh+lΥb]3{%itpʝeܴb% #F*\1;95RO3Bb]zkWװ'fMmܹ޻N\ܱڽ ɓ6Q@H9s͑ouxi,'ϛ԰k"K:!XUƁZfBŗ_GI|K$$ Bh; - Y*ȁ4E1&ȷ+.[TEaqFiEst%W``yGq)`!݁Fdǜ<4؎kGc x9>q'-)e+" FC:CJfKdAoƌ8N~pYIo Pf$8!G%R2BtuTR_,TTMT[mA g fV\1'slcJJkաR$UpKHQ)T ˡ2 $arʠv)fHUuuU_|^J(}`̃qiG V&)GttX4؁zgٗˆB)M8&8bUdƗeCJy\rӨYNs>\f8t&x4eMw .lFlFm[_X.腖$0G8c 4hfg (@:JJaR3$!’3J!D@vШ?vQe&KNWf.{>x}/~xw$L>o5PtXp\;iJ vK(9$pȶ攤CAdp M[;IjRB CS^׭XP6xAb6Ђ7vlXBfZuTl^Q@/L[(xD$0Kd"LRDyO.T;B%,2hh*5yYe&YT#4񍿁b~C{ "D$G9"r~T"HQ*N`D50FȨT  0F ITR\e+("*Z&4.IR,%.{K`ӗ$f1yLd&Sd0Lh qQ0yBV0!wpf9yNtS%\';NxS0$˹B aD B0mEPFThE-zQ&IЅ$nhش!XlPT3iMiS4P;PjWKjE d(l0-;NzUӪY*WUd [G&6`wcWD% @(ME]q A 3WWV>þoAFfQM %VYІֳ-iM[ӦV,k9`HIlCASId}9s"Ul/֢6ens\FWU.u˥Ҁ]W%/v2K--yW|c^<}{da_XV&~E#hR$pB ¢PU iD`{pu;"qh/(h8 o}Kt qIX6qyԪaXL. &|RE@:m|e,Y˃ͤ ֱ%PL9TDp\Ф*gyrs\Si<$"k*!r'vft}g#H2nPzcd 7`G6hTZիX9ACIQPItYk`zD_PbK9D3vkac;v5AI೶}nt߶K[wY5oz\I5 8?x-~qg|x=n @>~rpD˃P@p\*y) ho@gs]C'zэ~t#]KgzӝtG]SzՓnu_=[z׽u]c'{ٗ\fg{۩Z Zw{.g* sw^; x'^g|C>|-?g^|4@ %|w_=ω ԁ|w}[c^{{_'~|'__~|G!9}>r~>}'?~g~_w?_O]Ы>? 0L@7=|@ @ @ @@@ AA,A7\A:@cA'>;ڋAAAAA BB",B#BD dM@tOERR܂H>pd$l@ҜULdH4^25XrHuhMt$1OdU=Uzz] MUh鼇t N(~xU7y`L5=yȇuXr@\y7X]MtNgOT:,Hˍ$XOMFIX8XOFsEz8,v;=REah}˶PYn۷5ؘ䂄eI+x+pESS*(MR*-IKu !xp @<`c2(Ȃ9ȄMrZZJ.h}dHCLfɑFH [ bfLOS-P.8W7(p8P\LN~,$8iJXACX=6 T`JWX?X ֍H>p\v}]%$h4N(Є_\s^{H#fh Xt)fy2sT8Luy|p6(zHfPQ 7XMxUrє^Zp58ir_|rhNmd``H7`؃W⠬S[sug2HX~偩йRK H bnb,,W,6O#.Bb=9[@،5f:k:aЖI2떄-<4%pa|CjOSŅl,4ҹUv6lEnu.xmצhanf N> dkJ$nmolwp7ke1H΁WHPt dJ>WX8(\08ebeʝ{YlpeRHH5lm7Y;[/ x5-}OO,8qѥL܃`$X]{ṪdV5^rX_\rr-^2_r5餮Z=6(*M.MM\Uo542O|0^`kLVAH,JN5+KJB@K*[7@eLDtKjV@8p&|=\̏<8}H_vC8džH08  eQ.}|W[QoSc4 rrem< [;0dܖ`BfֶRpon>wt?wpwqracbAz |I^۶Uⶅ8Te_kIdp$s~A JGEyt._=-]]>h&E:L7MD^&^,rtr%z<ԛ0#q݇rs0|]|8Sz1]^t^d_uPtyLs_j\|W5}LЁ~;_>x:sn[s6ؓkD2@[Aȁٓ09dЁbyW|SwOWu(f`(Tl+t^ZIz p'^*V 9P`V!шZς:0$Ȃ⦋gwQlgwm6wvgavd[^SF`+VIM(PxHF`+7~tX'w_KDfqFWTȄ[>0|TGl^W[XH]]y~3>%qr\M fr0# L f8^8j-2^qߔi$LJO=cTIqHL뵇>6O=Qi MJW'/-[[2bmJ䦝IdLV R[\a 6o=cPХ˜dW(XDy@qxzvH|@riHO-liyټ|N$7US-MS}Հ.|ᔆ!({0c;^i' tOŭW$/A uP~}']˾$JrIIda~/U]KDPw ˷u6d y S.'Nχb|@rDrDrzS5#Wmq$4j݉T ]u}w~HU/юt$RPɦz1IIn.K MP~U/u6~}ROԉp _ye^ͬs L$zz7N4USM$Ot@Ls!-:զc5S R(*P-m)`.@: (Ϸ~ mޫ'"`('96dȃ h z&ѓ5U@DŽioF# %}fB-vlelFdfԿqjCf̿!,PH@*\ȰÇ @Hŋ3jȱǏ CIɓ#h˕0cʜI͛8s)qJѣH*]thAK[BeJիXj]'YNpVrqIS ک|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬV9M+ꬴj^jp2찲I@& #kYRhKD8Wylc,A^4Z$Q8JkCݍ7s D w@%>EX.?5nOo1㱎^[#8faʽJ8w8sܚbT4>4J=5͘˖l- v_ -޶lv'賍#8m+ml[RI65s>N6ߒju^yo~ɞgzFn-Pt.n/o'7G/Wogw/o觯/o HL= 'H Z̠7z GH(L W0 gH8̡w@ H"HLDlH*ZX̢.z` H2ƭj*2c\F01-p0@AG|oLKvY035ԣh"G:%H$u)G/i@yC x1' 8]'_ vB:.YXd"H_$@FY *c=IR%BiMG79ym'S9M' !, c H`B#B ŋ3jȱǏ CIɓ(S\ɲ˗0:0͛8sɳϟ@ B& ]@S&JիXj$Oq g]˶۷pʕ+ZRA%K LÈp1 ^d8˘3k̹JI{HP?v0,!R;˞M3e+ t@C)I"d0У n`KrJAË[ViP|H2cO?Ͽ6S$_&C9u{9Vh\=v>uWT h.u YTI (4"k4V@)m %Q2  f)(q!B >T) shYd)gnAi*a)sjhxxJa(DPEVjw m4a~|J:]*ꨃ2vVMRj뭸z믤jykd_FL O"+Bn G`Q'Jp-I]#R1"GB`E aoD|AD2 6mZ 6 C*Q\(&|2,̛iicP Kެ¬~3DKnHh!;TӸB% ]Pm(c'h?0AC-w5PPQ܀_$:fuQCWj6Kq"=Bq]pGZg9 zk˖A u)A˯.cʤʺ/' oe77; ߘ+7}ܻ> UZŵwo>Ayc9p˧~*0 %6˟m+BL TGP3M띿@BKvG(0A(Y[aі^HCjEJB5jh2V"jS¼& HEm$Rbsr5 DbHFIKI >$c, EmY=QȖ+ADX d ;F>:&"(mLIDrWTZ'f*7IP.Q a &ibYr8An[k*(F sA`63SndF|3̓X`LArAXX)`fJe;I< @JЂ'BІ:hB%Jъ3T ?L@9y`-N)3eϱ-$ @P:ԢHMR1#HBza:)95Pт @RA)2@tЀU^8 xIL*GMAh 9EBTZ9CXGzH`XQz_w$`t%*LN,j ѡNjIOs o;(zOQZU !* JC#ʥ_ 8NCej/Yj1:*J-Q5 (Q`dd|t1Kxc)Y'ބHJje֔}D"&# ᗥ$Fo*覤ʤX6ʨa ZY\h[UWȬ':m* (d5`?MT8 GwiDݴBNA2 gX6I)*gʮIi(?!D A|0hگz:ڬp{k ˱M*!)# 1˲;1:2dq] ỉ; KK2MQSKO˴.kW U$[(4+iʵcKEzMj ꥰzڲXjy˷&K귁k묇k@縎[/ȤS0M{M48l|J{{+ tw˺}[k+뺏 K˻^B;F iKtE48ëS ~M+*;JXV O:YKgK_M8 z,R{ػK{"b[R^;C` t I ̻]%, \\|9x 4[ 2!]j6SB{@|UuH@'haP|)^p:P>` z?a\1dljlnr,4s\ǷQvdžAzzrQ~}ǂ\Ȇ|ȈȊȌȎȐɒ<ɔ\ɖ|ɘɚɜɞɠʢ<ʤ\ʦ|ʨʪʬʮʰ˲<˴\˶|˸˺˼˾<\|Ȝʼ<\|؜ڼ<\|<\|=]} =]} "=$]&}(*,.02=4]6}8:<>@B=D]F}l%#cRK=S}U#7μ` b 0: jMֹA]"@_Kp֍ _@QPp u0 u3kaiP%61`3#͎إQP 71p Vϓ<@اp 8օP~FV-p=}a`܂ { X@ ` :SV`I W} ` dw zӠq)q]0]Yot-&ԝ} k { 5@֑`u`~otP ~0 `0sqa082AĂPa%1>( p , ~&nM^ݭL{p~P`3 :=v=ֶ]KBqD=%Qms8Sj6Q:.Krq]rqm>yF3JB>ہ d>Vnb] m=+xWn_PQ4> ژ7z~$q.c Cnl]ܥ k-0_nގg0n붎̾!=~n^pc0?_A ~3)`:sK#m K>̨> <N&pyJC{J"5K%? [3s+@ZC/e&  x I 7H e`!'` 9 1^OI 8]I D7BL S0 U J2a/`X m 4>0ꍳ JD 3 q`郴 0F@CUXeO H8'hqPxs5+~Ek0>_ ǐid%۶Mr00 #PCP &d?M~%po45psUL_|P Kbp$xj>9. d>  0ǀ\D_0\ @`o8 X[0V} p\@W\~PxY}H`49I [R0ۜX$pC-P :| C \Px60QPS p@0> -  M  60\@xdd\@: \lmJP4 R@r@0` <Ͱ @@(`ɰD??Pn ?dpW n d9R$p7 - GYC@|njXoL^H9T)0ۑ]R QڲnmE pКpL7 OTu ό@ o5_ + E9]7CI G( W0d0@ S o-c0l9D[`r9 _Ke8HU 0 wz c ' w% '`XY ):p USX'G[)uE >ֈ]Y)cc-?I pSPxJ0nL >PW eC ;0`}D/ z `[@g (`n@|04Px0@f)TC@ ˟]0Y Xr`J8el[ [ "- [H (5_4ao5&S;H`p  4p-0ħ0 `  IWE /?Mje y mrY5mH`e`}/. [T -o |C^/#*#DCF.(46t ?MPxx- [Hp|=)Xo ݵ uPxy PT`[0 0[` SD. #0% o`@ HN[ (ijL HІP Nm= x@ H| MP `% V]j0Px8v tP[ ej%ƹ-@ 2p@~ F N@ `q@ aPxpP[0 [ ~ p}  4 nO@:4.@ H`m~q, n[ , P.` 7M~@o7P @ %, Cr ` L U t4υ4@md&R{&&#:{ψ>蠰9KPFPdQ}Y$4@AW=֎`R 71P 0Vmq:n N;2C~_G$u-фŠӄL!D1 "g!, H*\8CwĂ/Q"ŒVIɓ(S\ɲ˗0cʜI͛8sɳO"j2(ɡ@%8HML:SCʵׯ`ÊKYHfLdCp.۶. /XzuR\|*T["E#ZPxC3=K˘3k$2"I*RK _vOSm&jZG&bY^QQͩO#$;KNسpY0 m!|joi W2d:5ԲF$0]AG?4c07Y=BCh P)h!MC6G8$K@ #pUh8㎶I.#A0 @9D p T0M"d4'0(͂]C Xby: ͖08# G裐F*1iAEP0R(^t@'p`D $NqiC4xiCH\AzH(:4j p@5$CZА4 $AedDQ)MH_+KS$aʦNx U[I*@ć84VFѐ 'xRɠ 2P9OC4LC8XIP08hC8ЎPyKC:TQkl875P0Gt@ ;j@~e&&O,'7b'Q.1 =A6!“F9n6d0pF \ τn#IxvdC%Je~ 1-lY#4j7 m4$ IHޞOTI;,nDY8PB . wWTp jNPh։tUnj6qv ȕ4'Iյ/%Pp⧕8` z vz|˭PLqDLf"̡w= }qWc8b9+K5ZX,WX9U`C:e!}p')QdH:1Gs~3τ0 -1G EHB7$$'IIfBLYsJȑT*U򕰌%f⸊,8r ;p^i+"êp&N,fN'tȫ%FE/6S(I$g"Eg 11!$:/ ́B(ݓv K\*GUML LN9@yFpX?ҕ!*_ВdTyQ(A`[,i`Q#1U2{9PPx.,) sL XC!K@C>VքmW%zkx;X D% ذMeh˜.Up0da{Qkf1XҪ>_@{W* TBʖz-p0GWZ¬Ra;@Xu lZ~ttJXN* v۳V;`(r \ҿcO􀤟b^ ZnNъ)y1 0QlnTb3~-rdD NBbO[Tq xKZ -4 -xӋ1#Y}>8cA`P` EL蠳&tiA5I429pJ=a ;q1lNmIyX}!JDкg8Dhm͐%&cɥ'q7Z jG8*հ.HC<$p97UPٷ9XBcaLn>0,Qr@FqUI,R`w:OZ'3jYKf-Kao3K@ݬ5F"? L2XD!  p !23B)J́w6 6L0}ZB#WHPU+㐇xK`o}ZQO[&q1"?q\K@ &ẼަpAe@n#7B >VdYf1Q00B ЀHB@;0 M10{`0A` ` tp̐ l Q  6pj `f3r?gN#C!E: buU>coQ0cPBbehh5M`s  Elf CdJ q\0HPIp ?*i{с8qo0 P7@ ;4'!i6 a@ P` ѐ u 6c oKB 5EI[2/ҋ "v Qh I6825FX `@Kop c6 Ud@yUmZ(p F(`#H\ @}H 8I ː 55%)Y4 F&0`XU`##m X@Sf 0>d_SxdYcF Q&jҥA/Vp >2`u  Ʒo㹤a%$kval"C5`r[PHh+$|`$x*p 'm*_F 1tS}RS "f+F 4e1 PP,y)z}d $ȤNJ/^ H` (O Ncg:H` v B(b*y:4&"Dj@ |@ Hi@c & 9os+ P!cCp n bp h/Vـ.4 πqMyp`\Z<1N$WTU|CS(85LL~}[|XHGg#*!O$XQ i<Ta@Y':GFW7;uA'pU׳Tk^\G4Pu(;!Ad@_C U2 Ko}S9(0(xEߐl"^ 'O^D,i`HE&l\䵀AKW+gx i(DZ1~eH ڃE1}vBpXS<@*2bpxAjp3+|DH|D/1 !u rQTBA᧼E& `SG)ŽK3k bp7V4n`Vc8pHثC<yB$6-4kl t3n^ p j4 k\K@{6,k*І惴M?@< "TN^[{R|ZH"ęygw s*|PCq pC3 YŴR;|N@F(| Z@fYz<͓TshPn!P`Zͧ903@yYlfPz1D1S A"'p_2LtHjY= $q$p{T&Tw'fXf$e5 rF(CY!30jhj0 8:<|,sH) sk HP==nDMHA hJ}ք=uzEs֘s yiP8gYȌBLւ3) D;@uĤ ámelʃ7SAE/(5w[tKLR[ʜS$ۼM)Eay,@f{e`MK͝x'QG _ǘ\W(4#!*8eXpvA & Am/204;RG, I`d &!VT.a gpa+q[a(d \o߫S\+u^䞱8F0ktP B)V`#A:;b -z`gpe F>t3Ӄea.1+#2I Y vEVa0ƒ*AK4͓ 6xJz0PQ1:м s`vf.v`b@hP<༜ 2 mq NOhh0zeU&~[oƾ\Ъp\-IKb/p#n@PWx 1hBSgnWgN*i/+ KOma#*"3E~%7?Qsa=uuF_LӘllpDHD $`A .dC%NXE5nG!E$YE `i%B1 &9SM-ypǀ8ɹ*XfWKK C(rJ*J,3TZ S2D&B65H#v"! 8Ehs) f&:B M IrRJ+RL3Ŕ$wqPŌ ; cl*' -t҄u E:h .X䇦v! dx 4d8ȐK(<^t[n[pU(u!gd !Wz{Z񄤭?hhi SĥJ(TIH/Hb Nз؅<Ȫ$bRq3xc;# 23 cw "@P9( 3V҃ ?" (cgP$)9ӊTY cj/MHE&wX50a>:5A֖`YI8k{o;ztiF&xWk붹۬#|r+GKlhq gt2KJ%Ѓoݭ]~E_v}'y  u9O`DKۊD|K2r#$a M.]u9IJ:׬,vX 0\YaaH⒫C$b' :Dm D #d@3glH>6o#51!>1걏c7@}htBa'I(AVH`Q9j82gl @*(~JVr|) Y2Ģ,A⥄chGIBbXbxG縑$g9imH YF&H@8 +I(!qS%@٘IxnhVI DvCŠ D2#%iIK5+;LtE(2@(0;B!hԇSUC8W1AӦj7LTl)qiU^R/ծ^+Y ճWۊ֭q[ʹ&U&b>cvprV*ִ5l}+`Hװ:vG=,\++ƪuj{Q/'j|l=XqKv6M$:KTV"@ ͠b+vens/?5sNEYƦ϶unw]1V< eى@1+’po}="#4Òm04RĬh2 , 8Dh}`oLnC 9 wHcM"X!i}W/+fq)+.m Nƥ}c O*)IH!5$zC j!:_@|e,gjHALH0u`+ =f8HBb1!V(MP:pYыft.']j`X@hLg:ltIhиMZo^jCAMrj\纻Fjekr@Ơ}M~VFvM[/yv}D^#mpAs"`H[[X P E,h~ew=DbAA"Go['x ~p'\ gxpG\x-~qg\x=N #'H\+Ҁy3/]ܢ4s|v zk~r'0Vz5:]iV~G[_xuS89zBk0=2C-64@0| >9?:= A\;Ct=7GFt>s ?JDODP EQER,EScEPFh9S;GtFud! YǺxsa"ǭh[3GBIx,D:NFRE<7XYNt gE^tUXtP|0$Pq0뚄bTυ|#@`X]-Pr0dO:u Pr貄yM{ȇ|X.;WdXM)S A<ݎݹ),X;\{IZA8o TA}ތF`J.(EI;1_(HAX8]QR`,րʆXbV(Z.h툃82K؃[NLa輅@0J6s ==[茂B?XRJ9WpDS׭tfP_5X|8aU^LzXUx,i(z{`$LtZ]etOMi}+H@Z؃ άވe8J@d80Z3 aJ K$:SӘ*߶EλIPdKu]K8UQ̓TfefLKdf|f:fjhiVLl~e&Sx5uAtl]xmg%Z:0T 0P}g%2傔vWc8۱-?E[ZMs%]T]ߴ ]fUiV)I8|6 KdHψHhtXP ]XU(#Ђ#85փ\BnTX`$`(bdUhsޅ0˫8D1ȁڼX́@x;^NCX882l՜QlQwvZ(f foY/lif>fjST Է4}˩+h2 xr^چm`pWӄX $=xC^I+B-%GșNHP,h6o4^FNݼu=Qsi00r Ї{℠cu 6`]yc:.5(φ8͇45P=pG:$PA,RfWYNH+K8l ZI8g^צtz%MzX n@nbX 6Q\=u.o 0 ef*n6΃8W폵sn<ss3tMCK wMy*^8g%/o.J(bTHb8`pFF.PTM6JXXoIuQh(U1OR8=zh>Frx\:>pd?O4>9gOnd8iP*;6­< ܃mEX[N>JXhX(u@f+ApG@XAxJ^To0(Mل0Ofgs=jɂ,?|w9Z?>f78y='ArA7L^Z,^ ]Bi}x؆=*PS/tdPO`>S0I =}5NOJ ۻq. @oWɇy@#(r8>\3Ո.zI(#(w|Nt0.Pܝ׿ Pa FJIpdosuWrgx~%gPόJgh>lxvnn_u߄bbZmO*V\\Z}A>ppIrl#-Պ.JNUQW~͗$wJA~~@Kmmz#H#R'S0mS Ӧp,TtL~kt{=ѳUNvgN_MLLaiY^%<)0SBd,i(Ȑ"|`\G lܹg$ x{I#H@(`K& ٜRР@~[8`؉WpؘVЃ[,7럃-p&M xG\x0g?Ѓ0.J.h#8CH[.t`)9'dI=9I.%8PaLJ&;Ĭ0'A0GsE)caU&.hU,Di=I`kZ8񇠀` ɨҒ ɐ\ I,8@@8 pЁ4ЀȀL`? zuԩ = Ȁi T`%`b!$dh| Da'p'/-`ZH`'R Ӹ(^mԁ'p [>`p' '88p[x#[50N7Z 藞 VP-RheZN @p" '0; ن O "-؄OV]`ą)x!$([M #x?` N{Vh4Á* " %ƜI˻I DK;0{kyX ҍA yC⑝`Ɋэ!zɐ<,uV4ǐHhM(&mƣbIҲhȁDlz$2PAz |dǵp@xxdq6i4Fm*y mȠ+m[g,|GeiCg*i*Ád:&Q[F.~Fz5|F,#,xHf,at,hxHf6at,h/?l<@lQ|xzW-b="HGdFHFIςFLFFHhȁl\[ IIIFUXȁ ɟs  hF !,L  hT(btmx|` C!e, H \ȰÇ#JHŋ3jȱǏ CIɓ&P˗0cʜI͛8si@JѣH*]ʴB^"⴪իXjʵ+zKٳhӦUB*jʝKݻUHx L>I`ǐ#K<"?Q̹NZF`ӨS^Ѭc˞M[Mͻ߁uȓ/|pУKr8lسkސp!BpOhDkZ {/,+}hdf8VhU!5u(5y':܇$"Y7 8ь x@)dh"sC&d&٤PKF)&qMf Gd^hBip#ybxߑB )蠠Y oQ 6(a+kg a覜%#R P4fZVOX*2j+REd0)n1+1@Q+vKH!PxkJKh԰^oAr oU Qo$C箲\FN@p=K#N0C>6ң;cj {y,;Q&t XGA"pP*A녠x@&8etC Wr8 fA@ "32kSpٱf-<@pSE!K.z` E1ьhLxF6nH:k$`F ' θȋ)'$B ȉ4*B!4X (8J땒L%*UVccD,d fH ( DN(SX "4LM@b0|ȚGZr C xB&,z"zM(8`+<;J>h-o VEBH v;hCЄ@![}<: o#NzOVuh܍TW@_2`! -f+t 0q(V |݊i#,`i㋅" hDQ\@{% LP `W @x6 6~ׂ.0,(fsp>ȲnT ~@0C{sB#0\Bp \0B+XAW@ ʥQB) G@npr8ǶuQ?I j@f%c`~*(XASjp  p ;}Z2phAod d@~0~xqhTAT0@Ay` &]8|t83H9qӘjH7?ţ 6_84W)q%.",JAyge! h Duuɛi"D<9Pĉt#q-15MP/U`;W I)d9dԘy)y=/b0+Ea ;k<49V iyi DC)CJAɚQ)))z G*`a"ݺ8[aJgBg ۰ЁtW{*+۱!3q/%X&A*2'۲!?)r!.a"}oA/&:;ND!b v?$KN[=2!`ҐOsj >P8dhxepP c[rs{xq=&з};@B]46>8=~:ܩ!M],'OQnv!!(nX}|$X},gKY1%>>lxxF^&Č> jA){X :Ke64Jd24hbk"{]JH48~v `{*P +^BRE1㪘6p ?o|DHOp/ x + p/ E{e}'W.) R NϾ3s0 xpl! ZxųcedEVmL9%G ч+glpLq3\9`95 P(Q0. VPjɂ}>#N0 IP N.@n3 gZ dM=G  5@+070- ZVxOq@=  Z0xF0D (jik}/nDoHPӹCC`o(#>@ 5GNyq,1ԓ~)*lB2pHd0g!?zkĨ#뺖V|Og7޽ޣ):7|nީ[ޥ̌ʮA@s轉ý1V!,`Xhgf1!!i22!, +H&$H#JHŋ3jȱǏ CIɓ(S\rBlI͛8sɳϟ@D@ &ʴӧPJJjUE9կ`ÊKٳ"pjKݻx5R(;KÈ+8"K:˘3kKBb\2ӕӨS^1Q)ADkָsꂁk!ltȓ+_~̣KN]9'Wν'N󾱊Y??cT !'GA"a @E (F\bI(∕k(5p!W(4["5b@)dTHԂ 0 PtpXI@ @aJE`^x)h9E c"T.tymv8矀>] iNFFx` HUbp*y oGꪬvtje>G ?p$/&`ܭk#"x6YfVkTwvz+;ܼ:=|T.g\'s !}l'%M20>߾a o?njϿ (>π Ё'(@I(ʠ5yczX`;`~K!0rQHz! wA, w DⰈ9$25PL8E*Z1@D0ZXL@(:U^@J0 Z2%AdC F+%AL!EJZN$5Nnғ %(B J *!F J +8 ;E% 4t8`] @&A*h/,Bp&(YEnz '8IrS}ZH2 rH^)PB0ИX8C&@Hj0GlpG69ɊZ5(7JQr H;:Rvri1A rC+XϢ@R:15$(~Y@ 48TZkG#=+X-Iխv` Q5(0 :OR@)x$YH-x&@I4HAȉ4d'+Z֫lf9IևA 09ֶ*C\,Cp/`P3L@& vTE$z'>Ѝf5;]Jؽlg-71ѭ q30Z^cͮu'Ho7/,`eB0VJyѓ@B+P8mbBg-1Twt-oz&kH˂bYةaa\F 7j(Gni8GyShpww0K-|A&ܧfO-X@ .ȤZ9!KD 0'YD1 ,s6{Nh_Edg:#<6#um[6C/lE7aNkTSWZp V`4K`2'ăwRc%8g3 V r=uUxT`Z4| Hb:!=#@ M@UWVw9.4HC8@yUs燽h6&hǨ(ɸ>8a. ! ]X(!0E\a  ,9Z   1 &79Sӏr㋒fȐg*pWt2nG/dP@)P E@v3 /w X  0 8.SxA? b@p{`a!OZ6  ;6)JP vГ8 qPitijg6+T|)*P=@p 1}yb?2I9REy>h"w>™ &Jsp /КAYbDNMq sK""0M!ty R`P@/B ",)'C=#JcP:Tz#M:XڥzQEpbJ \:jJI)̶pj\15Hqzc7LfDpgΙ+R$$С-C#??Ԣ*%'өZzڪ:Zzګ:ZzȚʺڬ:Zzؚںڭ:Zz蚮꺮ڮ:Zzگ;[{ ۰;[{۱ ";$[&{(*,۲.02;4[6{8:<۳>@B;D[F{HJL۴NPR;T[V{XZ\۵^`b;d[f{hjl۶nm57K4bʷ @ 4 ˑ HX72iky{C?@wj'qH"e XLQF<?Lj _7{~wy1#.}~0%4 3`]P%P 8i9_]m -902Aח Ns @5h9] t] 8ЉqJ = ^0TtmFE HPG4e}ܘp =:+_gGן=k$P0>GڐmC`|  G {y ٫ټ~ڑ A$p-j/Q P] 0+E S߂^ݫ͉@Iz@ ǰE0>/&rPa3gM -p }tWqX$GF -^ Uط4T0O@p D M0Jt|[33z}q $8H )LR0}/uz}o 4,`NoX\^ HvT xNE`pZ@0RzW@, \IqIEn tf( p+}PM|3P@쬀UpZ^Vp @ M\G3J陰^qIP̒. nO``m Β9S ,{'W=cpm_j>S + dp&n-["~kj4LO2Ÿd8{pm`D c/7)}9Ֆ8nZNᴀSP/ p֒+oH Y}M@F`K@OT d3 *u@ wF-^PW*ɒ+c֝_Gx'tGp@np6bm >?wp_ `?F ֿvP}m"u_w?"_NX =?r0XՏpx/qzX/-T X}޼0C `?O~jKXx 3qW0}` X3_.F0 =?NoF&^mK ͧp`av8_ ]zg X@|zT@ QF\:P GX GV;: fU0W@[ |`OF^ XpL`rВ omX:@ԱX [;q= @/,[gv_ 'x.0FflP/P; [8 50 rr>` CD.,!5OP  8}P--^ot 7KTn5/ 0P5o !EZDpuwC.nmtoW@*P O^6OTi T>~ _![`tP6spHhB50XH0PL P}%p q^Tۏ3Zs` Ҁ'(@XF}65\g@Wze D6PG0.` ]5,TIAoq,~|^w- NcP-OP@ !P2p7PL` v8@M@ 7SpN /ZP^4% TM@ }k#|@ AgBqEq@ 7p,` p2i' WLu,I%Y@[UL Jp1N00:v| w -2Pi>%, ~w@~|uN?u% IdT%IU,@ }k [00` 14k ,@ 7p{5ut.ip1m%cВuG, v7c@ /!Pq P #.|7 dq# ((>;}{37P3D 7pzt;-(-l}r&53z;Z$rS 9-7,e7S 8suѰ#U5wToz?<-^6a;]w覩o ~:a3467oS א LlkX5 ksUL h,N8Bsu;Umlk9Z;51sHcu(L:Ȫ5<Q;Ʉ1)Q74Vؐk،ϤӐH3Xg!, ' H*\0#JHŋ3jȱǏ CIɓ(S\ɲ0 ದ͛8sɳϟ@  (E`LJ aAЫXjʵׯ`Zh թTB۷pʝKĢUJP/IlDÈ+^̸1\ Ie /zk̹Ϡ/fB:jԤ?vR yB˞M8+Ipv~]ȓ+txT"DR t`سkνksk!{`/J_Ͼ'6 Xnz͂ʁ*u-y #\)EF !X Bf!VK Jkni3Rai92,XA @%rဥn*|8N4jR#CPC-!*0)E* P 2af%N!?ӥ v]M,h0YT"``Fd4㈽(*j&v B 4FL2eI Hh`@A bBO<[4tu8qͮvX^q xF,/B=7/ 0xmV 0 _06v򆫽 qlD,"B. nqgL۸P7qwck{\J`uďq MI&r-Jq ?TbXAc Cp >P |y*xL7prd| ,sD.,񱏚=Ȓ'G# NmMbIoAhQCze}5 E/yz$/+Ə=ȉ\SҪ[m@]{zqz3> ]U`i<ۈ-5y=WI~)[Yaw_Nѿۓ߉3*̩;fmd~wЇjǀ7}Wx׀g|HjB_du (o1a.Fi|ߦ{7(ǂ8 {z#'kI ApyEK@m0nM"G6'Y|{\зUXia182'2 fF `p o@>| @҇|01avpx@oH;rF|91hѦs2Ć6WXP Vv-p `to` C|@  ds6kqa VZ 0 ҈eq"LׇjW@2F@ }`_%Y $[  'bAp 0o8 #4`nYXXG35cҍ/2l+Xa(A s -?%kMAVs~ْ7QD@OQ_4 Jp%. K dς+~7`1*Ak'ghi`؂Ǖ4HR}/XS# `5X5#~ Aw k6 u@v8%oEi Є wU)Lq(L` 0GkڬY! % kv pg+  wPP_ FފdZa y3 Vתo@PeH>s6 A:~j&ː 95 )|08 63Oa `pEE282 i10uWezP[\?t<*ԙ!J'~{m2Zgt-%B w o_Z'xQ۷G8R ('u[k$`qwe mku0lcCg{۷F9C$n3Jgj #kY[~]kg\˂[$#tۼHQ1,S]ѣ!q"EM``q۾CiѰ녔@ۼOt PA4,噿.ˁp esQG8` F:q3yeu[uaf!>*#Sq8:QU>nZND  򟁤)![N,DTQ w9UtY\U^|ƹG@E1M p=!p9O|LsR@%}|?1?p,WӃȒRY?z!q*|;ɠL&1);&@?ʮ\c*:$OZjʸfLQLtp4\ΌiKh.Ϝ͈Ļ'/༁7$ƺy`|*,\c\{Q o1}\;zYy=]} "=$]&}(*,.02=4]6}8:<>@B=D]F}HJLNPR=T]V}XZ\^`b=d]f}hjlnpr=t]v}xz|~׀؂=؄]؆}؈؊،؎ْؐ=ٔ]ٖ}ٜ٘ٚٞ٠ڢ=ڤ]ڦ}ڨڪڬڮڰ۲=۴]۶}۸ۺۼ۾=]}W;qz4YJ:LK 2~ ~ʺI|dcR},=B˖|o\yx W*uK le~G^>I>%z@ @<`X=T. O`E^1. '..N:>N/$^뺎..~ŎC>t` .S p  @q0⮾ N><βWA2n9f[Nn{²nA_u ` + bOtwznr^k~ z>c0oW=.p4.p:Í.z`9.J2+ PS->>oPP c0:b`4`n\JkW~t0^qqҠ51@V060 I{@/'>nTQ?W0NiW V@" VPqp tPR? eN?{pMi_(_H>@ )`o0F)90s@/ Pq o^@  {P ev@*&m"HPKq ! 4n( FdܘdɃKK1eΤYM8`-byC0F\`U+Dիd}:I#[ane5ЃX~5 V5u 5\bAcЗve#8 Ot\&]zu|=(zȡؔ)€k$;iAȀ|3\q ,ALQv!VBڳ'ɕ7bF FV͑(UlPFd>!?)AA{VBB 3в>a =^Ćﻄcټ谢%jǔQ h$*㖳dq%C&.Çdi M"09Rj4N<=O@@-4!ɢˡLSp@8 fmF+ ()Ke+\B3ЕO9( +z"s;H v R\Q[1W@\u5ǜ,L\t%'*@qIFD XpG, $mwۂ7G`6#`CA @G%YR4S+a[S&`ӋtsD >k"buk>Xb 2hJB">4Pz mD+ fAd? ;Z[KMYu20I"61:ӃOҏ$B<ƏO#6@'y(iRWpvH@fuD7) {}vtWF>a$'K 0iJAsNjt@ls T[ |CIJ _J!TJ A  k-b 8: dcMT!R0@" =@V@ Hk{rTDXB0G* d«*(Ħ% 3 l٪\k`,NGvԁpjÉ'w{ %! Nsk~#>0Spك(nw?%G y !- `ab, t&$*:`21z&Wו1A0`(D{[ ^3tWfÑ{ $8rgΒ38(j"y04 @ @ 圄ޤM9&j eB@(g?OѨP /p!OU! /'f5CL$ j_`… 8ii)!6CD . DvBx6RT%UQ! %<3@ꑒ"RSP"e]JXa-H1ͪѼ(`fֿbY2_Q6 A MN0ͦMF6^3 ftivfu`L6ͨ \ [k? VaTDmq,BU q#MZŵeL݄Feqk5fW]FԻ+\iBՊI]ڠ*:l{6i.il+/#8H.lbYZJZMigJwª2©jL.%)=.sKN0xFba0v8`*o%)g z5o[|P |-`i&I![Yrr$kPW,[mN 뱍)<q(O=dܒz#(fta, v,tGD iG;Z&nt3Ԑ.o+S|k99ld&#5Wld][j{ehG{r}m2cۈkWcv,06'%g"ee {+>'i sœ0q=t7F1 i߱CxfG#l>0%2]+?Z"JKO^_Z^;>c{ԗq?PYj׸8'}&ߗ.pC:%-Nw[ze8Gg_w \p)ԋH,`={bkМ $0t0x,v}>| ѕ8|SA6#[?#1@@ *(9[O,;:0 &;( @/̀.;CÍ8#XCи(!ZXf;%X92y'd+@6b?82<`,%xA P2 T5@>o$ >kOX?$x c>HDID:#π9( >й`O܂k,L ;@rd"S>Ёp pPL DX¤-WpS$RE<$0,e>CkbG(LЃ"” vhPI 3$-ЀQG7c"I&m2F*I`Ij)cQIO\G` c\d1bB0O<&w-؄ɣ[o|10:-0I$e D."(VX @JeD0ET:J|KKD#x \ (s􁺁؀D0ZP"U &81& ?>BLKZ@" %;L@hJҹL؁:4G-T )E|<D=K7HHNP&LdB<,%+IP"l N2̹9d*pt d=IxBV)3C NS(PAM.0d8\2L#ĴpM28\Аa- QpjD༼>&QQ3 =#,ЦK M#p ϐQ!8P9KH=OpEϸ$ǜ*J )C-5R P89ȔU+XԀ,cŨ2M( 0NKL6(SDpOK$\0ʘ t@ĔXpV(U2Iȹ0Hŝ℉(?5O>)ȂkDT+ , $UEPYǃP:P UR@08TIOXHq5yPeRќV[ XSXQW"#888S.( E 0-}0}=}%9.QvP0Y$@UYIp9$ "! ٍٝ}0̐V91kB$@xTm VPhNY?$`$ N`A{P> !܂MZS`ԁAYlZ B"qDQm}㌶L0q R`i0؝eE؂*@Q!H+;6)[ h FCʽ;5\g˻XׁP_zX"Ր[`E=G ( H[3`Vp' x5pX;fZ&`%Hܦp-.'%Z@V_[&!= ^<%0 Bv@E'5N `l2Xvp.b(BKE(AZ8^%VG 'eMJP6mT3ʃ*j椺(^20ab`Ibhe_/n1R]9 #Q aЂ x*+]@'u'A,E_R-_ Q=ŻҨfQbz>m<ƊcۉgPKdo>\$gún=3!ݣ98_hAAX7}gv>>}n/zV܃Pa.6m:u^_fnh<.z8],i>ie!e;rfֺ)ˋ;6;: Rh%Ѕ B;:Sfؕʃ_Xf,K^摛_khe>n=kUņlκN9aˣl^vFVfvm}l;>dV#mmD6C f~mD+nNn~f)dmȖm8nn^nmo>֦!,c D !, c HA(paÄ#JHŋ3jȱǏ CIɓ(S\ɲK >͛8sɳϟ@ 5`Aa00ӧPJJիTa !IJٳhӪ]6R TP J HAж߿  @p%JL˘3Op kMӨy.}[ h&NM۸k>37ءs NC#dc|KNeSО_Nިӫ̚/[~o=(bZWY }VCri@@ftʽA%D80VY(HZ{{ v*h~%*1Gymp!H&$U0Hܱ6m,iX$o\r p Ҙh]}EAF4TxIQlBV8W砄*[VY袌`9AV%)j馜ڨD騤b8@ѤXꪃA0jz0kwZ+gklm!D^, +L #0@DXH@ P@` DC0p'5$A)9bA40.l@ut+(F!E.m-QC* C-94GS?m,SK|qDrk\Y! ?l< 7 D-@4AX~bk4-d`4o,, ] %5|@LJ T3x. i 'N!#@bFrAbݭ @q퍈͚7.yCРM^7Ef^zJ~SL CKU vƲ>;uY{, ݡsJq;n|P?m_A}$(k6RB bGV|b'oB)/rTQG6TqH= u!Źm{c'bIÙox釿5F@&"LFb@y sv ,hG$ Q(EDLZҒa(1$fQK$ lXe-ɥ9l3 MiRռ6nB8WqӜTl\ulH#D`BfviJ~~kԎ0]x)Ik.3Qf`XzqU E/aHbB YM: ֤>xEU`w85DsӅ@PIԡF)Rrzj`9τ̓@a2UH A2W A8 d ĺ/H!9V 9( i@,+!B]WZM:1tИ4yXJ3Ʊp 6h#F2'QY] RLGVl?P (1 oc@e 5d+A%@q~ M)d^ 1 yв( `/ٞr(lьr֐B3d6Iar#JaFUV0hؒ,; ᆦc*XAx3bY#H& 쵳}nQq޵3 xB2 _5b7 k.Pv8 y@I94>oM5 $"Jk :U8A&o|1$"/yE)VAt@QlgP>a=(6d+݀P@Iy$@(0W̸Af3 7Ra0 d Ww wvw8͔w(`p`xn1n  "pqCNoaI 3 ""h 1 #D p R{ Wio"[, lrB ҀrtrYWf^,B~`f&akEq~F' @( `u I p pg Y\4"v` xGՉ(PhvU"xg Yp59 kMHNʳymcnp`zC _`-p `X2Y JeN+:y tOHor(w}20*/ Zq Ӱ f~A0 `axD2pi o` W  ΐ P<'tYp veX67I9ItDUN!M;EER5 Jr"TK`+p_$MgHIɰ0R 5וG9nhH;9`ÓsRy◗{aITUT6OQіNScUٗ~] "ҔcIpJh"i[ z`x5Y=<9lN`AMb)PԐ ɉIyiǙ{YT)DE JݩNO:Օ5ZEU(wYI0tܙDT 9Z&d ml9+מ$0GT3@Oi(,ڢ.02:4Z6z8::HVk9+? >+t75 p7 !BF@I;Z\ڥ^`b:_#mr30d `dPC0PE+/+p 1H> 1 60v4Pd:Zz^G&!s3@ Q@U L0xJ?S]YD. 0 ѰB7:ZXjzɪf jIG`4nآ-Ua9.7 _Q*ڮ:a< b1 B:F A'@F b T3Պ:Z{+c *:CH Z y )CP2a C:j@@B;D{1R33?K :m3}pH` v:rn0 VZ1kpEpr;zw㶨c9&'ڨ ! # tڸ;<癟_ `:%7 ;[KzUy C0Iz{j N? =1ajp5zkɫ̻K!JT8>B'p A՛ǒ}?| ^ҴOB?C qP&w+T$;p Ls lԣArp w19 T x<$)/%,|#o`W-<4L?PwX<71T\"çF|$1L)N"`GP\V $;W\ܘG]VW9Eb|NlTBZh-lvǤTp5 pp mDŽLLYȎ**Ħ<Ț\,n21w(<!5%P0fBʮl, E"ʴ ˼,[Q˶,KM̥⿓œ̛b˓\)!ќyBH )),^~ >^~ ">$^&~(*,.02>4^6~8:<>@B>D^F~HJLNPR>T^V~XZ\^`b>d^f~hjlnpr>t^v~xz|~>^~芾>^~阞难>^~ꨞꪾꬮ:+D;/1x<C(Q8  ^~6^uξ썰Z.!.w씠X@)a o"_"P.ǎ ׮:Ԑ{ tp+aN^K 6D@ XL@."۲N {<& @ P.Ş??_0{ op o6pĠ0z z@. L:-F_7(Q,-h.k01A4t13^^+0uWX`` 04\_z +_Ty8 =Y~ь.0~pO_ {< ;_7.QR՞ +_-s/ 06DJN4pqҳ^-;+KP>n.xŎQ ¾ގG ^td狥3m??;^TE?O.oK?9/_M]GM@$?{Om0n<^^/oAN~N7NGc>CkCcoOmpo6'*ySxBC>@y53CNHS1;s 35;aC> r> : q  \tz=67z+Ѝj&j R3Fp7DՕ3P&}q~?gU03y0PS< / |~y;* /O  J0=3৛ ~ *S|PP&F7b ='kP QZ H4"n ` 4k0| { F=F| 1HtZ0k0=3907J =` rjd0 q J0q q 050 5@wp H0b`Cy {# 9p ~P -/%3 %P3t:yJ  F' /j[ `4@2P T@'3#uqP3G=u9Fo{ݗkx@-HPQp -9Pbp}X/2Ip -`Z 58``4.;\I jF]DF '~_/tMc|.6D`X0˟Ip겫`M* J ?p C` kp   w?F(60K`!/*ⲌE/ucMcP . 0~@ ``4p_ /` /(|P 4x, \ 3 ,\ D `p.\>/<0K.] P:H:p0- E  /S@; Q 3`0p4FJ1~ >7FZyJpv0W S/0-̼'-\k }0Ct0`psa*X=`#/Q@1Z c`@*FC ' S@'9xF / \H/tB w <AP2/ F'l r Bx?US `d` @;/ `$p]0 :b. 5;[]0A 0XE k /XL  = //(`" VxZp Jw`w- _:0*pp R r@cp:ZJ chB@sV@_XPY jp`4 t)p`4{#p` / FZ Ľ! zP2/ _tl B1G /j!0k E[V /pW]X0[/ :Ҽͫ0p #rKQ[ GrK6s P` /&\@ ߋ/P (dZpS-{(u0GP+>F .I.Ip0[SF-@--VJ`C jo.`RFRy'j : {A Ԃ>P@-/# R - bEp v. /.2d45/l\P1G٪/x> 5 ҵ a & `d" \/#P ^ /͋BFO J`\ N`@ +KWNx0 j  @2 D  ? 7j#ph \3" *N - M-@ZIZp= _h@_:S5MY3# \E 7-({Z<3W<` #|JC Sp57p^7߻|@ ?NW N@ - G07ZP?C2a5Cc[y7[+z:zs:By2K2{н:pT AmP$ݼt<{ձp44[m#sn~FSGΣĬk!tE>%Qn="F7@ۣۓ2C8l2d%`W);:9=^CTDD!,, M!, ' HXȰÃ#JHŋ3jȱǏ CIɓ(S\ɲƅದ͛8sɳϟ@ @ (ʴӧPJJjԢ/ 1QQ `ÊKٳh)T3vL$ݻx[w-VVЅH +^̸c N PUA3k̹"UuI̛Ga~^ͺ׮RHh *"$%μm.~{9Dѳk}8uԓcvO'f[]7۾~z'dq {IfF( UGPd4SB(S9tIaC"ζ@m`ч h8$c"vaT"4iE2LPHUƞ"D^&#Mv8@ # dd/p$VE(P9nP* }'N})裐F [e(馜'U`n~E)hIxA&* ihj~誮)E@`m 뮼֊]+D:ᖋBƦnk̻ʼ+m^m噧Y9~,p ⦀3,qOlWqw1,# (|$*eaG1'ꛯ2Q}/2d ? 3g,Xg\w_-d,sEj( S87Bp0HAvlD(PF2X* {R+%@.o{C. 9 ^-[xFBO 2ibI`\|'7|GX A&ИCq3lQC/֦sXaMqB5F<@~<6X D Y `,! <*H"̀~HC(,P |)dHi}! 44\P aKU%9?$R}{.MTjϲfMZmX/A OP9!R.{GArpC$pN ۂ-"z' 6Iae  Z(G `pIr2ԁ;F*d! hnXmB(lɆ`5C0D4'UZ8]V.v5 k]K!!N2UH!.Do}c1_7B͞g==EAvJ*Xyڃ޳E9;eH^T WlpUb+֗؅(TAtoLr dP~F} D.(@akae-{'s,p+S5xw*hpOYyh@g*bp&/L<2/8dvt%=Homƹbpb(@ i[4Ԩ65);j5\80gMkYeεS]Mb%~R'n D]jiC $D`4u|SjjNV 3q7sM  @CMOo]Ѹ1. ?|B ZA:a]1#'ȩ! QFXi'x8Pzo{giWЀeŨIHCX@2^`G3oh@9qv7  FE/0 d"n/0BTibFR@dϰar!aဩKOH bP8ggp=2J`L 7$J! BX -J :{ *ZrdB@ |;D+t4*F\j!^$O A }'&j Z`pG0J{"u G0 D`2APW px c(Ȱx'z |ڐ р :0ԀQESwt '& U0 U`P{Y\] |23~_G {`$nuPF&g!R jfZv)@ TL XX`r`V n P?pD% 8P  nH $(P`! 4(+Gg p x GtDp @ Ѱ ' y2p-`瘎討ȅ p<`y34@ 70kHt08opoV `45:&r~7Y4pkP % 3  p z piM4Rx8 H3av PWn!Go҃px wSِ 6ֈP 08>2  pbr gP툗y|ٗa/@c80 Q  u\mocN/VEp \Qp ,pQ0lL> uv694 qW @I2!P0 =! Hv FjP 8P!ԲxRXy ӶI\ y Ȑ P0R p@ǜXz:dtr ?~ᒘ_WEZ}H 'Sb60u8u(!P.` 0smuQTh$0^ }+Ȋ j+0p3j ȁB npY`@ `_ކ nF _*p0 ip6E##yQIfi: 6 Ð 04Ÿ 2& k٨Tc4&l!gmޕmZǥ WP=Tm#pnw!BAtiҰ9*}iɖ1Y_lRj:p:&W8kJJk#  ի:WeIƬCd7wd=*lP0*g_*Dڮy}n篾V +{5)7z- 1,cm&b۲.*{$ *ra5{ a# i)g@F-{ c&w )`B4310e!|sx~I.5 :.G;d+laoW}c7VX#e۷~3b`(6+90TۆgZ@Q}1#3'Fy[| +1aKJnS0)iX!r4赜U9l(Zй@*Xһ=4ȑG2vI QZIr#&vxһ +rb;;3.!ç־e+mu+<+`ҭ[P"]Ekmw ɒ; aa۱?- e{A!G,u la&1P}p>1f1 Pj *@+|? ZU P%h >d\;a 5@9fn{oo|xqFU O0\yȂ\(EQ j5l<ȊUj{ɒ1\ɓL1Ɋdx 2]\nl0fh"B]Hq2 aN} O]1'VQR^]%all1:`!Mm]װqPb~q(=دQ}ج!,mĈ؍a؎ٚ!,ٖq1+gؗҳ8>Pڨ^~ >^~ ">$^&~(*,.02>4^6~8:<>@B>D^F~HJLNPR>T^V~XZ\^`b>d^f~hjlnpr>t^v~xz|~>^~芾>^~nZXOA,dfq$;ee!~lU 0j,`_9`\Ų^끠VpeNN K >N^ n"Pp|O.9ZLj| zeaKDhɮў~풨*ղ@@3\otpS q\` " Ղvг@ g@ k Q2׾{3 \0}V]Pz`*1dL~ 3{ 4K_줐Su %_W ``qBa@U? tpc |S? | 4KPpQ`| ?"p@j  o )oQp|>y6Z pwCOOQ@ _oO_+D]q@w'^?  0J p`a V ű o @ | :: |!@ [`s? >3:I< "0?6'3!!P6p PPVPOdh .r`$T1oo7Q?? Py6q|q !WTSP 94p*| 6H-  Qƻ?ڞ7A R/r휟R/.?b@ rR_o?_J!?ԯoӏ!/P?^ ph? oe@09Pz GZپ . /16'R_! >'p!r?uoP? \`2D7گ ?oQ? o>q0hiO> 1e^*{ $ NK 6R/Oz/. ._4pNO!v1ucp|O}p|\}pp}?cp|77}`t؏֯A0^1_0n| ApK^>>7ھRƷp// / O_`uO? .3PO(1bf?r> Mq P0ڇyFr"}z7ćЄb+i#v2 -2 P :oMfՆ 'v+.R M6mI Pž5+&` p:60~ a#+Xh[oP+u~ O z..A{ Q 3 'u( ." b+`+`'C0oV  }~5/:bC'j \sb q u &H ^ Ds~Cp+@_w 3 HF2 $!P(`PTHLp7Hؓt|t;qaFs{#H L7kps9 @i80-H r_n kW_hk0pst0 {s! t0. 9@8sH4 8P0| 2q r 05:ғ_S r8 KGF@  SAtpH ̫0 0] O uʴ+8 S E0t0f:7w)w0bG@  0K08S C00ps+OL#$+, ^@p ~[ $3` z`}p[rn/IyXP \!G@ДXf4~>y -`FH 0@ Ǡp}I9Ś - #`0Vb -0·\ >'\4pD$@-X0\Иx& FXyq y oE^ o$ $~`@ |0F@X@p~D stHG9`F ` IEyHX~f+ c I~F@ X|Fp8ywx tX\|c T>yS{p#+p `Fޢ@p |oXP \0F@X-PoP 'g }҉ d~w r~@+pPf$&Pf$wO X`$+`0 к}'Ok :f`F`+E?-_ pCzKs#&X ,H 1Y D[Wad ł E+= EUQytFoP^{L;Ky `r @s0~1`c4~!W|` G׸}9 `0pPF uٸ: sҩrb0T:D7зw0 E000EsW`0-D{`FH0F c`@z0jn%vQ E`rWZbeJp: 5`d T` \aW`t8`3P0QE7kr P 3T s50/pt)Ą aّ`F` 7K|`Fjt`"i,z >Z2/ fT>,1(Xy T>0Q,` 7H6b" DC|sLnV aG(rLkFyqK Uo:c4XY@5Ըg}kFt_Gn?L)-Si xFM?5`|]V0[MFR=Oʹz.=e >pwKPQk |rwK` > E g!!0p`?.И ` I|` ⳉ jp@ _ YW njPF6#`$ jF[0#G>/@ , 0.0qG(6~8(\ 94:fWvW @к -:;9 ;`!` Q4 p<7蘕@} FuYe? `Fp T9Js`H?|W8yXs~Y4o`Htw;&w~4pQE MyYsFy `yp~!f W@kW3KPQyt׺@ڹR ^Dv[ q`FI |PwqP0 E c|P#UX.F oպ@ Uc ൴ ï.2p5xJ 23j 0Xpv Wa. H"y4fY ; 0%`d @ - # pC xH|!?$jmV J095 7@+N}@S~` XAE y qz?JqPpطdJ$j JnA0Ix~}+#&xI F&:0rXp8ʾYQOPF5 .<2v@&@5zI RF$jTf ȸ e5+p fkeoOgC#v.3FΪֆz6 0pa+v+5[ܮ~_ Q \m#7iw!%9اoҩ  Hd-30 0zf4jo+r_(,.|$5!##f.[#竗jg Wj2bfXFb)83nf@nH(.W}2g*lȹ##F0b)Ik#.N2bҹNrݵ#zIk6Lb[pA{a...Il8vK17I;b+6(%r&zkR|oA¹r#%B.. !, c H`, B24Hŋ3jȱǏ CIɓ(S\ɲ˗0cƌHCɳϟ@ JѣHS.Xj`SPUDmJ5jBvRٸ0ׯ`ÊKٳB&PKPS ;h˷߿&@.T PZJ3k̹E J,AZ"ϰc˞Mq YKa@o N^Qd]p$ سk]oveZ\#$ka`/7Gea7jdABŸ Ȑ_,M݃F(!rjQA^ym! DP@D:b,26QJm%%M3 A8Di$`vM9(ML*$`79D`N PGv`$TTISAM:dP\F9aix4&i)lW71f^dh6({Nf e[EvZ_*ԩOeYZd)Iꭸzkr Qmo6l`q LU(ú9埀K)8fũlH级t+kM\GgahFz,U`"Pԡ*+ L4GaD\"!?5ޤ IzLkH'hJa`nmk/c\4VH*lǗluQtgQZ}6D0-jcZ w}-is[91k,twǢL{oP1rܛJz{Y88\ysjog:ب3g*WoPG(@KW`pd D  |p rPUJsT~@'` "(sʆ!%I|1@*@d78$ D{" ."`@&L 8p`dx!b [H w EPG@ 2L224c rXbA!,L͉ $@ 4(NPƔF{Q48P`(E/c J28 xha3?H"IN.0Zֆ.l ' uHb=Ta"4 jtʹBHMD0m4 &I';Z$0-ZVE|cd"%\8H_(R!bZHY'gZs C4 cTNAMdTv D!Y<tu@VT/NwӟqdC;u&OJC%OԤn*`i ԧz,gG@`f!aZ=@\` a= t@PX*iX*\FA:,WCn7(!hq-భAIkEo }mAUՠc@vvɲNbsd*աUM*RSZu0emmv4iw+U-pKܖsgy26wT5 "Y7X!LH&qԍE%5U6!4nZ˴#M|ZObqyS E0hM|p+qEkܛ^5 n , ?H~;];NIfǑwC4K0p&M٭pK/jWo1a Ȁ8[+,%ow˖EqO\o!,ι :-l Yޒی*EL ?eίc%T4PXpwp~0+ Ew.rH G/ n5008]1 6 ^Z Ah̀ ħsH1tAA%dBGsPxiQ[`W #@LA"VR tO)c` X@8ڸ%3` HW@]qO)WZp 0P 0@mf `lk {3ъV]T(|q!TD9B8\l[RlXs \ ,@"Bhs@&0$Q^1 PEr ^1 T@'v^I!$  Q zj pFSB$(Lj@ lt鐈6g3?>#mCJ,HTKPr>ZpxRĴ x2p?UHG%RI" %+tZ Pj!a L( +P Y0ul3xs,Ir{ρo7Sr#Ґ9 -F ɜ | cZGcS)|5&)Bɹ(oؑ٠ih(J$g z y *#q³(bKdStm{&7QjVW9ږ)ڣ{2iL Z`ן  '@q:XfѦޑ]2bx0 Tixđ)ʩ,캰3q[:@0aA)]an%ʰ*{(*":De9@Vfi H 48Zi RZ#G0 %T<6U94`&ڧㅕ١O 2URfbia<IW')˶k,B8C=}PF@7uS]kcd F89n +nJ/ ZPSE;*˺l[XfpOR1CM&ZiBK2KG]W$ `J B| 򖉔 Eڽ ċjN_`hM|@I phRm'&`P-OBzW0'* (]*+ Z9062p c@oPc~rl Ɗa D2 ys-{P,!K):\̄ks")LB|/$B wqΝPjq,upW-p@}\`B  ^0LS1|`;9r&!~!iqЙpe`>Q` m/!:5 PT0 : W38:mЪM/5j(7Ү\ZԚG[ 9TRKDh|LBi{Cy7YjbCL8;uQzkFo%B:򵘊 ؊=WE| :]b^8]ؖٞS[ԣIJßڟe%LQ Zۻ>bm& ȍ@[6S  F0ٓܭ;}p1p H=$X0\3]l [' h ca+} ͹!Ava),`aGP Om*}@ *Tt| w''!M` B+Y,g4@Vٌ޼%L^aa2_f$'@UuXpUTs -ޢ1pdSoA kUu KDE: Q6 D` A(@Ds8A n1|"HhQGQBqdG>!'H [H>JA#X@\*RX2D411T*`Ӂ7 m|]j@``x $贔QPJUϱ1 (+L.=@ KX"AE>A1wQ*N,b~ς%B\(* M)I W %QxX /* }VA+ܡdńǹ&BsA<-bdC-@$x"DR# T|6<4lV5dMR+a)u#(K>©eB Q ҭN$XaHy( X~@1 vnbj#R/ z9`'0Œ"֢Z} 2dqwbaA(HFWzPbw$vZ&T(4&w MK6E bH0dda^TwUt$,Њ)d`C ,-PK&qU(=mć*#j"zMVlHH\M%_[(AC8c4o =@2Pf  ZI*ʜMlUnWV !Sǐvj k93*,!D)챳+r 3>a*CT{2$OQ԰S MCAЃE'xVQn0l7SCSqzsW-/bَӹ_pl[d1B4(g$2k(޸ ASz p՚&H=jW"3R>%Z E=a_i_l;$ 746^O xYe1VD#0 %;0.HJ& D ){قA.4@SBP ̃ X J҂OR @Q!@!X5$MQUPP-AM X8JYFIBM7]t3mQ/XU&Q'pg umסd \yל4JNzW{AS`7`\XI`:u4V RR,.l,$R!LOEh$ X&FЁ,؟I2- 8@.J L$U?XL{Ka/{TͿ `6,0kY20هm,8#Z]K$Q`$[ #9(5 Ȅ Z -x=HP!,U ]7H` ]Y"Zg>}@R`MH\ '@xDx Ѓb]ܯXߜGɈ&ǂEQ@-hT^(-˧4H>`~V`m`ݣ8R X1БՃD+ )H턩U1V,Kj 4 !@aa]"TiqO `JpH5/Zv ( Ђ#6E3--4ZxOu4eL-P 򵰑>؀EIuxmO₈X@r-%!`6K؃u%Q~/ւ,!- 1[J؁:)!6 @\6h!ЁNP>TT..8bxe[;IJ]\(e_;C.XCh$ϠT~g\gxwi1yIwH,Cv =Ǎ @-P7LS`ar$Pc(VJ*_ ((`5,H "{aҘɂH eBCM5 8${4N !I᏶d9ЦfI}OhMNe_5jP p8b@RN)Ȃ,hlvc<F똱b;T>6=nҁl8XK:`=."QG>p9^^<.3!{g@Έ߲y &*l8V8®N~gdǶyn`f׼EÃ@8[D4`/4׭_-6f(dai95jOp*HhL#UX>.vB0?P΁Vb!X]BC.[ ddF-N~]Xc)f @ nJ`a $ ^[RIdU(qH\KhH F=0 $>f*@pUbN"p(*M/'1"(U0 p`8Fob.P6¤tq)HTba `bʸhi>#H CKpE[li}nns@ݛijRvavb7<2%5]x:-` 4ЅÃB'X:,c[Hc?-NԴj:Z@4S'pc Q0p5@aV'0QP1`hxl{Ox3W-vjPH).`Z^ey_"k*oOoHLwKk|x`pwTojoe⡅wj(n`O <5V{]uyV8]apX4Ѐ}ʘTO;z,&&0>i*OI1nw{OGvb}~nNO_'OLݤK$ML_g#MX KD0IiO`k4`yDh~&QAm74u AC~ dMDz}mM_W dK WޗvJV`&~OH׌g燼H,UM)6Pv6z-=vm|{.e$#_'G~~$mvJD(v~~v9_mWU,xVEUTEUTű~?OK_`RWO6vR}ׄdU~Hд!pUEUTEUrIUrTZHXJzuq[8ھmnW8=qyYf9zJ}UITz賟e겯M楳z:pa93ka7ORp]uo=]޿)XG_8 >뷟>/ӏ߿HЀC>j3 H, `>/|uq@B9P4ϫK 7>}/lag8! @aHxD E VzF$l`Eh-z]"F2hcHƹ=G)W2 IBR5^EL#!Ұ<I*ԟ)EQשT5e*a*I,sUX!dV9jfdMZֶp\JךڵxZI284lB& FT^;O Td'KYղͬf7;9T?N')!] nb5b5PU/0"#SnCN)Zi!€ .ЈGzޒYzZ`  ȘE4sYΚ%v[^󶗽AUU=nzK[D |x+L|3PM!C:ԁ*.` 0.pl!  "D0?`J9`?$|;"F/V$3٭#E&_ / 2$`b<'#E(lG8EDܤ"OVd4 @9!ID h0'l=:`B Lф(IJ!N#u`+'ԨNWdd;,a˱@Af esZ <ƑVȬN;bfv5EA@Sh5a%V@ e*h6Ńfg;eB 52@b f37ӄ ;%Æ G% Z@g9r E SU5!2q) ǥ\{1lp $ `'  EG6k a D  lp  U7L& ` e8Bc64ӈf~3xՁ#78%*#e =G8 aQ0op,cj .4as ||3@%(xB& h bBق DВ3A1 0Uw G q 0]P)H 5[7=485Fȏ &" 8'`k2<1)!d[   y@@1DP5^R  0q)#ٌW2b at\_7vCK&!bva$:#+)֢ 23Pyyk尋US#u0; Y &Pa  PXA X#؏6}6~Y<؟ٕA$1r"=HXa,@y@`40&g )%-#tqRc @/ \ @pv'yr%R:TZVZC@e' ]RLÚ1(ip`)a"/] nh@wbafw n&.)$F)t 9W:Z&a$J2ag:cYڄN6 AvY`0q1* 8^cʺڬ7Bê:yBv4dn31X ʖ_cՅ꺮ڮB:ZzʪӓگPa^&@{ ! ;[{q8!CR ";J ?` ֪$02s P3:<۳BsP8>[F{HK-{+P`R;T[:>vp +,V`b[3f{h{űi۶n;t[va>w|۷:C~;LBuw)۸>hjY[{K& v5Z&:!Bvf[vADy};{ ۼ_;@K3{:HD۽ދ;;ze0廾ڲ۾;5e 0ۿ඲{LD l ˴ȽR@\&,33e'.|S/<4, 5:Y8@ F|H;8еKV<Þ9WC|^lL:;\Htu : D+? D H1?pDI ?i0,X-aZ+2,b}L?P F Qc$-+3pE>۾4a& }ǗUmݢZ*my=]=]}=]}>^~ >^~ ">$^&~(*,.02>4^6~8:<>@B>D^F~HJLNPR>T^V~XZ\^`b>d^f~hjlnpr>t^v~xz|~>^~芾>^~阞难>9~ bRb:ӡX_2Nכ@ $K:z}l` oA^_PC=Jf7[ | o!I~fśN~Q!O]p=~ {`N|P`pn|O q@A x| yoD-!! Au0Ȏ 6e`kA$Qp`,SK? >wK4=#p Pk/@NCS_3 e&=y _0X/ V z~ap Y r41?/_E15 "/%p p500Q:7V. þ? | AH4APoQP0(!}t

    yߥ:5q 3zd_>`_Q^vPA^``p9_qX_'\ ?сx}ov?coyp? CNԍl&.c@+]:Կ˶>A+I]O_ƮP~ )` 0c1O11?2nc?c?nf?A];?]j4c c}/t@px?d ymbxa~;P h]``oq_o`2  0`o]$0ox;?@0x?90}?@T݀=cs\pB?1>x?x?y1x<?>O]_՞ П ]__2_//`p@c*s){S yrY8͵ C3h&rU`%J^0x> 7W> ps+##E)S!'`v@Gv@&qVq2{7 55s)P_\` nzx63ц@C'ׇ%5&Q 3`s(c !%6C@sx[@qXpR` We0mƀeS 3`JecQ 0`†*j1:r + E"~gӀ'Y}I,w8vPSkWlU` p7kEϱ3 '+Pk0S J  t,!\%"+p]Ǡ[wo  l fv]q`A+a8Sp\ \q\ @q`H0| T Uv8PUֵr@]OV 9]P\3p @ wW8 B@@aak|aM0r@r|r"]e -w]eZ`X-6"`HCҵZ7 ` ؕm~ PuEq F3%\5A00uga8@ o:oT7w68$@] +u  q ap]u\#@~0]"p 6)]7wC|N Kpp]xr9W~R@:ׅ xp\k@yXqloe wfו]$p\S՝#ipl2`6a0w5vcE@MzBg-V`D %"`zw`pW׫dpc`Hpdb_F`Ap|ȇaƦ  sM)SiUu v 7wI|+0|00` @g-Y _v1t\X\q5vl|' 3\0a0M`tz-PRdpcpNJ|6"EP[Epc`CX'ϑ`\pzPUW!a(aC@{_@'ug0zPpWIp `:#iX=@  Hb UAp|7 M 6F 4P,a`0"-4P=Y0CP z(g\/r `!%pcFP`WP JOl>Vc]P` `\C:`psO G )fq@i&"uTwwVD'p6BBGOY6"0lQabzM`}G =@sqX{e%iP '`bzI ~0c@rS"RG ) <"yVavfgP zMp*@g@qgyFm'pgǪz xtAJe ]wVqgvmhW'gU'R z@cmIE)F ) YpRg_eup[wȦ ZVHPu1JmDob *:[vpgq6x%2n# I&rzaWRf Xp [w #v࡬2 k`iz&a-pf@ _X[ J]p sRR@AVy.w"uf r"Pbzp&gw$pIPu&_Xp ` %09Z%Ȅw|g+` )P 4@ wg$f/ #!l` 7 2`WPo` gBWS6"Ɩ8]p{YyF0%Fdm: kkb0a r0" F )"K@oVr9xԖh&' @YprH `z) u@ f6*\M@$\3#_0" Q\ȡz&GH]P Al  DqwBs 0n5"H@x0aHcMoȦYi|fJ g'sqС@qgxa *Gm0aM@gU %fmg 7wlud )[qH@:@'" uM-Hg&Bgfpk2Wg|5Vu& ):o |0a `el; aBgf/(p Mj l]%@: yj(Dvf'L+ BW(<7a)rl`R sL=p M7aK (`fZU` P+p vep M@yEA'p6B.0a".Pr Sa(l  rf9W~/H^#P. 7`ǡx͖w{G 0aiP jv5% @ *x~` Qd{P +P h|` bʭ|2qN@ C%|ޏVLqVgE07xVN@ E(r zPu1 bwU'ʢy b5pڌ6%w-0bc,7p"Fu pxf`vGy|`NyZs_u`zz,Q N|NPj g!z jEip(Baw|Pu,axw2"2gy}x ` :N| Tu{J`xjs_~ 6gwGOJow pwZ'!gxN` ,@ .(q @xP&%   i&1[ *flc|'f'sPcUgw|wE@ 0ff-zF @0nqx7xja~i7Qf64WZ³| !}Q7q8|r|*E 1m|Ʒ| bYUgI5W2%JC`F}ZH%!@q|m|VAwԧZ5,W7}W ]a>f}%}7AWLg}&% *D a}mUa ^,>؃7a wxVzHN*YqtŃv0zZ)#FF}Z(1^k5<5H@ *~, n nt/VYr(AG(e^(O!. 2=qOxԷ0ڐJYǎoՐMT&QHާ~eDwUXXqr \:n( ֮W+xcِx@h0UX2NȮ\1סY,pV :7x孮^!, hA \ȰÇ#JHŋ3jȱǏ CIɓ(S\1._BD͛8sɳϟ@ *@H*]ʴӧPJ](ԫXjʵׯ]J`fV]˶۷PRXӪ:8j߿ Pf_T_S)``]v k̹guk6 VtȘ-̺װ'&[9۸sͻ Nȓ+_μУKNسkνËWz8AϿ(h& 6(TAiVhN&vaNFx~h#e0bEF 0"0dɁ@^ @P"+"a (i@:[;4O~3/2QHBgHADp;CN6Zp RdE@4Stz$M"[-ʐ80Mlъqad`RX%Tڄ$߄Մ)mqHQʫ`E8ֆq;kTZ /;jkM!,(H*\ȰÇ#JHŋ3jȱGBHɓ(S\ɲ˗ ͛8s3L= JѣeDʴӧP)իX Tׯ`Ê& ̎]˶[i0ݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNس{tAǚJͫ?<!, +HP`X 6HqaB +jHbNjn(bB Sf9Q˗0A|͋3o9rϚ3M*'ѣ+="]Z(ӣ9"A9Z4뀈IkٳhӪ]˶۷pʝKݻxz2-c\%+^lq4K Q ^ Ɏ?wv NգS~1 v۸s{N|xȓ+qУKnثB2N<%'-e3kWAlKs%R藝& 6B(VH4B9HCKgـ%aXb9!a\d@Æ*(K3lX@L&@ (@>4S@fpƑ\:4)E%㸃E9pkl!GcvT|2&h'ݩͥv|^xʷܠzhh5ꠤVrZʘP/Ƨ؍8aM/b[F/38fN㣰`9:ί@1<"64pfl4s[ҰB.r8%gR[֋,Saeʨll0 +vgGxaL7( !ȭ*[Y,خPFL>Ikza@hl =䴃L2@#W\(D s 9/hl6-WPӱpl2G(:v2}"kl[cjg4SI"t)eaVc5a!TQge-oǷK ULgT* !lZxAC|xf ;NT5`60Ȅ0SJ l!9j^Țؤ–0F-yɜ@ _ niLӃ腗,(.e"|g>~D 7o8܊rI΅Fh@'OVF/ڠƒ!85 H19Br-hFgњ.,6mhN!S5oDC^ѻ% a"TJժZuWͪVծzt1HLI\AD;:M+X*Wxͫ^׾ &dՔ*j (t4Dm24Pձ~c' YJujf"6uKLNH6# n -egK[ֶ-hu5&3oڕ J-Ev\6΍tKF&jvυnx;򒷹kzϋ^*w.a]֪X`x׿oup `B/: `[>+oJ,P8znb gLOE wT6r,d"^KPD<*#Vޠ.N& `.ZFs58#19%CYsVG)!  Dx2V0 ܝ,aɣN*<2p9[=oBX ˆ$Aҟʒ@e/}kWϺvn[I'% !$F)/YAN$Y# X1 pf{,05bM CBxЃ+Gls X^kxy}]Ъ_kC \F6`p#Oho\B afA '@X."#( "`׮*۬0 \ Q*Є@K/1@[  >`"a p:Ďx>oiꄷiW1,#?ٓ0,\`"7w7M!lD~sܦp}x؁htPqxd*,؂.02x$`&@Z1<؃>@B82(x.q 5_~ANPR8T!"ńQW`b8dXPhqfl؆npDžvxxz @aB{8Xx+'!'18X|L]\x88`hqDmX ۧSB5 ؋;81/qȘʸs@"+UPU`ոxxJv  P#Ѝ؎(!WA}X!185(ՏwY邅r Ɇ ِ)Sx8J8B!~9$YI>w&ْ.DDp 8: M?:iFy9]s* ?`vTYYAa5=ab9 A:`AlٖX!r9̨LHzv |)ft~ zX$(LpaɆ XfR_NY)q %v郸ZH5)Pl$ >ɔ=`h+aٜXX#PؙؑIש9hbI虞Rٞ|BY2qZYyh)9ɝ1XY:E Zi`^ڡ9\3DZ !Zi.*Dp 6j? ȡ7 L F*Gڤz#„NZmDVmy\Tɤ_g v!l@ڦv:Qtw%Y اd P1gy z*:ڏR9pڐ>ul~WڏxY۸3w7ɑv fRƫZxB*TjdZvHM +@ y0ngJ…5~#o l隯TÚsSPcwP vӇڰaS3Pkv P6`mc!A;Q8! i!U/q[ <ƭ${RD0 P0X6PjJ6o8۴DP 1+бJNOhAj bNXyqȶv jHp eO&e"w+>aF f& C@۸ w[-x`PQt"['l`YS(=IQp?y>:۶Yl5ڻ;tZ+`ڼ~՛*q;[{蛾껾۾;[{ۿ<\| <\| "<$\&|(*,.02<4\6|8:<>@Bì]^ldx̘ SA^} N}-\N1̿-p} ̌'lnjG` x]΍i5o~O"pЈ~gľ_P Q.0.陞L>t0X5枎NKq+kW:W@^"MЎϝ/=nɞ^Gn3\. }qٖ=cnߙM>+A}/GMN}`덍x`ʄ,k)` I|FKƃ mKa7ZPy&5 ka:g 'gBņ\z` sOVs'Z'dOxOT\hwߌxSsH0m_ ϖozYaߑsM0@`R @#sdSP_>u`}qeaV'& `/ l0V C@  f5j pIuX4Ni P`pe@㼎 r-05 PT \r : @.MP `ˢ@s*0ϟ]04q p fp p}p68'o n@ gm,qOzCf\}_w ;W`Wmp ƏnpU + d H gN̬zz|z/y`{Cc@'mZp55=W?wz } fiC| ~`RpoE;'y@yP g~p$O` f V` ].*GP ||Z y@y+@l GvK |pW@m6^f gim1s. b +. QV]_H?CJ 0 P ~08 4p<`<P  rrnz  -RQ@G7 ;\pz' }p5RU q_ f_ij{. 0}&M@ X!}rTsqI `N[L+P>F.@ 2pk@, 4iJs0D@ 0 %9r2P% e 4 gp piЯЯfnzp+0jᕸVEej~TvV }PaDep~?пdT6pXoevf]FofZF !Xoh!g PvO 7&x7j66jp pSNfq5j}ow'jfp7F?vhzkʇjfO[1jvxլw_/}ydoj uʊjj815_j{ߐ/hWp66e jOwO?qcp"kJF|KFwjkũ-vfomf_kevi/p!,  H0@C P!ċ3b<aD XH’$ ~8Ċ'3r%ʛ6sPsgN:*(ė*cҐ2:SXӪ1^ *%S+nmړaMf۷_S zmu߿ LÈ+^̸cCLy .s6@BSZְI K`Iձ/F8O{tѻϽysUH\9ln?}߇~!H4Uf`[q[" &4Xrg'fұK~[Ά‘e\bt.@+X8(8?3 9hI67xdPXR8ׁ\ gX҉$ȑnƁBgxL3C=Hs Bԙ9!jvA=`f5vF;qǩ}iyꝇjwYԚkH*jF뭸j)vZvQD@~H.v8RYcP/2Ν]BBЌLpҸm b/:8)F;4{l0p6,2kc̱+#op}l,o ~` !DG(hvQ ]L+L/Ͱ4fl@6(INga-B\3D$?4ʱclp-tmx78&&|b@jpK #UWP* SB&{RiCWZt %U4drKB.Mb@c*1Kb&cтX@Z؄[Q"@& €D´ Q$e/pJDվ~ g/JZ0<5sẗ́L 간  أ)A  CZ==` 09-.P/a`Y':tCoK 2[Pv[6&ohO$, a;~d)B2![nȩMQ`Fx*>8}Ʌ@PCK  BwaGϼ[(ї2-Н:.oJQsߜ U $ j/QnQ3ȇy>|D ymIϗ"EѼHCOΜ9"8kW'#0 B ZP.` 0_x@N.0~DP, {W[00oT%V`W'P nqjp Cydywd)6y(FkVw[14`1U 'gyhE~!ӆM&]mU}g/OextZt\jƆV1v$A ׶E5[ UdQGq[cfZ_RFe]8H(IWu8bϤO-͕ }7JfW(Dxq Q YX8\yRcCgƱ%+1 P&f5W'FXP1PA&(eAP\+uPxa*́ R d F%0iNcbA 67' 65lP'}w z' 3BH @W`v86̐  ְz 6 (=i@Xkyv~  C0w}P,6n!i `nЋ  cH& H@mwТ'I0! 03Uj@5^L1(s7 @ؐ р ;o@ oYǜq"x9ՑBNP #! <3(Pa\VKW& Av% 3&7k<$p 99z]2 9'ېSh) !q k9eip U6ve`wPP{P @ w}` )°vA& Р|9 АV@n`,cP p A2 $%Nq$#RbϸI9 )qU @|8*@ PiKK_6+xcZF˷vW;& 2!k Y~vaD[eyx !jhDfk]{|p +-Gvp)SSU~bt+"tG&<{.<Կ$,)WV\~fER MF||OyзSƵħ ]nu*Y<ƅbǴTԦ hZl]kZn,DŽHtLGum%I. Ӑ9<k`j) ER C @P} :OlnAÐݿv;_E s}Ie{eR| M Só,H>RGzjc&0j^}Ȓخg'p=nUԙ{8C?0՘|ͨlgz7wkݐ?a( 2f/* +N( ˰ 5l-0o0óW]߇ 21` ² ڏ {j0+_@`NDvѐG=VABPHaAHQ%&N #ĨbCMZ2K1eΤYM9uOA%ZQI.鲦JaԎ-a~,`aŎ%[Yiծe֭8@jUl;,pY%\aĉ/fx(םuN^%ح2eǡE&]il*ˡ!R*A{ Mo/['lY=S+O^u=HKfJӯ>{ϧӽLN%Ao6 TpADm8H  .I5ACqDKJ24Pi .* Hg1sO>TKͧ^k.2bTtQF F2B]{DtSN;U@BҫbGFOSUu'w\s6b*rLXuW^Er[-lXdu6XevYl!v[nu К.@ v]v ٚ!tU]|՗'pk`zs_ 68Vxaea#Nb+X%x/xcc5dKcSVYUWve?yflfsQf{ƛzh&h9ihj zk i[,6lF{mlmS;n۞nϮ:oѾo7ppď;qƙ^qț> GT" #'@I^2df(wGSp*YW氒 #'iIձcmHOf)xQ[#@(LT%,zGaf0gM<᧚fVYMeKft2MWoڋz&3s󇙺g2Ozلʄ>OYgүP 4^8]U`X:yQ5 @M:FS  6%$.iMmY"C S @ jQS&UK%jSj=8§NuQAJMAӞ! }(#J2@1Uqk]JWyk_z׿aNW|UG%WU0Tt0ԫ b$~ARRMӂeKi-jaK[$exmoo[[%.(lˇOַ@H>q{js ӂB<ZyY<"I*Sa:uDzp\]bϊ@;*ڃ5! (B͉^Wk'|a Vyd-+ p]ǒVQp,ch%m$% "+V1;Yum;ht@AXrLS&  ,3w- |!F`P\JF{xn* &V^-|ӪڄE Ap->1:\)h9 4¢q"(šr:(650aSd^ŭ5/Ta CV#TU <f[La TZeE;d|W[wRz# ~C ^C0 7%^+6"$(aoUOt"\^xd1'q6T=ZԂKrgB :q/֠!h+O5H`d:,uY&&0+H)? +rb zC2so{97rK,F0PA4C$+22>H=+Ax3-B;C &X/3ᒽ37 dɞ#CM1Hʁ1)0+8OCsAUx@8@9+T0$`AD8𷂀/W:z(p˩80:F(< t8O~hO@`Z)⳽[X$FЋsD&%KGr3>;8蒳LHwl?wxLȒO@(f/z[8HGcԺIDCCƒfҺ;l =; ɢs!32#P<̂1(8 p*@+@X= E8Ȭ ?:X&!™3=;0JX-LG-$:`X>x[>Aa^{s,G/r‚$@I:0,a/ *a2UHI,hI(܄W0Kz4DJ@Ii+'``P*Z @09ЉO J,?0o #RTʿPtPPD쫸9a28KE ƃ ++@#1$*>s=D2=PMʴZy>2S/?B(']x.}ė(δ)$}N,/T**f~dH|%c5dAT9zObKVM&(NLme2 f:6@H,Sh'A-:`#XĽ՘@!c(' Hi=NT)2- R.&SP^[8ШS(uFX%Ɓ% X-؃=][C='e Vh8N ^h7>)>R^劚hK`Okd>@kV `bKQ@@|& h„`T#X.h(6łjHLIVȄ(hݨi.sI.$Z. X95HHx`.Jj.r ay(R~'Bnʾllnh#ln fip 0`+Øx^(F# (Z؃IŻR.V)7 ǁI1@m 0`DV .=)4Cp,/Ѕoܮ HHc>ifo ah?pNp&&6gfI`#`)@ Ȁ(H謨!8,HЃj%h^n@`FFC$^V+܀*˂r)nLܮir( !pHd p7b9op8;sɎ?<>:WA@NmF I$F"HxU>b1vM%-tNт $d#,mR7pO@Jᥣ@Ȁp8 .&(QL7`^Qh?Z JХCsdw7t 'Bo=zw gx{W~w7GG8#`ZO8VD 3d&8`1a$8jȀYrW=%go0 5@kNЅ& pqp"h'08(ZhQ̽ "/E(x׀覂$@]';~otx_h/a}E&{o&%{O0{uhhHxc.;{"oR?DoL:دB?eE݇ nl<:f}S [XP}+F~ۿʶ/|OWh$'~}D`nߨ7)41thEc MF(1~@d'i?@⑏1f\d !,c D !-,`u !,` V!,c D !,`u !Y, H`(ÇJ0aA  ȱnj nHD%S*xaKRdI0dM G `&J:?|ِ(Pi\yfQBy6}dPCԚjLUvWdZڷb܊Sݞ;;uja,ޘU˗nṇ+^̸ǐ#KL˘3k̹仠-@XiI^m@u_8pԡ"9Ā#@+ujT-.)L{^sϡKN!+MB\d5 fЇq`#@Ub-gpK9EpC$ё}P;8D݂ *F(]Mg`E%?w @x@ Ah@ а K8BDaHb@ᦀ>Y4ӌ Cb 4BBs845#mB$tA 8B !,L+2 8JZ Bn5QUAԐ L*/U$'IIF&j8U09,Ibs' !=fڧO Q0L/TJZIYz 8IrL:vߴ꘩\BTB` 6hsFAF/LBІ:D'NyrG4b~@HGJҒ(MJWRhi,$RXiNwӞ@ P:Px < ~3YXCTJժZB ("p TJֲhMZI@ `xͫ^ׅjuTA pMbػrXfN4ͬf7kҿ^TUjH9KҚKY/0l1YpgͭnwʲpKNԷTs ƍtK]q"=v'Ѕ;B =|F; dJ[./N5G=UPoԨN5OE\dԪKZ/Rw-ZCᣧ9Mlƫa]f;g[d)l 8c톤0@Nrb_yیfͤ6t~[ݷU|p$&Kb[Q%x _F@5Efxe另| &09C\@r-wcTї.ez/PO's|3޺ElKUpU  zAA5p9a q(N Qkw x!j|w/E{?Oz&`ѫ+Kջ~ ^Dv.q-nv@٧-q#.!7؇4x{s h/u?ZmjD/|&5WV/hp WGa~?`5>GX o0f N8Nt!,('nT*x|H'X|_1dMo\20?O6`:x.YGeAaP H !4*08 a`REwpg Tkxn:C DuNg؈8XHg؉ȉhQD2ph~60 ~HaX/(x؋hĨxv ҀGG `WPP1~h؍88Xxꘉ8*j" 0ҀdF`88Yy x4Xj   ۸ !9$"Y'(؎ .#2*n)p8u+,)DiEyF9H .9*h' ` بIK`bYdyhj MR p1 W}@9f)yYْy!F0G|ȏPؖ"(}Pٚy/၆Yb؛陹Ʉ鋠!Мiy҉׉9ֹɝIw阘tShGA M4H 5!_R#8NZ ʠNN jz(J0!*#*E&a$AAu`ZZ;3(=z;A*C 깡zIGEj^O:*JQZTKMʣ?٥^`b:dZfzhjlڦnpr:tZvzxz|ڧ~:Zzڨ:Zzک:Zzڪ:Zzګ:ZzȚʺڬ:Zzؚںڭ:Zz蚮꺮ڮ:Zzگ;[{ ۰;[[FuN´Gmڱ>:Lx" `Pmp 0 @4[096 : p)k@X:o@K@ D 0*`by0F+Qo`&J` 3N| 0A{ p f  `` V@ oD P pNS3 I{ I` x' <`Dж[;XP`w| Q0Xp b owK {ද@dx |зMpq*вdĐ+{_в3 q@q T+H=9;R8Nะ 8 QK#DkBh  R;,_4R !8"6@|I @ I0X &\p 0\\p/'㔾xW,P""E)K : 0@ƗK5;t@N ) Dgm RL`MJ( ř)898 t\9Kql!?NB[}@lŨLU_ /zTɄP$ @ @9Ȼ\;[hmPlPŅLNʁ|Ȫ|R+ |(ŲNy<˝\0$`Nsq YAsq!zUHNG'NJgVNI QLIц~@`=9O7Jg Hz M +mhC"Nd+ oBX '=`1C Jpe qt OM'F@ 4&0՞@ S=b pp") G+9"!0 9ԞP58"2}rqw5قd\ R bPw|@2R 2-@}@-؉sP4@MXp zm- .\0mݱ8E|gP3cP8s3+bMS;;mCp[P+61)` W u .Pv`  W 0pq3')^[9D334\F|>G#UC Blq$C ;% .pYp2 )8 1l=X:J Mu;13=}`8+3't7pPJUA9uDPdD`S.8 k` s@H\IL>HW/O)P ;1F`N~S9@-#<9pp 9zqr> F֪"LQQ4 ~ C,@ GcDN ` cD -`Ak>b@ !;ۭsnP,@Dj^3 0i ~;dD !TC:A&!8<0EaF55GK!-Ls8y4{Q,)qĤlAy``Sϱo/S:*RwPjXS!,sϑm执޺ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ\'H<& 6F(Vhfv!ZnƇ‰(H[ !, ' H*\Ç 0EjȱcCIɓ(S\ɲ˗0cʜI͛8s$G8r#H =h'.իXjʵׯ`sN]‚&]֬Z*3#!J%aJO]8"a#KL˘3{훒n$-M^ȋVT(u hl"n &!УKN[mUTЈZTe͊O`hL@$3f(\fH1*=1IsGA#A>$X464$RG2 @MTT݊,0Jak>`Ix 7\P `E ,47l>Is`3BNF 6 3 4:|IаF(^ фK CpT*蠄 S*E j6N K 2o AFx0{A@PP 2ЁOHp4jXP: qjH39 DRtPG((A'ƜF+fI$Ei!Koā%ͅ(|:ZB> 1j@ $p@C$0TΫtFH0z*A :%&SWk(2KvF" ?$VZ$ bApIa IZ4Vq]NnaFɀ$ 𗖚֡-Mo!*8K+߀C[It5@h%P4Zl-ЉɲT=DaAӠ3A'!E3ӬF) f&@qd7h-]nB+JN&.WYg=\;koFqN18Q `N!E oN-8@m(3`GzCL{a?̠Kr-Č%!o|Ɓ @Df0 @Pf !3 P/j(ǐR V D`:PDYRR)4 {G$ʉ%QL)- HsK@{fBMI9HxƱL"-d2WŘaazLgDeSIиRY#'q# x;3+5ͩQ.R#݈b] S b1"D).# CTA(p1`$2Ir&3zdh:bҁa&1pi~7!L$aRٖ-/V)o^ͨF}P4ƛKd@RQ4d>њt&:=O}HIh~4bX?$*)ծ> |`E%#?&T!WJ׭)f=MCiPʢ׫b˕~0#Ow?ʕB[!25YFXzEH l=EV!eC֭bP%U(BRMא,9M >oKM sRbʝ@ j$ [P €L%FZP[*/Q+6HQ*'VO @0yC@؁C: 8cI F&!x J HVX]؂b<90d4Y$d"*PjkF;Зİ T,X 0hU$tXR:&"%&J zɀTd7X5}*3wel'@`E5^B -[%Q]K 7/|R,(OA8[QepQTUڪ=Foi87VxXi{zyNW0*#ʲjymcكr$@]%LkT_;4>gjUq7-,Jb:%uL}=4z#]#η :2_{-Ƞ&@Pvhcl4+I̺ V:#@CDrasuU>%UV[G)py{Nw56r Hua M̷UH0G/!\8m8LĨ6mȄx;  tZO="NVv3M?C`u1UO|'j<,z{>OM=[.y˛? -LvpYF۟d,jrB IhZX-@ 7 (8 (Xշo' ?` :`h&8GMBVp vHGBs¦0spD;?{CCD\gE1vRu7}}w~Džb(r68Gt4|cV){jdžvd(X^x~Zehsp1h}hEf 2V1fbp;e\Wn4E)׆V-}(S *A 8SK7W T._FOeQEy؍u—MmP8 xR>895\gAV2p6Kr3Eŏ)F2;W 8 JA Y6D@XlvXe iL 5RC !W6M!ْYJq:uV Ǒ.nAKwO= X I:A!ȔZ)=NS2#[y}l796 WxhyGJZ!@nG4ь 2HxeyI=憎ylr iUWKtUuZjI(nseehp ɒ[''NZ=ٜFJB'3e 8)ڹ"9EAbeEuY@3  N|jqPXqA!})Θ Ĵhg ȇf9*jNua9Yi٩"Xؙ{XU#0Hđ oy*VVYo5B@ Nls):2FHƐju0 PP&`{x1s&ʧڧ*z:iڨ+ꨊ*jxf8P~yj (1 H@&ȩ#j:*Zz.*E?AlzP a0t؁e*Aj6rznCڮjK:J-ZJx 0g%Nj* @z u˱ +;jZۯO t`#j l Ȑ;(;D+*[HG˴NxpP7H.P߀ 0$ O;gjl۶ng&0ͪo[Dp 0:p; [KD%G`07wpi0 c*)e۴;[k{)1% kE!pvPF۱ '$봹KË ʫ y-0k,{໽K+)n+4Ck믧K{狿;ڸ&[* ,m$~(*-&␙2>1^y8,<@B>KFE.LK R ^XΐT ^>Zd4~>jfpr>Xv.Wi|~~臘>x~~~^~L)ښ~\.>Bޒ4Xn.}Dn~^Ȟ-w^w^|! XeFT((]DX|׆ںrTl{åޕn(d*LP1qr@/Cvk MA)B7o]3Ņ|1`=$tQb 1GAm3-nAK 0K@ RN_XO\ `r"phiok05(nOpln900mk_`k/@B_sO}?_!a-qA%V`qi?]?APv/ h V?b_?aO[eW?O O]/&b`$@ q0@p ?&Mʇ޽ A 4!mI0n܈`ć!/XRa=&1``zq:5'uXC?nEIӛDN_**UVбbkՒ:Ȭ]L$u-mݫzړ/ުnAg%T.f<)Pl]jV.5oysϞA_9sѥ7/&t֕L=:KUer&.K?ޕՃGSvyZiFz<3(;F=H)I -ݲ{ٱ '̅72:?`($g ǡz#A Ah4@Kl’X6R^jQX$.bD8J X8ld=`D/*ᇄ,xh*SqGXc+1b냄r㍍kX`(*lHU%8C V@FRL3T%,ܪb$:ިa : AL˶9PiޘaCd=b3=>KTu6=%Ila.(pcbÕ7x\  d64h)fTtMm#Qcaɧ (ƄPBtc8 F tJRPJb1Ubg)LHx+lsI(\#SK| S *AZկx#8Wk(Aȡ9,+W01LUHG"%wqM}刋h4 I8KAb 1$j=7| JI=V2c(t XJktZrƄ$CRt{"k@ !Zhc!s(e5|F hwF֙x`D%[ J2fr!H0MLƘ%) l0b! 0ex>=b !d+4m8Ātth@3 .r^ \[`ч>VnB (шQE`88QJfbBؔYұSm6Zx%M%`ޘ)ҀuK>! +i>0DBpJ=ԺŎr}rE.Gh1nt&FF{_ G b MbaLJʑ=$DH@葎K 汿̣'>ȡ%`2XrD4ZIB(VO!kP6Mb,hW؀АyPnDl9D`5UMQU Q `JpIN_K)(K@t KX3m٣c`F%ZU+ t4 DWWJC cLQD=K4NyۛA4D8JBFuXAIN\ |QHx6COcxxQ[3J)~>)d%Kt˅LBÀ:\iSˆ>>"l'$5A|f0bik#05SYwUkNH 7@=xZV+t͂A,d?̹+<7 AXK$B S`ֳE!z8QF ly$5 tP%@% \YB- Ĉ)d!!}-HeHW `yNVen :fOfzӍ<.k͢աW!8DIC&1CAT=ę+ (dd*Nƙgw<@c 0b)6 0$007 X@3=!{ 9FX#1! j1g)G90X+ ЁFlg'3l VQ!EPmxORMӯgK}1ߨCӁE6&}X{(a\Z `\s,[U  p@’=|(6`&0^k*AT>c>3c@s | @2$˶h #@DGxRȳ㲄P;Q ?8 X/<.505E<kMγa'U6 >Ń YŽ`E IBe {ypIElRꋒ ހ IJX65I~k KG LGq#>L BD&ĀHA,ȑ qtHr>p@.i/B 4G9 xvHO’B>XsEZtp Y#|bA #$xYVƤĎQ|,8zrd[HsH+$[ȂK@ϸÂ?#Gd lKD*kˁ4[<` HTDI$ITLI LLdIzI T!ʂa4B'㪮%g.@j@HсB9M H;GTKcXMá=N 4NSڜN|͵T|\>MM99 dN%Q EŸK2XԬKQ)6 Up '5P+O,Cؿѫ2mm3O.S@+JS4p>? SBUOC83T~M> QӫBSHXM?NJIJ8B=Q"9eS,DS5,N3=}WբU45h(0QST)(g CA5#0 N-UZNT`jV- % Sr[SʼnPZH\DOdhEhS(.~mk*PHV2Q XpaIYE0-VmY1Ȁ# p؊]OJX S`AqV-Xl&9-YpAXX01c8г|5YC5=Ye ([ 0XץMO9WpQxZ`]X\%=Qj#ZMM#x؄23WM5x\uV7`jZ(Q119[C^wۤ%Ƚ6N _9Q J #O@k}hL<`0U1,\`+hi:Px_)>PhQNBUE@^4c_>V R& ```xLȀ_K$0]R.V.Ne}LR]y>ৗVЁ:)p?WFYT b."xaFa P(p8&_5ѾC7%!`.& M[X-%!?ȃ?eT}] H"؂<G]`#J &%F [8Wxe Xc[aT@,YZ0dԄ 5#`%p9We Y{Iya.0FYY+b#S52aU8c h @k &`yn5MX^dL _|hF?#`6_s.=`718O>5V))@{n#0徽i\`1PE>!@8JEȅ P(80P Xi\ZjZ\BI`lJG(U^ Y\Ox킿=`H͕R>i HiuDִV 298Ϟ7SЁNPe#Ҿ-i'.VkFOY ծnbjViJY%[LX+>>,U~^3ꖆYݮB+0`8X&HJp띎MH =H^k2H\iZRjIk.oXmh_R$n_>\+ˮnmvoiI1Lhb6Cڌ it=0D&"#be-P&N" Yǥ3e0p*8^Hn]Z0p0 6(G 7P`FJ݂4/e&V^<48&,:"mS]tłCX^7/kؕár熀JAa)q oHPn HJm)Ux7ZoV_9rt`dqtM3PE ,hmU#`PI+`.;bM_.lGJ/^0 vuXWOT?y=$Bs#cF/ԜLaL($?!S;O4 +cry.?8dpg"H嶀U=gVg߁:i>Ѝ/1[qq>?R&HY9#`϶l'0xU[y`/K`,h6F{.CO$ VG|$N>Wnv.nU4#HoNs.| UGg gQ`{ah6|@@(}ꭄO}K@@[؁i[ w(hd˜ BBE%R.,.\𠡀: JK;oYBS JS"-h"IB-j(ҤJ2m)ԨR1GPrą'Fbc`'2Z8`H P*Xb?;zF,HX8c2a"D)?t1La!5JdK+ Z P #I\/ #Nr1QG0!.j&P$M`,>QU8 }OQS4D$G>嫾IDe{|7 [ 8BeDgU[߅\ 5A1r^p⠇R! 6WU`ĩnTqWa!lf T$.$Q@U<%Y )T(! |T1Xkd$Fx exтZWl&r^4bE#DNu**QPP76\$EUIA 9"Ndl %ݳJӚ:Ij l">-ѕ-tǂ[;エ%f%/*/jJ R CkVG6>VCfى܂=tϙK]DtϽJ^RS>9{z>z쯯^R.:ī:[= D#]433S-5sk;<;o|>/{@izcxi3IȻ*@! '/laA%}?=[  oBP,HċQ  a , 2`1 fHXIj$$ (]I!XF\u)-b$ ) 6GL\%Tc<s 2e+; BȀ_ҙlB:)W]'p@qiT6A Jl&npg'<9M]'NyޒJ[GIPc%)_LC} +WLbq@" B0 N  JWҖԢ/Lg S8uZNӝ@ *OkSx3# ʀ(!*ӀD p@ RCTY:2L@J 0TnX`wfcY׽~`6MK#P9D*5 Px D@ހFSP*6a+TuHN _#ʿH9?C_>]_=WXe]7%;;f*ЀG!J7~cT~ P'ti  rWS:Wg:~ܗzGBxV=X8%D7eTȷR[TF`'$Q ׅdb8$Hfxhj8Ok؆ner8tXv4Az$\Op qax؈8x*bCz&O F+8XȆk %KsYO!GMȊȋ8Lӄ3 MPp`#`@X؍Vȋ~荽HKX ="3O0p縏؏ݘch ԌAp`WEO@ 'HyXTCj33tsiRɒ.0h1Ut(:ْ;ٓ>w(l.G E c@ٔNPnHh$v`Rٕ^`ɓHN +8#bpr)BC;JtQI?X "KSKOMc76kRK٘d($ɚdLb"PK?DpXi~#?Wy!YY# ީc*YQTcdÁ9Yyٟ:Zz ڠ:Zzڡ ":$Z&z(*,ڢ.02:4Z6z8:<ڣ>@B:DZFzHJLڤNPR:TZVzXZ\ڥ^`b:dZfzhjlڦnpr:tZvzxz|ڧ~:Zzڨ:Zzک:Zzڪ:Zz4yqt9Y q** M3 `pKں ۊ ܚ$ZZ`ڮK  JĀڭ*t0!ʮڮJ:t { qp$ 8P\T 뤃4 cz{&6P "뮱PQ 3wcR o0_0Bb0˲ oo@0J5Kx Q@kHxRa Qs?@ 3 o"b[ Q`{ mۭ ?QK$8 +OQ9P`W pQ K W`0бa<O;f RYp8 0nt@K;ypK}JXԪ`@OྲpP:!1:q@~wcv \*, :| (Vp!a9P {{zp/}p 8Aø0p Jqvx .0~ PkΛ;+ҋY<z Z! % a9;6,Qp J{pï{5֢ۊZպՊɔv\k t AL"ѫwy/;d'; *` <aq1TԺؚ],aTԗ]зlٛA-v<פmeiLɥ=džMׅ֬+pM p1 HODuU`0u\I]M+ (mD=JOн+&݌@u]8ݽMOvʿNϖ%*c?ʺtL\d?F#=+f0Ppm1BZEהJ `-4ș"&` PM"](I>up"Ǖ㽅F ߫<*~GTEWC@ w/H8`~K8Sct kS   JF]$0Ǡ Lpd |b pp>FFAgvǰ @fP Nu N0Z nF3@_G@ M C0uzyP$SY0#@ 'NnY cJ >|0`@AG0 Z|40] `^! _YLpN0hsiaJGK#0 I -@z, QD  m7-t  [p eM6$b&.v@ۯ@Z_`Ll]0/,0'o i[_X`\E"pd/Q }Fm7NEwm  |&X 0] qP e0s0{y0 sT#4 pOPZP (D] \-|@zo)oISNΘi@֯ =3@PT ?l  d0y+\JO -pDžpqv' LPƊ0z@2=5` ~ z>Lv OWnIO =@0.Ho ;@ 7`>zpD@` b0 bDLP ;0 MWT @ =W0΄|#ITJ00dcD wITJZe w3|# {Dn4D0F@ @sl߆pgVݕRvXpd |)u0A>`dP}PYCP z >:e0uFP D0v=)QK0`/.OW0S0znsW@X u xMFpEUK@Ӎ2SӍ >UpDOP UPI$np3L JITJ]vUQlH SD 4@ @rx4|G@'ൣ7w Qd@/o9H &2(,EE PDPPp ,00㛫uk:r I-EHP$".s Hb UQ~P_\~ `)&E V[[0ūQ ǀFs@~V (p M 7☿@` WoIO EE`Ik|WN@ j[@ H pHN@ tH7Y@ H`d)1k+#| p- ~}0} JP_|JN JP_IP~j .`>GpH5/N-p}d 0 ` [ wp|@ .7P#P.NID0tOFG - J@H@ \0N@Ĥ@k @ \GG7pDP FG@ T.04K  >0Q N@ĤFD@ ȮG-+ i0ޔO,@ZD>%@ I@G [0 GpH,7P{DF0}v. iNL@ PP N.#n0S9&$ZH /QȬ1zcoY߅FT0򸨏VyL~{y A2^)IyrI!̾y\v?XiM>\o)OD9C->#DIWeo9CfX-HH# -.-2NLĺ}GĊԁ4IdHX=)"5!HH4PC-ިhjN].2^$Ι4=oԊ!߁Gh!$IWK-ITC݊oԣH=j)~T)%^H&!, c HZP@CHEj\bƏɓ(S\ɲ˗0cʜI͛8sseȟ ;4Q̸tӧPJJիX\B \ÆЄ7TPPA{ֻx˷߿CxE@䒌K*FN·C4(tnS3ϠCMD  hpKA\Pj37У [8d,ڝ} Yf.EwLkν'9K1V 8}*qx ˡӋusEB1XpބVhA`)@t;Ve B@Aͨ4L@I'PvQH&L^{)ؑ{Q2B U1K)G%a*(HAEYtix%]R& \|acqbBHm9@'P \Hux0'gyj뭸I)&%ԥ%ZToM&@2G4!"IˆL#Aj7BLC4LW9.wśʛjXHY!v}[4lIEGl*.K0jQF墲3 GK,lR"M$Ŋ-Vɣޔ*\/eWfBcL71G=Ԃ!_J[@[wEزd'Nڶ/T,6 k}3g@-i%$.lqOz;nuFMygyo'5wQCM-QIv5\F["}$oiZ>\2X]Z4ʼ~w}V6с1ϝ'{W\(o9 ZW^KWFޭ.L[R?Nd}u!Fz LMXj.&{,Ӆ0L̤6rt.@ f63HLbvx6\n?LtH*bT@/LE.Rq4Iv:++ DN8f:qiXhfq.xB2bBİHDpT"MSCJPCzv*dFWAa p2]™Z25̥.(D(e#:d 8ȗT= c0Wp .ܥ8ǩ] @l7*M~4)Pdn2 @@ad©a+4"%(mHYRjO% [P0Ж"3J $" 'p0\PuB &\WX$ %؂-0y/+HҲb_ iNl`}\G-(2D18((fҙ a(pb6@սqt KqqCh-`\&.2aՅ\ ֟eiځ'?,w6"^;O4Њz!k7XFM-d{̐8R3-Y%@-Z|rRWZ>vP rH0\ p&#<8^X%l8qHcO`}!Ũ>=!@X .5Ͳ;i LTbAD>)Kf4L, " a,Ԁ4pF!dĢ@@.DPL^@9VD|DFO_p,q\BiUmͪD|n%)ƀ.*d K遠#acrgXJ@@RX MZ1wh5[E|X Q;`TT9ݑM@\ M2&Ar<ǗГ +@@tdq\s!{]@ݷ2~~H& D~PJe@9 #y9)XH31`SaIbYfyhjcɖn+Y|bI`X y P2S Z._!8@ E Z^b4s1S ` ~@"Jt`9 p diٖɛٛyْ.! aXzŠ/_*QQd`sg`J` `@,P 90 P* k0z@~ ;pg  X5h0 P)9 :ZjzHj Fŗ W0 {V"?`\ D1 ? ^Pl1 el0 9J5hn` 0R@ p;#Б10 xzhjlJpT@&ʚS  N3Zb)@)^`%a0 Gj_d&Pk $ V*`F Z1mڪZf*Ĉ2j x (y#j~; gV: D'DN"` p` f Z M, T Zd*Zg9y&`68|-*0ZJ Y` ~P/D*rT`@ +p[A1 2 튯. 1+:y */tq QxEy-5Vg4kJ[MP{OTGup9r1F 5'N;|T3˶Smr+6[ I YPZ ۇ9*I?붂˶jk[7}vDɵV6 [B pcKxXiʕʺJkZ0+5j 6X2< QX` dc;[k蛾껾۾Xi, ޖG gV>"*@AK1@ p%`  +$<P*"#;hUNlǰ`0z24 2 H, Է\0 1Ł>(  %+AX˵3>T &}]3~1+r@de]Zy\NSG׻, r iHMkQ`GpDGdW+ ψ?^0N|EC ]?A'ꥎPO IP  ȆEbkc뻾nNٮNn7Nߎ~rnӎՎʗi܊yPU#75a'N>x|Aw6ns # T^ a3 1{K3/wf>N9)LRkaP /G.]xO_/CSOQo1u7='BX _uX_8|^Z<:Qu\/!HMm7[As{~ ,O|˅VȈ4dFE ?e.44JC`|+m!_Q OJDO/Jt>3@?E_a]ꔱ`jS3^No>o?կ~UЀUPC%NXE5nG!E$YI)UdK4C 9 `%K`$ƙ1.eSQNZU:",EV &%`e[qΥ{u]ojI4"&\aāy3@`5MgСENZ@%0IsoKPLJPˆ`^uٵwq@B#Hn*9}{ApZttp@ t=K"8h|4+B 3 ;ó4qDK$CS=[tEBУ;SsqG qH"4,ő @P$I#+ ` h((JsL2uL4TsM6tM8sN:NO@tPB 5PDUtQFuQH#tRJ+RL3tSN;SPCuTRK5TTSUuUV[uUXcuVZkV\suW^{W`vXb5XdUvYfuYhvZjZlv[n[pw\r5\tUw]vu]xw^z^|w_~_x` 6`Vxava#xb+b3xc;cCydK6dSVye[vecyfkfsyg{gzh6hVzivizjjzkk{l6lV{mvm{nn{oo'3_*VjC"<+ǃ|6q$R7!g G77}QbhE*V1W"`&RD'j !*B"V:H:p w(XAw $G@l` @D&!!HG&|` $2DR 4%'C)!Q棔`)CiQƲ%>T#>x*IL!eC`qAR0"BE" +DR:Q9,#79F0|, w鱯|&[Ƞ#["I(')Ӊ"A P|^\"i2"C 1.L4c>Tu`AFb*b (.KEoC<x tD ’ f"EP(С"' ҡ2|=nb X-j9J#,cyVVZ) 82cH"`|0!Q(:ZeJ+) 2D(y- [0i ſ@(,JC/}y(s&uJ& !>䅖}^oa(0B"oHf B}a9Dd^a[A8VI >D'.[ELDxMJT2k%Фpa(L0 &)b|C"nx9 ܲ j"e!]|ʵMfAt8 B"|@*X lxeHeUXY`1jj!3 >219> PX"Pn"ZZHt7!2鎳` 9ox.I1|--~@Frt)&n1%t5f^(qF.{HB-@h.%PCZ|2"C5Cj((^LDEB=^tCc o`av.nAJt7-0`se1vE聏3D[ Zcs#=DRh{"X FeJS eca FHEp$bXoE Hcx*DP:$aPj <'_[ZCQ5E@Xr-"u9@ lS0'8k"w}:D}CDbBÁ9vڽps> Œiϙ C;KSԮ8:;S+~.yO gCnk&{b^i#@ GI.yHLAxEcR$ 5Ff}XĚ"P5Ѕ=p"c3FB*>CUl{)s)H@%Hy5HĈ[ l5D?BFh"-r+!Akn!qd2DA@E.k/YD+;x[  8GHY!1',ȂKV(#( :-@VB p:$Ȃ1Ce"Ìp'IlC)h) %(#3 #$. =TJ*Kڃ(&h+"/2MU10xEd\A.H${ˈu`:0(XRh$X*H۳+d2'[rĔzЂ#&A;)ʃ#ꨥ vk4zt$5dΎ > <~ĥݬ dͯ8vAt4*OnXlORDdOCҥ҄KBCZu |(FF:*5Ͻ31)&aaMrN8\0y8 hY(c] 1]]N\\^m_0ȵEe%[S@O+3 xh`}( %`#H/hX%PBW.`(KO@[%a>*x`a FBa)V+&c&^Jϋa$p*hA0v4NQࢨ.(8&hERh#P+a2 98? 9Xdc cѹ< b 1a15.(_^ !M-.+˄(hJLF@ x8[#xLne)p.06iRF$Ё-ɇj&.>f.U6.fxީ\^\`+ (+cg^ 8 H- CfHЂHX`hFЃ"$G `i;f_&PQn%B(0e_0:^jmf?-`gd6 Ei?gց}iQ~e?1i.$`hdgfRSe_PݑQ}T@ 7HK؃,Pb(>L8\Pb]I"`m-bFl602(,CJ\?ր$*ƽ漌2hj9\Ll$׬2rlԧ)P"S!N}*Fs?^4IMʨs#UQ|l%DLѵv9yM;*5,!TC1a`"w*_.5D&Pɢ}@-הcMIGԏ#Tg7+Z_.Vf^1Ӕ!,$HH Ç#JHE22ȱǏ CIɑRN\1 XʴhI̛!&~d2УU,I JsXjʵׯ`ÊKٳ]&LpʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($CH$ŠІF} "Hh6v$ˏ5FGbhC_pDmҒI9@qAC:#yы!,XH4&)8ͭ](V dZp,1Zq;"!, +H@&ƒ #HBċ+LhG-~x`B\ɲ˗0cʜI͛8sɳϟ@(FFPaǤ#)݈2eЫXjʵׯ`Ê"\[ pH`NKT C+jH&]̸ǐ#K8o ¬W 5XpZNI!A)˞M۸s, *T]V2oR$}}d5 fnB6*#}E!* M @p)0#  Kr]1NybPdN9 TәA*+Ea9PZMN.ۡ9 䬷:> -[ AS(r=J'kd5%2\b8ь_8K+/sIBd&6u፮h|ᅫC2%ڠ֚-p'Hd*GYM:iQ W0oQfh4.ڋܬ4,q[Q H"$@ 4l4Ñp%@tae.z.AbB?Z1fD&ʞB0_T38a>|(7bxL"< }1B/%?NwEzcY^㔨B&FTu|%(gI%cr!&#XYbsrSc4-$ u, KCjf h9HJnL:C 1V2qUd~YOp!:JН%sB%> ١ QhEX1.! *@ t# &… 8 AFv3(?[Ѓ{D8AFaM;aS:zœ.a,ss9UT!X"V:$@,T5@pA+" +iA")AUTavQָP `$+l NBk.4X8ऎqM'C-S{-qV `>H`@VdNp)\ Ү $XFL Ls`O\I qiړ 3hx?>BICK_Ebd GK1 kӂӮin`;5M+ " .42ѦY`G( ~#3dfg,cU#j:F|0 4]R~7a [|d0J.ć$4qf(s!IV+|I7IE@?HȉZ !AtEHi-FPj4FX.0O, UYY<U Ù1[7us +Az K"'‚f +ý*&d4 %2aс&C&*] ˘@![謶z[k^# YԀ|5HTr-|MJ8jXj6Y )Hd7aC절'C\2#Pst亷]Gdj5@PVω ~J @@j |` у]L?@B`fHVfN%*D& r/90G dpA |xxLT✈K$`Ǥ)v[#($/ Ӄy>! H8$֞04v v8I`0Q1@TA k[>G+"`?{q\ڸQI'59p_un-H{S`|7?bd9ޔ}DvN/U @DWb;4cs~4cPcƗ(ZsFzDBF<=vhĐ8Z@FeS;H@:vBCILTHJtgkliw/W0u;&0nMX tRwv$Ts!  ?1A`yQIf(1 U6{`pP8 eScC0xE op!h)sKu#%؊(݅=. 4 1S;d~ ¨]}C =JSYR"H& QYXA/n7+w@eEOjh`ؘ0h~'HPHb0鸏y+5aMnDH!N4ď 9Ahn^5:V0 Ӑu.&P@,b|k#lC*q[2`CpaU-7JDYQSM0P{e$ eT&Z7hRpaGHVF~0-ԵpY>}S:+A\w5Ґ?%0}5$;d[P ?@Q|ql99CĎ k|ҘHe< $i i Iy+ dE=b)%f`BNJb'kUPp}̉+ƣLႳ)fi4w~ٝy눙AH_9oy޹=R9 b^H^9L YĞ'ә 9}@4>vt_R ^ )C#tu }CȠ"P5Hj,ZrP N4a~#D]1<E5&YUA`-i!  ^T:YG,d @` pl` ~09pq\$0 Уv  aC.p~ zzLG0D lohqk uaDpg :` Y }4SwcXTxբ/JWV d h uRZ P,`e ie0sq^ 0G#A1:   Ы%`btv<,a1q `be i ӠrQW[a>p2@ ~ TA# Y`v_) u ]x}۳`j`4/6gka@ , 7W U0kx.pDD w\cjCZn(jjykhsߔkXѶ} sWKyLz׋;d^ M,oNI!$&~{;i޲NzKj1`0%9jpy>+m>~s7}$?,=Ӻ̃76E yMI 9F`>d6$T>PqR9b>d^f~hjlnpr>t^v~xz|~>^~芾>^~阞难>^~ꨞꪾ>^~븞뺾>^~Ȟʾ>^~؞ھ>^~>^~?_ ?_lPEYGPJ~뷜R3'/d#ܹ `H6ld$$?'Sf7i]c7iAV.L=mӔsK}Mk:#VQ2ԟ}RMN2dV_ 6'+PJ0K5 /&/ 6oV` 5@+Y@D Oc@qc ?OƿtQ?(`PV`F?_X_P_X M=O !1D6dI-5:SLj!%^ bdžU:ˈDB4ѫ>8g̗oO!{ Q|IctzIc3U"iQMtj@M*Squ7kPE`辥RD`\ABX}+,W_ p; nD(p.c&Miԩfk:\` mQJ_y YsE,b>t8\|HьǍ" b|B"xSǔAE2+_t :ވ"O$BF(Dbr)+ifzʩ I ;ʞq+CZl+zDA" b["bǐ&:/ jt/܉ H*h :7"!8% c.x(̲ pqYEO@D[tK-  nR>nFj"ŔScD@j#b ʱ$x$& <ӡYc?U="Y#"?*о2֖"DܰtB QtȁH1(tp&F~O ,Qȓ*.B"jL|dQD@+@!2L*1+JC J8dfFŁ7>0EJ9OZvެ*eiUN:Dg )$Dz7xn+z ro&$7'#n@!7q(1|%ډ́"cLUqU}k3n+{+'Dy;?mFkX D`[@,Ё #@ f4tzp?||ޘĪzC!``A/UmI[^((HF > RXDh$i#y B8< J"$x]HFb&xZD";ּ¢dd[{jc"Et31~X=,h'+\oF/88uR%R.`  W b@J%JC=Xt2#" 1/uEDikLr >Pfn &XzPdCrY9\yfpDF@+@1 EU*`*Lrf&=H"1QZdYb/` yKCHؙBVjGLW{+T E#ċxʰ h* zD0@ z  f\Ẅw)~Q (AI@OD~ɪN ,"4#x4,"S j-6C{k5b AS2q lζRD0WXK`0%2HK"i2PBfr :zމͤ>`0x")1Qv2Ի׍!G=*5DCyv =k 4 c$ƗJ0.:G;KF+SqW08`H_zCP,L$jRnj{\U`h0s#[AD@ 0/" J r X,6QfS`ͬA t3qT',#ɅCQ44To!El(P?p- 7]ʈq\X0 b'4LB": $DV0X,,A$  @,p&+~"KM<67rH:m,1s1:g27bĝ7oeLLh?D3c#d )C{3f2XUDL6##Fa30"E'B=WxZe;cd%RP'9PRrBpCw 1tߍ[ /]qG=ꩤ`;hE؝cVQj&Z cr4z+TsnPj| >ghB¾X6Kxj M]RT|)q R%֮(<]wGy=xR gw3ɷ`=ԗq^;HsuKY_X{$<ǕlW4Ԉǫ)pYa)=/ܫ=v>룷7[&l@(>,yA,;ALAX>X O>8A$@ >S\?!8cj?#3>iB*B,BSBp{fP)34L6|+C3|C7>$C:9,dCc66>:?Ý@|C3;\DFlDGl{DIlD ;CC#)DS7= ECHC5JLT]NTFUlFiF| 1El?aDjE9A[īF; G`ԷADSvEvXCFLGHE0R,RXFdDCTGHHKH'"hbRh0P!҉0u,AAEC;<(%F\(㐗u@тA!S$Ƞ]AH䕒b-Z Ad-LT!r<9`&d7 + $DaI+LwKC;栌 YgR xF-h|# v)Bm˨L4*Fi骰IKS;*F N&`Ak-`lv+kK;ƫ1),l' 7G,Wlg1Py/ |2\ 0cJ4l3}-|`@kAF'ҥUAjPWT\F` )9Ʉm6QlD@-LmIޭ|߀w)n'7G.Wngw砇.褗n騧ꬷ.n/o'7G/Wogw/o觯/o HL:'H Z̠7z GH(L W0 gH8̡w@ H"HL&:PH*ZX̢.zȢq'ThSd?u(7*09A. ݉UcE$£F (+RH, &6IAT"> JRb"V%aD:AAVX5 ')A- $%&@шL^r$2{8H2̙ `@:!SY@9P9'-Ju Bˁ +$ z)` &EL(s+˔@iI 9>@ d dy@R0+B",j?)eB0KjѓD`&1){ ӃH@ +9@(48'EpC?)u`bp"ISRӁ4b$|N|t_i@ДC :P?  :` 5F R r +}҈`):x,Lq |bc"P䢘-4bcA]ςL o,`dv,-`WN?Bg{˂6YOGZN t")WtHcEkLPE pR?V!aЁ1Y `RzD'Wir]ġ V oHT!tpx =t+WPDWFi % *>`D {\h豄 ka jSu 0ꑷB& Pi >E:Abz wcXc(! o*k`e89]? dG eN3&)iS NgM#CMIM2JALsԣ,ȵzc Gs jM6SL{I[9( ͒t]Ws-ٞsRHih;M͗#?bL1MS;Js9HAS ?{i5ب%n{>C |@iago0GM.n 7@??}t>O 0b \EUJ=/?ȓ~v/yvP}|1g^Vz"!,L|H*\ȰÇ zHŋ!&Ǐ "IɆRN8ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJ:4Tj-U`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkν`WBS.H|”8W[6Wi#KKGy#1P# V`@zt% "|s=2%M!@dV, p!r ȗ祧zRdwD*K^FQdI ie\w!fxdi̙hjטehuѣ(w"ЀT'%X5_FcE@<MD& 0R̡meG }%)B'(XF(x~jLnq@XebI0*&]ՐB HŲɩs]eG ,^2-xeqyexkoRq2z} Vye&_Ip!,&Lf H*\ȰA49Hŋ ȱNJ ~I䂐J\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ N8Am+W69rH "kENwf-&/\¾}NⳟuO_#PMMH|^G#A6(VYW{e]!| !$K_D%Ru(t(g̍8:Y4ڈ: y'qd#gч-R0 KF0(\TyY0AgYpA TD!^JWt4-`*wɡg]q0?򉥜p='Ǚ!,LX _ !, H*L8Ç#J"E3bȱǏ Cɓ(STr˗0[ʜa<4sɳϟ@ JQ (EtS)MX*5C Ehѯ`zKVٳhӖ]˖eD ȁjT M/]q] /#GÆ+^̸`{,Xɔ7W 0$͉SC^ͺkկcd;]I;4 &*<&KMاcP]Z!nF0✓ǥ/A]& gϿB)N0h2 ̑%#~v _2G%4܃ C"R@]O`9X'G qbCBG5Q `-xP n+F.N0@¹ Y4'dP<`@4 3K4ǖl2I;ƛ;T8%g.~E@6M`&p\gACEDClBTq+J 4&@p4TRB2M9j;lKB=@EjsBZP*b'G.l^ҠTheJnP)5J6T| SpG }x@ cB'zQD ^C4M! pzLPOF 4t"|{?<#I !2ݓI cV`( ˁQ #`,|:Bg5 c4ȉlBB: p+ b(G9 X E 80)@R: P?ȕa!;Pd\q@ :Ђ(H:x^_E!lqZ0# Jav ?HItB XGN%3}EY(5"LwC-~8C y EFqZ2фq )n!zC3Wu@/t 4a;BV@=ҋv$q<0 hb+l9d # df6~l0nL$\F 9S0Ds8?0rA2 >p 0 B-Jb N"(4K"<䱂 yKD{R(E C@cLux& LyMyag p7A fUdyt@  p $x rV> *)5>ofzWFbBZd.%{X@@q vA4 &Ss: p]VC.P5s/BJZ  8P`w PTrK(068g@FG `h3pPU I#f0"8CG" `l܀ ٠ 4P 4z6XL$5&_ѵJ08FNNMHPj}`lBQTUW Q3"$+0$~QS1@t`+`v dp5 nmj T2pR)u@/n  '`o}z 0 'F$fWx+eh@!X( Up8>"qT]T@EPh1&A#O X j# A(1UE'?Υ`"]@S( uihjlٖqsbqW}V'@ y9i airs6'b)'r i(31ZE1O ``3+$_Q@[5X\)dA|p]A@]%[RW,R b$p WVrG@/0"F!UeO&٠ !`2*#usEyM JQ0o` cC[EJBV}@6oqM:JǔћI%"0遘A<8Kv 1Āj X;@$7{ Ԥ opp:>#jTP7RZW\* (hͩ@&G~ZcuNh; CFC#5~'\UVRp@z!3J1x^pQw`O~` @v\WQR2(e#PW ҪL98:>aӚ:9SPD8 P ѪʯL0[,b(H *)1- FP)j`){5r2!%hqL' *[peY+Z0ډ{D1Cp8ѳ + 98MAWifZE d P>CX;0; ~!s0.svk^`,hW"2:X0\E qP uX`φ*ݣ o{@zsvPw[%3`{W`=EQ`WZgߓgU:2btXk{{bZK&pLX|גDEPu0jj"8"0" z:Tk` bPp nH8} pKxLI@$? ,r{@M1I&*=Ww8¶`Zh;E[{ ЂÚeX{LD V [VC }>}QLAX) fz . B` ?e1 sr%&#.c^dfh>ѮСL;1^#* jP e <@pO  φψU ` l'P ,n ɗ46 kꩮj~櫮AaX7]Tm?б>^=n~BQW^у1 >* mƱ>] lt LVS?`N>_~/ o+ώXw0p5Pn  /'$#&"(ʧ0q; -$p;q<𳚴! t yhjيI,rV{[ߣ8#0\ I@v#>?Sk/U>Ow_ՌW-1 [j@? dJY\;pޜ xf>`~vM AYJyxo3d2iC AvZ@cY/:3m\rPj`{q D"@2!p0  d#0 ܑC᰽ ZPGS04D  Vf\8A . !xPbKL8+Z@U2Q)6%͑c`V_$ZQI.eSQNZUYn5+ ;ڭ[!e (@ Z*֮ʢjz1tdpԸ㣋W6!Ðh?Nc#ޕZfɌ!sъIR@4Y kرeϦ]mܹu'k. nzg.B\Ri\TcI8^sp12v{pH \SIJp!tJ] ™@OAtAʄ`~+648첋.! 3"`n0@e | <, w[Kj2蠊*B4 Lzj2d00B0sL24LkDCNJ sp hp*%RCZ\$DCUtQF mL8=P0 @ra$>w~{QNUuUV[E429Z!uWffsA&4X5cUv٧&u6N9"L$i*TgYr-\sU7]v ZbJ \@_~_.@FwЌKDRI7.d5*T@TncCYwz"'\z[& Ka%ҫAGg) *|Gq1wЀwR*Y2MfbnPwzzl6j1ԊfUDBeiY~Bl[?p9 SiV,F\d7(q`'W}u[G0u4|ijφYNA/%vxWLLհ`Xn3~P$D{osĆ*{y7trf|Gx2e0$IѪp%1®veP`'6%VՌ`rdB CMxBw9 K)a mp^8 =0(LNP]Nbx#"QIdb'vCX*c5$ ZbxFb`44ܯR |- ؔD4R5܀;aA0*ø$@" r =I2Hp R SP%6c'AyK\ڒ\HFrr#gr`4jK]BSKr©L*Vp K s`D8Mtӓ|gP iɺU b=Om1u5h4 UhC6|Z5zQ2Xu`L hIM 4-D =6(ķx4(ImzQ ~Q lY2&w6 iS5TڨXG7!!,ˤP1TU!y=mv,*? dh]k_ZQ (K)׹v蕱|fISaN*;Hx[6eĝ8t軨AF9xW5{׹G∁]z ZRT A $U~H@A"@ aRct wh E$FAbQ'lI;2=JJB +ӛ) dxa bpD35xģɋ!f@/` DVp *P0'8hzPly A|2D/h<q4l3= iD@:S@/@9SvoGUEfр*x5plTըCqm3l@HD8اo q{oY;tr$*Dh;Z.M<c"`P+Dч4 _#0G0!$AB&H! oH (|4a$jh1Ni(D1A  v\ ĸ$ S[92j}DH3ƌX 6^m:#gT /Bp nqN%{҆؈NLx0tӢvP y j08q7@ PEj` 2`XX&4X"]A JD 4JB4z@W(pTt0GZ=#a&j9c j l@f_:yM #.Q8 $jXG@ X JgC0=;@ႝ1b)#c+؂`PЂ-Ȁ<0B,w* XK`A@8V 0LDÿ89Ct`^(#9^C9hȴr ھD2|Z(Ʉv8D X>,aH@p xn@l;-P 4@dȔI  N&/p '/R8p_K,[Ⱦ^ܓ-l$+,\,]@=> -̀`D35hG5Xp4;[ 8<ip@Uh-lM#p 7Lg"KDlkM`.jT*˄PsM`^IbWD/&R ÿ:EGa, +?)ٜMڜ%RZZ R1,Ē%/70M*O4) ϶IDQb0'( ! PP`] #!D RH1P]y12V Yd)xSЂ А74l̄R΄@| eP$%I`X7ȠXP.{{[7>P;H8+`z;T1$I B JpB݃82?RSJEƱ nP=U :$<3#x(EsR udvJm\ڙS,A iY[]W-i!hK8ΏRIUmE aTTZƢ78dI=m4ZbĔ9Є*ɤP-3v0mA7OWR}Yt VX1ط NHuMY=R1ͣ@kW,Y0<ٙYm޹YؠҪ ËY]ڥ-F!v2K![\F'ur*ycP-> x9(V M{]o3B  -I*\ PZM^-衼B i *= : >xIC4؁ۻ? hbY^- AK^hHEu-o‹x1S&18SX_]_R6S.BK_AXP;ϻP8q]-/Y;^G1hB`9ӭR aRɉXK`ՒJ&*sWcbbc6r 5N65E16a ;۞cANA&B6dD>DEn@vdE~HdHdJdLdMdN,2RBm-y$ 0e@NqMdVQI]e_e`fafb.fc>fdNN^fY  P[b=RF)PX- "%P/hXˑx fn'XS083*@M^f懆dd舶hehehh萮Y6,a7}*0vd :5=8K(,0gCCK[PtesNiJ,9]8S`p^@KxP| oihl>ik&铮[kkklNcxaxdB(PG!@,m2i35aS&lp4))“F+0&lknn~nnR vCN3>o8iK=4 'x2%4nxxxpx6X_`kppnq'梨$k22hJaM ?,K8I? 0sPh HRpX'=^"n6xpmgi.q0ss1/s3?c;y #: uq؅3P2 ^P XJ[A?Ɂ/rKq`6BXCB0mMLMZ4/uS?uTOuU_ ^ G PrZdEWbuBVrZ.&s`vavc?vI΍b_vfGvg_vivkOkfe/illv8o'vr/ws?wtOnWwvo*-ufow{w|w}dD.+Zӂ#mHZ hEPCNx2gi*c:vP>Жw8R;0_(IpNJx Hځ8#2wʢxhVS Ag/5Pox2ӑ[U һm)63o6j〆# \U[]D=z+=tS7om]Qm>X 8pX/«»)r 8s 0ބp$ Xa蒹7vxN]7-8݋#oPGHޭ l /sTo\nGx (6d IB '!4\4F"H gСJ jΥ)2]/!,i H2w'Р:ChQI2m)ԨRRj*K&Y] bZjײUl-T[j0 ;W@JZ$B?g ~kZvdҨ,Yήm6ܴu[Mll`Rv"]!\R@K 0NB?l] JNڵKLOdu\"9|$SĝpJZxaj!Hfnpb&f"a)$P-.4a=#<9$Ez\'&)Ŋ ,$Qe$Yj%]ze*>Z!|y&i&m$q馐uI'y' `hssVF2ݹh:(J:S&Q"9&fpu$RʐI0*T|uEI<2mJ(bߌj#J;-;C⸃2Lr*(Tjh~jh2  ph-/"DW;jJ&rb9*EZR/4`&vif1.L8:Wd'\r M4 sBjdyk3?=4H|AF5"@tOCK@q)K4BwױDcOgEh PhF 6P;[7}-bɰK +8㍇v>2z;9w yz>:a(衫u:뭻/]>9nD`;m)+f\%CC>=: d:RE h$(}~PjA:Kc2+X ,A @܈EQ0,tBX[`jF CHG`BVPczC &!#0asCƕ@| 渰-jku "!Bg%! 2RC9P% @c"ӈþ@P=(BЅ2}(D#*щR(F3эrX*;*RAӞHS \Jc*ә5)Nsӛ>y*ԟBE*RTMn  ӔUt*")n[W"K8+&raVXZ?׹t+ ZWC| _e 5l`i9XG_p,`!;X`40H  rV&bA[X cjSڠ%bnV!L 4mU[vMo+q.\xe3KXJ`r֯>P:h# ,֕cYEDb}Vn@:_Ws]Ӫp <X =쁏 sx&E/8a#$qa(KH4ack/汌!F!@>9jpGȲ"NihD'nqe 7XEcMKX0;޿V,|]XzuV}ds0Bָ0HAtl걅 &׸unq}]ֽ浮}\`ؿn{^:n#1tWz@f-woZ-LA3{y^&܉$a7&D 05PlF 0&`2B/Qq(y =~ӑrI\'sGas5:pd=y/*<1" 8D(h  h#*w҂fP٭ " X|,P8,,k 0`e vsO4xnqkWA.`8Au },7tDa"CJ@>8PJ !dHF̡ }OlSOz:c<[5@ S(okCӂ(7֥vA,  -]B0d 5Ԃ"01$Y'$|DĹYAYAI`й̹X:C54C@ЩEʘ $A˙C2 ȃXX=ԃC:ȏÆ!)<@X9Cb:4:>}͕ B,$A.I#K+,``Y !,؁}Aމڔ@\cIh) 3ڲqAY8x]&BAEA |VR[I'FA1YD]$Ԁ 5$B^0_<L}Hi*z$HsM^7c:c`ś,c 01zb1ʝ,1֢`-W_dy^`~(]Y`] |(x-B,8_YYNA& ݏ)!JI:b=!9L4cLuMfes$L VPtfO]`+_]@`9 _Ad|Ue\ҙE[,A@$8!?bM*Z)Y% ]X劕Z$-Ǚ $kkC)*>4A *!i+fa2"B e.Q#4ﶖC)e.늫N0[j$ B0L^&(A W+\AP ]'nBi@uŗ)8 Ud M^1VD# cY4,pAPˆ-㇆D/Q)%!B8@V*B@͖0`-W isuqr{4 $cp L`ݵΗ_vOi0@ f+=lf#DJVn*A b1<A zn 6[Ղy(2j:a2\=/:N=j>gʋ9 P=!!C:!޲4&"C r20s/4AQa9h1ʅQ"Oh ,`m&+d["Y>j'8̾W}z% >O4Rl4 GAni! %-HnO(]AYl@HC y=(\KTgTD#a4lH)PiP\-zihDX%~e AD@lBj414RNӥ~$!a_tЗY%' 5 xYHj@34#@pNkOBm 5(!Li/M 5(,C:d)HzQ<@ A6JVi,P2TbKD'+#4R  (a)@ E-إ%^f(.h`BBb)I:5&v%?Ø8:=tkJND4"ISGPN)ܗN浍h.bi'3HaĔM̤$&G ls:5Ʊh KR<% Pʋ&X$#*wI\d1#1jL[/̗!ox]76 a 9,mALc 8UBbI:c IzrHNpB v$hς4"AB?d xgL H f:F7:Iԣ%A'@ᇈZ`<іXJ(,)_ZpN Si&ʝ5mEġ:Tz<ծR]̼JVnˉrYֶԛ"cnNV@N+J؍"$Z|drt; ,(k>4YϚjؠH}V5k\-p[LTkp1\ZA2lvmXcnsLDu. '75x!b(`X`r"mo3e' }L"&* `;0.%c =T{J8H3L|V pKⵜX)Ngky wo@nU Kjl Q2SZU@ r){UPi3?& H]ۡ!,ZyӅ%gx4eʰEʜARBVXC$ (HdҁV\ZӠN])j*Pp%e[Cڑ!`zk/;Hު.A]I.6{Z"ԕF(@ HP6n. .VnݼV tJeBqU^ !kWJ$7؊s'VWjȘQFG\Yf1,O%SP򚷄2lSėh+[S:@lFy-T.Y6/7{Bz`vC*K\9UY Poɜ,У<ԯ֨}հ=14G|s$GT(H~},).7i 2~K!@p?^[=mM?ow X]::yƇ~('";GOk{S[Ԃ4^^C;o>.\4cλOO%`Wg,#kdpbD#l^!}se~yBw&ĆFx35S§NsSTwS{8TDD%L(X"(L qHyGdKy~9MWyFS~Ex~&}Ge-hC؂B}'OǷZ`p`b(T:xImvVr z3{mHh1xwS'Z.tGuIObtA#@ PP Kp`AسЊ@_0xhȊ节0Ip="@xX\@И4 *48* 384740Hx؎HH"؏(ĸ؏<`ɋ` ّ鉹p $#ɒ..IRpP6 @@6ɓ? BiAYF9P/ 00DyTG 4>i\Ұ=`UYC t5 p 50s0Rt ) (V`yyIV:X =sv@Ћ`H/P ) KC)w1A 2pD88xYיةɎ:ɚ"obV\yb0Ћ V@ IЙ YYI$ j (-)b~ V) C ZPRiB9',:`ЕP-\U) dǓ8J'00J qp_jĠz@ɪ ا3 q iࠒ)& ePQ QTʲJ覌SA3#"| P~p"~PSi`>䀤```͠lP8`[ IP`Y[`M0N\KPC0tF4 "00GjV)DPl[`C~[@6ɤ+|@ @ ٠|p ٠rϨ ~PW {A%p* W` Аk Hz0zj9<W@z q`Y\P3p5=qI0z陱ɽy18oYP c0P=fMp @ C8*%toM 5I`p JPX8;L;zHq+ 9 t#G  J\K Pz˱hۑQ{@ XppY2|p!| <@ 0:J{pH{H 5ٕB#0Y`зj BZ p{0 @ `&0\)t^I}2`h0_۹k@DIlP R{ `Ã2T@{ tMc X_ye)P D@A(zW`_0B ] Rq : +{0ѫ* oƏp @)c0`#-jڎy,r/` k@NQ`Ь2E EFڮ 9t=90Ԙਸx=RƋq` ȪuP+_s` 0ȧI@I |  )3 ~9U}{/  _)QO gƕ j@`[y0R`4 V9N 00GPތ֝ @\rlɕyʜ\v7Ђ ZJaZtpф:4`6`r9 ɽ?qڊW8Ji4pyp"Pn,sy*ޣ:#-C=bP=5]%}F'|*urr-88- Hދ2+@)J m`kS¢7v Q:+8ꈩp<@k"ɑ 4K" N6ٴCYD a@$*-j:ɣ@n+}Nɵ@ΝF0 0=<彵 >IZ/xڏ0Pq:  J`С?z ;|_ !هIv~%ڢ)4z0=:8I1&{7 [ Z0ծ` 1  >' pR« YMiJVszOpxI݊q@]/Ra| ǛU 1l~{=o o0EX@` `j Zȵ.u;O>i&`mˎm[ j /`;NQmF Yk=6 $#/ݺrAK<{ՌÇ`9t5nhNb! ԟ\( G1ЈgS FkبIXTÇGZo MKNŪ"X 1L,Qi # 3da:w \x@JL[*+Vc*Ioj)x`-S3|PLٹ>bMFBZ j+$Кh <{l~\ ["K H\d92w 4ҼׇEYݞ{=oM߿zKG/C4ʢ‡hS紫DB CDF<8ДCأ!7># % Ԫ"\b.(j.Jy*%*a5 r ?HĨ2*5+|M7٩zNF )8DJ cJXcfxd3@ &e[veL.w@{EZ(LI柵2g~wrrG8yLHAj0ju;qFiB̩3P j&;T6ӶeSW6]6no;otsEm+ĦO j҃nf q_}Ygs7#؋]ad9WڑOno?@h*׳ixc9W~ҥHl0~~Mta JW6nWe|̃j\Du'`# \37ypq~@)inw F<un\Pw.xF+ǺNAZhߝ^>D9ZS -݁kcp1. N3W᐀aG{S=*%Nd"HE"|8)d'эb,#N)P-b$#)0".@'fʾgF䇖dDc]X2` 4i@0~f 9MMZt$ Its! :# Ѓ9$BD 0Ρ :K = JashE*QJ4hHAD1ьT#5B) 4@@ $ꀋӤ,iHm*S7iM쀠=LwJ3A4U@*SU1=*U%ԨVU[jVծU^QP ZP+YִWkI#PW:%POUBԧH@:DAc/P7 cjP!`%kVJ%$+RI@2Z/[VfUS*YC@ ApVk{\"ensŊVmsźT pfH.`1lոFDqgXCָ6 WF6^ MT@Y @ &t@?YU`V UA)%`v0E bG8vJ`4  L!āmj`W@CCqۙa k ' dx! l8 ~1B АߑQ/T ᢔ`(  0 9  !`:Kʒh Qe)SX($\\0`|֍nI[(Uu]m]ajXCygZc86 l4@^na̘/ 4aQ"\VP$TmR GڕK2RP"@P!` ^ mxW D}hX lA9tDE%u/VPm 4dM[T[P!0=HU f0% aThbs86.o4/2BB/>4l]xq "O*m†Kt7NslEsq0\9pr_>; xċuhakrF dT\'ǀqpTn\1|+ BGpU U@xfKp?دe0> XJ7"E)s?@)t煄{ qGGа8k UP01@)6Zp0R6P*J*1:,6'?5c23-@ 2KïK<|AJ5,ZQ4<AV?"#p'yM@Bfpj(X_xY7ЃpgȾӧ[ur~|Z76=.)6 b71c0PX1==4<2 ꃀ#,*p14P`]$8V# rNp)8*C-[X+(b(^PL3@"cFJ634C@]C(AcВAwGAz)lGV[5p}4_zjp+d 8 B(da*Y(Fda؁zDŢH70r#IU(6 I3AlE CK0FD8 lӾB(2?:E"04)pJkt;PÏ;JK^ȹSthRS7:bJ 7c(hSA˅7B;tJCJ4؁DG(P7kA|0 MDx4T, jmx a؆m8klxx Ba_ӊc,?! )HSl,P`$I 0B089zE)3E ^H([QE;P[˲b,?PNPh`]`7P}Q,LL2n [~L-R/ґҥ];P/2g1.6(3h8<[{,5uxBέBS-C(RP z8SKTLT:͚Sע )S/6~,KԏbTK8FӒЈTU2AU^ՂUMuUeMJ%*N]V_mVy )2իhVoV*TW3M==WrV,eVv}vCY0cb J*s?)1K@10K1J -c۵`Rx :O聍YX@0Ѐ@dL@Z-G80!Z:80XAR!K& _VN+$8.`L8kȻR@>(- 8l\&Fɀ#xW@he*e2TVQ>xcFf UhW 0Z*w6O }՘e&OJs6f0%P-h& X8ee`V0ednid&jmZ(]^据 ]g?xu䤞[{gZ#0iVL#0h`395mV X3 @ wiaj Xk 0joc U8h /hd.pj.mX+X%>S%M؆lgpkj $_& a0hUg 9/ĩxK:E8l 7KI8x;((P+H'>C>L >@;\nGfd 4P.VWeV\]𻲅-. `)>P! zV@>곶8zυ p He:PpDp0q"p!ЀDxp @$(G!p`#w |nq=؂flG`q7`L&&`q; 0r'o\Q ZP!V0,b\x?7YqC:0)GN jK_p-gM$Wp@6DŽ3OsH@+(p-Xt_V`&2@![ rƞq?Ё:#(h@9p1G_pK(s.D@y1s02_t8ht(d<x#?5cPOxG.bЂ^uD[@۬:ػ@X8a,ff$8=nR'p :0xzC&=a :XHܥ;lψ:H\ [ex9L@0K)߀@'$H ]\bq hmU"@/PxjozfȀmEglXqRd0o|R_|ܧ"|xLh |/%KB r HZߑCK0}O}o!M||N5BH$Jd(f@G^Bpa #xRօJLj!„*0.&P1>"ZPA 5P #nNQSԝ %zȳ`D*BPi@`QJéUܠ-) )RLiБG4`=2H/JtFW’S$o2D"f _6X]PTQ[.6nw$l`v"UlxE- AU8(ܰgJ"UļmߵϤj)!6z oQf:(b 8"A| [%wQ`D) QF MASI#ET L5B/rh)D| ?1[;IF!ԐB҅EHJCPueA49(xO>A=%`I i1`@1A^pB;D!ўC)4,-tYJF|Qh)+29JkN}j&$Ы} `tHyA?9Y|5ɦjP4@ʤ@xZ,x{/eHH!XAo4C5`k -^I / C vfJk <0{MmH9bpb\Hg߄ 8=/R+D6"L*I 0\*\("}O/*@8@%;\w (0_PKH~c:A ?ADz) &ჺ Gp%pF+CزYAD$Ep/J DVd@ QT€ӾCrebK>eQtiN|̎Dv`RBZ"ʃPH{~Ng)hyR  gŰY.-heq 栔,n3Pٓ4=Z(J+G‹r3 \ Qؾ@S9X=gED'J @X5RhO.О 1*K,Xȫ@tD (_>0Nh"))H1hN &\ !x/. LR T 1!"!ILԠF\?} l"0ak@61 ,cmcl` !n")@(f" (!i3@'n'p @>d "?S$H \` | ZQBÈR0aDZ4pu:u J,)JȔP@!ϐ5J@BPYJn4@'@VdbLUZx+Y`I֢ 3% R0ҊTl -s[t X@, )OBRL(la u #7l-< Z%ehbYXD4 P`LVe@ atHY9U5@\pRiӉ-U.,w {>^EBܻ\<́@ \v%MZ/VĞX44l{V%|:#d"ZVVZ[g_ Q2r;O TB`CBcEArd- ]R8N!N@0f`Hv8+㡳mf;IC0M$:$slEw5cr|Yځ̖cC@KӪZzz(J==zΒ&tc5Y6u[Uzyl,A'1װ^3]}3>i ;XŦ~|eȫ^|(ӍmCz.SՀ #ηGkC p  |~=kGVugoO7ר=>="qb\.ON 9'W[*~s6Ǘ} QV/3Nvnvk[V7@t<yʿ.+{v}0_cN񷞝mQ]yW]Ji3g|_D<3hD冯:C?g ?xS9t4:6GA-yƝ7ߤՆH:*@YYoMW(DvA%ґڝBi\A)ɧM`A2Y`t!XD V ^堇D \% %_%>\%8a)!˅[@"؋ixLl@Ȁ!.ԍ%m@Y*^qQbYB!`0!ڪ "!M (`HAM L ޴a_s@ @Nl`"\ ,( #*#=&< (ȁ>L QNhcM|"MbA8~=F Hΐ݆l5<5E6\6 $6T-80,t^G>bDt!Y$hNntNm¤$O (X)ԤoG D)ԀȄq D`LA $P1 A-XEW|D$BHxB A ( x[ B pBV%x@@| tU BY%WaH!cb` EU^% |Ղ\ %tLA @J8Bh1SN _ ].,( c`'e2%\VbBd)&p%EedS撞f.Q!XgBeKv:e{2t"BiW#TcnT~l$DU(|%@ePG9]GuH%U5xZVe@x%PQ(v'{DLqfe/5[ =ihaI@#c6D6XBlCh\@dfZ0xMDN䈌n\%8"NzMaXΙ\}x"h)B d @(A(x -O* X(@ 4AiWiD4E8 p# `g%P\bBF%H&( 4^ttFNN 88kT\bDFi|耙0ApBeN4)01* Ppb 2P t$Ւ*E]54) 4-la!ct ptRADF%݌X WJx$(5Td@~[A~[KPp|e̳pZFg+"MQC 耐&h!Rg+]O*ǜܖ_$b-@.TfO-`6ɴB 8nj0i:] Pw25 55l 5-67YG8&<$ar\J Г :Վӥ1p؂ʱ/@O(?-ʰ4!.2(ײ.A=;d_シtLC_Hr!)x]=/@&L'S_)HЉx }1B$Dw eK(p!nH ǀ-ďHx䛴HW@%BaH6pk@6^L x[46 dnyNq ("vR P5@@8p#q؜aAcIaXJ b ,HQ Dp Re},H@${VFkA]4@.A@ӹN*C\:-"gCޙ @EsQI (}j5rAJ Ӕr,e:8=)VCU5 X ] LXuh*wHg> MP)n)$pbXXȺ%X! QI.' ϙXIQ*X.gzE׈<= -HBZ׻q-n}'PE rP(,l flQ!9 F8pܠFHe0.|D)ak 8w/|N@:h@ P]!k%F0^sypR`=2Ikx\~@ ` 2? ZC"B 4B X ] \4Oh/؃Fq:dP&rőaQA'(PJv -RPD*؂ +sE0̅M3&O8 aKや7TS]aybL@@ɎPkdes"I#SXa h! ) p3іv}YJT 5T\V0kjčU(.ap!'xm{Ef{ŝ6V:;b)|p)es 6ebr8-F8nyBt0.\]-1K T^2O+r=bfàt,DFr !4 Gp?}4㜋@ h|`Y@ aEf׮m'SLk}N_/}k<`8eo0upYAU#m}߂"_=_|ǩfpaʵ0QW`#t jmz` D)hLm[T ]bq! 2c Ier/,ث|`Ѐ >0Nvj6`8n.LI @jL`\Nh@ % P@t6`C` d0y@Vڢ). ƪVҮhfbPTB˸` j@P JP xR2@@L'(ˊzat,nBm 4n ! T2/f@!X!]J ^ᴾo(!K @ qv* 0PQy<$d//d O!MAAo!! kd0.k@b,` 0v[~ `b0 b b*A Vr, ~ v |k!`O0 F\-J%e@"H Lp%haF\| !(`'OrԠ&i R&Y`-F@Xn@6+ 2,yL2 |a< *Ȁh!|!.WR8В X2,F  s0@J PR%mR0$` r0ǀ/ hsN!p R*-Ml--18lA7\,;"10/C`;r/t t 8#)j32*!)t""6d*4X:#N4|$&:Q"NF7$J$VfBVBF_M7B `T3B*p'D"=T=4RB`BIc>%?0cސKULKD@V9E(c$%LNTcj=SOG N#4OsJ #AhEQ%S35L4G~!TCUTGGԣJTP5F-R)URaUVTc4VsRuWI85GMOR Yӣ-=FU]5=zkD[BD :BF(U}5!dJVtXT-"|`). YDCDTp2}SBh'5'K_eWk3H\@LNϐ?L na߄2u;|V/7LC %+NNeSVeWN Y`/" 624 K)d]!^% 2b_.Af6>(ZB FabzKI] Ն&U$fk!v'D,h4X! qIA3r2%p5Zda{ZT" !.dBv'Dt bm Nf#[|bz*!~:/!cDcD#2 >Lb ;s7NrC kkfl& %o& F s`'{j !/ tsda,r\}#uvdt4Mg'H } J@#Vh(`4`|hҬ:7Ur`f a f 6x Vx C @PH H"{Y: K,6'}Ff)K Iz4`pX{f g|.X}pwTnWf  Z @V5uV@- %$`l; 4 gZa GN ƸJ-*g@%eW[ٕD!Ԗx UFan`:h >I)o`l@$|5 햪+N7rF טN @  Nf@wXZZʤ!5"ˊJ24" #` xx8 djr}@TZ"Bj !$@j\,`j*]$X PhJTG7nj a( n": ʪ1AT}`ˣ/Hj 48+Wa\Š vz( nX4V+ p=Щ " Z꥜jy*k ;F^c[O(tstM /f&YN+-@5⾷Ub"髿 8"r"# ls, t $  N,-#w& $z7-~0Ɗ xK1Ǹ,xQ {)Kc1sD]Q}TA'rg(tǁՌ{ l Zll1Ŀ ئBxyBIPfr@O,#` 3`Վ>v lcJ ~t,P--=<Vzl =(cgKSCո!`M!ݚ!ݞxw& m[;YgsIz-ÂJGb rNZ7r;, Ծ_Oy=!Vb.$ x$7̝Z5>~TTa\R/N9s#~]vH v@vWj//&1d4`"pa"Orv+"%b"P,;l}p.=⠀/r] 30$+' @y ;R %B*!V b~p k<ķ` )Z{1Q|n"*v>P% x@1X9FyňrPbI9|tMp͝6m,VР\$0cU xP@I&Sd#QT BL rȔm\yK a3'>pqpV&MU ^JNTfD1]I((eŌ;~8ɔ+[N|xVs 3!h Qʆ j®m0U2l3FԢٰJW$Hҥ XPk_!7\ ?:4K2ӄ Օ_vSPbp0j @azR%t ?0}d{)!E50a dP_SNlQ(t +Jp,(C@ d#<>1B $IP G #0<0rY0@U^!N\ `YJJ6B.`bv쐢ArL 9"}ܰ& D~B{(F -hBWi\% D/F(i m"fRh }( & 6۫H I@EGU+Jb4BRb=A1u8 2RFIv%7Zkk~'A pOL0d)<饶u4!@į\ op;|Ƞ5&sLr*Atln+=n.fź&Ibs4AX\@JvL&H쫓z!RTw̜ t&i-̘Lvu8_(SA!mwdFKжanG58Ctݷ?3NaTo'^Qn%ɀ C|nvug{~wm 0hg 5mS%ϫ-7NqӜ;UBq~=;ΐGϿY~䩣~? , B=Al9K*]Rr<-w`(Ⱦ" $Q@"0A`!b1fՋ`0ñ ~ 11  <]p|y #/&hAfŤab@XFLЀG T!Ӹ7q{2a! r$#gHŋjW ĉ 3` p QqGT*X I N.ԛ@ʴ`URi 2fČ:K R)q HVTZI><b$)LQM4 %]e68+nw4#F5v! xctD@hPPJ !LG (5B@%H;G (C%SR(PGQ@"9+C%M 5pȐ6g Qe1 B Kq ہC4ӌ`\"@A҈n%.U03PLK)FBaYȲJola&Gyu|Xy؆5jll8L6lS^Dt/ d0  Ȭ6["m*BN V _ |ZbPjaءЎg!`h'f4!lHBx%7(=%[!6P׺OZwS2 B0XA e%>n,`"|{3Ç|!B,|M-o)r lB{ SPq `(I08bpNw;qcøJÖ7-&?BP*3Юv`>0X& ut,6@ёهmd#Pa_`#4F*~X2V3f f,J-gPIO )+MJ u5>%4f TD` UG0)( Ap` +yX`ʯ@n<@ d6|*Nz`݂qj1q"z(#FЁ7 N  EH9 `\'E *1Pv!pq{,r(A-!7B \ [$+<.?S*tLot {AP1Bަ176l=vy%,B$` B0H-#R@AhPp/8cfMR I PB̰7Vi.0cpp##1&k nl(  90`];*6{!@!~DHBR p #N;,lVHE7KG\ +K ̑ \d `u JpD W7${S._W0 npuuj!;qv`WPx]b J@ LXfSnSS@drng/ d`&)[G=p'f1wuD1ۇ@ VPG<  cF]0( 7cP,"`!yHAvRP`6gz PÐX G{)q{0 G;fa|h}h؋0 s ҇DT\5`:)l"lp!3kk5`kAA0^ww5p0b @/DF M XH,! bF Ń p*}j0 ;@/ru!X@M0~1K)U4'!)0pF  ip4%8b6Sq0 ;[В:Plx]`G*1)dAk - v)*Q1ZIy &dy#H hb!z Ӌg!z% ő    ‘ pd0 >@)P1ǘYmp#}G2>z%!fHZLJW\A=_ Q`c .Bp\A~ e- %  T8YVTP1`PZ0-?W6S,БsVNID0C *3PX3-H e24O m2dRO,4y MՔl0 hؠDP xA1 w' -Dr.pq|"8JQ ^gsJ1k9  aq ᙾaq{v@~zJk; ? D !PSa8@~0q:~޷Bۖ"+ЁFP))5M)0 &(ki` C,2Z"p]2't&rH#H`B g @"1"'1ŅÓf2 P.J ?._2.E`#'"54p( "/}p-`rl> |&_Ǔ $s  @}C (24sovuBiji@PCB$. 97iUWvaTTt$w>Q'EikDSH2F!ܓ@C =_81#aVŀ6Ot)L5Ld6.175Y3x?5Y1Es6JK'J ƶg `]H;z}3@CoŻgiԹT TTIU7kMVdfadU˽ՋP Ԑ{K{]+m˾k2VswE$C{FrRALeeV;W5tV Խ ) @ l F۾<8*D]GT;pAѫR_$/ܿ|1? Ĝ³ 11HdzI )}I01K)Rbd%AT>ԅMN1}U'CC`cNLsNjE3d=7`XDcē4EA`G"%qjBP#1 L@D @kQ! :EЄUc4b5N+PU1>m4~p; `GlٱJTKY0Ǿ2;$l;/mK|1P:`?`@~XpX-Xېz&[ 9j&J|!? y˩`!m[ `e?E\2(\˕|Fz %X]&*^cb %o u]p+"0fC]^F3 !]Y H_13PF`_|yVe0CP aX]]]a!x Re1vgp Jcs.V3 d_{gM]bH5CVvFavqb)F  ]P' VM0aU!V``d0^f1>ܤKl|^ 4 , iI{6Y P^@2jQ kħvk I:> 7W ض Wj pXh ܦPwR`$@v[П/ JiQ pf?nIn8x-`Qwo wu hgc sZ=srgGQH ,ՅI0p9xV rq n7~6&0ȌxZ`4. Vq7u;obawoDpu8x3Q.O%`uoUJ>Qrwv'r|dx0;!pq;q I-P sXk$h(P 'X{ϐ/|1R|-w~!`Xιv~h~Ɔ3@3#ErH``wI@d_8HV}Y\h.@j5 _3o3PP-3&pVZ<)p8dX[[(.UN)[&,Δ9s*HwX8;QZ@-.ꜧo@ w X \ 0]!``Pp f0n|y}Ѱ.}1R? l LjʜϤN|GMF+ɎaIbrF81𝙑l^enGE.'Tp`J] op[Y>XPY9q86P5EZahx@@>\Vu2@(,"CW'Dj+VxǏ;EA2Dm-S)YL $9rK,j^lKƍ:U^}X`… FX 7EYU ! 5XEV--aR af666KC?P\g;\Ba%b80=9eE%@Y;l BsǎsR̰Ȫ~帊3c"~h@ d" TA Z &hH"I&5`b#@#s`qQTd `BrJkh o .`Bn9A |hB S|@^h . .ި x W̅)|B&p2U@` 2ȑ C" ?& f" Y\ªZ2 P4/Dz 9 ȏLs]8$N.,҄JH>6ZiZk @S&o.: &Ya jbm&ЈUt#nZ!\.=C~7㍁[*ɁJ L"p;{H~aZ5Jp…,|!&X\p$-]hZF5Bǰヘ8@QL#k?d Xh%9vanDZRP"NxDXA#na ?Ҹ!k$nEN@aE NOgiZ4O[p x:Ѣd:⏽t?Xq $(V]57d PHl$2wj`DP%V ~npD<d]u?<)TKMuMtӧf$|)UAa+\$[շ5üLdM4@h PҺr(#/ )TF$wiyK3Y׼!j!l&EtSu.v;d"ʺmXX_6^J5l@&P !I uXh`@ f#YE j &jC CkG00%B[P!b@)Љ h)Zx"9k@Ѐ9( !fYL(AY8,7(7B Ik9YJ,pFp-UBd- &=@X5*EPA$~b8$e kD`|}>ܺ_-Pxm#{0D

     X!Jp3Ѐt2BE|z.|@M_JI Dpx.r7 A LX!b9޼0# 6"Rg [0BE0\py`BB(3\#b j*< &q [_8~i_x>A " `B:>Gc. \+[Y8G⧓<ɐ$@XxfՈf vgXA0:J(09!!ȁ (́Z@kNC$`ٞ!_9؃@ S,/bHX @o3 Z NC[ `fRJBDd{,x8+A"+H9DJ)#( P.U$ؑh)E&=RcEG4] KF.P,硄_zkz.'YudG¢OK$ĢT@xXȆ{*8AڀLog A3;@;./n#Es #8ɀU4;#CC()!-0M$N-p< `` 0E]jJIT>Q6LDS2:| (%5(肱 Z,8$%D `JK|M ,7BHʱF6SK(n#</pbR3Gv$#'fWl Xڈ 10낹;Éx/R3N&\9+: ",H5<4|;37;<( "`K@|HlHЁ4I0$-P+ L#L!`:P;y{QUِQyq5POdH4ɂW"yk"@S$؂9 ! AAQ.0=0K+!TՊR4 9KKGڔ9w AA,tQ(sm zi|!Fha%1$Bq/H".lq hXKqy4ԏqٙ(#O<XSPq()5(ڑ I-@I9aVȘ)s_8W˩V21ѩdZP}}0@WVlQ|UV0% 5Wl5X$ \@8)HjM:Y9H;UbZꯦe!;KG5" xB&"&0:"EB :B 8$ aʪ3Z Z}A:Z3*[( h1ٟeE ;s#U[ܒÅ\ɵ+ܞ*[+1s'p ȡ+] #Q:\u)xӚ*^E^" AC-.Y" Z^Ν^}^\^Z[8:۵R\ ;?Rܗjm.!MݬpߏYԬ^Z^," ^+~Mgӥ`@j]`u$Ru1ީ*^%-b!BJ0  "IP^ t+8j֫ #"j~-](. 5Ua֕\)6*>"$=cn\+0& J 0/9y:|M" 0b l/O j2 +K1"U7+6 L_z @*5.g!^Zߕ[ef3VLJ`Mb>X^óܔ H&2Ȇmk8 +SI#$4Sp3`<-!!(C)4414^LN5EsχIK{G0L,v $50( [k7#2\#)UJw#|#?H7_c?mu!Y #8^;i|8Xh˷6!6xiY)`Sk?֬f>Zs.50-^[?(O.h7.iis+5&l^>$^VNcY7i 85f<6FHElLXlJ3v407 h`KkT ȹ;N~k\yKdx9X53¡PP9#4p b3TիR>dM$JC= -݋c&>" q>8q[Pó=- 3q &F WfVQ 7:˽@qq ?KPL[֛-&0 L *o07Ȃ^@=P3 ?ps OE)=s3O?TP+rUPrl&jt-LHD:ދ@V;qVJQ7-50>\hB7??0xM?v {DAX@h\ K:g JLг&doY}3)p LUH.l:×$3"UD ՘pQx Ё;<e^5J$6Cf<Z%UmRēk؇ m,L$2 DPd6r%6DZix[4!1xHQi FS <ҦK;T# qȀ4)paT ЊK@y'Ѷ_ƤC?J? m>@y2@Q&Ƅ hBi R?@6(_a@p6NDʸ$|Ԇ ըbL$ I3&¯GSx3xUJĉ ;cGZQ#ED8 pmƅ̭=\y@A]j.Fp`ˇ"*A >QP,S\ r$]c H#/P0) t&% Ty"Ė/%Vx :*ukEL1lJ- Jd&4@0:uU©ϕ- j @ᖺe=Rcl9rZ1G}TCn\-wޤUG!C2)WCx"9@%VX8t$-qPsC늠֯cϮ};޿B̟^ E \JA}z~VhEq0D }l 36PA0Ѥb`#olnoboւo//o0&p;/p/oA\ւ,@ l*b/0 F/oR $W: _dX!ԀSpրo*@%)0^Ao8q6CD1 † G 1 /qq1qq 2 r!r1)p3?or?s@ 4@3AA4B't@_p B-8 Ds%A8G^A0@6rJó <AЁ#B % q0 o'1zB1#/*Btt܂A>NNKt-3 $Bl5#RA $9T1 /+ # #A@L0#‚PA G+3 uM%{$/3qA+ecvfkeoBwg6hhs6@p.Gt_-e6 *`!3_òF9['p*s6`G1,ACNW03BDU/* B*ps|,<77 '2<L//K7*0|G1:!xIwj3BC, oSW6v{x;0)3 u̴,uMӂ+[G43 YU[!{B A|#s{-$AOvz^rupeK0pb6.K4.xS|!|DA,@)3y,w:Oπoρb+8u07B/9<\Pchsxg.,rw| ,K+ 4CR=򾹥?q4uxzA4u 2.TG,x17IB1 B&'q DACvH:$w%w^ 1G!xP+4xz+k8;2—3w.w`GЂ t𺳶S1k<̽1yw t9msK!lyQ23@<]OB%sO$A\'8:A&&#*\A0pA!)kwG9(}ro,O9?7ԫ/1ͿH pB ;KwKH7#*?sks:kwss4c9V?/xױ3;BN>iWNu灸 oC6 #D67++??>t_1|p7No{7+u#r?˿>2+;;u#"="'a ÿt '?{6"Kph#r"##?p"K0"d!(7t7t}pS>t;|2??_p,ӯo}, t?Ww߱SAzmml)j~֯Ն=j[&..ˮAʮ:>ϮXmNnI覀< R(QЕ%[.B;ˮʮʮʮ{ɮˮʮ ֦DRP^暭KA "EB|%iAA LD%YU b<1LΔl2mE,f 1* " |^!k hn~ I5,hA.t,AȁX|]ެN[|el/ bHX'l˧QF#,cؚ.0pl{F&EJ HPt Vh=q ,hY&^c,0B*"|+`O(AyPDY%B$wr kzlTݎ}-,@ެ!, *\ȰÇ#JHŋ3jȱǏ CIɓ(Sv 0cʜI͛8sɳH .IѣH*]ʴSA|JիXj` *\JٳhӪ tеpʝK]u{߿ V ة+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sYЀoĻ noȓ+/eУK_zسkI O)˫_R˟>>7E߀7BW F(jPf8> ^fa$8Y&,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&]:^]ayj2**-y# EI;ni>rX,+/RnvtAރv*1_|I'qp!7YpSoÍ$`!I 7L=r6n҈OK& w2" 'KH. rj%4p<<<S uLu67/wm>h*VAiڛum6|ŽTq Aowxm7;R*Na]2V"@ pnX"_ a9.nk~ p O|֥poË?v]_Ͼy˟/O 7! ߀@Yr`Vhf҄vaa~($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+ఞk,*[,: V[^+Zz>߂m覫+eG>UzzyZ ypÀݿWlg\og]{Ar0pr?vq05< Fj }Ftd{@ L/T q8{`-dmhlp-@Q΂+IxH2`G.Wngw砇.褗n騧ꬷ.$A=/o'7GOoGAbjm觯/o?+'uKz80!,  H`10Ç :\HŅ !^ȱÎ CIɓ(S\ɲ˗0cʜIŌ3B)AMwqѣH*]ʴӧPbWbݪՀU'<$̝h2|Q۷pʝK] ~ K&g[*^̸ǐOU.\ ³iUӨSV*2 ,d2Ei v:qȓ+_rF08).C Yp#oJӫg|%Sҥ@xN i:lހh-e'WgWUAv!v@@ JQuE6m8a (4"gd*" E,X@)|Y ;k`])TVYQyM7U QWFP=IV暌HW^&& w")hh:%j(Saۄ9Z @j饘f ۈS-bLU!C h꫰4y# P[Ȩh8"F"VC1#d'`  U8P-S䐐0oqS& 4B|(ABŒ n]0 uZPl;@삪J FCA-X2IPQDۋ+q[  Dp0q*[&t-0_j p1,D  ͇ G6@`Mhq .={qVv X ( R)0oFK(6@7PA c lL@J\` (5 $$7H[K $;01IRAtb/|řK;,) srR9H<.b;G_1@=h[ *_elqG@/PX_CQ3x/Q4#h|:3E@)!h ;u8H#+D B "(U AbZ.`oL)j$m@=O L p(b9 ؂o? $W ($A F\'8M{*IH4$ &H[pTbSъܶ20 `#@Ї1xp) XN f& 8 NA:a-2S n'Z$@ d@+h0831p!Ger -\u $"b@@C!Il7$C Br@Ó!|R,B6`"6sX)?V<#Gu6DKY@Ln"ьjVш:瞲 ]qi;!.xQ,T: O 2dB-d -MjQԢ,jVᔧ? ,pTUdi݄ZdqLK;XMvoPײ5Gu]7+_2X ح fCi:&'UYVJ,x""8 8$pg+#4y͑@/[XfKE%h.`$GPmy ws*P hn8-]OM/q +1J ၷq q'n0.xIh?ԁf@B~`|IZ.%-rغ;"jF3! tG 8$i @) ^G̀@LfYP$q3^!ذ>9dAzA!RK0qwaSEu|)3&] } `Fx@ف ,:&>H$` -Z\rZXE+|Q^T (8`^0(h8H/1bi=lA߸9gh9HAA%шF`jށnH1`69'Rgҹ>,G3 ;0@tUkB j\AX*3d_n%. ;@hA 8wRhHȽA BjqB"@llCB.& E7׾*Kt:s(6amHcQ .f@@xBa%6n."!hp5<^ ;]Ѓ@r F0^ @MۤRG"@fT 3 z[X$Lx[U QyC9nE5J\ .i1 0`|8xTp /#( 9O088mPj/R /2bTM` %H0P  Gŀ5g\1(S$NRp"tYiepwLY050`5,d7A96xxP*Sdx2 x%$ &: +g  0wT P^VFM0o1][X P2X Sx] 2zaxc+]BwIZ2xԅ1Oݦ[Zѧ[PXZ9d]=1Z(::LZE^j k8ьB5̨[xUVǸOh e`^|8HihU4hSč&ŐFOSYY E E!6[~YYw)9ोX}'Xy*uZqDu1F:'*uXaeDv ASp+ᓔv`<[F % 8gi6f"5WŒ6)Vg iWEٖpWf Qgő py6u[/]d[f ine"W!D; 1)0/b.6>0\3 |:e!IH9hp5E"7I@t ; ZpOH 3md~bpi9CA1BrÔL4P8_w@!!{(ŚcUv)9,I * % jmCpdpA p)v?GV IVH5a ڢK!"*P ?IvI5qX4 )@P܅_$E),UXֈcFPb iTC[7(` L2RQg! WJ*%:_@Y'FUatf'9|I.Z=BߖWj"zX©Z ep:xz&)9ᖘGjF@'{jj_9W!8cRP}':NY$,Z*GA䚯91)rh@, ptX ǰ:Z\Xevu|%k}f1E9:2'rPF䦭GaY P8k}B$–4zTfDjRap 1ͥ9 Z!;cb:0  , I2AGZYzV(˵ʋm9_ Ā,Wk:t Ș,lɑ+E z- 깠} igŝ˹K뺳++ IӣN p |AǂG9MtUPr:iPi˺ʼn˺+$+狻+eMTk{glyˤ+iǖ ̽ jL Tjj&O<RΎ ) pGΔQF]l5 ٖ&-ڍL HM* Sh5h[*kFq8PY@*`pVD=[w7D 0 7Sqպ ]Y!y1S 0=٧]ک]=ڏmSJ-{Ќ Ғ]= ,4TH\  B,'i p WW\ <{; zP]@NA+G ޙ6p S):Yv` Y TK p YPs+qd%BN䀾& QB 5 pG ePG@` gR1 9uŖh>^ 9 `^ ˝j; '0ҋ䕈oÂqq"=``]t [ScW yg g(0X Y wGK7.2 N%|ܓݨ0~ߜ߆J^X<)M=Ԭ dElҧjPҿҕP? z$RD_H?OmL~SQ$_qܺ 1)~ 3 ࠌ3 [/<ߚr#/=/O ڂ o!M3v B%O} y3o 7Aw k~~{ Lj 8acOɸrh0@]sNCsLrEOܸE )Gי3T K -4肁)"ǴKF>2 wː:➃q0E,Q 5 УQCTy f;65Irh5 hgI$$ pA#=  ͊AƲhHbpEЬ+tM8sN:NsΜ0:;7sH B 18mp`-4DdHYFRm?kV\suW^{Zh  =4P[#za45d/ĎC<8鴸 N&P d`fEZ Tw>΁#$a MxBvq rE8*\r`h La}C QZipP 轅 :V5D 0DCbE0lPFT dg$of G`MT#%)iEl!G.1BRRT$LŎcuс@vzvMq78eSjUF0:3  4bk̝lHYUs)I̶vb&D) TAtF4=XȊ#H&Q|< e!ÚLj԰4em;嶎)5ĴoEODS=v@nErF7G)GPJ$ vBz$ KXP[規nSz6/Bnr\IWYCHjHi(mI,:*!)_Exэq!͉Y 3sܕ| s[p'\tA"_ 7T&ʵuF%9Lcug!.8 0F/^ ďkP}\CSa 7內bZF !Hg?b && }@ ZрnD M H@ /`ZӲPDM۲`RZ4!Ԝt< jV9VtMĦg3;ЎvC`5 @>[a 9G`C'щ[ț҈f,.iz /<Ԟ^?` " vŽ텆69v.( +c4li+ `s?;76yΑt[._rEەI\ 0@ pW jN3&YF j\/75,.@P2`pE(!daR`@H`1$6Nt"fW:,=2?bA0ć&C!iK Ǻl6{k dP1.K$`Z,z0B():8bL1x8E0ޯB[Ł9<3G8xHCˍec3j9z@g3[ 94(S6H% Wx7x:`?؃WxG /+@@$lcXN#b+=(8(*P#1X,TBz[+AR1pyBS5(؋,&A46j9+ :D@lDGDs;JLm+2 ʊ=h:8GsB 8E34cËFAɑL>M,Q*bA [0;5h)8V ȀR`L[G5EY?u!zH*}Nx8糖+F06( QpL6󪟔ڹ ${4|N}P] J4N\΁r<μ;Nc IY+mHLO φ;8˸NO|D'/n #kOJ5-UIl Um G&%bNCN;lQ;QFӸM4B304  GTe@D TT $"uB7t5#2;NR/=PL-0HS 8bNKŁ,"WA}>K0U?0bб 82XM$! K!)/נL({KXX->Y~]׳9d0 _4aK\UÀׁ M6[PYKg x !UlP}Y[D[J0-v p=j@ͫ5\v ȤYYT`3=])5ié#EB3 q_XNBŀR"֤¼S -|,MpJ c؁H(-Phi"pZ.7v&&bKtBlzl$/3ۚ)kUSKdbH-!f$e;Qv۽`bC3\PfҥB1.NUfۀ&ދ5>fX5h9p[=VeO0uV `X@9 n|!X_`(2’r"~j/.({ JϚV-O+i]c8fq ֡i,q)4.Sf)f4~= 6>j8)Yojru8㊥ܞ]5ҨX.Ͳ%{V~UW~c1V;B hX:Xujnֹ]9oŭ[_^oFVoDFNVi. 0]nn"tU Ongn6r%/Z)fjq2ꜝrEri pYfrFl&+w^>p/wc9!^tEoq)q/ss1rCwrrlapagpkLoLOfftequ:4oC}Iqtje8q aBؖ.*iIYp2U?%`! (jF-P,Z .幉3w:#O7|&yUЁgVZ XS`R:*$]Bwgb(: 9O)(x)b%Vo r 3[j?(9chQWjyx<>> -xAl|[y3+tzղgՀWL:a(.ghpW8Ѐ`_@f ࣲcXa]uı`/5;?M`1;V~:.V0C X r%X )8!i*#^A1 @hᑄ'"ƌ5"#㧍 `t0!,@.(@"3@D ,Í+SΨ;͞,*2h%U0`PH>}5(F"|,C*^N0D* Pty wԊΎP`@ &Ґ-ê=rPt\ N5T㥰iCY 1 $q|"'$F$JFhD])\BvPHj[@ P"8a,  eY & l (`. D@A"_FX,[EpR<` kD =dAOG E^ [3d(-*bomQ ;CBhLX3l$&ؠF:dͮcŊaE'R0L%B-)\B[ 2#Kx Dp̰R OT"UI8)ZaI `a@08+Ll,*Ϡ82QyQU}* ` \ 䩊LyR 3sFq` LDM;]N!!"iF@oC^Y+J4v"!: NMcV`TEJ4P2DKBz4LhE4\ jd\aIK5x$atR2Kh /U@Z3lL$R_4.[ 9bdzn&LHA NUC"?iF\))X )!Ф6/H7d'OО-Vdٻ2d-D%4Ii+h;C]h=A &!`B"a(/F'$Ȁyҟ&KA Jz[yxE~KQ iߌaFи,Bqhrw"][0zxpx ѐw$!J''p  l[;'+f+l˘<]1WR8qM;ۗ#@DG:3oכTo̜et> 4r̾1sOrK1pZז{"fٗGW*Zs0;MqS{"P脄"t oF)icIOviD1|ܓ yseʑT&ij]"ޱdgd[-^%\ا6˒)mI' ns=h!< E[t nEk[* yzԤδog'"9i8˛ӓ*%ioh1{wnpz[f-ܽzI; toDz1D%ΠYm|ȷSRa9#6Ko@t :KcMJ+ϓpG>MJlGvxކC({?.繟5)M\ \ A\!H1,ੁ58y{a\ K!IՙV`y߼v- cJ2Qmľc@E0]GL^%D\irɡ]eD9~u6+DXZ6Qia)_y!4Ze\Jp mYSw(fg6mqqw"ߪ}ϥ5W\^`}6T^wv7|۴ig^l?7t wW7p\sY nxHޡ}4^sv d_cyvfw5VzމC\ku[OC]an647r JݦɜpK6&8q@z;[/ee30cw}e3EC6owu3MVn.8~ֶ%U ]J@` b7 R+sO!MgplRW(axöEKYZ[iߙMo^ںx|@ x7kSГ__8ө9ӔAm;TnGNtk{a+T+5cRer\l3H!]6lV&RB Ĩ% ,񑒭kn<(iFڊ)(*.!) 9G]tIL$N PKbcELJ&:b}B@0bԂ0)?jD?pŘW8 8@E8<^|AάQPs4~;@#LǤ 5-O0PJ"tA"yv}ϵ/@+ Dlݗ]6o, d lJ,a ԉ=\&kOo7l-q49G}Oȉg|( ;Z8Qxb#e'4 @(D :$Pd|B;U\]ϐ j)*D fhCH:h)ވ P`2:n0zT:P$Ф "Ơd,@|7ȸR ( [N(6–.CA Qނh(k*`MV*&=Rb iH,6s, b2 !?-2P<ݏ?D%K,&$aSzhRL B5H|8b~EK۠HĪ jJ[&ĺ`n/A p=UT/)&R K3k-l0zb8pA $p9נXq P ;zM [zcph޲V)Z9r ; B |4!4 +.;U.$t1>Å؁R ii)ooMʢ% i?0Ef`x@Ƙ>7FA݉x~+K!X F2`χlxzn-Wx,QUHM"dp0`-oҝpR !?CA_[ ր!e12(_-8-v[x02H&A~`@֠ zQ#HD2( n؇&h!!zaRJ 0 ((0 fJ01, j; Gt!0 ' |2)[2˅ h&4@$y.ʼnFWQZvH1)KXI@.)Ixcjug\1`!IR 1{Yn#$*QxCWs')P݁8჈k&i.I S%Oгg6ߒI(<'%]ҟRd*Dd蕮bK؁9"K@ E!@inNgAiTZDYQ_䦢n) $`K3:#<鯔ӖjT˧ EÒLœ/S_c*Vi2}%DѪ2Τ'Ri1VQˑ2.N ־+ X`a(]}'"Ѓ 5b[\%XuY\]Q) 0G L%cKB d00 ҵlL{^ "0}Qvi!6"hK05tud,APMو 5okQ\߫3}J%2d掴r M=x#q8 #4$ޥK[ղ6N,5̒k % )"0t@DH"Ee,a;КKrd"ԩ*  m,F $Ù >upș@j Qܣ2  Ď?K< ?' :͖ &J >h<3١,~C2.H(oL@Ͳ(Cn7妧D-D#VU\&" tEEF2`XX\REPu!q#83EfvM<Is\[1z͘GJ<x+"T"3h`` U\ O++\0:XOOH[0Jy~j},8B1J4! |"0B"O`a"Xl ֠!6BE8M(X RjBf씞yj'n!zͲ),c.˵b^`  NqG)b<'0atcRgqʮ i8o--ނDERr U#ƣ< 4MWicM()>Th` ֐(fn uc. EN# `d @ p =xȀ_j :~bfRML@q"P`,"!FȪBt fv{\`c}m-d2/v "~ bd#@R^@zc7r%,C.o laTDq&3d&jTA4&?g & R@@X 08!(Dx <._pJg,נBFg'R6`"~K 2T$1P&/81Z)J ^ .A{'F̤/vbd,O3Ь%bhn\k,&ot`"I7}s*!q 1WD'@ D! y|($˞ r#X!(`,BN(n\9+ 8 vv(!'D Av?l.TE4]k#/bzbnN!"h@ R!S6B geJM2b |AVT*d'|FS eB1+.*Zh! &"P_@ҠiJH `bh( t B!x0Qx ;)G(LӖHK)$ZA "gq_F@b 2UJACU(לKJYV$9`E >MN/:(B*SWu ≥XտED d\)N 05!#5u|k\w("uDU^_I҆Ɋ/% T{*B`5^K`#_W2K+[Z\JM-KPH*]`ƢP 8_)4OfO6hDpFbqh)hM Dy/XiAlHD5]e5NdEe#\w(2"raEɌHVԜjeζf۶nKaqv"Iv`G[v`213VGaVf,Ҷ)\cJYŬ-C6slRHlqG.ucʸJuhưG RLVh  oc ,3+B iB RxE2,s_[Iy׶D,o)!k o| IUfw?ԕmWj)mW|Sm*y*EAB dbWmT@|*^Fv ZK4n(L@~J `IeDDZD"5kg+~xcs|c˅2,iV$|- ww.r7Af BM"tigKC5.'+)N J@|@*egk-Pتe̸KȊ|7"~`9Wfb @nJ]b N lGSV rmt\n#6֎X.Pm)$";z@Ba 8R !řA@0{(MF6!1Qz*L-I'.hl!l Z`@Y~R 4 (xNR'oeMw}( fvob7 @`H uz!V/u6 XGK@6r'"p# "*(0C&i$2 A ( &TE`zqnxJ8G'/p> r'HsDN&`4M1,X@0tDfG7(GKSj \<^b!?Eq bj&@ TC:'4 V9v /S#O-p p 'S*5aVZ)Esw %1j`w_έ'6¶wq;e_:xB"ab 4`;m8IF 6 ,ў D} MIӉV `uدW !&{R0 Q@^j!B@e[k! :o([q)'wB4bV`$\ŎwL B`R"% )鶋Oyhq>uQ_6tT 0aMa!(RĀ'3G` 4UY);qR4n @" d8h :^&s 58!E|vzfy ._*M?ؽ>*}z7:_n j~3:!h]} G顗g_8Y3{mUuwfQ_{H otʆp;xwʵ S ~b/_q0vM_} Y!yjxbB" ;  ,h "CHdX1!& ȑAĈpɕ,Sl0ˑ^a;+oItcNB]igQH44kUz 6رd˚=6ڵ 9pJ *>x&bc\v+v)Űݱ~ԡԃCZPA3KD)'\SJ,I^kZԃbhVUX>h&-+S_anVգZ==hA_p"DUW]y5Z@Hc*XEdVy $T\rMA1}5Y+UQt.ֆHk$"}OP9UK_Q%0bwPD$wRNIeVg]DhX2FXAؐF6׸S*Bu %4F?0B g%Q }^A)v("@R(!/Qd# dD)4E5]\p!q!*A! z |2OFȀCZ $v A+yu=J|B R+VkHLHk+0O2Rd5j'whG'P։"A$e%/H] ;AHxre"L2v%x@q D7J5}$&3` DP@qJ!1@PVY DtJ B0CIc d 3a(&7J+0 Tr2rA8AuHB+ paC 0+^fAMHBbB9EPy[T"+VKh@@+ ѺCIF<&# V-+F8~+Rrh`QRGD8aC^A0 =gB,# @F! L`$ɨ[N# 'T10EZ?!CDOA/Ũ.@%oVGP!E)#h a 50J\AB D&&o'?"HNa_lV Zb ̣r: h?@BZB̊aFr A4?'"Eoe>DhҬQd @ B#/w׵~KaR+ABEP!kH8M bQ "rP%X6m&8.F1LT W ՃS"Fl(Zx>t?tA``D 8D^N$R5$%FR@I30MB9z 'S;@A@"eBP< :Hq6p]gmִ-̤DLE6:# C*$a=PV ^Y15"6@)xd6A&ʦ /b m(" bY y1!! 86y OPC&N m @B(+ 5UhW4 ly)BDFQUtڇ Fn!:-` uAT0L0gLk8bXA@-ŀ!-] h^C2]oȵjx,wAY=Hl CbM`a֬3hP ԇ2ɏ"=S)]f#`! ``daM&B'(mnOӂТ'L2`8!-xnh ) <ɶPShƖ~rk{p.~b)''L#)7ELEBHEa 1/ C1F#B Q찱ȉ8" `7kOif2D$KƘP8=" PBVPJ-$y=Ծw22˅jIwVS!Mћ.)x}rkРM:AߧZ.w$/vk<;L+K>hfv͹mRF^ q[̽AF&;Jғ.&;I~zUstBj58A*X}M@6?L tAp讠] -ylLX⭎#2l [(LyR,#=w~ bpyN_r*)^IlaϖYƐ2 .XqAl{[_z=~zL P->ڠ3x /u_jdWt6vra~#Fng)s&^VnZnFrt)`o7zXWuwR^q@-y|'wJ?s$p 25@'r,qpF2@Bp瀁w*'cm;Ww V؄,h^9؄KruMRzH?P$3}Y 2xW/-vS]G}(VFufAֈׅ+&ΐP Q3 4 9M̗s=BP@tMMYa>PPecRwSؒ+)P H-]p2. GV Sn@-5`v _9VՍ= 1A {.X 3 *QH=Vӱ.H3>łvbpyeC d0#,hzr(h?a5}@ow2s tW p9q4>Ix 0ts% ,KqcU>jxpbQ>vikp`d KIP98h[C6|3_4&&/A,P +W \ zCR`hEX s,pQ3kC0Ae@:0(@+`Jdb}\|X#iV؀=K[8xpt*$bP5 9`vs!O q 8~ 1IQɰu@ߧTh 08r#~V rkݨ *eX,8 [zY$ F0ol`Os8x$pwi 1gL +`q (q ҝ5䦈w=)pbAA)|A` Y>J pQHZ\* U#0PVdZiA`jC*1r`*etF?>YI EXPfM Vfl AR(>Qثpp|PXPVOHu'N r1QJVrycX4^3 ѡ[Xa` D?@B *ccZYNȹ`>DA) 4 1C{`/_(7r]**j %Y)a\/ª j$6@/2sB\x u-0%V9\ ȄjZ`IwR #`L0fp5!x8iNvɧ t˰C'Td F'(Q=˖dGz 3Bx,lE65XFiTq 0zF$kj[ k:fO0Ֆ) S:C+_+1\F`;f>04S@`H Ppk˼˧B~ J v,_o Qj[8ktSA1C҉ns+nPd*?fSvO'Z`@qr)r07ÃT[l*Zoql3<^mMV%h? `AGs mI||`(BG>7"l}q[G"+;[wx9|PH+ *xlNّN<|Ŀ'4źlGwht]?(W)xml҈t@ìWxt {fʉ,=l@Yf$\~ķiG˾NG v(| "h,|?{7gLrʸQ=}p|k¦qRFzy\")ufp#~b2s6>`a'I.B= ~9k~jʦ_ I N3;rm=>R0"@Ɠ"j: }E>4; >:ǛR.>0 ;I>DZ 6XcGԅf +oDƺ'|Cm4ۜs0 ba}~lUm uԢi%X` >PLs:~.ԗB MLo@m|쳛\P( "$ F (Ȯd먲,B 'B /0C  /R@ /X2MbjfA,*0,F\ z**Jz!;HBJa76R&3)µ+d@¢%j.2ˆ2 'Z eݘ %?x,1@a1hVZC<MVhQ@1 ͉Ơ#>:I-4>pJ %bSOF5mNMöX8AH]#A;[Ը 3S $J05X@-rp@AnXȖ$@oPnzt ? C0㌻d' vM4!kǮy~W'˭g_ pp`` UЂ/=\fK CA1z$PH t|OllC E!N H5Aw@.\AlRE&N/ JX 3B&!!(KD w>-/y[D7dpb"h'7atzF8!Tbt%P$%I9v9/1CB$ >\ (6E+-:!JM|1lp`lM` %NlhçǙu4ŷO)Ѓb!Ww\%Ln8S2 π(!1T3ut}TRC. r[ݝ [&5R cmcXpc s ^ kw"K0!BY p.5BaC.Kn,DG h>z |` YP? 4iE a * G~Et0NkLQVP}*zePޣkC+m7?$ *YRx'A`sD#Bl&{a>dJO)J,i.g7JOG;B(kY &GvYxL H 8.h3>p^1F1Ux܁@t)ЎL R(y>CH;*b#nR9(–XBK8L> `kH1B; ~Xv23KL)W2SB#0H(Ķ'ڈ-x:x7Za#Q /Rn BF#h1Z,;& sQPYb|b0U *>)q *>¹#tֱaǪl*j*{Y!z@G*י&H&AB䓛:<4ѯ‹MF$ҩ&RG.{cR>3.R Ց7}>t򺀤ZRB0 '=sךbɏi() B/;zfʳ\IC *p"qbIim2&U̾#"**L=*ǩKČ̩;I Ƅ"@D|B"'Όҧ:%_C˘BqZ :Gr +$&+L$ $GK\Ei0LĤI<NbNIZ)%Px+ ,PbC@]G:э0`Fx:+8Z:1LMP!5QE.$u*5=8CGUG[HkmŠl `+ VZxfX1Y SF[P`5]E>FЃXXfx=Cu҉MRG9Gs֎؏IV][ 70[S`8YT}ؾ/Xz87`£+7ETO}QOUm ST" VPXڮگ4WZ^E|Z@H.[H1MQT7xE0x(E`XĥQШUE0XLM\=ңZM[SX[0Y0vR5Q=XU0Q9 UЀҋrRuƕ g (8.r۫Bd 55պ=]EWTSXӘ%ZxXY{ZM'xF 5hԍ]bW-]` bd"V 8e`.85b*:0,HT}{W+ WR% x`0M.:8Ճe\T\+p$Ͷ| Y,- _;50ԑƥu *M96fע}3f+mQ2Fe b[B6C>vxG85V47f-W ~% yM(Z) DcDvW嚉d=9]&%En\Z&^.QXffv,\c6mSSeV_QZfc(Sͤd~q&r&4H.|K.ġ-s{\"2N>g?_lOE bǶUl—.P)<J%x!HYHE3 ag$kR"aini 6i!O)j.i>@$PJ5P%Xl4=F?+9 LxL $8Ǘpi萊问랖^i"AkjfJ"9 /;D +E5}CG8`\ȓ~llɲG*Æf&jlඩk.CklA nkn~n`!12H-G#{<78`.XI <|vVnipm.pݤc9L۾L[p b-P5`8>%܂R-׀ "޽ lX4/\_lp. p~ mlrޮFGrpp r+rr\SLKG*J` "jhXEf+ 8*](1#$%#WrEo{tDoViμrjtF-tu,wWh:H惡tJd0I7u^>pGkt_T\7vm1ctq,Ov~pTD`wvof rr1Qtvwwinpwqv.xmgrv||'?(j 7wgfCCG0քgOuۀ!, E H*\ȰÇ#JH6@M CIɓ(S\ɲ˗0cʜ ɳϟ@ Jѣ1ӧPJJՔ^ʵׯ`ÊKٳhӪ]˶۷pʝKݻxꭻ޿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ#mtbu'P>`O~. Kv(kJ?=Ϟ:}FW} %Qww`V dH , `$d((P!*I0hcZt"7#X!lDՁ:hLB(MF)%O$@(N/#\)fIU~9hRdY 9pigxgR|:v)%[&`6FJIj1)(v駠*jb,`hjB7k뮼+k&9)MVK_N-IJ)Zm.fҖXOnLᎻGerAt Tx^0 D`bN[4(PP Cۂig plnDPtPYQ ˟In|R7;C=qH%OHrj&qB"'CW|ag['mPAb]PٛqCۮ?\%tx =Ra?&@[97ێuĵ}lѵ,U:rٝsgg^8o ;}ۭ(najCo^9n:єGlyK;8/_N=;=} ^-?#:j$ NAP#$bAѲ(YQhX$6}І&Ȅ@L`!Q$PZ06plKY(Α @:@"* u`SiȦQ~#Jp`T0$@Z.–D.oK0@ܥjQ ,Ȃ`2\Rl49 f-nz 8IrҜL':v̥ x`Shq= }bat';4$Tg;JPm2469d)Vo,P2xC **(Ё-=zQVL+OiOSH=Q:T&OujTTZuWj5E\r XgB@49!QuSɌY:X$ 4'1^R18/)Y0At`M1t8A՝}(274`C"0}DaSF@P5sApeDE HXRͪrV5TjtKVQC@~S1d! 2"x)#a܁Ɓ) `8YFDzxaQ1NE,b1P."JjPغ̈́}Q*qHMC$@*j1ʡ8i0 ȁ)c+0 LBȜq5=F@E!``qL2ٖja2u-LQؐ ,9 F`\r\dY-Q^'A ` a%!@ 4w"2ULPB2efFzTFIe_90*Z tneAŒTA\`gv-Z:.sG7INT Zs^K[S6Ōoq7B0R9נxc_ \H )Zdbdi+);,ČMnZf{0D-r@sֲ؛~ELAĈ@$Pq krP#RPRZ{3jw׽u:Sz{*vW{ٛi;95Z +`I@}8Ħc +tJwΌ$<3‰a-yWhld߂2L(d#^t-Z#:-Q5b 3Pueڷc>vdNGzZQoK{Ih2Y>0D׀` g, dV GщxsV I@ *[ ufhz]Lz] Lq'WPV9l9Fc^I ""p

    b8d )p3u?i "hp KD`CEٖɑϢ \Y;n f "*Pt9HI L/I1' &\ɚɛ]I?.!Ul٘¹Љ9Y3 `1 `٠ v ذ @Ob/1tB) "rg) 0yC!v !jZ # npY3R ~Д5TayJ r"B$E{  `UKp PFIP`9Й q Q*)$ WYt%,"A)չSڥˉCA ` H" P c d) g` 3 0)` DpРcj_a?WZ  `Q7 >"" 2A Cl 8YP`zf`ja< Z 0'p !yѧ1A)AO 3\J -@X!)б./˪p+ [2e  ^pjSےHi0+ UPt Gٴ&I! _;J[Pa\1{TJ9Pٖk+C{sk?K($q (l Pր * 2 G s 0"#y;"[ :0yb&̨ 1$  D,}A1!K,PP1 yJQi;#4 W\hlw ? Qp\P y :ǀ *0g/ p .q^q A QɢpȠ\Q(4`ʪl4ʮt9<.Q<S'0˵Ѝ<%aĜL:\L-eCPќ YpC׬Bb!,, }@pANC\ϧcB:WH\?,,c$BTQ = =0$0Kk 70dr! :~ЖAv,[~, 0Q\$қ  ` L@ v)FqE A LԖu@0C @H d9n^M"`@(!AB@t:% 7PK- cqPF \*A"S8> ϟ%3B]AB73/3ЬAs5˴m7DqH$#؝=ru+ zj @?= .s ̪4鯬mÁx|&-깑޵m.@?\Ḿᕪ`I;^y >nT5ۮι-ݹu)}ڮ@QGq祓'IĀPV4XqH_l Y nK 114 :@` CJjoC- Y2  M=X@>l )*4s<, A @'p[TnN/7 (  V/ D>p 2ca 'wm#1\ .w&YH I$-1xPWA3 ^cy7?R[Qn?Z괈ڲC!)]ТWDQdpd}_!:T?ʏ+F]bǾb@!l'+O P3rln(+/9A" !2\_^OjZP?/ 4 c0r^P䤐\36j-.OO,9V}ϥʳ8es+/+"2 S$1SPRmR|P|` 4.AP y +qjP qRpgln~ ((z3#&;3yAoɿM:>vaIa0_o֭yѾW 웧\e6N7r/@I0#Kqj8?ВֈAqKcḠz [CO [_p  !`.Wɪ)@Ţ K;o3 $xeBkjEιWPnXP/]ɿR}W@ЩCA7]2  ܊{$/57oN/p+A3F?1~ PB Qb) PQƄ=~D %D@`J @)RL/[RSgN;}ShBE aҢL.44;T^JqiVW&ju+Ά6Y@ئZϾ5ܮmmn1]B x F0Çy)^@ M|#[rK1{^YɯR-=]'cSU}mUr*vVر~ {oAO}!:#$qtmE:bjݻ -w7"~zI}c̔){`@P=BCYAAeI0 - 7ABEQ~0E>PEXH@BdDk@ tD8PQBx$I%I&|2J)\)Ē,-DK1ԲL2D0\3M1M5d  ,dS !$FHFt dM,J$#OsԽ.mQW5W;R:>HVEuNhQ>UUIR<3Gy:(rB=ri;#IhAG [Đr7T=B1h7 kN?k聎sK}K7S`F8afa ~J0`Se^jX>7IKUO:y[F4}U1$ThX5ga  T{Ž=.a=<䕄 FR•:)bJ((aZnу+VcX?֐v9F fjxYvk`N>efHp\ő+)!Sxe϶ r^S; 1M6<7򶓘:`[V b~印pu^!|XNgJo1ewݳ#72,կ,B4^ 44[mUSP:NnRp2`Bh*> zU 7pk`{˜DM={C|`5SplP B<+B!"зD4,=:\:%(O D|4A|/ ,C(iӳәZPHP`=/X4ط>v 9v_ @E (f! TCÏh;`(GD/N9—/1`f0{Y_3bfbT$3MAb!"!N%鮂AlO䐁<#N+@L1o\(>9ZQ%K<`=,-Zր x8A:P"%al?CuvBcS^A\̵ iRWP9,RIˑ ʥP[ciڢa |lT Bd1A sѼ\qd;Q~ Uї Cf2;MH@֤*AMq`ZĻ{8;0w!<e-$|-Xa!-UӺt QO%HKBX 7P(w|xnCmP5 'p={9#%Iuѧ^ݜx?lִbxL]Fk&NB" ZB@M1|e4 L!^q)[Wrkb(BˊCyX3͘g2,1mc?}dD+Z"kEQLYw^yd5-iI4>zC#TW ܄ɕb?KT .*9rY?Q q)s8X,%Pc7\4= ;ٽ 'a٣1=*ٽ 1"Ybh@"d )B&|1ꛗ {I,@1!vHItǻCy<<[` =pB-k5xH24CB.-P`{6ܲ-LC8y#\AvCAaDdLH0) @PMDوL`ʭ,L˦|ΠT̙-< AKwKeGGINu$(C.A5EbD=y4N?1jXUFJԣJ*7vФɩ1_*WկTQa ¨-L(N6C+FF?C 0@cH~PA@ -2"`c(G3|7 nذ`Ӣ8efxfl%K'C˗]3:$^@lXE,![Kly t[&'2YА4 A /, &Xne;)lk5Y3rkZt@0Fi5"CKs@5_ N0p!6u}Է׽Z-UnvVw1%z =>4q@PCn&;p%}} x4?qQLU/FqFG4wҀ,dgA/|K!چ4KW~% 5\<IپH/,Ϊ> nK(^ma(l3a@aOѡ#Am3" Rdtt m~P by +gFaiЃp/oַw{Ua-o* w:=NnoVNwfmj}ˤGdHs=+G<([VZpY6|r9;??FVn Y#odd 0͠n|KG _P W]|LJ5\6| m Y}o }2(}jX v`  |Ł7{HGLz:o~cp\ 0`gp ] wlpY_kF}\wW ֧f0 P\Z5 ~;zݧ`iiňay舎rp|6`|8R2AfW_& Yut`U`ng֧_f6up@[|_Drhk3vvwe fapp _X؄`f{0XXRe:^ g2 W\[ `En`g+j.]gghP `}IYРrKȊj`U@zBtH6Ujvz-wWaU , mYDfk8 u #f)H;YY`}aȕ+`MW\&R90i &5 up GZ'rsY_QcUD 2-BUGpOP^9Y炑+@iI7Yrql 5E~ 6p\WY`E7aǶfҗ g0wIG%yوP]@+iY0lp \huE&@dZ ygYEej`&D惦 ֘j [tj Ym(7'v|YhtiV*FX@o(nD fIwe.Z1] 3[Zy(O5ՉF[[\w^WŃf7}Aړ(oGzʥ^桸%JZAZ75M5Y i|WktOZv)^-riz8n]/%ũ`ʥ0FXezᡄ%eL6*zz ^djX 9+ Pb20wEZb]:j ]ʪꡀ PzYa`^PcfaѮVf ꯊ5"*x* {Xl cBs Zcak+{^'{ `?`/X`׮ Kc:?C{XE*7zIB*=Pp 8P^gTQB!;A蘱U[BP?*k{41p zX}kr{pۯu5+J7W~ K/R@UXgª{ p Pa`tB`Kv`{h (B` ^ w{p۵ =^˲zɼz{X<{HkP~ +;+|hxz`ʝ |c,v| {2`  e`E2c K0k|{\:B ʧ`  ݽf%}۳ҘYpPLc  Ph*YPϑQ^Яa˴PlL%^1Pfas90եЩz H!"Mf!Z<А3 И` mūl 㼺\ < k]Xb e&GpP`I@60n ,ڐ ` p?V U a\ a@ 0>ovҤ1q J+ݺg= 0X=sD `@Ӑ{ i` `s@fYeغ MzD P @|,@s  *Vˈ| Kv_p/n ~P i >& P Fr…]0ㅐ ; \-:M-@d`[P h v &~EK) `D ޲$ګR~ zGbD %N|#m =: .\W`ٌQ K鬮f.xgP.0腎RP|Z,AN 1. pg x!Hn^yLV?N啱9  ӫˮ-~ ; K~`>|P N œF!-^P##Ѕ P 0v`PP<fO4dOP =2@o @ 1d P, (@ƭ-0@}_H4Յ )` ͞Yz,8 P<@<h;~,)o`vPaήM}cOɮ|?U0+f Ѿ;/ oL  А#C8A7 @K3@@Qp)~ T 1$KHbc`?|$R#*zm`p唂2|ЙbAJqWhҵAȎpF)cCSlR0 y^(z@0ҽ18 @SVC*b ^@ D%(3/XS4Oy)c/KCR PˢrY_%Jfp+pVe"4!Z<$)%=D%yZD$\V`FDpb#)p{XX\SA)1   %\f;$CsX*h b ȁd~]xK*QA 8qRPCh0 @`?mD8zW,R0 0@ -R7 7iL {\9@!Uq`n'9ɈEN#Bv ~Èd WӅQ*`  VJI($ `da 8?硴 XR[`(S(0vTêF Bt(7yTFࢡ_ ^0@dĨ amB  驈0Ca(H YuʀB9!]cI< qЊ!3TIe*9RjUTuUx(D%Y=PBj{, e@iG6 XENx0dTQJR+}5@u=]ݠj1ZZNc=P P22giPjN֑MkH;pKB lp>4-.TOUWҪf;lg3FQb<(aŪ^i7ВeVRSV))v%&N6V540d֏\A̭-kcBf*',9SvܜFsYБ!i&Ӻ-j[[18 aC[:_nqs]c'l7];g8׎t[Ow]vy.}}; ]#lG>NB~|᧽Lr8G@S g CW0ce}mnǞx;H7̇Kac_?~#{bo}%~/?G?h@h1Ӈ@+[@; =?;$ i@A@3CA1 < <(#== ;A~9&t882 @@<|-t @/U07U8@"D!|X?4@TkӀ8/| X%Q 4h@FlD$88 @@]ȿ7l 1L!x! h(VPKILèS 0Ā1!퐂`jSURf47ZlQf|EW1L{U(@)縄8QTIr#0ȃd6$0O%=0IeBY#4@sKrhIr /P&܄!<OhtKL.K)0C%XXMOAl(IHsCrpNJl)Ԝ˪lHL˚H$(R Ԡ|!TaUBR*? Qft=*lPl +*`>SH h5S WFt,*Rr Q V  8(Xj[=x=~i-TXؿ[l V D X5zكt1K4TCN能1\M8JXT>?%*H1HgO@Q, !uU"H ZKB%)T+0=? ` [!~*DrMqLNSá'TKk 1Pӽ ̥%7P\W |\Z0ҁ=7QP%pC\~FHE-OZ `8e :D\Pą_YzY+@X W8.pYD[u>Hɕ@{Y ςEPtW5P9HS,% N4GA@@+]u\\+(44 \r((d l|\%PKPEHBMZ=O0#~ F0b0@!]`0TOcM ^=Px:1p&2 +{V} }+ %!1.Z"Ё)ڛVk few Op}Tk#rIf h)ހeHPj'8fqv7pGP`CdbZ @hKu5unh`lR`^Q@gnh~hJ߯!h^_ht&Ae^U%Ysok؆^ax_rArHa8 RY)yMK p[K9@j[V1KT" bڅ!IX&FNk^knS5[fX^k}t| ;:Cto|Í;ǯ6K,]%=DXeoNnB}lf.rȌFßnn'} of{B0A5ï ep2S Wd`m.oNcpqOƐgBv 0Ojjۀ@AzYF$Z(4G(X^E5A1DPs2GVk.wPʿ `a8YXhY&? 1Ȉ0hUƻt)KO8Jk5tMgО\MmttQwF [aˆd<fhp?gp=pQnEg̈́(_ȄqQOve/;sH@&'V`e6 N"m`up@Hqi"؁xZ#?7\q\kw~UcZ eZ~tx`Dep s>ECAh6JcɃ_eOG-dj``3HlX Tڵq j Ye6'g>dNU7Ur!y/Gōr`r\-3P  >6==(`hi颏'5Urk7&?0'{ȟ۫WBvB Hϵ@rF&N/&Wgw}t6#&w  {Їw}'p6.o6ָhG oYl~wn-|G4 ~*d~\(A6A0B%T⑁ƌ7rѣ|,i$G !0Z%D QҬi&Μ:w'Рa -j(Ҥ:Z2 rdTd*GTV`тH4öQj-޼z֠N PR4B1nִ`aW@ cdGC֬fF?SJ܉)ܺw.l%K.n 8FFA -TUP|!Q*RhfTq^G܄sC#< ŝgEЊ(xn. 9$J8$d32W51FRQ6"9Rg i F8G+%O mPy9'vT M`vORq`*iKy `FC0d'#륂Q,***b^$vͲ5pvF< ֤1*+J;-IN!UZd/]bL ] Z{.骻nVuF4/z[}lҜ}/ ;d-{1D2*ٻH!PիB<3569&U:&*9= (!Du3M;4\)@mCfs6ϜEF,x5m s%>;Av޾ra0\YG%H#r (3B) &"lrS($. COmB;˔|υ6!"x> 䰈Flzì^N  PQG"gDtAIdnmQJ#bP6b7̃.6Їk@IRika>A#e949aܮ< H{ 9rp# { ]ạKgUGIE*Qּf腌p`Ẅ+l`u3dKJyJک}AY 46 t'B$fK5apP6 (}NZ"JoD#)( ?*әJR2yJdc qH 6Hvpّ̐FA$+!Ũ^O81,ih 7GBʐ #ȈJn+^d.>*R!;72:682K=vbFNB2䵲BBjITXd6ܝȉL[zpXec{>X%630xtnpF}hُ nbtȎG2:(L=o^$VPHB>PC2"*+jȲ!SWأ[z)` bSx)h=0C V j[F-J8z*v1Ь]C+_ASl8 F6&F0^&TXXѶ#sy,_'p4MؚQ m ,7p Cl;lf4CjO#,͹|IH( .,XˍWiy,#c1ZF;(R_&ѹϡ@񍳤Y55oFq27QTu _}ă Õ C lĢ0/V"c.@u׹mtSW<݀%B'jƌa  "4 #fLvMuoM!ơa x 聁q,! dDA.'4`*N&^Q(_RXQ@x%4|!ńd*z#)'#l"ZQ4 #`J`f艡, ($x&('|B-B0Db )1(&bd &T" z@-/^!D.F# `A0 `)_0,|`P$ ,DC(I#=> l!(bNP#?`=䐐B`@&aB"?"AbDV AN\B"9\ "!"A!Ed c0.Gb>EKƇ!XQKdo4ɤP|_OE@!,@@Iw60P$z#p-BX*T Df'Drr!;ဃ/&Լ!G]j џB)7-qSD jzqçJi:aѤh' ]۴p㾕Knۻvͻ߿ È+^̸ǐZhQ²wܑK S#WkXb  رYiV%[E8!b0DQ#yh=!r̮MtFB IUHk GGOpϿ`] 9ȲA0 Y)GVW`QJD `H (P ((h"(^88@dž/V #&e0P] ! #ϓtq@)G\v饀`bGhN;$˛qBfR5@%?4%v|dbv!-aG ȈV9x %4Hth<OQF'\FJ87XJ{Ifklr\P:1Bd҆maNmI٨tYpE}`CI (ɻ SGN1kaa)b%&»R0HGh".wfId`pE@+$-A "d 8 0~p+f48-'B\"F ! !"p-!PEmFlh|4]}̃>C. Nrd-zZ}E^`ĸG_nn۹޼lE깗Pu8pTWogwkqG=2R̾a<# ; `W=00L#@60Y `b" R|G!5"Q<BXs`. L&H BpD)6B@ C"HLb&:P W3*'};` L^w@ P`S:E "5 00Eqb$ GBLYF2ґ| r$!@Ѳl),dx<1F M7>!UFh($:uaS 4@2Yb!&!RC!ݢ#1MHJD:vӜ|g!rpg xrK*<Ґ#*FCzͭ&q'JP ?ܠXAD}A]<:%@ R$sq41_wH/4M$`AD F9IԢHMR*$  S3mOj(AXi1Hh5Ѓ!!uE42*P thL3T|cd`K'b$AYF0pY0 GM 7bd\ kM lg$b>R+r̀j ),@Blb:ύn;5huZ@6y[YlGտ!S'LqT$.N$Z}]f57D^S d3fQ;J'LaE,%iӹ?HP[ _gp,aBJ0A(A5l2IjV g;9tL#TE3iưXβ&rO tNH P—3r3w,]%˪;N8ϜA/ܱ2ЈN,?10M֞v4w#Zz`#VޱpFAu{f;{p1M`^#&kA ȋ~nt!zX&]Ux2jknN[q86q쓟|UsOƚ`dp{ 9tmU"1hC3\ZE4pMY8-cv1:8A ڎvǺַuVM׳[aLp/_ y/r.Y/O @ \uf1 Pwz%?yͭvW~,ӊxDŽ*Ozʛ'3/pGBRO۾#HZFOCںGjp//7xEMwA;N@.Smu\ACU. >zW}ާhj&Ґ > qlFX0z8$}q@to_P[DS%X6Hz &GW95px1PxL؄xhjGi 0~xVo"3XaXp7N8dOTY I-YqFݲIR^m?5gX|hׇyQ0b~؈{ߗ{oW (؉8{O扤}xH!؊N~Gp`j78jaȊx APxʈ0 `X "Ҍ=PX͸Hx明ڈ쨍r8xHxh@`8hpɏȐP`^ :qYA 9(' &ǘ6 @X (tsJ9P19RIUiXyY\ٕ ɒZ(`oŨO 9:0f A㈔ott t`V(qYut =ّH p z3 јo`  > 8  %3Q 9`@ɜ)bԉٕi鸝biשyz uPk c z ` * Ɖ+` e0q0Izp dYp3   Qp`9` p:(q P ` <NJ059` I%`c zɡ˙ƈ `k Q` Zlz|ڧ駀: 驧s Ԙp,J KY`` 5`XPqyjVAzٙ4P`Q ` P.zj٦ #ʨy W<@VcyplmBJc3 $PqpЙjp4@<ɩǘŨȊ:+<뙥,zbjX<# 3P9P @ ?D褁p(ܪ9@9zVX@Iiљ){ٳQ騌@jD*p=q0'4EJXHekf;)ޙj[q˧t;xvK褥ڱ` +а*;W@[W* p 9,Z8,圷iNz MP ,>Ck/cG:+;0o  +"*p @ɟi]Vڨ޻{u˕VзXt05`YW5${ + *qA ڪ1[1 Z3@/*ȟV@{y Ǫ۝KF t@+Z;[  Q۬ũJ.xxB(0P0Q0T-Y V- O`a g0_-[ji- Xa}kz|~ /u}svm،xًؐ}ؒ]ٖ}٘Mٜٙٞyhp (PpP 03SOղ ha=ۚ۶yZ0ܟܭmlܟܞ]=]a P $B?iaob]F0\pai РMہ g0 f`5 W֊MQ l00"E mm%lp6.X)> ӠX-5DorpC0& m]7 f0EC fMF @#i䕀jFv-杵05 ' pMJC.L&0mސ>蕎٭!OV&0 f@n,bmժNm0 nQ= 0 Pݼ>  cӠ " p m%  p*ήvM뺞;N@+.=-pz=p 0 1@`0 s&U.̀B !֔B~ N$/>?@?p #GP  B~oO SݖJNP R_m>}qo?!>>``Pط]а 4 lp+qlS\=^ʾMyo_`Nh Z0m#'գ ō޷@ץ C0gĮ0ٵԌ-Ю2 P Y '0oV_KoX?{ao&D#[ ͠ t>}zqMDP4z ]p?sRpZ<>65;T|M|pZ9"҃HoZ/tBڷOH2l&bqBȼ5eu*52I-٬Q5SXJ[/'jBc8~Go"CeMA+pR5uT/8Ԕ2rFf"{mܹuo'^qɋHA`*&Fiuc&CKbw^P¦zJN^%iOzؚF6tz !3l#}k?CgRjȂ/:hlFiHe2!)zGkm=tF#I,p-b+3rJkI EgS=6댈ʁ攏,iL}8BJIP9B 5PDUtQFR N:𲳎d!&;)|) X$lHҫɈV01Fzs/ ;l^zi&З!׵z62"if|`qTQq V/h=Xi:G.$UR#d XB! v*,qC5^\" 9GC*P|L;.PiyCUp7B<cLBэc蒖G4X҇~QIJvB 3 J4+&5Pt%x32l!tP?|!Ґi@vIdJP%0UP0P;Jldg$])B*@!s [bT$`|ўT qN6$h=+ȂrrĴF ](IMPHZBN)ғr4 z3<);wK(Q! щS}hFKTKUر vIњF}S{TUA=5@ ,<hA5\! QPq lX;3qYa+ (UP%~` D tئ*4U(B  RPv0gJVpV͐eGW,^h@':  5rD QC`@; țeluVv`^!l!4P)0'U/ ,APo $!!xTC)C T.@  bnaě] D x4Ᾱ%buw8e'Oa.r!R@#'^Ջ!#bƌr{:RDY;J-b&V$;F:) 7MuvC? x!O6;j^*]JW& ?.wV@Mx7% l/`ÝnB] c<PCÇ;ܡ8RXs\t nF6! c6WbE D xJD~P  Dx 0JE ;tɻcc l cF |pEx:PxX, 2`[0$-&z1Py$ $Cg3HT~ @1+ F/̀ܙRlsA5Dyd;ʳW[ زҢ4] <*P@*wWhov=|<³-q|IqoC;4/R0DZDh<PCK/J^62@L -KQ9H6PK;;R0 ?=[Ё`$@$k?0@6S0>1,;#3%F/ӫ:!;,"I;,K/5R8&d[+;U0SX>طPdC+Re(l`gP(sBp.8nnEVR3;rD=62EEةUp:U9)` 3r-s{+8\VHPpFFk>ҽ hD*26 C$00-R*a3EG|""x&x.GPpt;-Rz3 xyLGq$GN*e{CkH2^oGZTRjSG wԱDiIxHv HtH!R_pCI4 )`C8'ydG| }lsT+ɫƇL44K>mL-p>z }=@M]"ri Ry$z*Z rl.p;4XW~ ùz"kX<0BY0V +]5H]PIX.ȂJ(t5B {}3D@XD2888XȆ Єlxf8! gp!j['W%"E XIG4-;PPp:#FdL-fځךJSr(&N5.N:0? a@Ըe+amTk8l-Ⱥʆ_c2 Ȕ3H]7c;hMsc9MdJ-Z76(p2;AؚKЁE82n6`pjv"*bn*bE%zZk/Mm:);6bg$aS'"#e(ӕUܶ(:unNF$47冷asnBֆ-)oVbDORM&@#n(o%oЩ,Qon&p9BtNV(雷?nSF #RОlOvtBNcHKR\(o^j@R2:')Gk%jp/oMnq>*/|)*wr?L.s*q //'rnr"Fs-D5>"Qo&SjB/tCg's8Oq)g1wtNs:qEr8§ep/>s~3rO?4@*EuTtKouUOtWU;?llg^u^YNgtYOue_HBvXPuj;)L'l/h'hv(R:ojOwZFw/kw,_(.s Ѓ(DjB tgsuU@{x[7Dl??8<`耍xj2_!xf:U7W!X7xx[_fSx' yVPЀ%Wz2x)WJz$ W& @3cd*!{0qdO!Cx{؅ x?x|G$u)j$h |oVoH!1|-@}`}IQ!~}{}_~}$}}g~y(X|w~reL!kmpj*!_\!aa~@Hp) 6nE R1h"1pDRĊ3 ĒJ1 51`ʎ;*R18{%2`ēr Bj0m8dJ0|x8V'YOޢiE"΀WиcA<`&|:BIV<6ld LDPdJ Vti{`[Maό[JLζrf0tԕ)dtDa vշY|bO`B*!JXd}::Pm,2/$~d8MIV,|;a!1ݴ]n. Yd`PAxb[r}@_%|@r6C" IfZbppآ d) M,0)2Z@ tn/'hK&AYL?v ? 7유׽@wP;Rl lBfh\C3 GUGqnwC <Mgg*c~,l" \b#RĽX*$RF"X0l$p ^ [u$`UPٚipx&`j*I]Xkl(H [B0e0㬭F*!Z`VB;őxdDYT4fa $) /NT3rFLV@劉7].$ <Bx_޸ F歚+AWA ;j \0#aoÆ(΃[ c uo`A]; 3E XCa,8/܀.7$` )a( l 5+rL' u3ݕxP d@ofG xXo1pS/wE)=~ygFxYxEco[[h |L }+,8 S'WPB^ ˾cU@Ƞ$B`X6 \Cؠ0"$6F: K ?D9  Ȁ\|__)i@Bx@aDXu, b4耸A!ā .dqYJA0@ 4+80TW.tB-a>!m-!TC (a d@!C@ 8 8fU1Xl|u `fN%0! % Rܹ!- P%b! (")FXl+@  A .n "a]uaP# bz= B9؂`c@dN6!( U)X: H@HaX0b l##FBt#HvemWCNE:<QIL^ZLʤMdNdO!$$WA%HURd ʈNSjWHHfRrteWiADdQT2 X^PAZR:__xWdJ?e\T[P@\ $&$ We%)_neUBfWN¤Iv3QY&N$ygVNh&G`dFfkYklUQmFdn^PBO_A'sf8gsFdirt&qPfuR'irvwJxu2gnbxgzbz{'|xwnPsg}wP~jt'j|&{h|(h*(p2:胺vV'LТl@М'WTτNg(N`du(xvU@bgt@F VSh`^6Ahs^zJ) )ʤ!铚)enirWU\ 8 LfJ]h xG D" '2 酩&)wVe$ J&c"7"%d`vg+hyb( ^m{ҀTPDcy)*sjD(Ql5l^@6\3XAd6lC5i# ZB-[ItAy&LX 쀡fK@IDPTEF Rk%`"U l=Hxi @a[Рz`lF ĮGтo0mp|Շ E, ؆ Y_"}ŠH 8b"y Az*H'@}h_NJ X%%n,h4& rQ#ȏ"@Ihi/qՙQ..-/c!e6#1,x@23TK@47xTeZ>eAlV]'>;+ XDӕFFJՀ.*~4*.Et@(+ P B,dCPJB[]^EdQ֥b D%g*gNVn|g/hmP% Upd9ǹY~X*h#Vm @a l* %tHd+H ,T+U6TL0pB`hYqkbiTQ)O"֯ IAL۳ L Do JA[xWcI/8ԑԥFո@0zcp1T/x|$]} )2Jt= *hAhHsA$%Wt̔49%0^)_ ܛ&͸U-$nGy!]H^(lݞ="=hUކDb&e] y(h]=9^[X$@ۍz)h:h陷:Wh@&5Q0X5GaC_hD jfĶ' U5 \ʥU  Nm%  R''wuQlHz")! <6zmB Ukb+\y |B8fxi`!|Ǥ@ q+JZ@X/"</@]s -E##MMyoHԞa>6c3$~zO n*cIiO)ml~e SO]e[Lc%>0w>AcW&?Y.?sK>GR&>lv&~~M Iz%*k&^664(`; 6Dać5@Zq#D9vH 75+Y~leL3eִyx.hН*iRK ϡJJSZZjX=pBP׶uKlҭ{.^y׬P/ċ3m"g)<˛9pgѣCkujիYvx`B1С=4D,MPB%9gf1IM* :B-HѮR;>4Qw;;@<M2wC~0-5! <" ԰#  ]HPI0њZ:d@!L 0CU >A$6bd ?#! VB#`J$q\HmMy$Pe*oC`g#)TJF OO`k Z  z8bgSڡnS+ר @ ;0ԖأrQ12!E @F&Xxu@p٪ E^VnI{Zt#Ev%@3P. .0x1S )r?~3IPE 1,5lfTx0a(HU(q2:;c;C̬W+D@R) KRF u2vQ )h,]uL[,jp.EHt|"0p6 \8I^h+KZRiUγ.{^z>I&K\7m(((A[^~0im v,*y¥_¯!pefX@I*S\2  C{`@on,>@1,aFQ;JN@{[1@' @p@Ar' & MfŭV9.įH֛_h<+ F l h󵢑 l,sc#R 3+n;h$@TAeB4(|u(5L`(hh=4a*K QFE T&L;Mp-Z%U؂V,WJQ"MN 'EN! NR~C\5RǕ\q"Babm!jEZ`AF u0 b2]b^*]3R]z(JQ̈7b6 ^|BcDb j`f* ql(apz֞&r& ԇi4} ~ T B4nt@ @Vf5 `}GlBu>fɧ^ J1bn=@gkbwV{Nb-z} a PCl"f@hO6pPJ ]/6Dن, cAL@ ANJ[-݆"5R .!ӚewB A\DcM ]fi;VF.]%_ >@&F!e'uI'yY@ @㑠.C@+둆 ,Y<0 hVA>ÒţָH^FR0C,2(s~FxC0G2~-$`Y4(o藲"(Zn` #`-j7?TF:ˆ?ȡAbT O_Vg J s@nو!b#v4T!sebL|3QSl?lJ B<4@s4:_>.r$ʓMqTHdſ_" p`:|bĉi -ƍ9 `R<ʔ+)|̙(Z92N<3(A=$YNLE] TQZ:5+V\zذcIWolۺ}-Hti^yJ6i >,Ō;~ 9X|#@7A(P,w;h*\l4\ aJ>pӧF#t XK }ҁNI 1_Oi9rɅnJ<׻/tK/BT&hE 2@@IB!_>m5aDVjy)pg! BN\bHtR%.z3Z`(UM]tQ@R(؞5X+a:.iSV2u@ _f6yfVfX3O\S'l3U  OPD|!"".BJ I*5AJRaɈ*zl`%!a kMր+I `0{$!@> 1œ=40*%( zm3c;̰D@~S@0*$EK0nБ"GiOC+$ 򦈄䆴Boz2 ޴AtB k0 nk2fxGۯP r7P)$t % dO|CI PHI>HGcMRHA6Qth.xㆭRF"mM*趍 #@ڤ6A5.k*ELDPHrj9yDr:k5ٱV0C ʷzGW(%+ 7p,D  [Xr$$g84 8A0dMB,l!a-$8?`!:x`?pE:p1  @VLF@B!(la#$RdP >HHhGpҤLXAFN@C !@4(kS4LI|\LV!A %dn Xp!1@VԑQ9-R! 829q%y PkE jD ^ kpX3:FhϸFHO= x 8>e@A y}~0 $@(BP(%H-N`=, )]E$biz BP 7\tL A Z#<4l<+2tcZK0jNP|h2фju ER $@ %t` $2:rB ، U1Ԣ@i%t#`" >1]!yxqt7gW# |bJ0\ġ 7Q ^^=hA1׺"qmok8YH 0ЁI3`XT` Y@ 7; 5 b^z-?)汏HA BTAx`$p? F?V/Fq. UPВ%:|2 0ial JC"|X&X $"'p @@#WYIE7 dQ d*[ + < v@YW*=3!,եmiJXH.pV'fRv;53aXBDL%Zvժ%.'DPyK .\D4Q :DPf@H*Q4_>t@^N?x^h]%d{>XARE C*:kcCF PhR) \HA @cg@$$4BA׳Am@p ty"N +"P@IB\PIi? tS84E4F`tm\SM` cȄ-TApc'XJLyI58Ece920Z c3P U˅Y8vR娭s 8X)`9z_O7y^BX]ȭ Wِ! lP ִ,ainw]nBU+/yD@+"@@.{@%>BBfC|;jx%+a.#L0%N@ H@ h vL@ P|a``%$ @}@Ei!0Z`q&Q+g#p- ~ }gGЁZd J`MCE0 PE\"A6=8P xvF,؀H@>t4O@;.@ BC%YhGh.00E>g @.4YQ^ Z(#` ~` F]QlPqXz(vGQxFP LJz%"kzb*/+v~v2E1Q BmԸxQUX(ݸ 9aw1w(Ku8ZHSRAB%8ȏHH!X?()HAxx '"Bq[d2r pSԐ8$3Ic} =9M[Q|1ˆGyzF;X#ILPP[@PXypRW3ҔeigW:pl!BsY9IIR!i9TIIc`(*ɗ阏8t`HKz@1UQti)oH+1^3RtB&њdy !YjpɘÙ )KI'v,q >B۷@  ͙vf@}bG7'"6ap7 tga%ѹ0#h9Y%瑟 y ʠfYn+qmf hc ` q'yr'P '(̀ M "09)"+"5P<&tY!b+"+@TrrN2RaUw'F2= R S@\-0q/#"y ^ 7|)-ԢbPC2@c@2|.` ^560#n--]5R(891ys-oIV7$/-r7[.#1`RJo#u7*+ 2:S38)'  ѐ PC:~(F( rPm;@p;1`R!f #=;=:ݣ>)>`PsHG( JT OqH3H%`HHWT@F9` `%?k%tMrFFE@d!dtOW&%K@WD`:j`TJI0`eĴop?4 dC#X3u+B aEdF `iG!0My;ʩ` ̓`|d\P }LL:? M$N.=s*f^!_P!+О  ;`do  0}>0 rzu,`S-bzC ]54fGfvW#X0%0\e2UEI@ZYſTtWDT%z6C`u۾/W:EEcBrf8UTVRk78}=8\Y԰lk ðL$*M żVP_?^6<@`j!o ƥi1,f>N ;ntj@@ye0Ǻj[@ ;!0ٞ"˂%T.HsEQ h D.#JQgH5\噎^nK2橡|wEDa{瘾n]N$>sNƎcFYn׎J%Aa۬<>.@");)y&)ZUq=gپ:W )٧ۗhAo߹ Gky4A3} ~@$Y^.QScE2AQ]A 4jA)lJڡ𡦃: (b=$!"2Y%N= GK:M^5X՟g?<@t20<86GS7S&:ACfj:>zJ%4"'#CЧ{m*A 8 0/q:4P0ug4gxʪ~-7!--П6!5qVw]Zw1^0BOT32 p%ȆVpN i1!~f)ѐ֩$@SQ$z(dacS< s ɧE RM:4TS^` ~BA+8p5K`[Ttи6IɭV%І Kh#d;ИE%~H"Ca${qk?R4K&qsW;?\imKf\@z[ }:B)@$B<`j% sWe .\ υ/I[LE`@ .` 1H)V .H#@)lh7XB>lT8@Z@ĂK;0=5(рVL /0=Ȩ dDR*d!$DD* ,ψ&|lK"T|E f1 %DLT8bKP(t"$Q& ?HD0@/3 P-2 X 8Ïazt Q@LBQ`UT>l/X(1PEdeYJM4hӢ&`W٦(nf(pĆK!Zk(̨B 6@;2 .Vw@AS;=  S*J8A ,rHc#n8匌$G%f p C9¾+a"" “h:c:+Y2ԨꆌxAkX1Z6mi %8 E+V܇N A:U tQG3ʤS VY7"i tXOCL2!8' `bkPI N~1wc@pGʂǀOFs"1O|ybZ?Og)Û@&SF3\+I 6WĢxƸY+#C8gP9xp2;T05gB( W.сЁP\XEo`Ìq asUGpCl7:O+k, .gMCEb* X@R@<:{JgzLжIK(&,KFc7S`rAg8Pj,$36A (LHLe|`$DJ9ȸ}+s55 `ajf ƩdED`}i,.L .qXh,DrDvgFB9BOQ=X<,PsDiJUҪDP!`\F^l`L4 ,# FR vXh+` `71WD͵Ձa.b$ :!BE l5 5TeC*X0S<<؋T֊^n渑la@B(4дl8e2`*lgԠ @\pY$&ݓ "@X.XZD4C-oi U$,-@A&l̗9vnH+X0i pl"j}W B>|>@ha~2"]p rn؞0$f ڷ@`R: ( e'.n%P t"PXCwZ*\@'X di<妈奣DD `p l@ a\c,0LT=BF4l` Txb@_٢,]";D !fZ,APp@Ŷ^q2 YsJ &`-`(xCX0k@E3|@!&hh/z7(A0K ,P h?{u  dvw+hpN|]@ %5jk; Yu Wӂ֊xymlA &Rh @o/hwbI@ v^?n]=m) $H ;́rt4Ai3 "P g7أ#7K4y!@)?J%?ƿk{+H}Fߐ8xCDۋ4R9~y;B_)~&ϬC泻QIY>J +3@;D@S?ۢkiKP=U㿷لc)ܛT?lYRAtc?㼭XX\8  |@!T#$ܿS&'()BA$L_o./hB(3B T[4|*lA89:;$2 ah y#<Xs3"D 46L6B33ХT@B0ˣMĈhO؃ꂡ0Kx|ʴL)G.J;D.[Txax_dB8ۀA1ij3 /X̨3 K3X+i=8R JhD d+  ,`+,0ě \YQ'4Ђ<% ,#XC ( q1#aLx#`/ؒ $!P$X* P#C>ʼeQ}9DC m)oq*m9܀`Xn;y*ї@"¼Z;013+`F9+9,R8 /[O0 TcZRF ɲ5z'9I xPa^Kڝ! @''7\+XQQuV}XExSͯ Ҁ@( l!rڀLa4ٴ̘)4SPIiU9” "( &"H()/ ##@ʣX23@X0@Y*|B-,dB'ˡ @Za2a@$`$@j 2T($,2XYXڠVpԣ"L)@) Hb3f@*†HOЌS2Z *!l+@({pEh"Ԫyj);@(yӤ` d-3b5-[- P\1[q- ;-@{U \,Ѯ "M0#2~ubR፲̬Aku1/X3ak4m>(*78Ck𷬕 *4?>cF 34_3;вPP۽5L3P 7/ZI:'>p*hek6zl⁏: okpȻ;V7``'k:[8S ]50+:Z$()fY}D8!SGfk 0l *̤ [ ?3>Ӥ=&.ޙ?־Z*6CG1 buĿ#b+C3n3F@F3< @ʓd'puP&A|cDvB~Uc:j]lIHΈKdKda"gifi(iG i.HXiNfiib Vyj.61@VNkv뷆nkk껶뽮kkXj+ѡ &Bjhl>`;OHh.>mlf2H ( mmƕN`A026nhEhj~~Nii0(vlj:@6i1n8N&iYP0(7p.Wgw6(vNl0o`1oH'CnpFЁ?ރ$Pf &mNh1x om_c=8&'AF%x H>gGЃJ*xʰuqOȁ h9X=0frp'jC'9 F8S`s(7pPktX iL'箁WPks Jp_LsDhnoiHݙ@N=xGxVL&xP$c<#Xyfڅڮ_>P`8.CyAك98ЃL(((`7؁:2)@ ;y1[(Vlc{旅0:h(1rO78oVٿ4Gkj}ssjtwoj(o]w{0b 7`wbHt='`@vFLlH)`xn?zqX)& " XV@alDhŽ0#6N2@K<|dTcLJD2(Ox 5~NX@5ӊ,F0pV'ƭ"vE@D4"+_l9oV},Ybޠ "AjdRa0t "6=}>V[\0$c+Xȱs Auk{la=d92[,1QAUxY\D.tch޿]<ϏG>Qػ/wHrH@ㅋeAL cu@6-!G)5'LaL"'I҂eq(No$1EpW.ab9XJ+FԤD,QO|xA\E ,6d>JVjr0ΛC!x od8dg3:~RTg֠Ԉb'paJAB4(ECȊ~n=>|x2tK˜Ck/щCd SLJ )Bg3< 3wRt 9t&a+'ANZE?J$<`NT9W8$wAѐ+]j׻F*#QR00IR58#fԛ]iMأ'P7/̔Zv H]^'ۮ jYVhlQP|:#NjZtB=Ns_-BNf"!J|4rl##P2'C9RN2W+[Sβe/9blf%[^s3SYjFl ?/l]Br,K@f?`p^߬hF;Ғ4+mKc:ӑ޲;iMmPOP$#(E a!x`KpГWH`X FH+럪F4 ҞMmP_o;wG` N/! [`ETI@@G@Y &7+mc8;<chʱn[Y;߹_<3>dOv\00 N -L@-P /ZB6v[w~ioqS NW!, ТZ]t>WC>3Y߳^ѱ=?۟_~? M/s?` ``"&2`:B`JA*Z`jr`u`` >]'B  `!, H`РÇ#JHŋ3jȱǏ CIɓ(S\2cC &lI͛8sɳϟ@ 0 "H0JY ȨЫXjʵׯ`{ZҞ%Kt`հpʝKݻxMP? È+^8 NثRK$7̹ϠCKTIR' װc,I鲸*a ^TAY~CTУKNkȹ)xËbrv-H!˟_ϿMYh5Y9g F(֔ZUvHq$H!eB@3x`FTt =0E SPCaLzxZ”y@[0P--\+CYɏ$-pyEdth^n@SR0@n@@tDDQ) vP~M@)4P@~)-y1`A*-!-=P%H# 6띔x?AXD ;.·8@&TX^.PG.j0x*8Ia(QG 7t-t'B [S*P@1+u$ziWe ІX/M,@"Ir@}_zP,GPXg=琏.bd-Jmh3!7iG0ɐ|mWDk`8ƦtuoN?nT7jf-ә`@tIK}qz;Ɨ hҫ(;Tm͐B.{,T+11Fj~gGE(*/fQCE<}1@(9Ph " 8hCVL =8^ g{ FCZ]20t`  M"!'>W+) !R@2.BFpQDHĊAHbNL[#~5Pb& $D!qET d5hn9I0 TB > h !P3A2H@Ԡ<78G DKsf;? ArA5V2[+R%EJ&†4C'9_@ztHc  5 wBdPh@ d^Rrd|\Ldx 4ܠD7[t x(Φь,s`DGpBA X B%s}BawJ2s zè'?B4@M99H|N&NnkJ$Si6jU"N\!dPJ o)eZ4y[\窦FW:T.g;})]!ΥR SVS[O/JYH}XME[JImYZhA 79w( vKhIFVmCU%:2Q0K+* /eyǛJ`p.:5&ŜxZA` vl;#(IfՈt#},} +lĎI;9B ha*{_0잶>R;n v%d Vp .@ p`q-v(0N24;zi+:'|ׁ-s=u,6RX06 $쀗65u2V>:OQFv 0 2_,!L0bL}NͬiZ B!>ZKU")!OЦf"i $!P2w];U\>!a"k4`U\XB81M @t< H6.h@ %!1Km;ȋD3\{#t_v3C ( D"$#P9]/ 2 b:)ysHL?wU 2y$|SsۙFUK;/Vs]iF> l^'t`1vbYVS>Oz[ty6&cBr࿃igp/  `VŃޢtu 8X?(ePTlO::aoË߉@?$@GaڿH>eO[Ͼ{OOOϿ 8WH ؀Rn8x؁ "8$X&x(*,؂.0284X6x8:<؃>@B8DXFxHJL؄NPX"AQ肇DWP]b(SAc fxh'r!fRGq @5Hnfm0` B$-'*7PQd~ =1\: *pY4  q& f`GafU5j P0@SD8͠aeh`0Р F0O@0 j Ȋۈ 5)  j0@7ьX!ّ 9$Y&y"ًH"I;0 A-8 fA32?ga1ӴР+͐E]ȓA+Xj )[PNYZͰ`8*ɔQfP0vYn0j) Ys9Oh4I,yə1Iɓ'{89)Y.Y'-WQ0yXۘٙ9)IsI%&0%93{9bI2) Y왒HӛD؎ H𕟡phaٜ 0 ) ڞ"ʟ Нι-&q*ZZ9z$a;ڤʛW{~sGB7倢)ڜ@JY-C9+Ciْ1gٌ5,|i BJ왟z[꦳JzjʤI:;z:j|Mw}"%@<.zF5F5DgwzZ0 xs5&FSjk>`ߊJl434Ukwvj>RsyGV2:9p ez P) *J+u ݪPKz:`a" `kXPU vpb`VtP2u< r*˱;d{z ?3: 2`BPar*`J3 C5v` JkK |0ByP4gF{. 32+ 0Sw;X[5@{i1>?$"!=5z;+:?DP o5;XF@0yO?F[W{ ~g@i.2г ְ ^ ۰ (0 6`ORćh(L^a5?`L3lsݲHS ##G@#S J AP#72 V[3C q Cų03p _ E W " E1gJP `$@@Q7J ]EI*DE30|0${0 VDTLL4† `PSU<O RÀp H V UMyf~ 0E)\04!v`6;BN+@ 9@@C D0oL w<Da3cɆ09гlŰ@d, tClC D"̼R+8@ и=n^{l; C\͉ɡ2$KPܬ)ll -<˞,}, ~Pr|›ū0| 44k5zཿ غ ڀP + Ѱ̀ [nqs ]֢إX L}O ų 0K_ D'&X {`IA~%|cV Ϫ\BC.3BTR[r @Cp g l 0$ L p&|`- `24r` C_B bRl=ţ1zPCA%dpQ pYr8g2\$V@4P(&fRܘ0 6Ty- ,֘p&I[Z`za֏@H K ޠp(& M^}Eo!4&YR/eR u2$X LPvR">bۿ >d 'D{ﴠ]@@ < ](@ LS 00]!^*&+-+O/(,SXpêP4L :? Vz2@߂0)7ЈK "{lP(b]n@JE3p6O))j_HAu-{nsoENz{vP G PPA} \0 JAo"q?;_N>B\ KE+BF ބSPP"OF(3!4| _B1bĠE Q),FtB& 9-X`C ,u2h8)˅&7`K͐% ('H$@JzJ#LPH:a WUꈒIagJȚƉ0:ZÄWf >?΋ָH% ,ԢFh!sv nGՉ z@qPلMJii%B0d䅧1 K=0fM9ȕs)SB) 3#6р`bKD0/'2AH:B8C/;H:(&bAacb"r160rInBI(R~HJ4 Y*xh&[I%l@fl 5'rht %4@ 5PDUHV<8\#a9@bDZU ҢAO&j%) [ȅQ?AVFֻPR h ?x+6 &@L9dU*@"8S\OuBq L` Lm]>0壊 `_SH(6Bʽ5P-⇮C@(Jp@Q#SlXhh_Г38F鰒¡ 4.Y j1)EB-zubE^@ÄV<@N{C=&ANDDƵX7`kWJBLf C;LufĹU#/.Lam(vIٮ9mO- A z llDkaM=f2 t%/A>!u}GGTphu^؟4EFvY@hP&`cX$Xs\K dy % BZA +vxB7Xbq)0}C'x8,0 `9d Rzh(a1H!Da(hH<$)n%=I(va!DddC. ?_"  6uu8[O"JxB+h 0Hp Bc`Vx9RiRȀӡ)M' ӕȒ SF65FJ mS 4A+d/i`fZ:Gb`(G @^9X`JGX;"NJ 7\@JKI'ŁTRL#,iM]F_+x#jQH"?;F% Wۉ^R Xd9B]Mۊ DWoJȜJUC+I@Ԡ\yIT5lNj$}ܪXU|kcZ+e kiY{SbIZˢ<(j,#%-u)qK\"6\^K{ɞF 0Kz]즃}*YaI$I-g~`&lPo{Iaݲf8Ys6GLg<9m3g*кD7` c$0~C,)WK (.R4g=fG Xţk,Oe,͒Q!JQ}OZմݪyGKuKk'>=C ~/#>}bߚ(D f!&ާ}hNK㖛S< J6΍n[RIyG\ /!ݻ0|5v KJ$MuC f[!Kȹ $)AF./تE%X] 6'v u\7 0&z10 a/{*'j3a86n"b6 ZS?HY%s=Jlޮ P7q9!)y}bO^uwz~>y__pǾژ- sT8TP 6B&06llHVy+`&* Qa-sOCtPsXj%dЁߔPp ~ > xJpFhL*5p[ ;-@S'0?>'1+5+'a4⡖-0Aۿ?0A60 l0ؿ=`3R](M0 @)""+H245!02X>B4A-C|?34l/<@j<6*3}C-XB3551 )ATN" 'A)Q؄\$Q,#CE|?c{;67Ax;C[̓L*(kAJclT[؂P:cEb3"XN,9$QL00)A\Sի{#8Hq0YkZ^xlȆlӆlpj?Ve_xA_@I5@?LpGGf<.8@/0LXȹBp48Lہ""4GbI؁38]+0^7٘=hC3q5JHs/h?P*aDJ*aJ(,3PRhɼ46"-I`UG$cBlʠBP8-"JTJD̂LH#Jp/P(d.t|!J60ī܂+4D PO4hD;L=`3LqL<TI[89`¬Kܬd')Mɿ쁠8=R(t=3JKЀ[dOÄN*5ЃlNUxT C =t e,0œ?2Q-M;04[PZ9;h K+9<72؅`FJ@28C?QN9x Cl <9 00PJ5S0]KYShX=u {+++8IS`M?rML9NaM8,x&l9PfyouU 0pS]UPBy5Jzb-=S)̅=Ϳ^@OO<6V!?&f$X/ճ$J/S0U H;( XTx4 ք3X\o5JIC{D;H+չ(N %4xX^:SJSZ)֑,ZeEXK7lK>r(5HGDd`Xg(PЄlY+pp˃7S5(?hCS_(V*8;&V T^ 2(^SEmT$0CTCT? K\^PZT]gṶ-&;PؼYU$83Z>UVfRܔbYokg TWpeL]2P0HXȿ72]00^HʼZa>Xy%7Iо^H_][?!Y^a;^82xK)AXaKta9 am6ү1U ~EkMbT#kM`_Iګ]S<[1S,5Zr0-p`(WHJ_B4Py `+_ܭf삣K\NNG>eB$DU,.P8ʂ,xbVO.8߮}V%VpAj@6p^8'o j^ȂfPEpK6 n :o7.6+2N̒^H`,Zco $pcfؖ[ԫjNӹBVuZM,TJ)1qg{p]m-*`vH^˛LB=jGd;9^'7κHÃQk8u{Q9ci]cm v\ٟY/;v~kBv\NGUsXdk-.ShV18Cr P@I56hK M563I8!Zx!N(dsM`M6;|(o@ZmR@#R kBf`L$ opo?hEc*Sv4xE,F3\Å F8ӥ2)#:"+T4t_)6(D6f#eBa.6b1 c  {eFCSFNP 0E~9OdzI1 +Y.$^I$L+Op &d! &Z2%]j kcV"b7*H^(z˧"ӢjU iJn"5Crmn ?xJԤ/w9flp I)ǒ1Z &K.t^h {q J#:@r6FKA $(A 7Nc3| 0a0U >@1@(uo MӶRWse,PԃA@8$u -yhR) \]^茪"tґ7C%,i['mى sC= )Kok~zʢ~u~H)ȸD('ºƊe -xxU5XZ =u HP1)KmVAo)*SJ%Ip{Q۶-9` @UgI BhA ^pp77g@mLA~ `".~. ʫ.r-7vX 7lp5jP`>&g\eW 9QPAOq6)0E:O. )x4Nv"j{#st;xsr;5w=7ށ?* |4 A"}Z$A$b]8qMx cZ S8}!s CagI ^U!"A tsowdd9.DK?bChUv*ƒ)vЏkG v}^]G14~Z#Ё6`>gH S8 X`FN: f`ΌN N z ~ b` "FƠ Vԥ"|}B#!!@n6.!&|b`k` g` vahf v!FA> F BB \(~!t@0<l.aA4"`u!^` ! &a`&'BbCh}A~܂D%A,0t B0-R@ p #>" fa&~ bi4`~ !)J~!3Z):B#* j@ItB#\- D_F '  X!(AVc)Zc:hC>d)*$ $"$D^B~%bd)DDFz$IZFd @+"~LBG ,j0b# 0PB.B*$EvIz L)^$ d8 6T6`T7R^B66% fGZ%L5d b~$@ $%x%-$v+!0_adVfCjNJ%Df6:YR&IB&V68uk@##܂^2 a-`b.fJVV&vhff&fdpνF @&#p(!&,Di4'`@8mF%`|lwhaxJcv%I:,AN'#`Dt@fJd 8a'> `'v.eW(Ifz:BT.hFIn`OF}kԧU@tbPDZfzbJHT6h`:'j0j0|!bJቦF#,>!hi7^#C!() ii7d)"2b ߟ*&*0)X( 6jRgd*v**S_Q{Fl!, H*\ȰÇ#P@ŋ3jȱǏ CIɓ(S\JI͛8sɳϟ$- d"УH*]ʴӧPUZAQjʵׯ`J`@8ª]˶۷p Vܻx˷$෰È+)8Ő#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ3u M3 Mμs!KN-سkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무ֺVAb # `|N# {(A3TU%㬂:?Xel-R@F $`.V"%tp/qB+3I60 K&y\"!I\r¸YsaLds@U\Tr%7\/$6RM>GIqb0 BP5DQ$ K6pc@(O܁j3c#QC5Mp?p (, 0@T3<՘`3}EQ|yAQq@mz4WXXP\8FOZAsm4   `h`T'V|TpȂ":Ap n:(n > a؃ eG@-,/@$a ?q\TN%GЁ}&Q^@H |< m~;?# ݷ_`Vg]C@ DC )D $;P #k!GN<vL[Natp)@|=@Q;g@o`|bXF-zcB^'(x\(x1πY* JI]pס9ݧ ?<+n XhVr0r% wuFt[{~f~l/l11#@ 6 6 6U= U`ڣ0P:6GwPtO iP SiHvRoEV@ vO1O `F-P~:0SfSWXVft!p]A =UlgV 2e D ;i5` pFI0`&=Ѕ\腩Dg@ u`d+w6vV n6h+pEPpc  eJ@lnJ@f/ cGˆ(W}XLHq8X~40 AvvPp =58X{pBeiF$OMgӣ SWR@d=t8O?} 4@掔W IXAhG"dCV,!Op  `6@ рqY p\`o{7E9V 5Txe ' k kV:0U%}L[ĒE 0>{}` HG| >n#U8p:Y+ ` gyz%K}v (@Vmxi蚐ٗ` ]6EXSk [X .*&&0pj'Ut߈Ө:\mSj= uЎaɉ 3!U>0 XVT'28h5N^@yVFI@ z) *۳$ v /S3yGr P QS p mԩ" 3F@ o1 WCXJVYS4[}WPgi=p ,NX ~=rU yAiUOUЙd=:frFJVh.0r*~iR08epڇcc u;`Txw:Ӓ?9 0 ̙Xq*§hMq.Tٌ @|_ 1MО[` fO0⊮ SD^)g"j\vFkczcSCddiV*T?l(=z+zPT 1iΨTt5tstC0'!  `Q@ P7 P`=` 6jPtWuzNPhq,И37 [vEoV,LpI pt6J 9Np C â)_8&ii |+$ 2< Y#@ L ebhG&'Hji0I(P.+ 쪺@ Y{hhe\ л~pPX1 [pu`UkH  pee%G0qL; ! [@{вk@ p `=P.li1Te?f\WP` ʠD?I[a1~"lR SRY@  $j0Q"d C 0@& :>sS K]Nc~h/Vl  / Rp p# @ڐ cB > p׮O>Ƣ ?/@..-. <nf =z/\=`pY0Njfp>g smg D`8M``Gɏ3ЪͮMX~d1iܑm ߟMvl!BV9.  "N$-* UלgTP zZđ,3a6M< ?;Z3GOQ#*H'/B1EA)^ 8递BAYRBTRJ"?2#L΁ %.z4ilL'+,ʋV*˘+ksϡE&]iԩUT8رcPCˈ ito(SvD A-+f<2pP2ʗbSAyկg^vy!@Q.PpT@ hAM̊ C 2# Рe [x P,ސZKP {A# ~%DS) 1"[1 hTF?xȲR 2V,bPP4 &)k?Dx(xTZA] YR4@ ZëB!H`c-܈!CvPNxUld30@TnetM^O Ԅ(aRR@Q FWv\LmU \q[rm͗dh,vkvyAfᆇBYv!('pF=;Jd{Y{S襔NP'E:8 0K8i(nWsRhNA3,;e/@JÌPݙB0RUM18/I?/9PLIxÎ?\y1!,6# 1@#=EIV!bV? MIXնČ1-RLKH!hn A(EG%O7B_GUA"d'6F(f$#?7뷟w!6`h` 'J9*` lE6[|`s_`>n "6R`1|YD"+HfxZ\ 8KvUA_)C Y`4t1Aj  3VbpHsBD(ʠ3$č/N&b#eJ8 !VJYTX- f;"8,58׏4F;'YPa`A ؈+ Q:W q`3 %2) QC]!VA b2yP#͒sHAe@ۣRF Ī :#bZ3\JO}=$ ^$ oэ^PF&40!6Ԡ,\QX` $;h x C4=`L !, 7 J% ;)/xqT U≜N`=RuiUʀ @EAL(-Y*g p^Uc0BYT!@$ P]y 78e0R ۾}xa2 MD'`E XF( j_mkZyRo&em+(+Mug<R.NDA4 [blU2'vm%MWf0lA0d|? f7\{nn`bPFdy52eq `9d>T4$zEqV%brQld xJl"94dH3 $d-X!e (FEl`*|g7?X{泛CV`f*YpTlGZQi.!Tdp3MNӖY:Vqjpz\=dYsS'GjjTVvlh_oޗ aC6wHz<72kfvnX7\nv]ocB=wzg k4 g }' -ލ_8cƇ1_f_nGt' }6G[4 L ߿}&+0 g_d ؀0 >h= 50 ; JX#(;x ;<ýv?P!pA׋0ЂĽ+ >כ{P<<>ճ@뼱{H; #@ɃCB'Skmp竂S$H(0C?CbP#pS@>ēœ @XVq@%3A Aȃ6NpqaCȓ0<;Q?N٘qK)O#x0U 0R $8/بDaJ0De??Cl IcLg,@Rp NXB.`>聸[S{GHHxqHm8l oȽ`P:K #>+C,W:l,`48hL!@@Z$0J.rĂ`!? (-_.? . [(X:|#8SE{Lhq-X $&0L"Aa.؂wݠ;\ HJpfM 0`7`H3LDʘMCIqP[;a X8n(T@fK8bG J $7Z Lu|ǣd8@0L|ԙ)R 7(\UFytE ̓ T躁#O@ظT~|,`1˄ %PN#|^ #P=:7H@P ЁPQXNk:i5$B2|@,lR'C(ߛܼ DmQFP9PN c3Fۣ f; ( (HJZ A-9%QlOuSB麍= #ItlɅH(NxG.6hiJ j X8h-FKA`D XSjvb{gq`j=㜴H筮k`dK{ٰեa T~< |.fkȎ5 Hoe5'lϥk>4mlN1kX*DSSmڮm!quHkc͵mn3e=WؖB~n~l^ l&?CF9;fl>o.@/]}M]: lvؕㄅYgذ;g 8FPneuGWjH 6(W c k + gKppouaK_I)F_X( 0E vO798O=Ki2nTwq]qUr3?[U"hc"k(h=kFHF0@,^EsFotp/pd o̓ Y_XI @\6ttU_uA0cy.mdu[u90 \;f\vVo | qƼxe&vhWu]WE&WilvmGH8+c)\Dmo$W)/7D0QSygmswwp$+8P.r ]$;xGws qp!p/Ghqhظc;_xO>g0_ؠx~t"+@B75ެyEi ~W듍g`` 6{zg4=`ba2!NZq"{ɖ]3kDLlvl7{h&ůiψ|>u@|h|<fDL\}HWe}}$}bNSXqۍ}f;2"K{~Y8S .Q'qҥ^oQ>(~r}aTD0@dq 2l!Ĉ'Rh"ƌ7r#Ȑ"G,i$ʔ*WTq"0N4f 0I)\@ˤJ2m)ԨRRjUK  &N }SxOW,1]z//(K) Bj+)%\7s3Т9+@pB%(0B( ! vT`4‡/n\(G@S *'|;ǓN6ϣOWzv(o>? ^x t7@J8!1" `C8a^!ށtVx")t$1vFH#`&9[؈эAhD#%Kx"M:St\C2n }`AXk ybc\'P9'ubEE5ALT]S #BL&#@I¤G+4$)>'QP T16L.~@:Cz& J4:lENŇ'dXxc-| C';/x"8NĘBU,6lK"|'o0"F4*4)#QD&j1R# 0Hz= >`1)0i=3-8HTL1 yE<ƌ= 4Yk4Ÿ2ai}m-9NuMl7ߡ!IgL6e7+Yׂ4 8[N=G{塋>I$:xCT@!,+H*\ȰÇ# Eŋ3jȱǏ CIɓ(S\yA&.|XʜI͛8sɳχncϣH*]ʴӧP/hiˇQjʵׯ`u@BdUaӪ]˶۷>o ݻxKÈ ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ3Nxa Ɠ+_ޗЙ NسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*[Zr:IObB?4, ۥYn@F+Vkfv+Z6]Y{mcWDテe8KoD~\[pc %,1Sխ.k ѿ+@o kB0 b:(@@q@ep$L?]V@Љ#2gE u(_% F^܍rkg82,"[kB ,A B'W=*bȃYPC#b|,!;Qƭ@4M4d `La'I`I-LQ6 dQH41DU`(l BxQH&_V(a mHbc` 4/[.J8 ض@ 0M@kA)\j(3hd3 4&A4_e XXNF`b3v_Z)^._m*}:0n\3Zd0v鉨,] DX6ꂉM]W"IJ-{9k0_Ј6W"hU?ԗlJ?sHFΕ .^(Hqy5JlޞdejsvƆOfU]o j}&}s(7^9>/{ کo r\g$,qKvkFwwv671}!q)p֑w/'ך Mrn+e`i$P,hPhHK.!*X𙍡  nw aEs b[]^nmB[ޑ\^!d&q͈|!2DBpv^::%7|!PHU4w2t0aO]dLy>yҗ-.3" CmxⱨO|ülC' ,gyE3w`}~zw;WF] H P’O Ҡ0ncb:0&7ޅv4 W}p~|*Xq ^p[ٶawrSl2{,؂w%V7 Fw*`$S `Υns [ B1wt "@؇# 7%P4PhF%`yYggMP q|Ms@f }Q A8z'Bk{P ܅ |gfP P s 4X{D 0XKX&0W` iXqpQ bhwMXGӗp$K-X~ yUYytW`iuȇ?;y s}ewk }bGBY`[`  p0VvLD9g yYd1>&2E1)XU` A @b`@Ã${wl!zC Qae' %zFx@Z1C8 v׃~pDgP iWYP>b9yz  N l@zW>Z ;`:)v`v! \(B{xXyI`oʡ@U40ۗ? ~|_,Pehk@Ae  CV[ǃx~e_Q: GJ_ J` a `[ )   I  Y䋹h@HS|i 6JqX{0yA~xi~}sG( ת*j..z~uG/ז{ @jy]s8&ʧ>Ź7xwx׮0:ڀxtw} z ڄcY> ުy|75×J' Qy!zyV 9ز 3ۈٳ~?Zzکzx8F{Q5D4'I5kږ=װǢGzd劑'+xLxs[g-=Kȅ_x`|I9"7or ÜiD65qRp%jCjn@GoP\3i7j@ t#>[FM6qp) 4r1Os't1_IgS'i8O@v7{ۺ+E3P9 x/RPiuen|6 Aк?p6!Db Q_0Q ;֝9 HPF0 !u+` ` AX4|=>9JPiPE|Yv2)O17ԽQz~6  ` awl@Ub ,X 6`RC2hsFӵ;56"G+/"{5?')wFۀY8}ȝ"ϖg%֍ʹm 3s9 `OlkB*! mBf E5$ " ~[^  <r:P&"+@i@OHA+ ^l]TX^ [PtSEԠ# K >抾 ;KIS b Pa jm{P4e̡x P {*1oKx׳槮ZPA- XPP$ pPp퍾=.O 6 {P Q0 W"  QNnN] .0Vq.~A~<b.Q2  k>o/#]+Yށ SO.1!, H*\ȰÇ# ,Cŋ3jȱǏ CIɓ(S\2!$\ZI͛8sɳϟ"- dR%H*]ʴӧPAjʵׯ`v8Ī]˶۷p Vܻx˷$&0 È+^`†KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ *ȓo牙CN\سkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무V1{aH$+y. $K/B ,T{ b$=.X*0XQӭ %@{. ?ʩk l@ $vl Vmh^\m ,:| #{,rl,`fl<2-A@=? (0>\ # 54@1,$K2jhՈBGu,.+,! AU/R DaY$W"n2,0(耽+R>V}F>TuPDV @kDpTS0pDot!Nϩl[ P̨@l!Kޫ@\ul|wO@&& DQ3Bs'1wCPx3]yL06w04wܽN.6@a;{"-²1Q$X!2 c'OxĪe{/a!v 8dّf"yI)p;)xɕTiG23ɖ-nu c^bڥ3^7sdhFjɦɡ d7xT2 3C^60VJ`JT #P m: \ ` ~Ebm"PO zPH@% SH@|ƉQ %T \LS@N>KohLD Pt4QL@ ,*gJf;g:!e50 ِ ` 7 ؐ ܐ  zfR`m @`Qp KI%VʭaX@UI04M XsTYDPO1qZO0GŹ0UY01Q HۻuWj0^4`W5UϫU Y}H:Hк@3WS[ORVc4X0VF4q;$h3( P q+X~ .qxT `G{Yԫ 4^Z[C N%LiLk; ` `U[  @97j%аγ~fwysF;#U@z0tX Ud 3@G |y`aUan~ E hg'@ږD q` d_^ox6;r7_Sw%+p^7Eʔɂ7}\NC%J qEu~t JlF tWT^uCOb:;u6+PFV5MF˜)P pT 0N ,5Ck[WZ| 8@4 ΀| ;2o2FЪ;Vnz )` R&fdsģsK pI R-LW 0Pz0,^PǛ :,p0JP Q Fp6p;tVOL}G)-I^4X@`duewI0rp}Uen}gxg Jc3s`֓|8SEVi:ؘdsys׆m4hL_EXMr o7Q|"c3 9v ` f`f Ʃ@ mFI :KGMwvbGv:}v UZ,aP#H]>w|0v$]( pIw;@Q#X2 .[PǛTL|5@H+mZ G^ F>GKs-XN` ܆@dQ eԤ]mFr{>tMwg u mfgV7vP pOu)SJP]_yEks|.̨.=Zo{I/21[  `6@ Q&wU@ 6`  ^O} %p 9>}` GPYPSx|%X~aNp.@d~6~.A`0 @N | Ɨnxb˜#00p݃. 0dJ K /0Bh1Q~_ ȗx+2/7p| ~{k`DNi!|S-| ~ <uxapι\!P;O `WH{[O M@p` ( 4M"--S-@y0jvآ2?YؒpmJ`e:ڎcMjvU.׳CW*F翏8?Ml_xUڤnin7/ $h.T(p #:(@ 5xK'CE!EbE/|reȗYΔIM9u܉c: $0PtQC$M3$ZUTf5+!btp,Y˶\ڝb4kSܲw{D&z?>14@`lA\uw_~#H& q!Q^|Q&lƚ-gd eqf4@fB  ePcT%9*}-ÒUآLp }cb@TjZR,drPOhU^@hWkhWN(TZ.N,A?0I;DYS#j$(A^]LU(9TYx ,I? `Q8Тd-"f @Z3H?$k `t gm Yh8-,ʁS" t:%ѲDР]]ǝAb m `P!n 936vHAn"ȧ_ Em_B|lXD v~b`A6U @…!nu_E4 `LduԼ"Kf2bf#XUYpa7=H9b gT|v|y6!!czʆb-ʠ1\aWEA?ɍrilUFNq3X@8 Uvxb["m%xMxbzsdPVG;6`p0YopE[hqHE;lCE0.+KiC99 1E&؂T<] Q6UC( 3K>FoLZT៊i@+م@ d@x (p Lb3!0QB̊: B.AI($/aIbjĖ$K ``Ɍʒ $Lʙd CiJ1I$J̔mHG)$!J,Q0̔FiKȸ˹\0R,"Lt#" :LL@jQJgY LD4ʼ+yͬtL4pwɡ%@J$ӼMMsH͠K MٜLt8Nڼ>s1N֤LbLˋNL 0,2,vxPPR8T0օhVG<4 Iul]֑PTOM>E qM (ցRStm{ w NeV]TKu U6UW5}PIЕ glY7 e# 'pW=x9 >hJx CP/Z8P͌O%8)XрuIL J !EhZxVML0:ځXV8`LCyY]\t+ڄ m @  UPٟDm\JR t5R `B0& Y`MOYBL!,.ӝVMB I!, nuݱߵ7:`> - D`@ V0.[ų_VPXHjB h ]x8I2"V,}kL@"?[H~U#Vi%Ԟa XTUυ}Յ^S028%u c2ؘϏ m`BjWYf8 `p+Yv"x2>uG;.2QSY1x' 8h 86pI2Ё* C@,(`9IVϟC5PL,^^eQ>efffA4FYjl( lO MRUIeh؟_ntb @LH0V&nqgSeX$U|a P-"埸YX]h]*`hi6ck jkw a1 YL9USQ Kȃ  05pQl0 Z(b}E2`M"f/>8_V?X2`%X(CkkŬ-! 0<%] }Q S뫀r)r*lQ18  n>с@)՟p*os7s8g\!K K(t;ngp09U8tHtI+ў xl8qapn*U @I_uVouW"`" ܆;#kH>WOve_WcmtmRAavmvnog KQ6n_wvow~B* n셀LLww}w~eoxxXW|w_xig7a|- ƞRPplxxϳ`dG"J1<`n9KX}? /K- %k mOa+@BQU@~/oMŽJmJo נfȨ֒v\Fp7y{m*v-v5p+޵1pEJ ߒhTS< 08%^p0b"D@8 ۞@1@C QBf#4W6lh._Œ)s&͚6o̩s'Ϟ> *t(ѢF"Mt)S0xH~mկj(UCF(gEK&Ut.ݺvͫw/߾~yGB@X(9r$Enܡ)T ?-z4ҦO{%;P. A@ * l mCqC&B%!RC.}:֯V僈D 5X906NkmH-UPX B X"x@bGm-li cKA!(H}"!B3X7@:hF?bFPCK<"OBSRX[%G.qi^XgkX\I/Ǧ{٧{9UK)rӜscw٨B)LG…zi v$QFxP"w(\^ 2H8tXڬB@((@V:dClsQF_ 8ĉChP[/ Ĩ= פ60M^FqЌߒ[c*?|;lԤ-HI1Ї68Ԁf;W?aFCRȊ JC.JrFC)8pr`ݱb@T߿ LݤqT#KL؍TU\MӨ]ft/7c˞Mm(%v @ȓ+_3ځسk=4q(%fӫ |D XOow qf(&`KY F(R[AU.xaQt M(n79,%0I8(`%d PTHjw(H0IViv !=@6Xxck,OZ".P{G|%2ȡd:7,$ >}=ìo*WW귙U& wot%VuU‹[>0IVw)]QZHA4j++g.JWDk2 |xl g >DiYfaa8  Um~Sq]p3N'j)fqk^* R񋧉^(b/^ -C1]V`Dn}/kcj?a8ೊ@UAң"e#0yJ[a` A4I, EzR08HZ |A 9O/(@VȀ_XbE(P# S- W/((IA'"w$M2*M"b)R f7יHD,x/v3+o:m$@sϪ쓟H9І. I4Њ$$$Mͼ iGиP$HW Н1?^cL¦$3A;fSe5P԰ՉnQZQi"S겠fqT*iLTRYYjŵ8uA[zx9^:ؐSOa{:2y)d';@*ֳeBKZeMqp.;aK[|HͭJۅKܒತMq}2΍tKDz x䝮yFWnuKw$A0D&7`p#$7o ;7=0`aH3ơC|øWX9"@訄@@I@J)">1޸?,S!8u B(:Yv%=d52 kHA ?]`֊g*@5#-FG9bln84QKq@TlS p)Kkc㠇@DMxiHzhBI~"?#['47+8-q(OWjV_;&EMwgvxHx'2NLƆ& wAm D|al< &q -!hRX`o>w 8y!nh"b:БQ!АnD[G|CwxDJzַw9InHzۯXH Zؓ4)bpAs]hc =ybdph5YO;89 GO }k?"ˇ=j77}w|z{x{;qQ}|7qݶW"{vzs5uLbt7&}}('SG~v~fqw|6~6xz9vpa0 +ck}g|?X cIGH}xXr xHppZpׅg~|Pe!qߑERݧX{&Txq(؈3(+x[rr:m{j8Egc1H|2  HMjj7ycH($Hb8av0zX}+g(z؋eXz8+1WVXb#(47zs2R`rGgFvJhW3'\$S#:0=tx/@s Vfq "C14E44>P=ɑ&Y&i9>Ro^t8CგqP )@9Bc;y44C`IS@da+`4J|`CpK#"U9-C- K'ӦQBZ B%/ + :K*cP 0%Бp3<@J- 'q`A";zr O:APk'0+F%# UVP `HN)E cјې  P P u`t@7q :bAXp[ E|o0/5gub9F?Jw3# 5+$uK-& *`@ #]Py'- CA?J+:Fw M`U;AKY H0q)#:ƭW 7PJ@w@@MAP;! ; ːH0/@0Ӽ-M`\ p &FPQ:ƽV z`  |3# |MiXP (P@Kuu CK?Œp  >#9@E0|:}$uP#pE@c I Z ?q{Ku(6b'P,;:PoF -0\ &`ZYP 5A:t@-P @+p ص+; oG2rȤ:M #/ D"ZIŊvqsP &j)0 @ !c٣^,gb!?0R,)<;pEa = pY3;C;`[+7L;\P0A0)(](Lni 3tM>" np/p@ a PdbjiS4pB P{ӏyl0PBJ0ًL sd@0 p::B t;N#4;M UorI- @LMv&s# ` X0V ͐g\ Qb_Җ-w?&X@@+%ՊM7ܴ,loY" ,Qӓ]09pI Ecc*Ya%C s9aOF}5pP0+LUK22Q̟,JbTu 'PP@ 0-;6QϗoPc:.`Y9 BEѳ"j5LXYf:pF0% "]-Ps" -,!<۟ ܖ.%M kZ"1uA[ %=pS ,V0F+0dX%#Y0 `R@ s;t spn" SEM' ^]a.1܏IBC 0$^WN蹰)-Yp K\^ n iy):Mp \%Hs3?0ͤ/` ~P=εp »,Hj`uX$m>2,=8` ,4 !ɝz>  (> s}/ ` O\E)[C<lp m5[. FPZC>J< ;i[ | PuȦC;Ӣ"F;[³ Hp` M a5`:p֔>k/ &Hinl-8j.p" 1Y,p*`W"lm阧B[ rگ4 .ȑc# Zq3!FzA Zx|XUe ā)O'0ȢJ6Ԩ"eB[:" $RrK,NErW҇%9i ˭Cj$I.[ l3䄼2 JwHI( #lV tXIi."fN(vllE ̀_)(ҏP,NaR)mˁb) j`$, kLxKh֛(B  U '",ҏ#/%7V$@rHz3eKX5*a .cO YA""%`"(L^#l Z6PN "!@~ᭁ\pa ?";<)AҭNriB>(&),1bCr1S2.>aA Z¼$#Z" RX@"5ˆ87;?Q#g O !St~@^-CB(Z\ `JrOp FԄ\ 8y)j!eBOd1܄ݸa)tʏH 4pbVu9J d*Dt4!)C0j @def՗(<^&/`! $>4O,MRnqS!6 x8&:? 4Rڈp%9rV^[귋#/mKѠ,>(94%k-ȉB`$}M{׈Jr@-bD6ܶL |_7bO”Kmߝ?ϩxӶCœ^Ɲz- (oz~ :u3a=~MKz8'cnI7-@B9aLl*\)a pn6mo!a0 jr7&-4D0* 딧< RhA"4GDi-v  I#GݽϊS"⬧.R"4ũqkhੇ_s '7"ěrtyB7&S@Gd87yT'a2CEO!_SR1hIrv|eV@3zD"|C\&nHP4A Js8}\d$ Ff,Fsr-E/SLmڵUc<&M1Jħ?XJb ]eBsJQ'+@-P [1Pj2)rĥBIQ:<$9dM)/(A2!M">.#Y2j@2 DZm=aD`rT"^@(\Wߌ( !8"q,+?5WW晗Ǟ54D+6x7b%(.Q^OעU+Bt'TW_AYgKh*RD"pGn4pqmTIJ  R%q1X-%h@uÚҁ@/ rj™F.8ơpp( wJ"?LK_ 1papO, [ z =@֜T i%[(E!av cE H%hZ'P1O؃CUKC}&׮e<  (#0xx &H .*@O!~!0qh Hš oSkE5)A)nf$bm8*ڹ1ZB٦T<0j{G= KE6yz H-u.f+Pgж*PD`)6R l x0d<`XG60D}AvK#F ,  P?E:p#-N%HxR thK@:ЀR@5?K0,Ԃ @uĐ E.z|#hD!0shA\f `LeWὰ`?PA.H q0 gY{h80@â`>p<={ Q@*1>:h +؂:HB89"`>$H7 >KIB h4= AAr*k20] ,&{J"S! !XBHg[ g D+3#2)x;`UH)RCKX;($0/;x0+, H*c!NJ耑'5L0]lkoQ'MЃL`s $##lK f8TWH(aZ @ \+=L|$O\&4SHM;؁H+;TL+x,|RCCc+HǔxhGJZX+y8IP>R}N1I"ɾ.G1$ HOX0FȅHJƟ($oOU %q EzY(TpѥɁlH - ( QH DkN (0Z,r3KH'L0{N.Ka$8h8`I@HMԻz|#`XC1tN#Y[7 `J WhVЋT"8جhLZP n? FhPW@?w P:UL"/CBb8 (s Ȃ-Fc p`Kܑ[US4  @.@T9EWȞ: N јZm!־ׅ"VKPhl/ԈۤRT aCZKIȠjm`"m͊] 9h )@0@P܁(CH KPݩ(#y.gʸ|<`A2"9~E'I(%S)Kǁ aQ v ZŁ4(%'w zij<ٖnEaPXXv5M# Ёݓȋ %6=忁( @NX UVNp[ x#8^٫)8A01!`?!m_'K 5m@ a&[(h Zh0at?*K3F šD۱5\?)¨!*ȟ繱(&Í1TPLKh Qa* hQy(\ @E:Y?%O:trdBfRαL_ɪYdd$< :Uj>Rf%0JNAӮTFH,5X>;1rdIv6dz"0UYg1n(9n$YhBhh&ZwC-ku*\<^+V,À+Z%o$6[j"zi`c5vi5"[pB?zho%%+ԭ+Lɣ-nnkB&KꞾk6vnz$dS#.:jb73'\yk(ij4/::f#hA9T[բk"EԦC6\m#c="4,l(聰(/""0svkk;ޫgV"S2onܙy:`fw^mvn2,C\lgȴ X!h@2]cZ]ݎP$4 ( .ಢ([J8S' %ȮKqS?nPFSNeynnFvm"rRCDgp尖l؆djH``E%3LA ͹A),KDŽ&y[pʵ4O/Zn(݁Fˁ}ԩd0p!}50s%"8!50 5k+MrY >6q=قj%7TWtYmsXQ3Y9ARHXå-H itJÅE/@Uov6rO98[Ӟ{ʉ5pQ9Jaq^)%;Ox_xA~;%hWHa0A'dXUɻ30R%`sJFTJ %d <4Ig]cD<:VMNYp>UB kDCr>ÓZHͣ(2>$4Sʱk=<-V.X˄& [@T[cl۳ٌ'(@Z?B ӿhp*BAS}L0czܼ|t3*S}#(&܁Xāh@fT (L1)ǚkO ,dQ>W 7ĘK#ڄ7473`GF!@J0pȑc-q #ʔ{y섖)(dH SMz>(*G%Z /?˧PjU!>e5 c}Jh'$%BGgԀx(9 J q- r1xiQJq$H5Gta\@" >gG fC2.7XGP #97BSD*p\4SFqG|D9Sd1\>1ĢtRlA XH ɜA'[hC 5D47JG3a5w(2ǑW &-N2SV aC[EscZvęZTLԡ%gyt;.嚻f y*1!5BM4`0UQݨɄPyQ@$"쐒lxD #5  a J WG'=PJRRG:7HrdƑCvLIM4}…z0aOAFB+}-4%ڹpϜ*9.p%gT5Y*A&4 A C tP 4h$@I!Dȇ㑣-@ڥD.p @2fJ|H,hQX@N~`J@JZ \#0A 2 0 dhQL&0"DV"z#_npHq H*y"r$gئ\%Ny$ 0 |tl,9 5:Ɂ .z&6t]48' N( Bk@E*ނiuML-*3h!E/iphl\Mؿ%B;e_wz]6*tdTKr $43XAm)u.Њqdd9C!w|Fty kq# 2u>1[| ކM=Ysq =aYO 4 kvha#\??=9+pS-sײrX'ί6䠐gTWJiEt ý:*xBVLa aD p|Bm@0  ~װCo055xTwkH{J '@D\ .6AA8`@L~_D HL~NCla~D +<@/b !(w@XN6 >mC~y"7pq0a!qS(o)E*=^F l!B( JXB_葆A ` ͠VEJ-trqJD6dC6T83T$TyAڐ,/I u4`9muչ)@%`q4$X[  Xi AN @ZI+]!lamYA#X+bb%8 A0K~IBT "(ZZA"&1Vl1<08cTG%6@%Q2A9X,Z9#@B,9cT`7E)lAl"]/50#>ٜtB ,"hc%֎pc;6A`u@!! $6 G`H82(C!4C/d3lC5B!܇ |D8p2@A 8 oГ]xסaJTAX>B (a-%H%C ($d4 0ZV[Nϩ[@&(flf)da΁5p#9l ٳ)`„S@GX@Z)(ަ`q'6$y `eO-^rX,-# D pgĵm]}rB5FmT6gM%X5Z\/@l'$(8= *   A"9 @mJdVE~00(D3B 4.dæ%p޾#-4%h1+#7MqJxC?yxnP| { ȱ&[NkZyn&N JH -2y`H^3%1/9pXzfX ǽZy,.8FIVsqD7əZZqVq ]8lYN¹WHT[>r.6#*ĀTٖ.B#`B i%?s@s:DKLF'VhҖəz:O 4?mZ(LZ:03M)sPA11(C)\s? l-7[vttqg&I'DCuXi[WZJ\`Aw>Q&jŝWE_xisP藍xliDS; _3o_.O, z%dDe(e'DIBЀ]EMKt Zi9dQҜBwssGt9I֞ֆMS aOd?R4TQ V6 sT+!I;QbAt!(P@!,Qy}UT}7L̹ysA%lNYt?Ɨs`9xT%dd_@恩\.FV%|)pD%TݷTGBֱSvLsd{;MCqȽ{Mc!9E@:q58R6lJB4)7#ϗWs{ N)s|em54ra* %7HWERr@dc"'I+L@pH<1Ү!! fZm\uU6֫C glTd=2g`Va84 xO6b1)+nP=^;qȔtp6u;oh` pDl DebT[7%^rB 8T$)8H F$t-cMLR<a^0AAF!VncM/Hi?16`,jm $ 5EF9]k6'IyAjGz툊q 1x<;*;"=rȃ,LpW|5^U_]MB0n^ # `((+ 7+Ҁax#"R"OU/\WcKfM8[gD#X`#([@"+4S4a .Rp%:EYE. r0f)C )HL$Lv#\p+T(& @)(mp䈀)F;l% :,- @H&,4+ސE0|h L F@P 9(0){jLd4^ ^HG>EK{PYx\^'`U 6Q%VqAӨd HƃbmIa;Pbc첂Q%p H0@jQ @BZd@Np \ȹ*%l* H:u-JBPMe8!9HQQ.,h,@;@ (XwB"k Wn`Fc8 ^yMBTgJ4@: M' r|aG>h%9Gh4aXIӅ%`=ޣ`/p h W֨~pjKrX_ ܩU6*l`)kڀD̉hd kF m6-3 tuUm&*#9kMu iqF؊d2{3o+kYu-hs+$O=]KV~D6W$wŶů,rl2, M }*ojy\ft[7&^35Q017n05<]xVF 1^L'2X=|5@Jc#aiȬ|YujlYRm݇[k[H*ob.0^po3f72,ys-7^7 Be0_#Yq HyŊmf09'3mgz3^UgN _{ym?gOY9V ك_|~P{ ؝.҄`>@*B( ~,n #X! [`xtCl52CN&<"+t;cDk<fyv`"ٴg*~ 'm0V $؜TTqdꃈ^CtGlB aHT,Ȍl +Q6\S@0')Q"< dƩ4`䳌` #r(n+Fb|@a3! 4?wCOAK J+hݐNR+I_.$^EAɁy@ڀrBQ7"-=2ju>rxCf _dz-rO&hV@_R^>(&ʀl^Lݒ5N+bB@.s ĉ H` d@B:a;_o .(%&!ք4[! @G6 6%ZBbc&ǣ44-Ud{Mj ڐl~ . , 3eA׊7РT;͆.2Ss"tێkf B-JdV$Iv6Lj-N5hh[lOZoFm@dkkOr-(G5@- Vqn$I:id6j*d34.m̆17zr_޶mSW2LnoaVZpKNV-]`nX,km}6m˭v˴lVq6` @! @ UȠz׆quN֜V|T}Ër3ZlrְWxkX~>x8tіJV4 & ӆ%u%rC|ra'y5X|ׄ3p K Y64@)a w $-GVw'Bӌ>8t|_vWV>{U}alyGXxNL$voR89,~T8N zIS1 蘇OW,Cc͌:RP7K` U"|+a+=xtԊvԌw&Ltscyw#,m['Dw.zxfړW^#`al@`D/&8>ЅKd#ov`#ЃLo܆#RloJ៎ :pqIjjb ʈA&0"J7@R'# [IkZJ@jӺ XY앷Ls٥".F1~B~4Ķ"![=Y06_*w `t18'֪kcm e2% FN`g@\7n:v,t"pJ/ bP7"-DA%dx{nq;vf jw BR^Flr qG0a0u~E[L=bX,!|Y /#pgPQBW`Č^OT`ԆR6(ahd-ަEڹN9%C&PU K/U[WqAxBl=twβ9 !b!Bwd"4:MXApKR[ !JA^'baWa#|`젎bf-nSIfT e|b *b 66HZZ]&IS&F \yhf.sEwgiS86S: [8:A^Ayo '6`~݉d/I/\1p sg_tof}GyHP46dhzliPq A J-b[^#p(m[{K/gtߝxyɼ@Vu +UT5 .Pa-@gB-Vp)`=Bb KIJ ٚ)X&^ɫ ,A|0/S*oELgSp|X`\`)5D ȷ`ʻ[y\2 [ٴ'HAdjޑl۷re|'oXը`e[mGe٘S_7Vf~P|:`CLϻlLZVԺи˖L=׆ ,t H¶=k\] jӘ m[e*6fƜMpiqKMl$%yOuֆ}\sUǔlr۽טW>8)鬒8nccJkf6<0aB`1D VppbD-r̨ѣ‘$ NHc+nTi2Lg)$Ν;] 4СD=' 4"xJԨS\*P \1*(H lڱd.x @\DnÆ(i*9Hq6An9N0- Nh)8E $%`yPX[b \@ (D3͐1 VВUhOڤ)A\C B%)LxD6'< ?@:y#%=#=tsX&9FHKĕ/C{-vԊwoyK^ nTKʊG]eoO);!L.,K؀JJ8rV4[jp02rA/$L-hz_ߪ83[ΔрjKic$򱇏$+<"p{sÐoH?p|e c1Mc,Ź!rceIrj %yt)r9 !<$Mвbc84)@~-^/ Vr8HYEdNRu ƒ5@fun_M9g3J%4_+F58Ѓa@! Ժ[U/R"r `oG7rAj39Gz  xYY/D2'ݺ@4eXSYDC#oYJ::^"|* W P!~Qc"Ն g8B)?#0h֩Z-1tQ" l"kRN&?կnIgAY[072FM&5V[oۀM0̴rD @E'V0b+  U%.&F_Pl3\UF1NZ'aEd Jc#`B,q 07V(G~ON$-*b:|e._+Q'Yg`}5\NU!T}Fk0[b86_.)vhF]_ S^>veaSQ\G5g7֎F,;v2$A%hsg ЖKPQm]Q?链gEx4l7\F&`Oi]@vC$&k-+/a% 56_vA`֛#8i~]e9(c^&VOe3" `yKe[]*CqfyWBivQ]iIZof[y kf~@ PW*JA^RU_@"`'g% P+ڢ'(j"*50)2 У9 @,;.=: GXHʢzt@}_aB!a:hJgm?^pjuZꢐ@ڡJʩq XJ䪣W pJZf*K@:JIʣZrܭ⻷B,SJR m{u Ÿ~ Q`?+)X`+t`<Z`ʾ~ JIPµZxJ¶(;"k7rNLbzp `붲5Ăʥ MZ0K )VGJD*s, o`)0wǶĻZY`:Ip+ʹw˾w<_ ;KI` ʿK̽ s XWђH| Ss{pCoɼ;НK>W*:"0J@ KaM1p xV ߃΋ۀU%6 0|;` -Zj=:qp eJKͰMazs ӎ-\L!;}Qګ= \L}2)\|t0ˢ դ ;Zմ%L5k `P&rOJ+`N-|ǵk Gm ]jlSM̀{}S `L)ܭ I ͣK'|uQb&uMyrE}ژk|\[;㖡6~'&ZӦ+y0lI5 PBкQ{)Sp o\Nư>-yp` L]M o#=zWXr 4`cK^h^/p]xnlÁ@+*W@\Zئ^29 QIzaz%AV-;[ȍX⥻;ͦ"n"!&M ` Ms QZ:4PCۿX \k|0 ++ ,jzӣ˾]Z ʭ,;jε[PJ*Cٓ<.ؾ@~>ȻޯrJ˧^픡x_ʧ% .(㋸.>,KJ4;??W[Tw_}o<֎ak =J znqӨ;g5ڛ#@*?t^$Rs_d0ͥn .Xj-Oy ?]]M/ۏD5::2;ENИ:cپzl]/[1?l?KV)rF3E64$M*TS k@(03K4F 4 peC4!&TG.h  $ M` ̇ > 5X'c  $ zMƃ A59Q 5qjpDXAdd TBv| igJDRlYA -`$Ar M %:p:"'6P%$@  %G!p(dZF2SL@( mCA[ZqTB ո+ @0c#'Drds@/82E@A&d n !B| |]F  Ƞ'81!#zT"BvȚEN³$䬛w;"@1 l2 TDC32?`cu+1.$P0xW&6@Ls\AO%n[$D,+E:p1]T(#4'Ł`͚\ &0Ř !g'D S$qV ` !kZe@X(ib]j:Z d* F8lA"F@QA.+81=N%\O @̎&[(X<ITڍ#HV ā/n7* b6vFX,825Os G[Jw$qST  5/dc7\imڝ QA'OqigWe*U_2nV]s{E څS FR7nb}VP!uېfq0K <.&S$ok_P#8α ±KOHN d%'}L'oxU&^.Yo,`~ N1losS]zۜLsmɟwC{B=k \.dw S;>h|a@ pPOQrNY* `KqĘ#`Aܵ SQKRvFa|vj0'>S7@troB܊U vsz森SLkBd @֣ ć[Įnb-{ߎx_3rқH5W)Ip֝khi6Ew"m wx3((Tmd 487S KGS  GEgrx ]GMG0PS L @ &WO88SZ (}O t*~|Ѐsc&ZP {`ZHp'zOzB2%O* ; 60< Ǔ<|8' 0I`%W1G4D62U} %}sQY.V  X ?pY#?7 tX>X|@LG @AAA$; t0o 3cG;VPw@- h?@.A.n%HxP>.sbHu8I@x*)pF0i j@Sv%yo q`U0HPiFio6K 0ը 9FiYzHYHs '>+d QB Cc C"Cpe7@|(`W@nPEfxpJbEShKI@ ɒ [P u9ٟpD fDHpy|$S ZvmI E=vI cE+Iu F ئ;G z"$Gdpy#Z&О!X#LtY+PE)P 4dL/nzL*_oJ1xک*yÊ` ' PÐII| UЌ%0KIȀTaѧnFr80Y` PySh䣧V6# :0 Z R8 ` 5צɞ(Эd8;e^7"nSuu*K0C;)z'֬ >iH:` ]@-pD4dpS Ȏ XЯr0)` P*X{y jG9E{=BnBOJRbx u,xLH@,&LW*#=0nk^z DH5 OUp  Vڟ0 ]=TWy8 bC%^jQW:Vl:-0A cK s. M`rVCz仑Q /yrI@ vs;}>ж4, ZUH@p}k|;c K쩦:>7PV;Z dd(髼hYzHTW |!;ouT@+TuK.ٯ# `5:r)Uo ySRDM L,`xţ#2@.C؀ 0 ` p cV@ Vy6` iVG#:Y`y7P[nE NZ,L -;, \ .Op% ]9|@@ L J `r~P `  c9^ȢwuY[@  .`Y 0p4p˧<Ek i`c`xYHPZ \U< ~ ˅@ί\YW`|Ͷ 4PC+pQ(˧NʾpKTcH[YΝ@,8?@Cy| +';5r}G]l\L|[]eMe#$G`IGЈGeArIr=wuc֌Q0=+B'G, Q2\ǘt}R 6N4U1c'B*J>[I 3>B;B%,专l`M^[N&v@[v,ZOI^%b ?p&o^l>cmry~W|>养kl~rn荾䃞d P~ zd '}U|krb3i vn -m p izdQx%`6`b^QK( B@N2& p`ISǮ[2Ps3 >3sހ$8Չd큮z.c>@ F> 0CU;` R= pV  pW2W 1qPUp ٷ) K@~`P3!'i TbN|) krp4O_0/(S d@)`:Q2U`.p|$0پP+~PrJPr;O m08` I!i^&e0| 2.# ^?_O%;/9  iP}"\/J0.S?v0)~?Pa"OONg0 P l@Hі-JX &X2dSe U00~4PR"*adK1eΤYe(x4 Q ^8AB.1d됕W񂰀+(8@fJJ3JJ<uˎ3h?` @OK-DU+(+zKC4Q ܤTת`cN'K=@eN3lxvA$7JYF  uL09a=钁9X=z" ^dyy:ihe9E)?3tс-NA`P:깤̰ď "C2@K4QT[LE]qks(z#z1q́ TG&)fLB%Yvb& ggS`4 9t`&:NE?Nj @0)0,ZZ 0;"xPbkSdDr:KcJ%  vCGJ CR6N3v샨U %~0$5R‘Bm` JD /T$] W"̘KD6E\ciBqJMSs)&zAC5QV Pp0 ]% ~X0"Gi95wE}uZt:Fq,zhVzin Qithc&(rbaƙj_^䄇"2$M̸[BoY2sG4^F:%Z :Z|֢:sQ18$((m3И9A%^-x0TeU>KJ! m4NFb hsDW0ܾm}C Bx1mi#0^`c^V= }6 w"~X>10!/4n8n^3p'5qT?0P6x+CЉ+trLJa ](v4q]!JZlxCP;tёհ5lF1^8F!(l#Z+* xU9?0Kf88&PlԴ.<2+*0ĔN)P?4 /fPb~+ UP-v $hT%LufB Ftd(h݇+Q53!nnp()\!XL6-6P J "-8`2^R$ 9@[དྷU Y+ѦT@Ip~ _`-@x$Q9p ld:sl e4qHMzR$I# *H8z19A&Є0 0NY| RbЋ=*#YU&Gf#tM$`) Y~G curxbxE+B!B`PxUr.- O]5J,€, /Q/2! |8d?*R{J^ d4@ 1PꚔSpP| ;a-N{.JzNe [ 7DY0BـG.ÖaB^ jn_,-oVu$E+2`in`u (!Ia g>4p{d  BJV%c}dU1'QH1X1MlJTB ;I,#" $t=#x“SIh(DB1,e(h@qAe \nr|$ 3pk| Yvs)0Lah ! dX~;8FGX+sI,t'8X£F3M0՜>a][Y׮&5ҖzZC~A1%{L-SIGNhgDu(JT1 ̦7OOvknq׸8jc];[}a~wt a8# W0 0|Ť}qp-n5+B 3mk;™̼?.z߶vK_ o.Qx}s9@繳zcK>`-1Ճ0"lUB0u$G*p@zU1JZ}: <>Zҁ 44&{``}R{x:pf 5a#':/ӟ}뱪x׃oU'k*U:%!]Cn$"[P@!nTߒQ 1 }g &e2h)]Chߓh_Fx^ϼ)xϓ<(&k#x$ <POhHƒRBX,7`Ѐb(=#0@$9*>` AH7*#ph .8%)h -ҳ)pDPY$N0@C ȅ:! 7-x) Ĉr IAH<XӋO(L(HqC A{m A lk `:xJmH_0 aT$B:̓ X &S(4ǖCH[h=`Cd?>Psc`hKC(;hcBB@T`HzR`FR:dLpJt &@keD% :,.x dU8:@ x>0O.)RЁ4DD& -V2o<#A ȄLp̸,GsDU*Hm Id90Ӏ``pTD6>98H tkKFiJXH(:` /tL=4G&5XȄ&8:#;@OK)hE% @1"KZ=`ÈZ`G C>sJDP"S PNQK 6aMX`d(d*GpghUh` 3lQx 9BTXp¥=`.]PV,ė(HғV85XZ@ XR;Q )!XhdH|K*qT4U7P1p@1OO5 S@Zl#;GkZU<<[-[=y3KK2=[*̔[[[J۳c8E >%[[P`2 -\D-ͅ}\5 ]ݳM-o_-Z U; ܍)ݟ-^=^r=e==^.xԣ;{L[@[Zd^-_԰K͇Z^IJ>=_P[J_>`U( :G d=ll؆m#R ȤC40{3Qd ~aa6D[pƈj-Ad $E}̄ hT yb+b,X/h )Ddd n([x:׻M͐(Q+5bB.dCJ7 dԄ&ٌ=:oxg>E4P#QPC4A>dXeY֪Ax6Í HmGCWZ`?֪efnfg Xa i2 /j"ad:cU#Xg^gvnkңp%pg~g&]<;%t8}g^hC@y 8^mhhNޤ۸X#UB@hNi>?OӀŽt]iibANVaijf=]9Iinjgd)Aի ކ N"~ @b h⚦Pnk~늖+(aK`9kx_DЂx$ H uRn)哮(k~lY K2Qִj9(BT}l n<#pȞ(=ݒFd p ؍lڮu(Qj` VPF.UR&O^x>ISۮn>df`(~m`n.)/P#=(l@o F&ȕ onh[$.p璶op#a+p iV pp mdt#q_qEbcdB!\eq% (l :8YHq%_M 6qpMOo&r-jnVȄ%Or4O ;06ePF(o΀--B5s>R!Dp GdkHO>_tFg p @\htJti{t QtNtOt1uStQڝ0$SF()1 h0m-=?PAxZ(PXZP[MI$[B؂=dxoxo^߃ HqaA v9ue,><`?xOy=JÙwJx@W`5(Xxzuȁ OF@!huLNH+wz )FW)x`%8,8]^u>8:`M|zopN@^{Ϫ?WZl{[_|w{Bpz8|'||·߄8|xY/}7pן}.q,=Ȁ!, HD!‡#JHŋ3jȱǏ CIɓ(S\B XʜI͛8sɳϟ'-YѣH*]ʴӧP f̨Xjʵׯ`'AEѰhӪ]˶[VKݻx^)_ LO^̸ǐ#KL˘3k̹ϠCMӨ^ڑװc˞M۸s,i]SH8/[r`6 OmbνËOͫ_c˯%'}1b F(!nPfk2\BPs(hL0(v)0(4h8<@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袌6裌Uh~V:Zcqu駑J)jjӝ^DjkvݪZZX+zk. 6TVkfv+k覫+k,@A,m:\YgtxIS@:,*Yr=+a:%@tЅܲR,t*J;8sf%P4%KP[eUM,Tk+zzNJ }f3mj ^Pqg4 * f<}Rİtӆ;n%bh`y/aNBW: ݺehCbG3:q`v@a7Ab`x"c}貛(~ŹUf\"#*o 3 dRVH , PVPTnD@<P4(@%>H)bgB.B@ ڢ\`>ӅvB`C$\V 1~lyOQD%Jq"R d aOZf1e hF$NLbxH(+ ZV75 +!mU`Pu4bU@R_]t#"A(#x@E$d8bI e.K A F-D!8x8! `)K C$GP Ppa|H&9IMix``&'`KS|`g$ \fDE06B< |ឫ! la@B(qj6 X:ψ|J q7BW@ =">hhҩaLej9A|Ȁ@vL(B\RH$C-)3Iҁl3LSɉtw(Ik#U%Z:$ 1 l@s\c,$ JDRzK#:]*ā>5i0`HCEY&0ce;%zR` -.9 up0X""QmV;OtA +@LF.Tv\lA J0X Ch4A)Xe%]R%P[` -@F0qZ-t̪ yK¡,8h-:`iU(,:kYi[.pքHYVr00,[@ilA Z@" ABU4['p2s)J& Me+@@f"CʜP ء ^kKf/Uǝe1O1D3YiF|fby" 3<'m/٭tCf4<0H@N>ҔnhT:̣s1Vۺ}/X\ng<[ze]/DiNm`_yrl9' C ehCp 0V+@ v f8k0gnh ]$ Gt Qeq85j3y8ST AuTtDq KUE PRxyP|V`ȉՆohUx@P8gȆ$09K"1FSې @ې ڐ P 6m4( 0I G؃! 3mX'e[pN PQI0-c3z IYpKI |N \ DϸKI-r%up 0甤pEP&Y@tp4Ӵ~4NYbR$ˠ\L[} |i O)ZXJ9O q`U`z`h( jQy)V #@3֌hJ fyfZp wM`Tɕ[L(jxgY Hu@> )?wD . @pٝ9YP96}` ` P I р GK}SHYV d3oazgVp}h D ;Q/T5` p2G-$K0)hU[` Z@Y- 2PRʹL/Unu@YON S}dP+`:ʣ=a i~Q(Et* ;I*G UIJGdV'~f)J?V`崣6 $[f7p礢5JZ{D )*$؟ P(  Z4 :Q 50cc.|KP]hBLCX#:(!@E0W؀ W|% p 90k46` NhJ?bԡ-&:4J^^5 $J ME@ QP__YpbuZJ&@^ipవb:wIPe "f \_I ^u /c4;5by_k\D83@p_M9_IKj ,4^O`՛C _7_ep̒ѥixPGuͯ|M _\w#k[p]|k@, ZխE=FdciYxVnnưpZ%Wf^єWY9h0e^ {Z}Tw6e'VֳV}J-]vEmԛGC;=XElmW+yd0e D` G`W{gZ&We:MO]~n=\-*xsfV|AL*׀=ٔ ٕ}H7pCvg-1l~d:t؞=B Ֆڤ٬ڮڄ ` }+WMH P f0C}'O vs&Tۈ9cDt ܫ; H G<޽F CB(:r `M6Mߝn pQ~`AG }ۄm$T4C[!{ 6tW!ӐDd2]ޝ3>]ث{*8p4rVAANv: Ug` s"#fH p2SG;pބ0AGN(F^ݽ ;|~k~wVս`F. m3džpXWAP"ﻮYPun f k$C d SpRP R^bwB |4>)^AET/;{gjthݯa c}k6n R ِ B r 0 N| OܣV E`JNf ~P <R e. 1 Y0JV`AWpD mU,㾭>yҳzpQY,V",sDR^hH!b҇-;, lAsPv \M0T"~v9iTt25?sP#8d#R,׼Pգd8e`%|Є$~BY8%Y<iYy){_9p{UxNjLLY dlPٳg(+ًl̞qQHدW$UfY84P0-͜?Ifqɕ/gsѡ[ᆬ/iZB̔~4YB!-}ՙN܍=("AJd4R5ȐĒ-0P=@8 c:K $0C m1;`48ؤlaN Y :/($ް&72Vj=d9L eBK@R@tA*« _ *:P"feVH! 0HLz#8 .yqJ/R O lTR -TTSUu>i UhH;!^bƙjZ$Tf9Hn"g`K;~a,v[n7z]2aN?% `VAh,/ F4l]1H|qHIHB0CÎCB 7Gxr'@|\ dK(}5Nq\dD^c xhRLP%A 1p0 ,A8 \(&H!.R$e;ra7)H(  SPUrH@"Vڙ^!;pС8U#|r+7Z'SFBʼnb^Ij*pFBxmY*ײfM&vx⋏Β+㘤p{S@4$јX *HPu@[֣nз@ Ah~E-!+ôoKxp*`9`m H1<?l@|@@# 8SSY 49R@\AlA| JZ5=@A B!{,Sa+ۖ#B(B)B*$%Z8`8}B0 C1C"<IABa9+2C9C:t@C8@x7*^:DB,DC Cc7 @##;DJDKDj!KK4CPm:  B DW|EXPK >䠁k_Hmxa >HY< Eg|Fh K>#;nݻ>f@ϓVFu\GvP Y4jWmC*kGLHp{lHף*Pe\H,IL <O$YHam.Pc $A1ɓ JʾAe-|J-B%Lc\JJ K.<n 1KlKlJK' =%6TBMXxxXmSHS9S :T<-TN"HJ(8`؃G>FpV%YDJT\UR`C`HT=x9Ud((<4]Vim)<9`XxPmSqMFH%88VvmWb>PUmqUW)S}uWM@nIH }SAS?X]OFx7P.cU]]I*gXUBP~5֔=ٛ~טY5Xو%9TMZNp-ڜT>xX5elqڧSZ,UJ&DvZ]R@46T !, HA@,Xx`#JHŋ3jȱǏ CIɓy`bʗ0cʜI͛8sb`@Kv=+I\`B _Hh}~p ~0rG;)rG ` T R"}d„H>R. h#+iYZMПҳRދ0f`Ybě.\ \)b&m3cjp;eR% !, H ,X‡#JHŋ3jȱǏ CIɓ(S\ YʜI͛8sɳϟ-_2JѣH*]ʴS:LHpӫXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ N8h(;+_μУKZ̛ԳkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(,0(4h8<@)DiH&L6PF)TVi商U ``)f؍ihlp)tix|矀*蠄j衈&袌6v!$ B!*q0ѤꨑZ*A:**PJjj*ꭒjj*+KʪzB;P:ARI4dN*@ot#0 A"-V̀ ݖDL-5;è90b @6ȴQ" 75ȞXEĢ+zO. 4AbP˷"X =8`A ̬4>XLP0":``\02H\RWX0b-oȐ<3f ` S$3^##wR4S~Sjb*$ѸDyGPvߒޢo,yn*pE(Xݷ(\y4c?NN|п"pKsF, =ӫ}OK9{WTgLd $5 a;\d7,AAbpk3(w*Ѕ8Z]6)T(@X(r1i"j3`|9@ G("@j)DGbp]|xn cH0poDB 0܁jQüjafW`EOsҀWE@o#RZ Qu푎'\`{,=*TKV$a rmX!k B0aBN{ v6+x TG t>H859p=v8i6A"SI) bHNlV 5G\=e $493U |3⽝I4$,.I@ yw&q*RRsX 5%Ⱥ4*i*ΉО3O_~"j=T2s>pY{:t*AZzU l Sil7NTq,;uMԮUpA7T"A 3%5C΍s:U*cZf=YeDkԼO,*DX5hX:%u<==֣DW_A im94$-W56 CXxv@VwkX:C`R]E̐Ɨo~"u/w` w>Xp`: ^'횠%/SA@L '%Nq+yx 6A B 6ŒkX;}c!FN&/HU* !WrBػ(5 #(; A2r𢡄ǀ*xsY X*Bgzyi`+q Hdƕx|(zCEhG8vMO؄\i'3Y a/i ' uo=LnE=^bغF/2BagXcw5qmdcxF\ 0B3f <UK@POaW9 ]@1!#)A ,1(A^@30ꚢN&^B/ |(a})āA{\8( P K@ V,wXDh#%f<7eN TS`` Ծ#a >/-0ZL4`k $Xy kGH ѯ" #  wr@/>UsN]( .@Ja vKǸAbMGa;}7'Pݏe.M맀:vEzRFDp @hR ِ ې4p P4 Ր{en 0npt p qE?F@@pw_(o0` Dp/W/&w d)@.p 0?^_` ܵ G`3Ws x0ex|@&61{pZ~ 0, mI``pF`d\*pXF}eX @iPg`~ |pC, >D]l0xF@ b Pak(( \@tP嵈] Z@( Zs[eRpVZP( ` Hkx{5膪F `QbO %:PFYp}H]𘊫80X`\evM:cu`Re-''k0/ c'6]D@h G` 6 nD 08iP 7``0g(_PEU(\IB w9%Xq LD,B2GsK{}nP_y` XG}Gswh}q y^ndpeg =utnu P'` G '-vYJ c H٘npŧ}J@ $h@cfuaDxV0st Zٛ>` ̙c`;p@Wf zp7cK0fF00v`)i C0 7bٚiy3y3Z6Zaf@qł  P Q  J^ 0 }Lg@'_E])` DPեs`:]8qf +2^?c9o!ZYbYF0ШU ,yڕ3P7` ,u5YGp`BI3Q |F0 ׹=:i}xe yI  bPjSCZY Ti 6: 4`{O@X9@PE`oe7֩}z uPگX x:BeĶDsg P ѐ n ź c0ogq՗?.Z%p_kp5]^=3; s+q)P ڗ+v(H@P{zW ces0[ ^ey۹Z yvIae b.eǡC uLD8{2EYv VHiHbYQfH0@+` ccWЇ5]O6Lk 8L,vʩk  jvL; 95] ',xx ̳83|]G 6@ ` B X ` rU)zk[  ?r^[a wkK; nƾp{<XN O`q,ZL [ki9F j.2i@vG0 @Jp| ,Ȥɓ H+s}@*, Yn@p}G\Y0  ` Hn;,)z6,L\̧թ[&f  "#N&c`8EQ*m%~_>M]]]fpuRa'Scacdu BbnRnX2w *\v@r+daP(FN^^2l%FcM6>va.C]?^&)V 4;l8=6 o-6P"2R64D>Bz~=>vYhF6iwEki2WkNa^ ..s 0O]H%f՞b>Ϯobc@^a xXRdM~`@=fv A} 頞ldaAbip`-amynU@Q^poBA8ο.ϕs `B%Gq'p'qq5q)7q'v+ݗv:WaIw}g Yz[@~v~ˮ0,JwW8 |?p}n{A ] EvpXTgu6\}ϔ3~07 ]Ut\yRWw0v]'R7vi? pvzgvGeGDg+@vG,y. 9\@hh@ n\_x|~s=3|׍OHS0yq0wШHY GF>Kf THA0!i q-4f#C3JqTB4j"$gbXp !%IF3?ti XTA"K aWΨ Or֭#~A  qaQIeKCD@!€¹ K"]FZ$˗2-t..';ItV:V07S\U`4¨ X%^|3$F??In ` Md:lAbm `{-3V jˈ' L<.KP ,#N;{$1j˅-`/Ub *h8p#5 2рJTυ|4.1O#&t lNO@TO }H$)0BڀXlHgC;Q $SsfdҩR{Babbკ|UAV(R!fPdfтZ4ƒiI=t$aFl-,(cZl"6P{@2N4~؆Zwp Pb) l>"Ml%<: #$\NH haJV0Q<în(d/.2"_ `L# `ֳf6H=__ M6؍HR &~ Xa8޺ hi~!! ,a`PԖo)Akv]w@=5tJ(xI8T  X`I%W~IȆ.} Xp5!_ "ZemJU+Ռ\or(L1ŌP.a }f9 ZIU !PҰF1]@rx"@ _&1FЛ$  g0t}y3;'L3}Y;LvHQ"QE yKSvX;/kdV Hh-`PwH HF;E @%l=,=@AvqP:Tn}T G0L^$!()8*, N&A]1Dk>: \fxA" ŐA Ȼ%S2t=(]1`hb$ H60)iDC&FjO~ QBd&Sf%KdI0+Y~0 4 8%0pZ "2\mp`ƒa$z4R e|9R MY I ve ~'Hw(+L0`0 8T !* tڴ+|k}J g ):ǭY' "NO Sr.@[-0dE$2P3_Ƶ jʎ&f*H+I@ Ir21\I#+C Yyn FP]iNfm:F(eBq L `wS8vА&Y;~gb(f ny }L\NhbFX1>8Ť  $$1|a0q~ ":fC%P_\X\!H2-^[\2&f Vd^-#(7PBtP \@ Z ! }0h<8&clO0BYfzkPSk*XׅwCm l pa5om|=QL݊!DVm͜;A_.uSvvFr |A[ }TFt%3&;&oQDvhRQdʇn7n<Q3@>[@ +'@k@>=>K?c;D@ 쿽8kl A*A\9ABT#NѝAD@G  JSH< (( 1@[<#05 BDRC+9B9aY{7Ck;@Ý р80ҙ8_/ SHCKPlt \ClHO);D j:$` 9PT8>P  TDL:"ABLJK?4JU\f ŽW"7ZPH z\|5ipp1XhBѸH!q ʑRYR^a8d K(LlKp(jAږjMJh8R$!y aT|":J:" z#*$G*):%% #*8^ !]2dD2;{J@62R 腫E#Z؁ܜ9L%HQh@ND(Ez \\zc l('d@~@(mЀ`axghZOPH GF^5S8Si'iC `+ͰD !]X. 1\hY"#) ڔ ;z#Ns1!‚t) (,MN[oZ1-+M5YfH X>Ѐ -1IS:9` `ȅ. 1O$9HQXZ0;Az$@0,Pݥ_d20BMB|cyyݣeɷJj ؀eH6xT&i @0/߹GY.k: 4R3'tPʰ((m$j@8Ȁ4_<0*)1^$ZP (Ѳf & N +H9 Ab{ "(uFL Har;2:L8 j+Ɓf)p!a  Q<28B)}iNb~f+BR7~㗔c E;f\k7 XB?6/IXQٲFCJ1؄d4i33 Fz߅} / 00D@6RXV4!J(QȴASVM`Z"#sNl$N 9prn6?KN1S[؅=+`5^bF >fÚSkk2*&4CCǮ2t~#ӳ7A?_u_WHtdFHs8:hu,u\PP"v[NsA@$du;CX0@ Ѓ2.-t} ?@S?Ryĝ~`JL;U󻚒KH=9(GOt&k?i0{_AYxnui _.Fݓ  @aYvl|Jzfgp 'M!IC/Bu=Q}pGv}~iDÂ_:JW{Åϱ@Q0Gu.gPŀH|~x|>-Xd"%q}jw>/ĕ|NpwɆkkFT#O\IH) %P`p@,XBbAc@`Xj`Xơ:*j$J L!1`ʎ>kp ԎLPLq*TP PժV}"f.]Cɔ`1ժJ2RJHZ]N} )ɡ!!F0S Dd+ApށKU!'+.f.T:W&`q@<{XAztJec0eb,?5֔Cp!5c<[N3(@\/u 'څՕfZS|Hc̝Gf@Q5&!-^U8#4P#Y#=#A 9$=X$HaRE2`|S6!\+TSEm@0l 26 ]ƒd%:8D|OQ9(L)%;?V a?$LW|pIG(ŝDD< 0m/VE[D`h@DQ|BD~$J `* u!JB5a}Yv|a1Xp P*YQ-Ă^%uD&xDa ܂Ds+ ,`GhK\@ɰ|X@(qADZtkV! MK 2D3mFD  p̋qRKk!GJBGCpasprX O:E{A,apRsAsFfG*x8݈$,8Kcrx$YQtf!D ¤Rc3 ZMY,)pRI 8 2XL+ɤCRXp"d{ C.%OA[{F :Q `Uԑl(2ф +E-H 8=A] 9 CLCJrQJ|XXa>l Wʰ (AgT >bCY"u9| 70P`G-< @d0$w" @sӢBh4h`A.<BE %,PDJq]YPd VaF@!d9=n<%*%W{ ) 1U>R;|.x`j*XG(?@CLpD #IA$3JNSL&A"3C(*t t#v6P_I5O}a`p+̯*`'IZ1*X-5C! .rB?92i~L!,b .]]>"`AS|>@iNgv ` Y`Љ2y 9 At Kx@\4 8lҌ?(K}PN '8 ͣJGjE_ )]!_>I5 ذb$GbNL*C+2%C( (H* !6hÌ:">P LtFs\ 8̈xL6`) I.x ΰR1VX]aV{Г- ; šmHWPf~`xJQLpP*` n]ZW ڳ ݦq(V WEJF[ RC9;  z*2LߔS-:P*­4HTA  !ŒVA AW$(qz=>5 +;` aAR!<1,'jY$@KʟJKrӞvD"\ $IQhXa(=To ֓4`l_gD2 g!sxpJlvU {xԧ/v}UTn+J8 .`P,e)epQ{ ~HX`-bBLpo ! ]R@. FQp]$i/lJ@ CT{8 0B A)5~:ADRg-20B-6 -E+"&0B+ @u-`~M)a [,Da⭰8pq ({/ E߯)`) HW>ϧ=rؗAyiT=3 =D{䔎(Ǚj8 :&2M$ ϒAzA#{ 4 0_V#<'(;C̥}JˌOC[ Q" \0ʬ_VU_IH  Շ(dHH`_I 01y'_U@ߜT)nCJV_*!.aE%pJߥqa ~!f`g}ž \쁚$[ށi !f :5޼Nb2"%B!("&&`EX R_Un."b"+n,Ң-A."/b.Ƣ ^(b/_agŌAbc!(bz""3^!n#7`lFX"&!:_'c%%%9~( c6)"P?>N?d?@?d0c fa2c==#Ud("BxEvd(I$bGb㐌H~ t (16 A;>픂MC@qOzPN25%8bqJpᡘ#QLT-%% )SvV.%U2_< Xed턁ZeJ;6_%ĥ*H:M%Y[%SR_\%_C#[ee` a*&&&%dc2&8\Kd-]>L \Bpˆ1%@sf}IJ%e6I^ M-['%]eE-#pOLsu%dN&:.loƣ3Eft2}c'!Dz)`&}ާ[^%~9Fe&&E@9DJ*L2HAA`50D6`C5TC X0d+05TACAB JD(Gh;"< 2YۡԈ DOPG)lD (O.dA@ 4*0Ā/!BDlRBP\i\eE+(At@t qZQji,BAP tB4`MƩΝ@)A(TAt)!i"\QW=Z^ٗjI)"`@+p҄ Ȁ)l1n+vkzyzz븊}-44A7iC6 0*ث3T:#/ /(kN 4܉$<,CA٨AHy%v1%y i!ᩤ `ק쉨h\(A:B(G؁@@"T*AD)èt:(,(`٨S/*Oφ@j(0cNf,k( ª8xVf/@B.-dvZm%856LȌTB-HB.XLXk/k."Z+J8K@2A6D5 BrE߇C8pC8dLi@LSIJ 㜂"NA|@ 4`o196OqNA@GwLh ~́ PԦ!@ъHu.:yVj#0 ˗$lBQBI:EGgE ĄJ؁dBLφA|0\pIJBKA@)#(T0(+yÄ%d(@+l)BLAjTZuӮ^ BG !Wk2&o2'+o'r*C9:XVKB3@,0CA!P,C83 [Kj Q&*Ajl sU~d!tסl$eIBF$Kp‰3NJm' &10A 4%0/C0 4̰D@+(e=K=[S7@R)BS*|@m: sG߾;wH ?)}2C3}w~1CV>*,hA l℈)Vx09jdȏ7d(bXP"K2=V KXԩ;:} J S&G>H*48Z E/%kv@k˞uko8mo^{ץh=-LtPb)Cb9wY5ӇPП+vv컬e׶jX+J=B99_2N>d{2$$-QNt]\9K]"2K: u (AJ`׾LJ~N7 䯿f#V,7Y@3,s꿗.%ķybɏp#,66Zɚzr&ŞYPL#ViI hbIzR$p",~d*@ 8 zȎjPLC.=:M3#8Ũ!, p8F.?#:҆XOH$tJ@L_:b5!VAƒT @e A"# V>)M]b'uW\at8r]X QR`5VSp&.P,",ZK-"j % YCV$^Ōh_CA`ӇQ@ S1#YJWLW]V Yj9z6J=$ -\vcJ:.ZGKe `A PzX / ߔ“Vžt4Mշl\Q-ȃ)g*IB~& '*L5@ҎK2@K~HKzS3b T p =8!+D],~'JBAYqaԭ*!8%(H$1#8ᄅ ?3"d"d}Z/@B[Z@$[:+>L} q!  % P 6p%DA(`r?bA;``%.7x/0L*!+L A ~Ї@E3Aaa 0!/|D8"DˠC:X-HaL@f./⃀0 slb @DT ,GX8F Ew qZ P? ^YnBq?)m[Xa@ph.2b&c bThD 0-鍘ɜd`-` `l6 fIh JSjsN&J B J*>0(vT(X)P`S:@B-q ,QQ@ P`'@SN(BUa$̀d?Ĵ_`H"/^'`1!Y@2rZХXp'2T\OŋU >(-v0Ni욂X*H`\rB @2RԪȅUJ -R!FPWlP 7D"8kE64]+(Ys%-.Ӗ*X!Y~^j>JDd>XufA_!uFTjx rKZ0 mqHVk܂W/ʔKig~&"F5^MAK~!%$3ƕVtY@0,I!.QCq MbT`+ġAP9%($юB @NMj5PpSRe_DDI-| :[0] !lgι@aSdO 0bi ۓv%Dap1`. u0)w0ٕZWb`NF0IE ֪e-KhL4Ԋ3>gA v],rU/ VR/d/h! 3-_?.uhQ ='z}]!:-x o,ĺ0 6 p2^k.Lpl".0PRm(gz`{Fz$:# 8{2PA) #D8]:Kl0qgv P'dt&MV'#xJ!:|wjL:DHa ,V2`ִ.v"mUX a \Fh d`JianYA `Ve Ȁn\A`okQY@ԠV@b&`@p *HOV^qΑ61q &Hq  H\@LNmi`ބHmA R֨[a01<1$Ya`,' VQ2јPj >! - d ;Akx2 q$yf-/K>otd,eslątBdG2.. rGlx M*/ѣz)T d̂116TB r"3Es3Ѫ"4>"2F5IC5ZDP Ds325O6s45ޔ1#3.wSC,(&-F-3'2VD2s6Cd$dv%*˿Xb " 0s/=O.y3Snn?ds 4OEsG*@1k6 4At-ph A,r%GBCɲ:;T1)#~ESTEW4!</i;b=F .#": (fcG3InrIϢCTJJ,%!HT=!aLTLE>PB=s0qoBK"htMՓMb*vG;cBHc0Oy?;Co8tOrAbGIJR#URIaNeL.OBn=*ƒT!pFoR SB@zB.Z Q2}$&tQXuXYUYX9CD) .A|!"B3:d@pCb@"x)N  H T"T=" cx0R3x/H3+5-@@f@?1Ob/c3Vc7vc1œuJH6DI % D~f!ܪ  TA 3"KxNRƸ .Lh&` B/9gFLP&NyFW^Dj fU`p:^D)Rcf[HShr*eTP*s]Jf0A[4<&j΂kh iLmZZEl8Vs7s;s7WIUY)J.! % `! ' G'&T\J,`<; v@s~BGXbX'#*P,LG*„&NjUL%>iYs@zBDp0V! ZE~$xo@ r m|@`"g~(X ?04/ @r''^s_cYg?5* œ!l`w ^XNwv@@Eg +dU׸STML&VAb|N>J  "0X¢>죨K~c Sr, ^ఢM$`8m*$JJ vʑu" -#j eeؖok94GN,4%&l!P@^!Ò >);yK MA L0NjP%.Y} F'7Wj91/F`" gpb4nHۊ `Z&8H@y> .|MJ*JJ@ |#^tw٧5"xbk'f^!! l@R!lږl`E<`z]ߙ|{6MМvNga]W,FMNC .A;b{BCm90Yᢇ`'*O t"p1,@o8afH *0@F#QJ89xom"BB{չ[5t1vDI `#}d+@-T ?.a$ʯ˳V"p%V.,T O/t P ,*Vvj?Db(o/h@\{p@䆤G&{J3, {XH%LdF~xOȷ֙Zd bMJRDNdO*V8 [!Zlwq[b 0+(bҤQA 4k֤ղ"&UEJ54Db-kfL#N(U3J8", IԦR$:͠ >W(x*FAj +k;sUb} \@IFX]A#éC SW_X̛;&tiҧ[=ܻ{7).YJ@U1-<?Rpؿ%CI1q~Ι_R\%D?@`C(zp`I a)*(h8C%<#fW DB6P %aB~H +)% \B(Q0@ #0CVjXl1F DZ (P p@r\bQXEh Qlj-CpGzrڧh`ՐxJWDAZ^@>0QPM\`EА#|AJPTV*G kDSV]f0Bf(D{A#`BC' JHAê(A+v ˖Zt}qs=W] r";~PQPmI0KlS QP ̡]~ qI*'(KR@gx!ts"(䑀$$o,lB f'@Q)8!LgjMX( g>\`Wz~O؁=b͓,La ۻ(d~f>O4‰6uj^)u`OáPEӱjtu#@AtLOJ…kT8۸ J mlg4P 9 9H9O9p?;m1Z`GW8!' hD)#u@p>"Iw`M*=ýZp 5tTkVATf@(RZ'X'9Cx^YA EŃR #0ɉoj{, Պ,mH8 NG\Z*v@9l,TrFXDfdp(Z,l$mr#( `" Jڲh؆,4Èء)lZ68yt \Biy"T5 ~@P"!0Mm]A-{$\mH 0' %ŰЂ.pТ'8ʋi5TYHH Lh- |e\S2(_B % ʭhSy2bULT g[bHt+h0Zń%~A ) \ 2`a;Frpl,r[ UVA΀-Fr]\L8Dʫw'-@CpG]#)^m7NkZPt,`n"7 E]h|;4\YxJ&quT  ԜKxznnxnl[yS r }sԿb/I,:He? /_[ jwKvTv{f 7_Xuus7:?~/7)owU|,pCCoɏϗGP""$%$5#b5 ._c >q|o1$OFp 93P*> )Q$r!q~`CpQ~P rIv ?1d K!Dr! LY`Bq by"!bLC$֙u8Qu Egi 0؃YZ kd ?SXa#`+ zx<d$24p JF}U\/B2 @}%  G4`P IA|"Sax R 2TKpo`#o#vqLc! #X`ct#-/ 6)l V!{i`G'.0R0)sPVX'Z` 0 w*a$"0`*r&) z z?>/+)Di&g pʒ,Z )D E.W t/<0&uB ilrJ $%TR.F*p7N" m (RQRPV Q00/J_@j2 &<`@ys q-mcj$S :;|t7F_Rn% At&F ْ H3;2fUo@$ Q (32#w4d4хI%!TBPEӄsHbU\$\5g*8!a#OA#R5Kd}YPP@'I} s$Y:s7e:jAGʄAd AB4LBFbJ`OT\>=IvL7O]ӫʙHNs%`H$-3~: c"fUld4Tնså>B IN[2wEJj0].L{XUdZY@Z+ [jj 1J@DIu[P;p 6 Q[k 4)!zL`aW7b]~G KAX0ݢ,!% xŒxK6RMsŅJ%%I:MY,U@[U[ _ # 1lm:Չk` b0r/W ^' ױ24:6I ° נ/ DqQ18@#!"p,|q2R<0+@s ]TRv@c ? it,H D2Ѧ"m6/ jHci[fjhUdcAF@ PFf0v̹7jVfm  5@j @o Dm^n;DK6p!+>ZR&sǖޝ.oF @ܐf|J 7}6]wmj fsngNƬ- H  +4lffֈ4q f>~lrF.]~*>v*'1}oWUo|wh _Fq6~}5'XNGyv0pDg븮v`ԈĎ;wp;X;>G98a"$>s˟n|x72~[(US6~/Y i +g.sY` Ns5k/ɂjz1<2+6y%?W}x|/|y7OAXrZ~އ7XJ_9/=8M/m@ YPW[@[^T&OV)h1-Nh{Sk31~;K?{wI?-)o:Mv3T@*//eo+#? ~QI 2t?2kNjjGvCv}EFG@!=xD=B]@o c*`3{O_ySX(ݗ~rV(Gٿs_ax h!3|>) 2|@4HY@DH '0QL81(~`!"CD);)Ob$%̘515ylUG'J*8ҏ/g ~zCESZ\yҧ4`I~EVZm{[4.\lDzEk/ޱ}X`… F<H,K,+hq͒4Ο(ChSKC7BP"Ӏ=TbĐ5kXC ""h)?ERH>P9ÇcY0֘.0wZ"d"(^BV@a 02:)C> QB ,Ph!R[c pRκ!iB7|``@DZAF®JCRDJr"ɦJ+IJIfR8,˾J$|S 0X,( (HӯŕLIM40bXBV85b6RbcPt".W\LԒXO*IPC8S# =蠢""K.`D"hʤ8=#pϗ" R0Jh൏$#=OJ( fHT`5= ,W[\w9@ E~Uגr]& hU9l˨N'b2#[ JXdgT~؁$ijSg91F+tI <@d>(HtqeS!J܈ LQdЍC׊5&b6$(tATĽIA# -.7z!']?6pB?L"0#,` ʭDp@n$1Q>~ɽ>}L ޷CbZ  Hn0'|+$ɅP x@@Z׼V P`?WȂ & a ,x I@Y%Xi] 59 { 'P,%OFAie *u#LM@F\ N'$PRa @aәbD[# EH }_) ʼg'p|vZd$>JVRg(%:дL@ B3a>YA6(K)0EbqaJ%@:E@A \0 0@ )X?5 * `LCz|D4mh r&*NtQr"ADpJ3s/5'Nь: 1Hb EikB v|*GԺBP0R47LFL (EU |v)NUvKԧiBq (iXJe2JH,$~(*KLc1L'^v q@["(3B-%hA Y @@@ FPҢ>|eE+xUaCQC $!vȡj>^%%h@8-&, ɭĶh@ RuJJ":0V˄3-*0CQ`87PZBH)i 恬` A$XC_![H_|;׾M~'= K4&)blZA:>eW&}弥 .4-s$Y)g>yfAЅˉӄVЏHOҗ4%k8$ӟu`D0jtCPVC.bZ֛5 rk$YB kab }W7Ҥs=mjW&ĶuܚoAh_&ĮG<6!@Akwmm/ذA \ zy+;ق)XX5qw]T"`[A]=W( V= Dž>t&}Mxkp ߍ9&pj^&vEW FT ,x`uL4K'`OtQ3>A 2MAv>5E?~}Kԋ5}Ymأ711 S )7]xsx|( 8D1 uMO\xEM3 |kϫK}?}+){Ǟ=) =5YeY>;C=:Zȑt5)?iNE@4?C=0,sлx˿?׃l;5x B!<#D ,1d%$&A3APK; 90k1Nxj?(#F A:&$j?سCTCB2#Cӵ;[4 cK%ChC[LNO5ɳ+4$DDdVC:DsCik YxQO_a*!c&Ae?add|@7C74RVE3AGzC1M{ :CJGg33 ÐD{\4@ 3.r4G10GFt$4YȚj|[n 6{4oȏ2ɑ$ɒɽ413ɖtɗ*HdGrɜKɞ>ʠJɖwʤTʥǢ4ʣDJʩJ?sJʭʮdG:[ɫJ$˲4K$ !,  H DPÇ#JHŋ3jȱǏ CIɓ(S\ɑ!C.YʜI͛8sɳϟ#PX p 5t R @JJիXjjaүF"pٳhӪ]˶ۂBy5`Gۿ L_4{C)ņ#KL3E׫&jLӨSϔҹ T˞M )(HƠыhȓ+_|g֋(6ͳkνѡ-NB#ӫ_ϾK <([| 6Ȓ\הVh&aRvz.'@-FȔ!-,V@|ADqʍ(B "@Viz0"R]Xy%Ɇ(h}H"B#P4Z{0·" *Ki?Pǿ%2,s^hCp`G' X3Kf'`+OH`  Z#C7 ᬀN}"L!GBdwL\lg1!EeMB }HT I<%,We+aX!aS"N4Ԣ*'FP bjܣ%Ρ 8@s qT U}jLֽfvIk]ڤV6$-m_$t- O 3@ 7!^nqZxbH@ !$xC=9\#vV zuN AA" _0 (A <$ ޶EV@ hm#V. -qg`@0ԁ T@lM39D `}+I9Qwj19ÿoZxF h1 yV$mL, FC?Msf89#F$Ȣۀ7Dr Q_$1{o =a@wDtF>T~pF{]|';֯v.j?o}l׶a~G?ퟴׯb/wϓ \ \t Ā{B g'}k`IgW 9'_@rg T}}7u2'>G jjP'x>0(yqP 7 0'  BSog6'Bs'Gr?qp4 \o0'P7 !u?Pq px P%|lo:Pr@vfPsf'>rl#gwAzсLJh;9'-˖}z`rx%HWfi8moV({88pUHuyHXr׍1({@l`$qw XȌmΘp ĸXq{zFWG8Tc8 i(}DN[p4(HsNT5WБ8%1SFys$#r$"Hpr  r^xAv`$But.bXDXUF:dqYxq ` e4W\~e ȕ c`8{bJ9P@g[C-P"`ivK9ÔY=\Ufc ^nT 4 Q2'  0XO M8]dz@ Nr)=:9sx(YtDU9Y)`I h >ztC"P^&R:dXIifg%HQ : `@ pΐK  %l_rHvIDHF6qWD]E0  ! E%6Uj \$]09u ghLq '0rMq @@-j> a)pvJ-aV:0pF019c2.- S.f⦧ @$¢N@ `&> M8,pC 9 }4#/vIIjYq Z 4/=D촆ح+ @`a?mr-- 1u[m ̮)103Q"ftWE OhG2-1fc5-C y:4$Y9X~ 0`$MI]} L PS 73-L?mP ePPQ <Ԭd* `} )+Y71;Ј qƀ̀ y˅)x[>Et*$^M7 u1MZ1*`j v%` Nym0XC`3@`-& b ,OP60 Э;5-~.Fo#1悀\|-كw+_pƠ@MA M5<Rd es"ԛF j"3hKƫ~ ]L~޴Nj.H"LkN)p- |t:0Ύ\8P)@(^n.a0UZ| ز+ >1#@@]PMa`\M;7DyY4 6 p8U`\؉+` M?i VaE!sh ʨ~vV1h ÷Pq9X 9p<>ܒ ( B+M KPPn72Qq)#!ۃX0Z d a `>7s@V@wR O Mr#mЃ,Ƹ/,>%<@ `3V GoFpb_M`\mO09~pF@7)=NL`Z>r[@ eʘ  PN`o?b!7:h?Z<HZ'\ĺ*҂%`ď`L@ @;cz(286ܯ:WBG` C(72JX "5,Xtq\>HA:ꠏrnr+dJ86& QEL.%8٘'fڢ*`9тϭD"zXV-RLkviJ3~pcc k & JE6X!FO< Wa c4 .١F`C $} $2k̒ W5) boVP6|J 7an8APЙ"h*BE6d S pD]׾"ُE:/8DGM!dU7gR%A U :Nh-?`A,AS\Ԭ _ A,+T x8Dpc+,23m!fl*X|:mƆ."(⒳MpN#JTHip*ɕNeS&())H  5ly؀=B.pK @c@)anP 5 (';hKvNc@сgSD#:=!DXDpQH`-R\:`+h[7A r7BZ8b M$JS&0[t#G ȀuhPfx(hp ! )a=G0 \Tc-D[d0A ~0SZ,p)\#ȴ ]23S Iiapx4*n@jJ:M]QX`O C ,ZShoDr7 Cdʀ.Rثzy]f7.V]E)regW{`yOU@8ͬWߵUР\7fe'|L [8EDQ?@e)j(J (pf"JHp,1,1Q:8t+k-cǖq<'W8SrnF-lL.6/zӠ/{4we'ٙXDJ2e?+IWZa^pv@,Rt!>]HCg hA$ϋ6uLݜҞLYeXZ*W~ʜi wuxwSfua2_c\ Zڠtj۴SIJF8ζu6;I`7ɐ2A.»{;IJwmJ9%p:0dv;G<ϟ-.B79n?&xCi)xFfT@RKݿ}sC֒x|GAFV$fAOoxW ()8 #w R@mt >s'EPܖ[ <^^>Bg/+!p[(dɍg^79ϺSx=آ $.H,"{pق}-,c~lա-CZaGmx?+H\'~<-҂) b+~~l_nҰ{&!4cE <<@*+ZA13g80-!Xؕ S1Яw*3@Ѩ8Qɺa# b@;E(Ӏ` @I'XPHXA08`x "`>S#>QSinMh5p@@)OxX8O`#+0+*,E>˚+ĄMpQ@O8.1/#BL@!a%2b1JT8Ơ.@?&h(@TdSh֣C/)C@nFoL9џ+k_0Y k*w) d1">{ ר7s$L(jH ĨP :5*at[Ȓh+`/Â,أIW # 3ʈ0(&I`BC2x +,X.X똋0?F$,yN(Z(*& -2@!VvIȄ.h dCx4 8^t3hLȅ781 Hɕ$$9؂`)$p3oM7*G=K Fpp#,(0 e۲dBJ  ɊҰ D2 X;]!qdPzUXv)8 Amv O¾䛠5>Hl >? _>0+?8 8n˓jK!жrNgӣhzVD=4``dx 6T4U\(%AS`AdAHCD$=0/ K$L*lBB%(h]JDJ,tn0DZTGC"Ed%X"-<,u1v%ôz 0+MF5P(.#X5]=,-GDJʒU@OaCg A#&pSl5#"hum|S$s솱S" 0*հ;幤/c<xP8<Ц Mo(F؄/ @ )W؂ s+4-N 3,"m+*? 8 x * eD9BG@M4KT0 ͑e P\aC*Yh@_ (JbJD %)`aJ D=R#dr8 *o@n&I5хPU%$PrGv|pY}@UE$]n`/H}`CA :02!2)L`҇)>e f&|N: i%IBXSY0{fҀl\1Ss ,bQYkBY-J{5P$&Ӵ%_h=lE o681[|1g/(ŎqLP@T:0 1ct$T@`DpD$ aI@ .%'4*(2BpP C9ʹ A1TX`MHa.=PbScũBRĊd)lԢ0 JB.l ]&w)M( U$le/ dܶeL!Q0d̵qb5CP `U~C|dNao>4$@?Q@(Ƿ1jAUJDm. 4`1| #(1PH?P۸B DHSd,% %d \b%"hxYAb;hmkC  CZ& 0Ad JpЊ4X NB  Bh@p8IQjLh Z)|112?4&0ZH`:1$P'q/G0\@ %xJ(]p@ 0I@!%=O d+tH>a HҌф]BC|`zУBD>%a1½xf uX1([b D#*щR4@L4{Jk\U&10tQTB(=J1ڑʴVj]x)g!-QZf[VRڭ1e- H%{UMNbLQ%ōDh"D:\Ba&D&B,BH#BB5VfW b]#!\"@[=r3MFAs@>2oVHq36S0>O[6*̹=Ho:se8quVF7BRt.GrE= L2JczJ 4zLWMD35>0y$AM4tH@mh-~U5zmS2ITh$m$u*%_[tqPEvUdI} V $AHba ]Hv9k\USYʚm@R]k r\ Jm>ל݌ӭ1!wvLНmg$lY܈v~%_cAuYi/6wOvb _WÔ7oA̛(uS1vV Bb6~h՝!|[RDi3u HdB8O 6X:USS02 AM DFȌ$MܘxhBUuOǠXe@ xBnA-|B `@TLAPE m9 w[_F ~ XS'B_c`"hY){W_ylM9KELe@etl‘y[ x@|B@:HWW4…WO_-xhb܏BB:!&LfPl+/ƧKM,X@C[yřqV34 |1` x6BԁhK A*ڨizk< T/XA=2"Sj+HWOH.&pA ;7<'DBD/̃YBB #T3 dt < ȁC= uHh#iBЁ)} ,_' HޗϿW+ Au;xo8jz}c@ƿk| HGo1PyfNhlĺ1N)Du||'7 Ȍ6{"TWݷd3^BDՁgh_EC1l@pCD0t ,BdB Ī*a .  q`Ç1j(F~`A#D$!HD!Ptܱj;Gi!=($@ҝC

    }1tS4p6 DrnRu160v9 D4/J\ F"AT 07w }47vओ»)Ԥ  'V{M3H3Щ'>4RV8|%6#Hܴ섻PV,5 &P4FR81' h- +"h\>&iTDc$h6jUsj{:j&Ǭ/M G.rfe6b5≍6KlmR ɁD(ZHnFHaVȎ;h^JhЍ pƪ\B ybJ(Pyڹ?Dؐ[@ɲpw!l5:HGxV x@J˞:jIЈк-`X譌1g@P>DHA s,)$BP zIW4AX6AooH8@ \/$"+QNd$ L+.pLd H h P 2T9A tI% P\؞"J] iTƭ5ay@4d@mDAK_R&s BRbHTBDozޝ#>iQTCBA6B%1 M bi:hGq0lh`A+2hd-  вP3٠tB7A%4aD-fV(! {j/J@},A s,@x\% 9O#E5RFȖ-Oh pƝؠ:r#t04X( ȓ"a%50i=8$P)<iZu R~D  qe5E5Rц6ШQZVF֝Q!nKĖH>g"&}M"58? )uY D+mGMk-e6) eƺkpV48nڢ Rqz3.뜬w]a EHThжšwe=n6y/`[_6 ׆(J_ٍѱږ:YY KO^/B*&q\V#| ^ΫDin[QOñICf-(AQ-y[@B%}^(HyaسzGR6adNWHEž "6 F,12i}#TDЀL*IaXb54ҁb( ͖ Wr%!6]`1Sa [  14 WJ]u rqىNh!:EKxbXB7S~~? p%Hnn&؈F*A`~nlN &.P f$a|DJeBAp (p d2 $B> <0 e  d!, XȰ$ /:$ 2{Gn`,x(\<|}" \@9鸢&8gTDg|V0<`,``:H }P`Li O1 ҂RA#`J4"mH~!B*F!:SPmO ~ rpFR""JO"L p Lo $/r"m:$hbFvF,6Az _26J#.z78@ :|hg\;z.`@+@`7A(ft;fЪkj 1غ+"/#d@U<4P`*!̌MJ8k#ZQ& 8ԾEOE$"o-P&N`2J/!8Otr#BV)&F@Ģ0 #C`p A>/f heQRV!h`}3ƪ l&0 3 CtR0, :b(z "34O5T&@M" 2^6bl#.J#"pOfdL",XwtR&phv(S`4 p*WB%2">3 IJat6vBLLζji+tZZHDS-YY#Vb'k`S΄ȰU[(̌[+ ,vpmsЯ (-jXָVX-fbVh\!VivU+"+۰-\'bjud+@$bLEcvlv1c"RҚugnhckmVGVh%kfm\ k&ĭjjq#zMmIl.FpvQos=ms)kG44% t# bWq g}t@gBxWl;+F:v"y.zq j8EkѪutL^0v|ӗM4() .V7Dar@ >Nu,()| t}Ac@ o .E.j")(A&ĘQY`@?cMBsn!VG.N#d#1 lR rb(##!7/8HZl4֔s^pF#jb^^R#!'*s- N0L2op @h΋BI,0!xYSR &<ȕj$X "  2J1,`4 h3.$Z.u2 gVc,n2fX 8j`C% 6 _BPzY+'Ɛ"t* 1 @-Ir` 8# C 6 }2dk H!%BQ 0#˄!&0r 'd$x=)%(#"-wK][Iz">n`2@)`m >t ʴSrQ8 S7E3:C2>AfOp[#Mbh.s2 5D0*njœNzc"{ (.IU:bycIPӶqmPmG!$aR!:H7%بa\طrȤ$8 j o" !I ޠ|x('C rp;Nccc&@`nTn'@&cV@<}67 `řzJ8S(H''@.eٽr&)Aar@6A^'`2'$S2zRv  x+UĨݔ&8Ѐ8|U,pNHa61pt]'g࢕Ze b".jeіI# b 8}E(IFt!'|]F_X(MAW~cKVtPR baR霅rܜvމ) 5i%l68Zy()k1qN hf jfgbi]yƕZ^Q]WgylUF|q-*#Z(P-))]qFT[.rߕKxR9,b:NJof*G.[l*캫l /ۍTBfivjZӘBhp )`y |.^tP/sj!v1H aޕ@ w}+D`{ųϣ`ПVgyT hT80v}zl ~^tMܿx  aIBl7R(LD |.I*$D",r|:jfP w[*^XCaˑ ~5+dB  fZU2C/ viEv_3/Bڝ%ʫzAeuP߻w% $oH+|gMD!d%" jA 8ȡ pJ b(K9Q q*K3 WTA B:,\HS`46Ɯi2VseHP8,m,'ʃ!(7D`wfi Z0xtL=6L֔pe6IdB ,E@Bdm8"$w!`QHTIcBn2)SL@`tHD@T?HL@,;FX SdďYUTN+$1oHD=֝Orp h q" FBXJ,pA65U(BHYj xE,ˆ\xI37Gr^M%PAijH*搛BKA(~@Qv±L\RHFX‹w#"PCd\!NIZH koFU`( )*' 4Z *0v$'ΏXхOν,,1 6` Aj м@~9񔅻|afPHXMuީE 6O= :a=PG }qr:PߗS`j 5V~irq5d7gB;wZg q z ~b  sl fƐ zI1_Guưeu @D;"=+\-)|w[D*&u@:#3]AlLr  @Upp \ $<0kCs`x4 f 5lU H }pyGkY&` !PJP\P &n8R4%Q[3e 7DGH6 B 7p %(60&1WNp !S84\x`<oxf\ruy  d B]33QÉdzgj& C)@[;&s$s.Q˹q9qߵ7P+=q `G#<#? gK9-r[Z;'O9AY(Z+:K4 Jz5:p<3p mkJ3A`z=$c#+IrV+1ʾ.3)F~v#7` Z fMi0 C> aL<m)VRuGT@Z'D`b T# !FZth|rC"000 ^Rq J@H$- ] QIJd/Ư+tJQr@k KQwJ)IĬFoWcH{¨JLT0)!yT -lRDO$30P@¼.+~Qs(P۠Ekj @Dy_j +yFhuTy? Uf!@VyvX ѣtG i?0] X7X/,J\@<[p w R0uHY-A^cE e |A?%X9o|X6Puus1ew4g{2_%PZ`*(ZwgͥhصP^L Zlnqjb0t x$4 x$k 4+FԡJRZ SŦ{ hAbxZ[ͨ&Xl\!lfK̯vj{y4# -qkd}iT` S*'ݚ?íC,w/s%IF Q@pPB.er ֧1V^\GkzrYps]Lm f aExD4Z)XoP؜ qY`zzC($e F`|aƶƕ QȺFmg} LvEΊD%ZrD_ݴ %84-Y0 jd'pg\)d&ݐW&0Yxc!X=-ޑ0m_Oh p 1T \M{xy(ZV^KC.mfDX74d중mb1gL JGGi1j:p-H`lRʜPzfxލi>`%xh هL5jH5i©P^ŚUV]~}`BeU (T#j/T@p^vk@EX1YÈ'Ryv~'X!B#Ђ.3J'PT4LY$8ȴmFEm`xi௕~]tVVP@i'dÑu6#/r1ǯO_O%RC8C0AdA+P63 o%( 'tmjaR$z Ov20C >-2J)R)$26 H,oC&Kҭ3C#?7ᜉM-́;;$ 9P EDxԑF Gu*fjtREk4RAUTUQTUX TcJVFcUIUESD?xUPbP3%( :6gZi'6[mݶ[oYh\st6-nم7 7n':h[TF #]WU68tD`HU=_4B5=P2SO<o_n8 c=(`لڏZVZ+T撧S7N?j)sC[> ag&`hHSX0_F0zr`M ҃ A Rz (XRSHx`? 3!QE[`"zb>J H&W%U%1ڊS 'rQ!fmT. $a89NrZڪ o^,sH⢨ *q"P&0IϋLZ0 BPZ)eGEWև@Fr@+*GڵLvc$ dCAThgtb=]|A*aMAā2]&C bmX&qD:0?^ͤ;)~ʯ @sra%ub# Y6l0;YCwc[MDT_-H+@, `@;M=h725ց0uf3G! 44ZOFm%dZ\6h<4(cR,-)yI!W@JK )SH )" JxctzPH-Y4ЯZ ,/xt5->*,g+x3m\;s?iÿRTŎh \ X)0 =@kQ<ЖId!]l -Y7zKJ|b@ P7ga#:I?e4KuӮ˂dp+O LaMd$=sp\9c{^U G| 2V%1p*j   4©`TzU@SpbY{Ti1bqqսnvw)[(3;JzC7b`2T`[>0<)AYA`"!hۙ,&Tb߯! .``1X*.uʲxD~Sto{ܰ58M$ Ã_KYjöBhSmu6EdnQm{^O<~uĤL9 unL$g03@O5x^G3mG|{[8}IhxgojoLJrcC̸8⌋*忎í؅ ;?Tc)2˘N7 \[zi:+&S*!$ T©+?,1eD[@Y5tGHIg"1[,CA@İ?9B @%\s(8CBCꁊӊN%;鱺D"Ci ŌWŏ+E\L;I)E$,dȇ419 IJIOLD1B:FI FN ʾC;DxLiGJlI|JIHDzIRq`/G,/2l94Tɳ4@ɕ$$HX6"ِ.%iX 3YK,dtL)L֘˺< ,K1̌l$MLLD 2ILЌL *˻pΤM6qM9| L$L\NKhN̊ ONO yO2xNϯTϧNtONOMPOmP-N0ODM Q< 5Ml QϜQ  QUPKQ!"%e&eRU]P#(OQR)R*R-m 0P R#ER } 3 S4MP/u1R=< %S?S>%Pm!FROSCU}UYTXKJ-STS^iND\UU3US ּVeSf=L[]VWUT\TVPnV-3U_%luN5VW`-UwM=mWxV;Wb-WiODր؁%؂5؃ELe؆u؇؈lȒ،؍؎ؑ%ْ5ٓ%ـ!,P H*\ȰCJHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴSxJիXj5 `ׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟O}߿߀p . w%^`Paw^b%hX(Ň,~*?%DtH(bp5*kgq`ک믕m Ɖ&:v6FKf fJqI 9j+._p ;w 5Lt)5 Ip)@@aܖRHIe0ZvW\VZ1Xƹ[yy- |S q :E%^;R,9q$UAmds6D /(03 #< xBT rAqPd*bh`AJуT+w^  aD\CEt2% 0P) P+HAL%\c/ژ>֩7-PD9_@-0p Ā@Xo0AӗSD_'K CAQ!AG2B5 T[-WpAP#9A!jDUTj^,Nl >4rQ%!XH h6DAA<$!SA5w1 PS>OTKt B@!r Ȇ@ l $BB/``6@Z{ED۫Dz:9z(1qR }xaA|(Tx,D*$*$ =`Ҁ 8  #HkTEAưe,Ћr0hAe(8+h(j )jB$@| B +@@r\,B n[ؤAV sxI ,n*HIBfH-(Rц% B H&^yA&// b (0 Z@5 l rܠE@;bT #Z>X .`VH`A6> 4,iE;ZQBt8(ƶxR}(k7hH6e:ZV20TBA]VUӕnvn j x!hwĮ.Pk~+_Eyk>RQ!Rp@,A&#|` /,|Xzxq7aƊ"^mC\&,JKiOrE/@F4̻_W#co{fY'?R6\G Pay^6@xt<\u.L+?nuγ\]'q~I)X~;F7}0W:`z-p@*psg#:HL_N1;PGRwxզ6`k=[ʧaV@{kb̎6=m_WoS\bIf|1S! aZ |(tM*M~!F }!`%f+{ ' o#c8 !pmk:g u!@FyW\/Ͷ@[qR k<(6ZkJ0RQjK{ZP 68SBp tZ AܷHЧ˅`n+P ^}դ$@B~ۧ_F@>B tZ8@%b9OnVQ ~Dݿz0r{P[C =X`f3Sl CW QqlPA9"llٖnu~s![6PFP ƈ 5e0pu0 5p2@4`\H0;` U`]`yme9_X0Yw风 'АyjVb)Rx0|W 9>8X W7yɩ7P0? DY M"YUyE([ `60 yn0q:0w)i m ڡʡZ%o?' Ps t R}z 6t 0YW PdLFc WLP1D )UpNHVrS?PGJjejP/HDjLNnKнSj`dJei l0P 0 pW4p 9} -3Ez/:`=C0`NWp]fr^"jw^s旲PH` ~8QY \ pT@z h栾F^\ p pdVENe Mn 8DW>zDzn>0p憮i \ %b0Z^*Lsg>Cp."p` *~l> ~p'k^n "_B s;n2\` ]z4 "Č` =ڊ@RD1oZ-z=FnsF pP^Ph۾sA.ӰP >~ g`e :-*Cij7 S0uu@ ť@˺ ߆(D&@ jڕ+a pHfh#[P3Xfb1sVG!a^1;+>cyO*M.Ȕ5ogС5м#,eȱ4oL1!A!`$$6ҁP̑*P7Bo#@ckU ^g̔k;v n^lzccKMG0T#"ݪVohINހÍ8 "D"Dph7lB )d0&#Cp 42B!X1&h@qPr9P"c;;Re+p(?)Rl1 A&hHό¤*H5 i)$ 4h&FLrLO? UQKuԩ(k#p5̡ނk.6)Z8kу~ц[`q &_QnS ,aJ4p7\&#b2D<6r[Ev`E3qd4L6}`S儃̨Lk\ItY3* d! 1&qV $!+D[Č'ymZQy"r~S,nG4)30.KΌ(Wkl7P!<^䏐 #Pne N["bFBa ⌘ $.K8dFZh+ [ǛZ[ ʒ PDْvJ 餗KOv {%[TԄ/T唟V`q[饗9PMfTf2aY*@_~IMǢS . rQ ޚ ~-)45RqQ 2PtNABl!'l:`! n( ho@HM\!A?cb,a0`# Ap8*/ N@BtS&8 ( 8( a AqA!x3)G4L!l-S4TQ+]L "~@d1QaPHȐ@ KlPȿG?t! iVɀ&i/Rd GQJ" oDT . d֋,T,<>l#g.B8T@h RE1p6E!ʻ@&T3I1 3?OYD#6WT6[4 t("oJ%"R“CX`jѤx ZMBTEHܴӣ"3BPRFTQW@5R@ RTzubkYj֛ `2[@}#i] ` LSWXCQT+U%[}Wr3\uoOhO[ۡ׶?, 4l~<y8P#( lͮnյ^!PEOpE9d)fx2`@>r LNOaڋ%xur9 h( 6Z11^OrhV`Sd$܃sʨ*B) r@wk;S522ӿvCkYC9?KACH:xE]SQB+92EF^Ê%;?v㵯C=;s68tX64DqñuVǸ@|͝D1,P:6* 3PǔQ|5Q̴$@3JѴ!%Ѿq'|:U03ҖMvYqp C[+_J(M U;6US[>H "\!ZyT `CV6EcN4PmJ 7ÀIܛg7܆ʆ@K0HT3hP Y~9@6 HX|PYBʼn@+(M`DX8ɨo(  ѬmOBpAmYk4f [-[mɛC0.:A lw[£ MBɘH <6ȁm\}\uVRE9k`ZXJ H9D$ OsKp5] /$\N*KꒊA#7}Q^!x?NEL#@^ Y!>UET_5[ q2"M $ `ǵd(Ќ(XNA2 @j-ʰjJJAXI <ЂKsj xR GĎlɞl-RQ+h7  m6aak߷! SZG#K&(mNSZ g(x͘7f\ 猌H`mnVP(nfdc7ahXxGX^vm/@i)`W=q[ $:@._>$0A(<mw~out߃8CvuUxO5Wwxwx7AzBPdzO@uA::xpoy1 xu=B؃WȔb%hR0x:r z ozG8(X`炇c=诖DŽ7j&zu`GЃ=,{X@CJvOF@?Oxɹv4%Nx8['CS|x[owοui}u}Y}ۿ}q}>z}w퍾8 ~ IEP !,O(-\!%[Б׻ނ tʟ6pȰ!T0$78u9 XS"LYdƘE!_"Ө CH԰a T&mG .AHߨE +XjBɗKNسkνËOӫ_Ͼ˟O뇔nNx')k h ]6XWF&afv ($h(,0(4h8!,x' H \ȰÇ#JHŋ3jȱǏ CIɓ(SrT˗0cʜI͛8s< JѣH*]ʴBN]өիXjʵ׈*_Ӫ]˶۷n\1ܻx˷/Eg bZ+^̸@˘3kY.+V!c˞-Ba)'b ȓ:Auܑ`س+ @`d>Ͼ}u?m;~}h`NP'E~}GVSp*7 AOP)0Z?\R]1>Z<آ*?thHƜdvaYPF)eb=9\vVX^q  @hwj5HAUtՇ}r'i 6砄袌(.6*AsB8vf!FPj*Vr ^y꫰aGs+*9ĺ*>!dq&֟wvem~+T>)jekRN a"?' SX8%(,#Z 5J{joPY$,)\e [) Ecr%se᳼y8X)iBU;.ɵ^zM^"aݵnu@q#+uPW ʥ@<8vH0CV{I56T^ DrI; \6C%S$0S+D 63l@P3P;,x16@A&!I@$Q >"Qh@D\ؑr@VF`@"&Pvp N5D@@Dj?-I"8$Nt@   @ah( pIv?0$&rq4 c"?Nh4[C&t5j*Iq"9&%ELv@(HO 9 = $CI){n#!7C |ł!J`h` d`n?E^ % @N @ & $A"1pfHHX  пI !NZe:h&`S(bLNs6IN  )F#1m\~KrlcfUvUE(d葈չvum-V[)Fۨz+oc"JiJ 5gޢ\ p>׻kxTjvU,Fz^VCrB$Y/n+f$-.K`.ZS 7nkj)s].p k8@h/^1#rV)Zg7(^֖F״( JH'ӝls$P, 0yd-{m_OXVC e#bmasγgj7E%HJTv( PBP!T <L.vQƉx+LrBlcLkX'(b?ZŇ:ҀƎ6M8#w[։씄mX5q?UXg4 a E4F Wb"u(xDHc%:J" `@}@'\ 1| bq)H0jA020qHO@A,ְ)b8P'`r|!`'la\. wx Bq_BųO!s.p$ԂVѸV("{ R9ߺܒ5?S㞰(> -`k.0:!~yc>ZaO}Wh5P91qϽ2yпdCtKkR AF6R ` 2T!A\cޏ r|q0rp# P[aqX? # 3fF`EQұ@ I . yX:@|`jbP qd.RDD g44E8M pu` @C #$XLzwG6DNzpv_FI@Cil4tB@x~W8zNXi4P #F: 5xxT(!Ӕ Tpe҄PMMPM$0BJST(48cth1 |hpXЁD`xj7]V]?0vr 6 6 ̰ @ <4 Ϡ})o.D$a)# pipi@V'PU1D {@0 W r(Y1o7q䁒q`di%tb0'  5` P [yv uw50Y \Xh|):')SYDcYo!UkАqB$wC+`nva) Jn:`34PycxjyVy19u7U9 ӖC5Dd)4$l5iЩXz3_9EMBtrfy3`;؞9c}Ubm y P ̀PV0 -e:MY2uC0Iw|80@`D 1?F)`GryxL#x:2 bPlK3z?7:ФXKG,yX/ #d4CJNRD}I _ )\ ФX7!'n#VJf>( XuPzE3?0>OB#!A`++\{~k$lhVMAimq rM΀fQ sWm'ap)PKf6AՅC6Q-@!6rR٤m=dmږڪڒگ}ְ(-ۥڴڼ۳۾ ܺ"mܿMۍ@Rvi }g=9@غ}ǫ!n ;CTGk2m` 0hZ2P rRlR1fM 2^ >>}.! `# \0Me `  pnCo  ]?dwR 0 iP  6J`[  1P iyM`|q\Pa` P"w.;@`_p)i3y>e`蝐:@By9 S0ހ1 H0$%.d>*@.Z{}WGP T= / p ِ P `f0 Kon,EN gs.0;@v0[`q~g  l t>sj [Aq'g ; $ofpJ kD.=?O-蠐::{Y0s%TU _Mnhjkb dj t/ @bd& ʰ V@ V@  @0 %7R;?̛/&R rY` .`g" `[@ 1 l`pepWjns ?@o:BQs`~@ 6S& fP) @U~}l 0 *WrChE m륬Xibesƍj^];@@Fz9{́TI@B:88XQI.ehP">1DbC4sĊM\8e MtdȀ˗ !Fa")ËM)Pb  JM ƕdİT'^K p\EqVfx SH+Cz ,4d)-E"Vɕ_sѥO7@:ٵowkGBGT܅XW` Ia~xY* aɆ*dJ% ");3@~@)t:zA[h@% |3 0(;*a Kvc I K, B"4l4S/<+3t1b#M+m*̲J)X*8 ֌ʐX1K CLeQH#tRGRL3N( ZFR@0GN㣯^^)De4fm y a)L\ =R4+H C h1HTb `d9@b^V "S/ =^$h^lm P Z" Pbl< ^Ȑ[]vC߭>FCH"mE68UnEQdՔśsYRgҡuN5!茌)wQ&+5. Bv6Ji{9),I'P2n*!Z:. ކ{ѻIU kZp'r+s΁>`<ȑiPOv[ڨ=ZÛ E ;"E9KWqLt}έW~{Iѿ{|O7]u1~qߺwOJ+B8$o^G> pTcARP`#/~N#a1~a0+t>Ű) 1h3': AK=zkxDЈH DheE\ B~} B.2 E=k@K-("m䔆:ypERpǢXnb|vT"@F""NIALHR,F$y2.MZʇ&%Kd&B U$.Ѩ, Ɖ&!?xAPAB; g ٵ1ЩM|eb *RE:wٖA&I-9$"C(h?Jqh$! lQ\ aF.2HN4Tl>L4$qF$a[} 48, 2_%|p x>%kYzVUkek[V5)= ؆%h PA搁N"51UBIrlg=φV%miM? b@F5q @8H8F <]S@``enK\FWӥP",J3`q ö=HJT3) jWʜo}{_•[&:!F ڨG'U> [n~|>Xp9"ȀY?B @iYa5 чL h= A^!Ih(3n` 9)xM~rDPMjF (`*Rʍ~t;X>GVd6Aq/0E6H6Їk'xD8ݒ~vg-MΠT(g|4*wA=Lêv^MȝPWFٟkL٪3xG^ijC'ލCL=y_r/IO^W=>EMW{w?(x] f}}?xAQ'ZqPSѥJ^OT6me45.%u/;Ǫ^p_G8],6H҆(@,:h`I0U/(ۙg?tC (ȁl؂)?۪7R&ຢ(.@G7+@/`2*h@Y5p003BA/73H (9, aB=8Lhlx;F U+ɛ26CAt h1* @sk؆(;4e8 4<#PA,EU(08k;2ZPLDTΣ\|NTNNNNNN OOOdNL0;OFhaLKFP>:8$,PmF)SA` G: |" y@,Z~Zhh[mF0v{w@ϒަkm)+B kjB㟪"z Z~%h\Ҩ$ {:@) a{Z*d 2$[+ @A~ 5[\Il "A3B +E ul*"XMP4mrn]:DuH\p}5)_PZJ!?`qPX)jG 5@D!|zn{vy)@QC +(<*zj*#{{)"h` S  >xp+FP2'P<2҂+ n ?! ރOUF+m+B@+aP@}ҳ`x!PS(1A#0OM"шPHЅ/T &:Oa(B |@V Z?bWܺ56$|z{FL"s\郌C @pQY~aSjD f9X3qj33fTb Cj#!RX@HOa3c ^1Q}U0;; ܎Zu -+zP@H )Lz(i qzcejYRlECSyT' RĈ Bs("e 0fevfDp  P;s 9%ghN.F.0*@ N@ w"H` !1+`b#`"HTp9B6 0 >@iG,c06(p%p0*,V\n p "tYBh :xjnppQ7FpIX.  Q" 0& 3rpzQMD*/,WSku5;Z,#v @9+~I|ב@~kb&s2ps)3S0)dюBi( p)s)9+/BY xRS6R\,5@PnQdV+3bhn`_q9ڈEWg#gC=FCbrrci.B@[ymXiٙL@6t~[YhVIqw~i/j.i2Yi0u){dy~9(y@)R-"5i~9Tߩ9Yi^6y) iyٛ(雄zӍ< 1}Z))e *{I vM#+- VЅ(d))5Y\ Bfbp1o#05 Q-W+s =cPȓȤFS*(E)p*Eɕg2$35VZczPDEj8])] 0vnkt1ڧ/§uy6Ys|:Is -Wr8~4W0ӔS7"0f)5C ":O0-^nP(@*T @ p| <*83Hp $GU@>u;|Z bEUAHpF@d>Ɠn@GB=Fl6 |P> J<0F蔊+h;3k;*=aA, <ʭ3Qd]:_d pYDT=K~:$˘$;~0 r730rE~:`7Vr`Hd)v GW HdID0PJp֪2?U`  8е^SSɴL 04YB jR J.@C V^7FOJH`uPsK@%TH0xMKP?!`:P4ocx0Dm,PV|pNZsOoRbR\tPJR`KD0N;yMMo4[1 FV$ۙM3'˕)$ xxaGpJ'i(B:5p0:t^@9tsn6HT8v%#p مDl`b/Tar3@ r[PwVi=`0`zfuqܖw1`p? gsa= @2[y2R.i굊-'0]Zd* ܳB ev4mt@ 5nF7E\bt2ڻkҽW%UhdefpW~(̠e%f9RRq7,Gp|P;C` s4n$kƼ i`cpYpw^ _uĸ-|*v 3,DzH ], }L !x-D/N ~H b+D8 ݟzц(ʹг>)h~əx1N g0 ^CВ*,ss>6~pz*Ԃ{& j{Ζ -h-vl-m9Y.\䘗#k~)7Y|W`wW.:"jsѻnޘA!)5w;꬞7Fڎ웾Ŏh*.Z.3n~~,k!`N`}wj'ߤ' P.Y>.Nq(zVmjCnlQvaGa ')5=¹_Ytegk,!Is sUBߞ>/Rj*dI;"Q#JÂ&/~b2?*@X&?NKz1L@SZ ~MS7d7?1J(W_*PEh JyD4󮲞~ *a#}җ@f]NH7Nl@ɝ,9Ss|r#E |yB2D EPC I@9Hp/*`ʠL8`81pFQ8E-A4Xqƚ$8D:T I," ֱ*}0H&HdQ1ʙGGYS!$1}t2-M$!">qtb1'AuL 1},Yh…r-( d<Vı8󸦔R]q a# HPZt%2|L0g'^qɕ/g9p*0@tAOQ0`ϒ,]@љg8>>~ ?:,!̃ TPҬhP:|gg Z@ ` %(0 ( .0V` .|{(4С#^@.@"[H mPKF2DF^D nq KC* ҄( : "` 0kŠ`@8?plA:(°x$F"ܴJ`+.}$r$B4;2IwRW`A q@0Qa212 2$Ϭ壒0D% qF(c]S M-F pR6 =4UcUR :nc6͵k &HzDA$ydK6dAPF.Uϳ0ly939??)pH|B;jUj( H(&B |^҂'H4Q80 H VN(6dzCz"i#^@OFSLɥ H1,`\3.""|E>F//jrp 8MFR"ONl%(^~ҍ h>V.'C1no%0o Vry_&礏J}%$ D?cP`-xzAϐwGZ$w1߉d3$wCPAr@A@/PsYxD$fjĶ$Ӂ46gR@ x9SG):`jbOFq"(;F(F#m<|JOO 6jVR` {;P,!yplyMS>@ЅLDPXlР2'׸e. s K͑xEP?eoVnt+-S0ӄ7BghA;]U@&3} L?@rd [Bf": b>  Zںj.JH"T [+/ʭ(FUSE QL3BNs ̬{쀅R0 :JOv)A $  GK m_ P [P.(BOa|@w5oCBbd;P"!aN-*0H؁ORHh!A Bp4j6AK{*"l(h \!0zGki>4 I;H%#g@q1G p"lpJ3@q- k֠Qi V[ ۋTk0(C0 k.ێ! psŴHoS0na)^4/ 4Z?8#0d,5 e0y d(jR[A&ϴ9c%*cVh^ PP unӄpN^A`fHЏ H \d jf9O ԀȖ-vQ[m,A'OP \C@ nV%& p.Qd6+`fB5@4 lJaO,9h=6B7Xx B\35]0bG~0>w(Zf@[m!䗚vC@&B"w@qi^c11fr&;:a߅.i nw翪k%ϗxK<@D8<<3KZ>O y C @>hṭ8͛?A<AB"|pA=[B&s<'T'蠀@*@ "T2  3Խ3TC$07Խ4|d1AWTI(GLGAI4kGzlA|C(C7J?JD;BxkkxZqaH.h4ȱ*R;`4S` Ȣ=B*ϨzL;<<;2H:0抻A-SX(> b0 0+0 JXLո 9Jh'e+>Z)AŦN ORJ#hJ@Ѓl>ڟN :Fx, IM{!.MJy1! gaر exS|}]A=W"H4?mxa@KhxK܀`ouU9P8,У5ҙH"0+W+Ռ3R# ` ٟ p;2 {,0(`M鑤jYr=*TJ+z թK*N_J{%2~[WGuS: X[XHHj0q؀L*'0 )*, Rq ܢ XP)ILs#@D=(Py h`Z`]Y10N[Ё`q/V :pBRI۱'@P?Ȁ0PPؗ)\ZM9[`\>`%:3, ϸmk j X@<. x8k4;L I;E@UP]94M99K +8y\089(hxI7S5QK-P:-Y;B568>h 9}C:?`8-`Uk]`;#9;$`E^d~e>~dH6){? ̃üIجJM+RL| xJΣ7VX ~:a㣽>#Jl~[FHfjdlfuTaOh!dʼfgE^E h4dQfʚ.S *|hP6gLTpvhMJ|Fӣ ^h}vJfB^T,5d%ģins>|Y|J~BADQjhf `iBA׮}^ kn4weytd6jsk"j@Pck y0 u>$ejD-S 'lhF!uVlp&l̞h6k\R\іl`Tߞj^gq*(*4lNnmnnfP`:> )k&*ļmo<+o KfH@tn쎵fNv4Expn _+ pZVnWS0Ӷ]a3WP>mp3/Jn&je@^Hq9o*r+r-eRdb//!pP4`_P)Ȇm Kl7apWM04(Nf)BEK`˭Gt3T0FQAmNK91@.RU0^2ju>.Wt/xq!ȈP5ิaZ/-*PV߂`_Vp6 L@7?0zrPx㭁`_gv?PY0XNIgU%w9vv=,؁T?B/Ȼ2O?pP(`WiC:߂Xρ9ݰj68gLwuxZGowRBr_,?gzpN#z5?Po76@)؆lmJІlpjsaW_P@JlTI`PM<P1 E+"0+`IX*Ɵ+@Rw*hh7 '[ KA?3P$BHS)u&_0PI0[8+^Py՗`ςU7P@(85+8&ۗ00&8Hw}~qe YT`EdN'"*@L!^UC"@ѓlAC"FK$x%ـώ0c`RtҩFP~*rD @8 tK15"R<}Npْ!fNRY80-Sl1 /0ef1Ȑ,,eǙ7sТ8VL4ذLYf*0{ƍG!j^ U.#+q{v\4s$Qt2K)='tRx*XHpW9>>o @Q _374PsB )LqBK'zFJ!"?TIq kbF6q%:T@D |pbS+}0#$Ga(YQY'R!!A#? %2GL7WM q^ƀ&sxф ?}` !D 2ЄJW{Q?:YM AF$v41(f˖$D_5aEۙU -N&lii߾-l62ƜB/0L5 5ɶ28D&*y؝A [ 沢)?g`ܗB~8^!h` "[‰H]Kqg't`$SIJRx Hb)ɹ)>ctFR&tI]Y-uQ6 _Y}:8-) Vg%S/ 1b qFTڊ$Y'G sunxa 7Fʆ?qi >;ݗ\"_N:'fHu&X/PNfd1?;(7l)0'/nC}ohYiIƚr H78Q-t/`G!_`cEfQ0Qt\^ sT-  1;(t`"f |l=eAȮ P?+NUf0AXĂ Ak+20hAx-_j B֒j:ÁA,HE e%-/p 1qӹ(0 Xt#"$+`T&% gX{T*l1EQ'h$0P `Wt.4HV#$x$ R \PJR!Z`&-8)o+'g0Hh;4O6 ^ eD@6PlF'hR+`AKx.a T@IK@'^% 08j &@K5b$(@ A MC H8`C1: F]Fp ,]t%L  XL!!. `C,֒B5 hW2E 7Pժ~h@!] [ \ @Y&PU"6TE)y?pDll$p :Zad N\+b8` $pVQ(:j^M 3YVU"T?p6-S Mڂ*ek'0,ZEgz,`s|60:P340F1tU忉RMhɔ2kzbf( qdzv d۱{19;txFgUdXȗ"z2X(/f " E0["c32ì,1<.0ӜFl  aDZ“ B,A؈Ռ;Tty;rh?3e4eCX?}Ѣ,B .OԕtaYfâX:NXiٙ~%}ig,yֳlS-v=hKܲ7}]4~w1~a cΘwxZܒ0/ஶ[ݬֳm]198%Bx׈k~TЀЇf$T lS&{ѪdH.|\fsRpG}!l| 7^Ǻl"Ё644 4ithrW};7w{~;Wu+aRE(>Γw3iByӳD$uw+PPA@ ĞO=(BBU^%@"߸ ݁H`Y޸@ 4BUT؂(PB`Rhq Z \^\p i\j]f ]hTH@h<5L~6]h00e@tǩ@ AH_|[!H .@ \ P@JL"1Ԁ܀@ !&AjLA B  P4) .>|+1@.+@| ܝ&dN hl36X \+0X@hCirCأ%j3FaZd0hJ8h<}( P!A 9֑AX `u8"C] Gq aB5rp&D0@DT,BhtA.$&"*huA(4A{g/+F 8.2{>(Y`^fB:g#'A::mR#pҧh+X-)hF\4Al(nh$+-P* $bF } & h0aA> 4}.$L(d…R+|(XAFFB4f.P&02@r*@|䨃n㷴BUr .B !@BT( 4-i B3iFQF` ,6eRf.cޟF"PCh0C!7DC4+40B!aȴ Uh+\f||@|db)_P("զz:cB^J@zJBG)#&$ BB~l(z,"~,dĆŮij^Dl%Dx], V, $˾@l# ]'-ԾFɶ@@܂'hZ p/@.!D`P<bZ@2`,1km n'&lTHBAh\l-J9$\.4˚.Dx&-Y)\eݳBfBVorjfh ,5d/< 6D 04h|78C\=S@@h_!zFPQ\B )lL ] f+zЂ% $.lX@ |. ilB8cb@Vzmf. Cl(|F@%T,-Ҟb g@*b6"D1 H 0n8 ABh:p W+td'\'DlFA$AF($뛉J(HM窂'pk(,,*@)Ao>y1D'1Ҋ] -TkL@/R!)Z/m7Fd_l5DC6`/0TAh,0Fk+|P%VP8% V%:P &A{N‚w E0Ӎ@a> -`q$/rFsO%lF@ R~,K3@βA"/B+ۭ0(&$*z: NK! %ҖU[i?@ޭ>!{OWOK%rL2i)4 N]_7A߶Q#AOgM&P'%` B@ u -O5cuYM,T;4&Ե/o+ s%+h.ku=-XwK%P^Ff7>dnm/8sWAH^AhԳ P0X6Hh8hdBZC/aEa8 \`7pԤ \LU"$KUT%X

    ~9Wf ^곽?6?w.B.x?\?_!F?g.`6 6 bD%RXcċ'r̸!HG~l8nŽQtRI-#ŒYe4gB;uthSPK򌨁 ԨTPLjЦ[vlخ8ٴj PЀȺ,%Z.pږ3vl00Ȉ9$ %7,sB=Sat !{Xᐩ:MsLK7N4ʗ7Oytөt@fd\} <g 2dB.Hq\/H*L>?N,/(OR; XLe"e[ ޓ:{<>dCT$/ftV~8ḌDJbD $<%|JhP6h(vP. ƚmy(fg@7I f'\U@D3bpT`4"cxQ!RAJCZ4LlQP2b;R1Р8&HNc8,bZ`@|`Aj;EOR݆$HC37.a0C.g=$=eW#^@MXs+$V䋸XaU2:$> Qa ^$.ZBA!(DcX۬C.1yp!(i=Z7e `y7qm2ʤ^香vz"bO`KKm&_) mʮf6fffNW1{iJ(a W$5(^ @ V" h .?$xŅX" *eeJP""~$`& |~j[@(߆"` |fGP%C .lAj#04`2P p#HC ĥ)d @(azTaYwA=w_*(}$P4шIT.Q?Â@ +[܂@ @'l7ч؉M (sp B^@ڲH1|*q@ vP%00D*@(EG @ #F Cxb-vU!%0c-2MZuiKT_׾MZYd'h 6~kT ă6rTÝX3HJ $y: 0\˔%u%jJā!F:U#P0`C΄Q(.yV& n4r 0Pr7 በv^yhp80 π4qi{lӗ7b *)brxl$=SDU B7ԍj7"'@}$JD} G%Z@C GtfψHpь]fo}_QO` ɤ 2f%Tb.*Ą//cf'ģ OiN#?SDD; ~HA)D2$]0v({pBQK0 P& P !l:#@CX00, `A H $x@b` c bX#}1/@"Ă BqP29\5+'H*ib >`FOSv:|># 7 4e6\h6.v BjFxM:@?:It1)RI)BqS%:#m-#^h (B7@7N "d OV1( oo>.ab "BB>Q}:$TA>nDPH` 9jvD<<#M$$3@ ItQD BQ$"8'$D+p'}JR$P"&%B b`F.Vr+R++r+L+%%Pb*!G 5r+dV"%[ b"\ Yj]VYpgӍ|`\!xe@0Ʌb|3,Q^ R` ez,4<!3%3?h23[`2h f` W|Sg Z*``]J@.Vm[h@#4Hd%6F6UeAv7fT&baJA @7- !^38O1ug|s6fP4SYTt4_` uS.GDKDRJ*/4@n :O*%s`qDb>r`$y'NH w4 .բpXzZXx(X`0 (X xHfZ$HC2xpȍt`\`2`x lP!*!KRu$ @;+ ,W+!>V׻*r8 ak\b KqbrRm^u5i5F+of|@5iwOo!7"Ȁ}x\2. :Wt0!Y-x`+0F]$vzoB$ǏRH.A~,[jm HWX v2 ހʨv 0@. ̒L2Y6@4@] iW!68j%% t@|%2nmm*(tVEoJ `P1y:zy.Yve l9cVVjˍ Zx`9"`p"GӘgKy) Z! `NBqn @W)X 6 u΍` X`y@5 X@`.4/ZQB 2r %Cm?βn|af;nSd*;T# @@C8YpۋO HBK*,Z,X@vbضN @u!#@!lao7 G[0\:O Q&us#)wD|q'@e630p$DtHX-&#E"T$8]H9 Ią\/jj #{{pi~B(B)Ad3h$~ ?<q9|֜&Dxb]cJݨv }2%pPǛ'|m<7b(?=iS=ǖy+1&݈XGOT0@ y^CFB܊ %Xk ^:@ tUEY 4cԥ`=32tpG ZZT% j qe";C`g\s1O<0ؖ4i`~& *a>@X}^҂pAE4w M|! @ g"/~"6sSKP"8 2 O/KeO85M~rAxܧ@gX "[SG }>-4_,BWt``… :| "jX0BgF05&d2%Dw,(2dBehʴ@Qɦuv0dl`CCc\w#/ƖDc O%Ty"KD+P2(ѤK>rլ[.-uӅe۾m!j{ <a4>U/f2"@ +!xGFSRrPyd$8! ,옊 GUer&|C65Ec= np[] =,)`_+@@dboRU2eAA:ZdJ.)[CkEG`F^Zn%w BD bAD"םIG\f@8%)4pu A%%gv"PP 0@a :`א[1-G \@ECȀ uKp\PC .ش&J l HqMl7B  zAB(&X@CŎ*D+VTJR +LnpeÙ%oD&}py>P Qs PpCR,@3"v I%2Δr "P B#A\ZN8)YpIPI.~BDe@y@mK+}|Y0` }ܝD ‚Zs݂/OŠRh u G N1wMزFd8"jcvhÿ {6eeq9ذRR̔ej"oy:W<_tsʗX28)Z WM!LaofR.=Q旄%0$Iwn皆`/5o H9I9+ˌ^JU=POa7{;ԡBb!\K@E gl3 !&B(jq , >.tF=Qk<ۀ]d#F$q N>6L! jcl#!hG X ^WTKѬz-Ћ\QhA% HI !hBPT 0 !t4AP]ŵ:v!ZX*[}$  O>[DH`~9 'Eȯj]@^U2|VI~1q!j@}R  *>$` ؗ>[ MW}`e\{շ{և}IP ds +ׁu2d'|smF0?G4M`1p>I@G"zZQzXP .#/P~@qz'psU+A8y 4H.N#b /Cg}0gW>w+Љ"aC5/*O:p3Q|1'( d>;veBX#45)"Q);C3rU C!#%@/"` /X!?U>8Gg J*Cz uC` rv R^`Qvpa0 :{0#Z8 X`5a5bJA AGw tqE!p7G I0k V$8#q^GFQ 6p/M5АFO)RWHp2W6!Xɒ`x }HX 7"X+#@b5=!N#.v?b-A17c)C1s4i5=Re`1 (ET ZdP $3yG4>241'A@)*F&R "Ir"#@9ps|@2Y`ir#N}= ?s-eii R>| P   >?i$ p! z1 lQ>sIz=!g`q]pd0kx=%`=  %Kq[+"?!0p1HY=DPS:$@PjW񎺥Xf؟nRE q:s $+R`$ P 9tSǕb992 : rWc:pLǖA)| v#"UX&PdZ*4'+a@` r+r(+sQ&#w8a>@3 c3_BUь @3)8a.0"v$B 2* y%phJ9.:l͢u.ejaHQ0@*HP@a`+e+p2.+j/J Za6R\ Y 3ÆX!.@z;LsP|r50/* 4JHT1Y 㗻^ RgYzҶ>48*ʧ/#BJt?zp8{Z ~P>#?>!-Mq ^6l[/RG^,N**aQw;{9r_eA+7% Nz [# 72%SųZ:D#_P@p4y3H);`Z`4үC@` +4Q60N089JlC -J!55u%p77Lp6,5%  m;&G8r^M][261ĭ`:kCj! 5,TkH @¤F Ii[hą,5k]YF#8QB:Sg c!pc8;L =l:gu^ px0" Uc'ɣ0cpcus6g_,7JQ6H, qT:DY\># a\<Ƃ!PʧɦxƹpJ|<p q̖_ͷ `"g%/eQ0#r*v1[)eN mѠb57 OnFs?Zvi/L\S??IAxG6]$ H4LWV@<-z\40JmFa6]4$(XKm4[tEN?LmE`A 7=&ұKGRBE@%%5C %#ׯZ/ @z;7$R4<mQ}SiPCaA`|znVE-KSp` !*TJaT?K$0EMڜQMŭCpPtML-=A t^wj" o0s 1@Kr -@soY.銮畎W`陮.HJ { A`{ p q@o᷐kN܄unǎɮM` S Ke0# N Y7P 9@|ʎ\2.%Q`.$?>~.@8` [ uV$0S`68$/4 O~))~+q7>tN^ fNrp t@ >I^뢎 PSOUoWA f@ NK N r q + s'uowyK:ӎIsP Oz #\f?~p |@zgWPIn?}5y a>Y9rM.Oo1/9o׏ٟ//Oo;Xܧ/p E!,P H*\ȰCJHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴS>JիX ׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3krT?ڹd}k԰c˞ z`mf>'!ݻ0GX)f@N!'P\15]OQ6Ͼb@_lنSZ]j#h jSN& P&vrkfc t!9$Ƈ}FA,)(uf$rĎA@5!9h">`xЎ6!9ɕ`ȖHAI%mYfcimm)fglwډ~(aБC@BhG(N碗izz^IA\yGu9z! A9@^Y@E-9DkW2DH" @5D|&d"@2HA`rH0`!mk"$ an*GT)* d .]$oAF l  F-?.=02@:Y #P@.zm,2`4N-݈ , Ij&o1YުPyjčLliޘ,&+H"2#  RK#k!aq1`G@ҽhf**јg9֣^hH>:HiF+(k뫂.{,o~:,'LhEp@JdI tK|Pަ\",ڣR.=}9 QcLe:ʦv%ovjްPaiB*e z y+9*RVG/j  k@E ڤ $9`D 79LK P@ 7Z;C Gqj>{B8ؐzykCxz>! $@RL N° C 5N>؀0:oO.KWH`v^f94'S 4HSW uiT6x)^I@Z,V*>w$¬8!lKhn$ $P%K,o ɱ \X 5*ay3MA0dUt\-M-,[gVYIF3C5фB@f"%P EA ]I:]LdL@ ?hWX"XbuDk˫~ A n8`8@aDFAX\;@ S 0,a G@0A]{FX/d@*P UѶ Nq&*Y4ў,@g!v`Z@s xm&J0% Om'lIsç$@@c 3‡M(jM@Hwʼn~+ed0E  Ԣq1v "@P%`$W_OTB ,V>$+b PD   Xa`)=BZbyd%y uRspZ?wlsPO~QxQ|@Gׇ@x@w `l-2 #5`gAtW @` C @X~AYvPeQtb_U86 uuV L0;` V 8d\7'$Xa h`@(ЈÆN H9 R =n A pFE)X|he1h#qv e5Zfp v?'ёy fjAb!d ha&g)0pyY]pPP DPBv)]7}?o&&P wh pȨ1 r`ɏ W /\=6F0 [Cx5ЕJX P9 nY@ {px2EP  vPpy wE}>8/t'`@o:0Fq3pM v~cgiU*YcDH?x2X8;T I  h9k ۩01cUE2f \uZ3[b:p "#s&{&Ȗh ǡpEo#u[vm 8} 6d eXghlo]Cp:5P5hR@ `.poj[WoY0eЦUg>00V@uF.mwp`QЄ'p6.@20 ]7C$h7zVMεcqx2~ `Fhr@ ul!0uq2 ׆5DP09 qH*c 3y&cN0u ,@ 4p-2iy[e j>@L2`$N. FPW^F  J &#`[+ 7}ƀ k@MwDѸu̷uGD* Cr *#J~` z D}u@T9q&'J:0@NZ G .` 6I;@ OrGVz~<6dB˪ #;6bKb#i 8` }w/xab'aZQSv /^aFfʡoyv!*+_GہyЋ] vv7õm!!{&o׹[q zF ƫabf_ƵxIOUPANvt l뾐 cߩ `뛅ʺ /,/cnmyIv_{&d E FJI E@ H\EUL̿׿[Ÿü^tB|[5z70.*f.9b/JYƂ揪EeȎ\ɌLɖ,ko]|ƻ "NBȟrÔj [!GºC#\ ]pP]\k֛]a_iQ|ɼ` nͪv_wa h!z+q!6ub5v Zv)V)dͣ` pnéLO*e c՛ň #=-xHIʄɊ\v56ǽ F P~;dv6b,d*jβ`kbԹYbbfg78@0P s]bnvlF 30ɶqC(pAptϴo0(psF q@>@nvD75lYi(lm3{h&xIMsHl^)ds0 V J ,xa@ o Jpxip78bbgIX]MTZ0J@ Ũj``"|FP !>l>P X> F( @Ybz '8Ė&LqZг 6p  UGTc`Z @ޒ1*80a Z&*ZswY sXt(Q |ZO}Cz` >gv>oQp}@&\ ح}3 P&ywۼ`8>DH(&I `]@vnpj  ܭỈ{]&-)P׭>_ Υ > ŷw-wДrl kJAiϏdz!;& b8%@b *r;@@$ũ&hC @ZPCC$:hL@Hw%NA‚\2YLi䅋D:xy!PPbBG42@J7sF[ΝL@$F9FbBr Br_+@*0`U e뇯C-mܹuo'^l (hÄv.بUӴfSCBg|DH5mpb (X?V]AS ".qC V9@ Wa#4j>R ;CA#G#e ?d\XPB "Zn0Z" J# ?p '6`/)+N (B$ߊ8} d+7 ^]ba.m&XGig}]4h6Vu5wu9z]FWݨۥ1׎K.zMuuن;n#bBCngi~8a†X% {oUoVp0\^r3|-8d͚ s-YGo ls\ĝj&+_b/~xɇ߿fw!p5쵧9|Q PC&N1@Wl?BD . ad)~*-&J6*a)}@U,sIqII'7Wz>$">` FS3? t1Dne ̀+zC\#LRfpB;h6-0hKsN Fĸ+ &PN]%hpizt$3ia .N\ T/4a:$@!ohB>=R;Dh{oɐId#86C&<3@Qg p!hNA BR^9J ݎ*5QTቔ`"xaraBhν n% *I2]jf$\LhΉDtLӋ= nNE *w ဉЬ~%#QE/V $5=E瀘|%Y&%Ao-,b"LmqC$`8J ڷ0ڪ_9o+9> qjР( ^V,`7z|҉H v*Si z1U@ 8Q@SR9Ѐ 9ã+,Ё(Ⅻ VU#)ЅH 1 +@/ )\bxL0 9+ YI A҉),XA]s p3BQb.AX > @ӣ>O:+Q`'#`닦#,#290%hʰ+),?f$:GrD`Hhjx8kT'4دpᛀs3h 2ZHE +lwi`!pĀ| e19`5 E-?⺀{:IPӸq/ h1/` X b$QCTB( $.KD LӨ6`(IB#Ȃ8=ʀ++9| !ಒ\p=`א-`H>ʐ, s NЂ,>ȐH @04L,PȀNHX ˠ5$, ɛp+h9jF GAFI)|`NxhL/ہуJXJ4CJ'F wDi:%#pOaKL t &q%<ɀBhQh '(ؔPș&(خh%P -)P04q5P| CK[$,#X~PPZ`-!"]`$@'pRh-<m8 =-[Hk-5Qb5A.5x$07Iœ/-qK, ;M-=II-q5"O MUq I ?gZAp\[{"X-PA6 hYӜ QY| hڗƉ1Nhih@Vz #Zj%=Zޙkՠ{)ZUAVaA$XWi\د!V8E"jPJAqa^J#5Q1iY'b9$1 =SWIYن=Z^MXUِ_XGڷCj$}ة[RRZR"Z*Z52:֡1:mVL AZeW}oqZڋe$!ڇV:W #2z\ɝXP\i_قݻ }Ef؈Dзu\Zյ9oY %X%\9! !]I=[Eٛ]:eZ! Sȋ>#mU|JSm1]Q3(GF˜UjU+Bƅe 0ŘJ0DAZB 5*] %}%a1_ 56w+bpp-mȦ߰!4ߕ2?!h@}Ljٙ0PC'Eq8>1`9U 7p%~Qȉ/X 47hJ \I!@@pQ,b(+IdE-(XҩʮBӲy_ % ̵a`TmZ%hl8܈ܐ&Ɗ0 ?ؿyӁ˖83GÁb>+H'=0BEHeP?`R` 3Y{2Z]dૄ9l?11kk&Fx ɷ˳bp"x `5MMFT*صja`>ޤ fjl0 X=b ,4֐"p?m?9 Us?3Bզ8&1HCq %{)# ㎡>+Q" ʫ0c+ȡ 4p;l\ A۴^ȅڹ!X1x͞U:rH끦 ; jfglf_&jVha_ڸcf J_Áizc@>u8fp9@bp- 9PS l7r#H,/);p Q,JOL?6.`'Xʖ˼4JMp_9G h< Vuw҄?K.ȉNa?>!_1dC3ՄAWX!02K e"P ?\^d&*Qe@D5ءȧ *kjإU@&*~CADAPPBB Q`o  "BP‡ ɰKf"<(^AHq@%80 @r'(룫ѻ 4oL!C,b4& A sùP,2j (ztщ=mMf{өY㩑G}ARFTp02P0Xdt&<$ǫ)4?Th?DĐiE谩PJA[a%?|p#>rYRy%:%_T D9 fGWr2cVb8U ] BA|)HB4+ *u-T14]C'%IdPeZ!a(XQ! rlG2rbB"lV<$6 xl:}INl"6/͇`L ̈$H$8 E#%551bMŒӉA9 m"%rs4&I#=m`)NmZT>BxMaNؘRd<$)br0lo˜#QDlB[r t1^S :X ADR$X  :h4bY@SrlAU` Uta2hl!*ivfj6Ўk |dp)SWT !Tm,apڡMh)<=sl(EpРb(LY3HZ #Nԁ4|l*=ʔJAvH쐂P6"P{d8 Bf 货IUJF- XûNs4Rq>qqYpx-@l8{0̢_F~ %)xpR^bDĒSÁ @A)\>HgLVW2&WTR+7rkN+}=Tf? 9ĬU$@@RX.f[.['H2b!0h|C 4cPOCz׶U0C( \8#DpҸ>ƔC#c0Bn"X@!HL@  DT E(d}F PRcL M~_ VHMMJCPOId1N@ PDD H^E`dtxPRr%YSR BS>OdYd1P҄h$ҟITU̥WreF`BE\ O` DUEdX5,&$"HR (KD ,N@B!%)U䉁B@`I!PAt` gFIK (8R\os:G(gcpLXgV0TdgAp4'Typ`DԀJsjSeP$I! O&-')E+xR &N%t\@~D$T%WO:懎GQ[$#2d1l5C`3g1<Ri@0/3̂ L @ܩi8)'h҂j !L4 {&XA( ܝQ @Bg6Q )@ IjB hGN^@˝EL0iQ%N  'dDJM)pH (# A*(dDPh3yN,\'=,lC4R 'Lp>DKG95>ɾǛLL<#lWhgn0(`A.#4#Am ؂HjZV]DA T]$-B\چ d H;)Q .HF@N[SPXpABtmD0A,JA)C!A>A-Xi1Z6vr(GlSLLmRZʞG{f +ShLG~F$>lF-PC43TAIZ"7$_%L <x±WtnLl(R1H***d'@g"*s@.A&T Df@&lzf@ |+-@.`4m1,@z%W2'@@x+/t*AM!chpDrHK4D':h KT\ cCBT@,B0 |U靌"؇* d_%aBn9O!H#*OeD\fD'2- Ⱥ |<[y.,#2B|#|,j-23Vj4OuU'0f%@Z3;or*߯IDUr % j,& 2*rbH@߀ _f1A~Oԯ`JT,IfgdDKl}6p-5BpCY(x+/I b +u Dls*uuOgC,2P oyD'PS'F$qw AGL5BLhCl*hvn20 h h'6黎q_h6]􂟳BlnL@eA'Q^b'w Э/8**u.p/9u/p\h6 jC4'r 7co|S u?6\C4XxkUf\BSȀtBT i"d;pb'IT dV'oFlڐ b. |C! 0/A d|+pA d;A`2A3(4oA4HAԼtCAI-uϷEF"2܌GdiwhSmSfR>&-Na7yI "'O MD(DCL J5tXL@D4F\hѡ;Z\8qEyh%ISh!cŇ,δI%u,fGDϔB% 0K/k6]xs KV`"SvjlUb ٲf}|tm1.&gD}{qݺ/ 7fǒ'L\ybǔ liaX={QVPC}~z*ly[M*@,|ǵLҽw~;n\wN9fnfXn~=5V@}~ڷ7?z+ 9͂۞󮵕L %&:p 7-0<U\CʚotJ‹0 {2ULLG {͇Ф5٤2圓:JE!'Pb˔*s @tBHePxkt5(+o@ʇtUxHlHर7Y(+QrKOg Ba3VJ״dh.O3.A@씼!lK -FK.AC s(DRRuH%4|s]풀bTN,S(dH H=b@l Lj@W,\l6N]-Loޙi,K dgҡ:QMaK&L0v5S / +儞+ÇdI볁(n'r@#^ȅLh! 6h(9fO gA |@b8ԫS܃Vc|ihfXb<8Z6phFxx!  (N6tBܨ):ꂁOη֋u;oO$Xu;R0E 3(ZDK(XAf+?\+)۰DClPp$'Z1p'yGX{)FEH c"6 1r* )(!Q\nBk"1(E4d~A h+@ " V T A *2&"Lp+*T$"C`@n]VC0`;nACZ i#IhRoCAxI@>Ab-0A()118 [`qB0@AE,leda75 @ KfiI0T< Υq`QK P~TGf ` P'Z/j\,Aj\3菄6@2gF\G9VXjS։!#0'6 f"pop50zAPnhaIha1b&%pKb!XmY a|t'(hD_ 5L` p|mъxp^Cv,_Bhv@"A}BG9Uf'$)- 6:̊&ZrQ l$#,~B.hoe✮d\*'B%$X4P'*p'$-LkiƪBB`gvbBP$@E$$tO䤽T$牾^.G̯/,gc Ж.J **Ro> =e&PeDNP(RLeRS4H@oFPpƝZP,N^zY墐RB]P QRP3PpJ+eDP+H5$@H#I OvfR;@kƵtP $wQZ !vP ud%#0/e Cl }9QfTqB/+bdfدIxQ Qjf@!*AH Eq$*G.~f26%It@a1bE F|@Ua>:@#mh !$`sbB-'` 2~vΔ֭t.4@pl>>Qs,@ P XT Dw? o!4A@h!dGL Fs@ ,T DJzVr?)tBA @!`` |` Ld&4 YbMǔz&cQUNj0 j !3T!B% MVMӞ&G0M>f~G DH#TJF( ޠf8U.^ޠn bjH^ZdUh `<\. 2\`H62 rvTX!6v༾3\=qF!޺`4-L(K4b2uH P  K\N`Mp겎G(KT3QK)p^lf@MjLA0`Df =M#djQp@{ !*J#& f&-pΊqN e=njj~PEn{XY %!G!!a6T׆ j`Q,q">Bo ~Ťv mlTWuנ*FU*idoxO1y 29  y$K3Fl`4(Np,!뮴W`DQrs쮫9WeַT! >Lz0 NeX! ŪJKǪEo>úD6ɴG^i!Jؘ͈&tkD+#܀Ǹӥ@pL}XE W9͸0.!p!OL .<F1 @=6BN &V "ü<:F o@mkCz΍9zt ~iDyM̐ 6 C`@nMܚ`l| w*Yd|mc.w7B?!9pnl8A!eZ yGTBN(J "^--::pZzSm%*?~.& t " 2MK_Qj^vxEW]lbᔞ+l@Y'?j庄?PJ+(6!H.p!~ǎE*"\\`d dn@XA:!@/Po6h O HA9| x3Bj`` oT|XA l`Ll*P[q0\ \ \(SvA .2|P͕^afiA BX! faGꝍµ̠h:Kf$(3"_h%i]P$b":z$"'=-&Y:%Z*JQ/9=„`c,(Ap2#Yt/[I4.  Gpk%0$ҊROQ^-l>?Sy^<#qY>"DSsi+2x1^~t8:q&c󛇨c?-^[f)Ѱ. P'?6Ba:Bއ#/pcH-.<LS")/1_NX ^6&>G?)?nuC{5@ %"_>Hf kl #$EeEI q+S; ! (>J 20[]Xg")2?mof>y`ƞib RE,tE0U@ IL !9CRfWEgL R#e$Uf@ @M Dxufb[胙/,C^> VYFv `a YP `! JH -"Y8A|ȳb(Yn8Eк)A2` s DFef2"Dz,QO.[j7Fe$dT&EJrpJ_"Dp$XF5`aD"UW, mKB; 0/;0Ғ((;QԶ\ $;A 0Ёa&Q[GT3M*7D" EXU.ZbLp!A,,8htr1Lآk0k ɝOC\`+Eձ ;˩R؂e P!-AiHpJ/4C&BlA3RM!9RrD՞%K=ɡ@q @؀Ը5CIy5p(_E ^0%R+C)*P=":ܔR;&IUK˜@ )5yI0oz"(*4ZC8?q+`p>x" B8 .pPڶ!}%9,A|8ҲY@:5`13.RǾ{ũgw9LTJO~ߏqu߂) U9əYBa$iR"^R0Eqă _yc,oIi]B&ɦ#rO2JYoR` R,+V1V y8i01T,+8j2K!lԧ(4( -ٲsY 8jywZ# ZuDx~Yi/xʜ\a٧Fӄی3YJ4MS1[v}ukmKQ.՝B8.CiH+ fVU~7ib TXt y`ww#m]kAmW߸&$[UIn; S4@nr$7oPWH|Gl?sqe}mx\\IgbA(4D V,U`/Y5Kr~3zSo$^B|9^Ù0(Eny(4i+(3W Tdt%vvl䡦j}~̘@'b;뵻ӇҨ 1- ;/P ~ zxnvfqs 6Ah{E  2 uP9_C0|`  sBsH#j'*gbKTC*23$2!x$$@1D(!/hzT$~!,3!p$xgdAP s# Yi‚ `i$CH,E34K~+pk1R+02kMOtJ>#8 $G1nP,bg@S(V ZH& i&q~ :p yP1P 0 wl#||%+gj$g_)@Ő*!a+W >a` %T X 6GP r,` / ?PVP,Q`@-4`t@7: Zx<w?5I@J @ u DM"eRl_ءB 10D4/W $o37*T7hgtȳNjy1.E\7G>umP{3$V &uDd0yL\  `N `@O8[tw&aqGBqؙMIvI$W?!`(GBq89trj`4/Q3cJNR- } XsMxNOs  } QU L##Ki Pz8)eN<&%Pju7GR.6*F. Ab} rO 46>W1 0eo:GSr,`NP#@m &Q.pRK8^ۢlk=W;7+]>4jPTVzH AU2`(`}JV庻wP۫@~i֨`o\Xs01Mt+vi[EyA CZ?M7?H`ԋU[ a% I6>p<&[2,U , U=EJ+ EXZG.kpr@ S.i̭-|@ 20!l,%̱%]7 &0SEa?I8TLM ¢'M57 +L e]|7[i'QkmcKcUa9@ &k#ft>IXjѝ`N+kQTuO9l&*tdȸ \{W+1uQBVha ~ƋI9nbTkx3iA}fmAucEnkg}djهTLmkh2h2ODH=f! rx\rDst+sHA\&uͥoY{6ܰelQ Nwo(ݕ]ܺoa)#9U%-Tk Sf՗BT &jIF_~lFsFj=`G"cNt߇4iFShe}'M=YSXtݝ}?Af ^=k NotMd%n''pm ?&tݏD=0~k[ pI*<;N__ JM.%R"( ЧVNYH"z0]nR.gf 4+XVIqt>!nt3N΋nn鑎蘮鏮-ꊞ0^N^_.qh:xPn=NK?K) ~a`~>Eb@C^>_q iBMp@5psN_PĠRzp]^3az ߫ oK녎>>鹾뻮}y/32>9>AnD4?GoQ_0W =pŐxaNd/ V0.zVp zI jGDx P``]d_^ҎdI ؞]& =-RȰ.o o|. KDHj 8OP( pOz@H0(N (o @40^oY~Qe p P `gJN dA 7=\!AhH5EAFHKAPLBIJQLaIJB)h!#TRx$f85z8!+ SpH[LΫ9?.e}4 3ACQWՐ%~ ؃$ȉ=7ZhҥMFZjA鼦ÈAX°FF+3A09ތ a =?TE%Mcћ=97(1={ D{YLy+;Hᡙ`,ިMɁn7` KSD,A ;("q0QP/0sd:J+68CRH j 6:x$K+6,04K-S-L56|40 5ݔsN?.+Ӓ4K70 Ɣ#/*J u# S̃㎺=Q+.pA@Š 3F֠.T]IĠ$n?Pu%v C]*avFIBߤ5ͱ " EIB"'XzჾːbHM JN!3z F蕥='X-8c@%Tc1=r 2-6;4Tm!-nin qfdKy#η:QbII{>H$`E0:huV"j~k`/ה;6" `0X*` J(8L`j YEJ^2:M`i8AA8هzvΚ \ ЭɇPzBaYtX7D8g+bI͑sy䥇uۚ -D2&|gWS#<55 =-֪V b(ƿ^#)PyBÅWD (h>qLT L!.&T D 4䰐G_{ErUƄ ,:d"4($! W)eHW H$d.xΣƠ'qf$#F9Ɍcm2粊l 4c8HW bwy!ǝCA` #@T$F/QMLB@AY3!t:A> .Wy N|1Xu Cq3{')IiH!W+֬f5 /2%&s2`\5OzӞg>YƜaX E3 aQlFsQb-%Y%2@Lj@ dcoI*ũbP=ݩnCE ,e5 p)ie+9UVժWhO=<#`WDCh)cQjWцբ䨂VIfsͫ32%7}Na֖tP;Hw^Gb9+PjF\\ݖ:ZҖִm Vtv4"fi% XlF azZFkmyʧrmNYU74W TT}l8GؠּEoz%$Dy#k|Kݺ l\ޚm@ І6FVp <0x¯ 5awmp!l.9/qe٬g*Y^n%`Xt E% !,P H*\ȰÅ"xHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JQ']ʴӧPJZ@DXjʵץKٳhӪ]˶۷9Kݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟O!AHDRƒ @=xɅLw%?@%vML!SSxb"M^%h-@)p (v ,ؑBC VRMTŒUv9*Ԡ@Ah'xW'pۚ 6z[!F C(^0 aY0꧂?Hgªߕ>@*jé&{ީ쳢1K(Ԃ֬@Vf^!-FV@dVZa +b`b80ヌ9(_ ''oPc _8.fz-@E `,] r# נ j&4%5\VXq BEQ9A8Jp 6\-X @6[t@ dـxT/.B y r0 q@bZPD> AvKt T QYRn\>+md&SP@T"56R 21*" tE(3MPG6I@@D ć*d@d, #\4tAoHn*K)ā!]ų0M[3NjDAoD&]# V$@`b@n`HD%7!j`& QAA|K^` U⿁)E#D"$2I̘A /š=k9@]3ώ6MmiO[ECxgO0*wvl5`aN ZZ3K!u J•|PD>MxG`øB+W&@qpk{$?gN\bvU!DpZ@ k<45~]o#m0 .b0un5!@3tS* 9?L!m (%|N']-RT8{K0!f /{&gZZ#XRsH@ u]hp=TF`0zp X]C>F0-0+;zW7\@cq^c0(T@Qd@.g3g[Є #n I @`ry%V , M z1s5 !P&j %P `U0/ e<@p  `ʸ@Vh?6$`c 8 0I؋؏&&7Y g ( ` q ( B_4dig4< ""2NYz/0)V +wKpi6)pZ6P1u%J< ;@BN}]vGWPWyh:K D Jp bmKJh`-=$~)^ܘ I;PEDY `k^ )0X8yp8F!{li+`Z#@ 1k()7DؖYhȉe5sgU 6P 0G6P 50 ϐ54ԑFfr iiw:wxlyn^3h8ife 0V]pdrH `,"n #+yE 1Śy_Rv JXpiYJ:P6S `ȨXPpZKD {`Dp2Jt28@ )ɤ}`^D @zysK` r6?pYK2J1zy7jD1a:r:|Vd̓$<# 6p;HuR0S ؑPCN&v,ÅD@sz91G>-rj8((|s@z*/po8:F@XA OW$XP $XFňy|:ڤ QjO?ɍ|7p `8W4l ;@ j { K~i!pR= @r3zO R mƒmB?P4Ғ؀ 0 Q` א(LR6 әe`:XuH?I!N L0%UyqE5.~nXG@.@ ',`[ j+WNPN+H :[` jऐ*VzUCV^iG | 0T{k@[U P1[`I{iU#$PƻpחiK.t ` rF!PJErieFU[ +$(*N  0 [ ͐]f0Hn@7SPBC ^"Pw]`\l:Pi R0٠ ? 0 *,( 0 ,<*RQ$3z0WPc-)3 vЋj DP.`3}ӼP[1=Di$`pl$] ;_AdphP 8#n 7Wp7lp  *z'i)M5 g_0M L` P <5j8. k` l/I$Y W[ p%oқ2*s ~ pg  V1z Y Rg]W^/,S UNo>9PBN ~[|rӋⵠz @ 2_~ s%,M~lZ,UjA;(aCPVP3=ƣ_fI #ocTY`@ENOA%Z! D`I3Ǧ !4SHfVY>Tי,:UPjR.2z_ J'*j=ul+"fH̑'T{(sΖ^zx7a"C&Y>N包&-!|JF'2`wYv c 5vpJLpIWbj=uܰP1$,ҏ*űf7^( -R0dj+:a5CN8P% &v$[p=NX;ěb@RHc7 ذjp&J,rJ.$RRiԀhNl!'fx*j~yED@pyƥIQR(J+R(XjRM ;B$$ ˁXl/ YA¨ ^JO4Nh>;1,*B1$ҫ+R3Z$|dPL,Jp;dR+,R ݲc؁d:&O:YiĖǛ*\`wg}c}03O'(N.Q/Wr?m!upw0-;ZlDZI"IH*j!*]ZւJEYV<O` ĉ^ yyj=9m" "t:!^3Cp#wxխ~u.qn+(9\m C$+0!9'ñ~w`ׅC' !1no@*`kV)ɟ5,OkXH>{nGķ^3Ձ[6@ vϓmdۨFL̘ $ Mb9pӾ!2`" F"P !pdĊx?|B E?60J5)  %!x 8+ Ђ5X> Y.cCOAJ!AVP:՛A\=sI;-A @@+݀hK=<Ѐ$T@"^ҁ 6Up~#<8 :{D`!HJ9$x&8[B`7(D"J(=-:C`%xGȀ H,pC9(#cUS!h< V.PEth ŠBE)BTAel: CFk[.W;Xž@jOB@`8Eۀ``pxNs8HF0l 9XDCC0 al 078  J1A{\4xƟH: : 8%4q-DCHXLЁ\JpCȂHG7h|=;ۛ?KðG B (BAHj `Ba[j9:+~Q%C3 xKt#LZ; #9UQ9P +0PZM2S33KJ$L;GDEU:0HIIpFMEPE LhMe$ά:1ḲLCkЃWla輟xaa^rP[N Ы;O } P03Z̪ΐB 0O쉰#-XPų#0QюR!“Kz $]R[4"R+RLK,R.m$Bc*$ %]R4UFztǬ)u.R‹S.u+}::S%6)ŔtR-S1SC=T SOATGP,O6}TMT=+RNUÊ6͊3 XUC8 ؍SSRU\5T\U K 8 P!(Q D3%R0`Vo(Q3KD5oMWLQ{ l>IBF dI>uXmOтMX"59J mTmx1>EEƞREX 0TXMY]Y=-U1ՙ\>hHGבcp nGC76`YmZ} U1ؼ rL* :tWtg8U7t3&&"[-I[i E8RBl`HQt^=l^Rm\˽\}Z" +Y NʁDE= ($[\ٝ]ڽ<`ЀJ5][1ŭ-^=ނ%f8c˃0\҂T^M_ӇSjP_-.l_hF$<b __H11 Ѓ1JZ_n`BphS3emC FS@,U}`>aF1)xY PPk(>lGF Xh>Xџ>b".bJ  M#\tC `d0Pt֞-"#Nc5V㓀Y~E˰fu X\n8DxTXL-~\cDNn% x[E aDGf8]To z+xQdS>e ˆU?e 8)6G+F\~^z3n<=T.fcV0qbPam jTH_P]fP31>ftNg\E.ҺGH:>6Y1ug|nZB-Sxn紸^=|.hR1_^AK+ԃh^D_L<߀έh3FUAt}ZL V&iikt!m0;Ϝ h;Qhl9i^j!͊SpgHy%[z=( eMA&-%[Y.P@(6嚘] ІT3 kcNK\ȇ6bm,KR-G[vQ FWHQ.K> Cd8KpO82Ђ RP8]UPe#perdBو`n`>t,$0CVv&YQIsm^"ؽ&6Bp lx[( ?5>U@ X:pfGXZ#D <enǐ>AYȀ XեTr鐶p? 3M$9`ތ5qg@LhBM B%t;DtF/E/A߄2>J[=/x.Ѓ)FJF?uLDRW5K8gC@=HEbTtA@'A:%AHSXuh7+XFJ`G@_߃=Ybv( gvwq8F0b}+0Gg(58LX`0A@wgp20~@b(OKw@o%8`c_nCJoxl7 bXFPfOxNwC%؃8`wYx*CH >ٔOz" J`t%,XX\J)y_GЃ:rPz_j@'xE?uwcp{U{"WFM_{?h`?t|ȧo|7h?tʏ|q/}ufZ9ocNЬr3M} !, x$x1(Q$*\ȰÇ#JHŋ3jȱǏ CREH\ɲ˗0cʜIf*(ɳϟ@ *H*]ʴӧBJիXʵׯ`n!ٳh`ڷpʝKQ$:˷oX5~ҀÈ+:Ő#K Ɣ3k9*C3fҨSz׫c˞!BͻMȓ+f7@NسkνËOӫ_Ͼ˟OϿ(h& 6F(Vhfv ($h(.@e袋Pp4NDLЍ/)Z#bHd396#AE)D健<eZxbP52& ,8#C kXN7(@nc+ mp9 ) )oTyzkE#Оj' [DA0@MfCC?P<"2WsM<6Q$L}l`BudC[o@` \D@ct` 9h1~S&P06h.@Pd2 =d 0a H$ ~? T!S@Z!.AGP@ Q V$@ Bhql  *.DuТڴ9 @`P ½ +M%Щ 6`@¾d@dBuX#E\ɩ,rUڤ ,cY#ldpRk 4qkl H9厕@ q` d0+()O _%qTdH n٨:F𚒥,#Mua%"ڐ /q%&hX1nK!,~E%pmۈځThQY"QIk[ @BCInVMn{~MeaV)Jx8z/FMZI5Tg % 0jK`v-}kZ8_| , &^'w3\Y[[ל1-bc%&_-'&CѨo w(dI\K )De8dn%Yk.;Sp=5!( g(bq%@^#bKF@ 'BdP}}wpJP rr+"@j`@HP Bw$d@<~>sA p&A+mBS`S%7] P~ =`[3K HX?8$=X OhX nS i;\HTStFS FY(2(D ~GGWmqH~ 0pmD }Χ?G& @Pm(v0FP 9G pz؋QB.'w_ ِ ` Z P x|P|hm#0X`;#d `;wGp`yB4`QIM\ AvF$pw4X {pI[sDp Bz0H 縑 `0 .8Y00@E(ir/ptmpF\pFIp `F8X? PQMM`e,t F DYK0dX0\F~u R 2|GPy @eDɒpPF#Pi;*&Ztv[gO % Igy y nv F qP xEu :'I XrmS,=` ` w U@ >d5h R?fqmI4J` SAn@=s B b@B-AjAI@ ?AZ pB@ Գ?4YE9tv@ Pġ/EY HE{`'2KD+Q?`;0`itL9A5*ApJpP S YڠQTqcatB/W`Wy}D>*xzUFP @YPN'O{4W  - 'p,iqACFRTYr= v{ωP 0  , AါPPf!LR8"d0(PxiSjps<  4Q C[;9P$',b z >б kEDUı0B䊡皮E>u@3`?@vP 3znAꪧ!zIp/C4;`8G@GW@ z&0JVгkzRgtx`&^1's͹* Pe٠ *  > `eиE#3a!nr^?HpdI sA)x Ii*C3١ E_$KJġ14"STEy^.?QIc>`R6PEIzӗՋYDdD:60 B,@©@M0c\UPv*Uk MZnX CBtUT)E\e'D,UpiK{H;0V\O{FAtp`GW@ hQrz[F i'DK@"; 0 $p qLF3  P6?m 8^ю˩{!pU!JP0 #%uRn&D c'#Q7pK$Ux.0 m % uJhw;p Y)@D hTɋ!Vd5S! |iFPO4N R  eeȞM Q Z] jx g$H RM>&z<Ȑ}w " - (ǩ@f  uԤ`N%"jaU`D vp]wGz{v)&DLm1wxu^qHRVtu\jvtwdyԇe^v]{\oYC>Zf5ADvPHG ]6]cի7-I׫'s-![}waF 8-}c@:KHY׌Mg=Em? sg=R"= ڧ:weKvMgU׮ڰl,ݷ!#mj+f{]ڽmzzTPd@zpC [ܺ0x]ƙ|"7IF1b_= ݋=]ޥ_ߏ%_ma&g 0 9*s rq_cѪ>^,*sX*ުy=/]flҡg*"q ;$g)>8@}]d;N37 pLMJ.B;Y>M~Bm .S1K q 2=@" da#" ^e 2=}>!CH>8j"s8`x>8n_g`B>T^~^>& Ґ P@`] Ӡ  {c ; ΐ ̠ ӐU*>2 s*h V`R@ D` % S`:~9e| H9 x=p!/*J@y #rWnd$P+ P%O_{)-P7 s2~#v0_u) 3| `n'B   0r?sOtxs/aGp`cAni  & ء ώ V  iP5ӗ~2p jrk0z@sNEO5I|`_ sY D`{_ P prY%F@vn :p-) .y8*8 n ,BȊ"@)@F"dpbJsH"c'!<К9zR(.!lc"1*tSQN@UUj}uWa%[vYfծ[G$@9t- pp+Dk!W*f;JJpܸ%T0*Mg~`ȨiԩUfkז8t )BӢ k'I*ӒʌC@R˩2#RR!L3a!'fFX4YNXҲp)0h+PHC6H#tRؒR`4 ЀxIa7, C@J ; B[pU4X;; " ^jUR XEHh:oR 7B00KrÄPZC)ЈC;-c [ wPU+?lVVb@1HYg1:[KA+͐Vy!X%1dS`t9˭^+9,/Uv*R"5zA/LzWhC =Fn` jmDE/#JV{d__6עvl. @@3b)l1CWa,@3vȂD~8Hf*GٻD (; BUlO'_a\4VYWBBbEX6Է~0 &>lpC\/l}#` 0>KHۜ|[gd*QneeAw@Pep @BsQ9!! + * aPCR5=,#5Kdj)PLiC RjjW:+D9@'[H8HV.lP p.-nIqAho4@ SY@ Qmc'#dbȀ4BdI)2I8D?q0IIK ^%$2/ҍ @֋J*e"9G4r [!,xn 5gMbSd C ›|1F1rR ad@H.soC(ievb(ͼeZzӸכCHAtǩS!,\*@1Fc4UG\ <%h+  (Mp#8nÄ́}W3qy$J•c@\n ]"` dz؅b."@]0Y"{Yp(/)@Yؿfی+g0[.9R] @&,C/}&sb/d@ŏvt0H8) g p*uWZf ۚ KJ?0y7)ְK63`a?i&f"x+Pmp[&w}nt[ | !Ť0A(f8 :V#?ĺpG\x-oQ!j!oB1680\4 y کMP\3ym~sx: E6Q[\1jD0@Fz9z׽u]:G"*+a.(]D6Y rl;`'| xC2p%~@:T\0k jq9(YMzԧ^w z ©-ΫrWb}}{WG0#x€u_ _ӧ~ks_' gO{Sڶ<-wݭ(龜 p4P.?L@\@⃊3!=5KA,A̧+٨S gkH0mx*caHh6 X۽p\B&lB'|@Ԓ zۆWH{C9Өf0‘S kk86RJxB7|C8?1Ӹ(;fWӀ64"4YԸh8DID(U1X;h@ :>T?45B>#b4'KDZE[P:\ŸB8 lx(TІ9XT7uȀ8 c "P5l EK+7Ř{;; /܀հP\(G1;F=|G›X>@z 5D~\Hlo;D)2[3/#6pK3Ț3l@GI2k*>\I"V[It89X  @M=dSJD70p#暄U39bx0HRc2<ȧ=9KKȶ)k6bckJuxjG.ˁK̈˶?D/Ĥ+vKBv2T+HmxЯ4+Ġh00j!"H#VMILCʤ(Px+X;6BTp;3 Gz9=&Ђ&̅%8q[7`Q:AXη`pB.=+6Ix2:X;PЀ=\TӀPq{K8-[;XxM`WxS` ( Q7LUT- XW81RSk؆UGh GG$͹! 'Ẑp' 9%?7x4=Ȁ̰[y :9mDX{T4.\M'ŊST5[8 )T0\s p@H5UCU2UZձH)Xc0>U` VTYS1 bEVamVg8Y؄\%jVnVoVVInEpmtVBXgmWwu PWyMp:x=XyV,V:(u%wX5}l-.I2؃(0V]>м[Zl<@>`Y1z-Yu}@OP(Q Y0XOP MU؃Z)Y8`aڄOځ A:WU?L:NoZ[i5 zWPj(J愔W>ȁ$`Nt݂MpA0ZY[Е8Mכ[0?[F8F JH\۾P 9[ک} ]]mo(XԍLȄ`b8łG[̃`Ѓ![=P<^U] ׬WYy}XNU q;Z_5%]"[ݍ%`5` h>`IXҽ NaVEoa|avUa1JT^5jTba#'_]֒ !,p H*\ȰÃ" 2PD+fEy(##I2Ċ0$Q!$-iyΖ4z0j8oEi唼]fIMh@!Fxd~%>bVKid)x@ a,f 袊4A"D*`A%jk뮷jj*JCJP *ltljkDmBZ j XpHߚ*@)ޣ\*ry (L8ʰ*Bʰ aB`.eTR@+lP1$I\ jAƛ2ʫJD@r= ߚé;Ψ<@A(:D.#s,k5&4^{pAUoxz^@,TʁcY|i ?XRT i  jG 5()`v(zRWnI< B \T@> I  n[0%MLɭ45:(hC+@j{ }(C<`? k3uP{ qf ~O!Zl!t_4Ju&qZ x A'= h" ؂zB jy':(}PJpB 黚rX&xhU; e33hJZ IEЪ@B^@ }JS2S` SR$ ":$U`:1Q(7hB4Y ޑN0EHv`Z9g" )B`J4\N q@~Osp+2NP RRlOмF]hq"tyn jץw<T T.50(;L P c.囁J E0<1Qau&CW]󗹑%v@-^s0ъT/QܼdDr ,ajJE@ @+G0}(1$anh0O!̴Ԑ0 (ou?)No*+xêBV@ t!gE|JE lfJy|. X^+JP  WXa:@)` "Z(t`:vK ;DJ.QUj9 K [a"cPhN%F-sؖ s,F#_g`ۀ)dA  .P\`!: uXX+j@3A<4_д-.<A%u܊Z1d2:sGX6p,!1C>z6 ]]8q3J+ h]BE "|:-?r='0jS).‰ (PaxxQ3֨W u "4c }"M>n8l+ڻL>˼ X`š2?NP@\ᛟ6!@Pdl&F}.` GB$oU fjPo 7 Pܧ  \~Y; eN/G+57Q4t㇀:Pq\07~@Xt c7)F`e{ X$.G}8Xd~v(|)PHO`VgfH:{bx* 0)h [M_g E)H:7xgY~dc ׂߵy0k0RT?w/W 5:G'p7u S @ Hg)_0:` @Ih@|8v $6\uc[TK8)5 6^C F^F YIvf$N@ &. ~ PY2 Dh p=x-e Jpp8.kXxxk@,@ ۘtjipv`4qp h `rf& y,# 0./0-P`tLX` .@ HБM>4V-@ݸ `Hْ%PY*(c0B]f9rX  cPfu.088f0dJ@ 4F~)~7}RHC(VA8{' (HB)P{[f[U$c>%5BP+8PzP[ix/e5%tRRɲi tI@3~b"Di-)5)fjo0-ゟ76YڙLSYc9,y' '0QUq /vpS5^5%,z*FǠϲ9-jc62ct:J$5>,38#PC<^j{YZ(*j("Aj24r- aj5Osj95r٦t:*Hizz㪮/*Ns蚣񂦃:V102 QY 0h00ԡڰJײ *)f+.ފ+6&d6P .j5)C1۪ +/Z4˦3隯:(Wvĵh0}(uy0ƪP4G8O'P<d@prJ &] ݶeCǂ@35Ȑ=*8@L0C V"DA>SEZ@a[AO<ã?p4EpD5;J{'`2^7mw^:frm ]sP}Б 4[]0Ko%IpU*rV@*LdiA:}R.՜RNupe ^UYXP \yDYZA;-{'yc Ӻm!Ul(W[ v  `.O`2# G]@ @3] z@N )jb|wg] ;^(Vcc2u0sJ0^'`+gCo`sbwe 3'ߕj>a=”`<7[9j 8pz8 @Ƶ9z)L[)DPZz~b)F nQf8ևX&zƴx˶K0f/p'2]'bu0qp^Wfo@kgpXk< >ǁz@ @I[F1r&K:9ts0Ohh!{G.PcyQX6Ic@; 0 c.;h6ٔt5W@2UxX@;K}ˊ[{ӚH|9lʋ0$3Ƙ-Gp8 } - @xвP huI 8:vPocpēk٘7 [,0:| O - >;N!Pvި1&e vFp]ثiy \|-7P-ҩ[; 3rٵR3)>ٝ*Yg /Uz7jJoaP ھ1n~͢,Djd¼۬Oڵ ڽ۝  21$k)*-XJ-jCsh=~=A, 7 1   0Y]k!cj̭-*.ߎ}ۃja#= rpS?;*TX6ނTХk~4[4Ȑ-) S */`C-F裓hgj<^j}iN8^e*/a5;3>fB@0(yc&PQ4: {1G"%7 +1ō.3Z+R` }5k`;4#QZp G?cj&P+=g}Ӳ}ƞ:Fi\**s!) pS |+02QF}Hpwzwۮ%EgX P_e4DsD^A'Bgf|BFs,3_Pa8 60;w=4J ; pj@ GD?ԃ E>= ͉GAD4~ W!4=5t/3b΍?9{ԹT |@ Ohl 5g#0J)$b]DE%@'.`  "s P @H(WJ\$ .0*(gZ0QJgCNQ6@ ;UpPs0 rEW`3` V BXy 哔 2;"Nls:TUz5U-Ž 齅5}k#PkX4$"h4¥8T0:Tm `aC.T !AXq2ˎV)䐡:HjaNG@0dž.@`! "#TrQ|D`՘F6TharpE˧E2^[]9K_z&\aĉ/fcȄ L UlDh(U8gAܚ)/)U dF؃)Y38RXb#(Dn|w 0FSY KP eF$2Q #\1ƀJ+cjF/Zl d;ZzNc"S8!J>%Z2C2>aAb@yn@c|I ț@O )TXٱ%$.(~ CHQ\R[$M􈀌2“P: 0*cZ܂A+)kE.ztc(` +r<#F\po CC,oD% $ ȭVR=Ԉ2>->A:It[|pW  $Ny;HS\`m T(%ʁ2C S| cV|+\X TضPh3蹸1! ;B5h6gOR{ooTSV, ゙ R(m# "&LېiБAZUck%D s S@08آIļSܣUr.Zł=aFЂW%; Jx6 =UX_уC\@A. br>D]?5m!hҳ`F о#A a9hx1\p ͺ!(@ dc Tb6*{<5 T-0(48[`D 6.E+pSī[,p.Ga?^BZX@ @Ȳ.Pi}Ila 5-hJAX -닂5Ջ3 MRC?%BҐƒ$!+V"xw7aU ?A RB qőP .`)P|3RPC9soh$χƁ D Oo3cSvFFU!  d~y APCKJ_UzֳV$XkȸLV x|I]zWmJg:"⥨z9 Qt؁HV慱5J\X0ikfz9BKPڿnĴTkDVT}[CqXʭ f*WRs93\0A GmN.xB15\4FB* SV<>|o[%!^u[ n8J6KfnΰtX]^{1Zp& >1w)n ?XCfN4Kf7~) Y.@an{_/ q- f?F3ol7/93g:́g@#eN4ÅVu ܌U<,o&ٕBHsItUC dY-_a\ 1v)95̺5AL1-^1fsg8:@vn[`֪fu>0[E@a šX s8tfi0KXAd|L 6 dpHPMPboGBzqcp˪(mY?Hv7I^"@ë˴]` c@a/K[![ @] ?T^и#n>ߘоlX!Eq %[V*8]y9R:>]Q}4p6tc ( YNV rf'Pk?LaCH S h`OOġP#"Z?Ѓdo3؃  (!YC?4P"JyϻdHm) O) +‹~|ы'*`?z 1C=Xps>XJHO#Γ@ߣ/r"O(>.8?ы ҋ#`+$?U 0!*L`=J =h d>C4 ⒀'-(6O[;92YYJ2*zP3 E8 3 2=:6 C=P9dz2E@C+ fh&҂\p Ld!@X0 Dh)H,`0cH+h!He<>ўJ Q` Fx$(Fd|X y3yMxyx Uh+hn @l@F Xx R(y\`@o̅퀟 f ~ 0#Xh))?wES (8)Ɩ` t 8H" ő+/_Xpljp ȤX ( ` 胓4 8 jaCCJ1p`#A$ )P 3~ӕJ;耱ҋ@Hd*{րP0LH]ы'P`)9ࠍ۫{>(9X8/9R+BoR(>O$` \(z5PNm묋"6;10"0 f.h]\+*KD4F~:jv0AX"`Cܲ#eIQ-u;0ʂhsyVWNu.6+xB; l;ڐ+c`r2(hhfCb.fdt~fi2KЕ'3kxi2 X3X/[h/dۛb3{- *C7Ȁ6iE i&֌jƘ0 !+Zk^뎍k^k-γ=Ƴ/z6 Ķn*&Hix&Ѕ"%l\ӳmKq.G#Ŧfdi&צӖlm6Ǧ /+i޹nX. Cޫ:B 1@ n@h"M@=0+׮mt^R4̼knIܦn.00>U:nX ȵ>mx ?l5k/d? 4 # $ئB(O|ߔ0.oR;Ep(WltV( I";5W96衸¶14;3-x#l /.óm( &q: ɜkFm/Q<2;ƣ;1x;˽6 (CȌYl !T\j 6L &QС,@,BFȈ!%Ԃ (.PCx>4cP@iYZ$@-0@P?d lrA`Hvl@ &]@%WAsSnxrb:iECGّ *X#3tzCfM먜_"P ] DN 8DǂGz{EE+Z3pQ + Tx @Yǂ[@ 97$xGzd>` J dmps pHR瀄epL)u{$,V(a- jG{['9p_xFhTHx-73(*U n+H׌0(AH ~xLǔ_}).G$(xCHED8ࡊ @d*EdXF)x E*%aR1Zʃ/o̟n:ڷs;Ǔる.B {QnE0SXLAR^9Xe)f:`Q8( UI% q)dRTIQVE()IWUq>[!M(j"GSmA XSrsEeF/rCuL1-Y g  Dqq`7 4h0te^jDW։8r 4VEVC06E g:"FЙa4 rr)^\lsӾ,;')5g‡E|ƃ:H 2Bt¨z֥]%BXD5H%SB ?) +3KB &0m+W= U$0 lT=pp`N@& `-2ACʔ@h"BfiR1H?PP D)7;Z}hEt 'lPP] l$ $$HS2)u 02v%E'` z "l_x~; $2wǡ-saaxb(oy$O1,dE[J<X0C޾=:2oy9׃Ni/~tg:)Oa1C(A:fܒPTvS "6*DJ1>J &g K1@-5)`XRB dEU ҀbD@]]2A{4Vu8`2HdPXǺ.-` 0Lb%!z>` CBjG\o5oa[C HMU܈s nygĹZfA&)[R!p!>sJn)h! ZdF 0pTȾPFeϨ!2#ad/z1 \wBĐ\B&TNHȖ#rC ÂjE8ԭG!q}Yp@%@*rCg i-HsBD'HH^rK}~(&rZ* ((0FH B8| @ d!C K-cz) L12T(*8 `T0X MpFb wr{p~(!O;E5< T-@+ \aB'؛,`'AIHD)S'l[XrY8%*G%Pyb>aLa 4C/H9I t 6dC6 :mّ/, ߩ$Y=DtX"%<H ٙH@%H^DZ`@`X DBD%a(dyI-A ,”dH-Tb-@.tAx ! #B./J#6"1ʓ-4ڂ8@X-A!#-(r; ,:]0A>#(Ȗ́[5X8VB(C %0c@A AA!t .@B%d*`i0( b<>97edBiXA5FiaA#A_ $(G %{L"McIB6Cb5f8B)a^b9a>D`zY`&B!5 h)24@60@!C\8ȌpC83|A$pHL!;Dםjj‚HADX8EئL @HH̙,+vez-e$(4UvRQ(HH߼Ax.%y*zgzF.DB TAlX)LTA ``Bz*BTh^z68Z @V[B&b`h6@#g4@ '/@%gP[)l@%x|`UBiBD)&-H tB-@*'5Bxr|d`+%!\A@ T4gJh6&޻)@ w*b_*)&c.$$` @/B687 ϝ@(f!p&jHA\DǑ셷@ TR)@BX%LB\zDgS @|$R8ai$%?|肉I($귑迾81/K*,l?'!B.b rl*J-iAlzH&_@!80!@ `h}Rhk $(gߕ,XD#*! &`@¬A(x/>2)x-Âv H@', )϶F(; ,R]jZŽ^FLmm͖Z( jr(.nb0f0HHC3\/8A1-,0A!/B0\6h#!ыH\wȋu8AQsB!AB@NRD%8'$\D4'$Bbȝk>4HFBFV $ܝ @**)(/kcK8&DS X/j%('^ʤ:I;aH  y^E =z-dJ(@E0p '( HL%,mBnpZ^ Np01e/d_j~`B؞-4YӋxoVy7wX)~89%7z -y_؅wO9Ajx;K[x9oxwvX3{5y8nmuWvxGSye*c{yOzW7czo S:Yg08:/Ivөneڞzz:7G3z#7y:x;ܠ7;zw˴Tc;k:br*gܸ#Zk;nӧVB|PJtW}UpJe8ii;5x F!n;q~u@)WT:;|<ȇȏ.Y-9Ra"*<` -2 bD j.c%"g.Ut?>8{9;~uؙ47`"B#/PgK x:`$X%9]BCyUC0m(|,Rb+BsNh ([,2n oR H. O <\Er…0'*,h^a[n fC* 2i) S~1.SSb@ ($@3 ˀ 0j(,IdVI ~d0J'$ 6,+H:%V<ZBFی"NNn1Xm8L)A=Ph 5ýdRZYh+Up %5UxNI @->A! Z`ĵ UA!Uc-f |pgZglՖm B m 3\AX* l" l< gT@CǼ< t@tX$RAU;XhJ KĬVjZ J'T;ӂ~34b ^x!|" GuVPqoh4Z.57B)TD?HAXa VZ Aq%Qel@i&69o vZ_\q )rr- _W:(^*m`eAұM" av% V؁wD딖`#XjK X2ˌzRYZ9/3z-nTG" Yr!UVdhႸ  4>A`،(Y:ѯ7D2 p% E8d$@6( WCmp6avxCr { KAP 6 lKs a0 Z؀f.FX֠`V# ecpF!X%n\Z或UHHcb"J%e q 6d,lpG0B rKakT< h8#tb ~Ew Т<%p-Zq #BT9l0$* ̀ 0 #% j" Adܴٝ8@TI5 .hB-24A /Z?>„rMҴ(DUl0¬~9@-DLJB,J*4!dJ^8)i%@(J-4=KX-)CӖ*>9ԢHHZЎrЎRAL=hT#m|a8PJMUM ܠ&"i\JVܯyRWJ_PPbЩaݪ#olaz hA+ZҖ=C" li[[~#рlJXZxm"tgMސj;^..c W&pH8S&%(2{\ȬW &D$-í*Lv\*t#*43^F .y|`GĔ)X  a W A[T /!'& C7{>rOq=s gq*Q7Uvy c;yD&ζDi# ]Yp}.!$aId<=m0Z a]r% /t"bPk+5u= DY"FAKSlo<0tv" \B a a| PDaF-`-"1f|DCE.!<cD V $%> @L1`FՌFĩ̃"`>,! ~ `@ :X X@ "7'gf *^# d-0A_@n$8Nt@<9(QP`;.@L1@#~ĭ_X#-@!CC 8 +DT C,KD G 73@v#Lp3 dJp0nztDoFTQ%raq%t!I;RQF Z񂌅AAc }tH!sY  Heg:% j@0O ȀM<廒N BLED)BEF?t5V2Y Q2ΰ0 R$XXUp3[e3BLAǣG*6a2,.BNFL%DfZb736J9aZC{:ݐs8Pug@GSqCTS֓ߓfp)V>@)B . =TlsX ʇ:1f6` R!eL'To8Ņā8t=Tmc*g\րhנcգzgB 6 6! R5Tl z 3``Sp`!:,mfI"a% dl: tRƖ\<>+$TT 3>`k6i`*I !| % 4dVI54K@ h%;El2gOҢ86Rg rfDorR7&\1#3@v-%A"d 2Jf6c|`s.(!vY}- Uʕ֫m@9BȨ h,Jgr55^vp x/ NP^HPXN| p ZCYURN\%8&2xqͅ$Q<N8ExvC4Iq|68|5t~F\6Ɣw؊QȄo @)3PG(m4 ]Y lE?)(N%W`Z 6re؎ U\B,1=#8T"$ #  _Q|9uy~ [@n4먣iF9'*Y6|yA%gYO3@L(f%rZbEVڞ%g0*/h\  .GxX[@N9Bv|+ ~L:院ʖ0%ƀ<Fm'tdT X@Z:N'n H d1<3>Lh dB`O0Y*Z n r%, ,iX S@ 8I_ `4ZMi *`G*G# ѰC<Zz B| 4\7<e;ںX 4ق(B>zYii_BvAeZۓ̵c`e;k 0&KpNUb:fҎ;N-^ībC_7+XVh@]0v1KD#8[PE,7O#^QK! -fzM-oy,X;x-kIּUcC@Ҷͯ[\Z0F|~2Z%&p3K8.r0C}WD/}?,ڨmX6[x 9bg(bM3a_w9vb>YW2y#>Zlx&z9(5֛٫aɕ:aA`g6ccs1 L662"<3d&tdqRʌ5GWr{7QPtX8Φu3;Hqoc*Xuk0jH *4!Bd у Ħ@؁PXB>A,(6z۠%DeWjI5Rmp Pw- nL 80Er)P5!2P246%KƦ9ڞwL3؊ u"VHLŁ#eK]W 23ϨΏBqؙhI.ޘqN1zf%*.@G 8++a":%+=O|=G<ħz;@_)@yg `o[_AI†` 톘H RaAbͧHpwNp)pa>%I0 ȀɌ&J<2RH@Hq s Bǀ7>0/ k$6}ɇ b &PA]`~4`a oD%tK;8S ٵ7 !,vS BeeaC?~p~;Aaqd\gC'Da`%炭 Pe"QV7\`F@BуN ^7A %w 5;1^vJoA%0xbcR0?Xd:hyl! ցjW{ʂOXE2,D1W30Gdy?Ar^7mG ] WD!!GP%Rw`"0j">&JT & `$~~ 0IP/]!1M`p- #*VbX(0 |uR0Fb&#).19 #:Ha{pJF-!*3 V\R6R` `-,xprJ%p pe>Yۧ S&$R!4%ysǁ="?i'p&b]p%gU-ƖR(6@[M Q Ѕu i":P 0K]iЄÖH `f)I`50Orf?:pvC^4C `r"7xǑq‚y99QP@jzs:P,+H:Ga%U}@ @7Kd@.'+1P{A oTe%j@ s`NS8+ dp,Ӡb`# b^5{ &nSK: S0? *<ӢC`@Xj!*u(9c*2 2%0,q/Ť9CAz >zTeZ0ZpAs؁XrU 6GNOk@ VЁ5 LK'B30DL0lآ#`C   Q0<:40Ҫ H@z0$(v2 $prQf@{ eI@ ѰC9PGsy"8 88cš9/P`>1D9Zן D"z0; 622 S N84ƤI@д}Q#j8sU-Z;nE4 rP) Keu4?` 0@)b1ڗL ӳ>xP SeP-U :3m b?QX!KO״-b'V: KXȡ6#P Qs7*˲2)1?㵾qJ!P lpa+0Iepc-v,Bg}ADf۪INytmDcZ6;"fWĽPˮP@2;/P. L x4hL}|nv1Kvw  :a @zaOPHx.S>RaU;o [P.UJ-F%7HAe @X M 1GPqQW%^G`x2T[xFE~|u@S=\4W0@0.O!MI R:G` h`#SP ,VeV2-J2 ~T%Vy {[ 4qVl-"NmPMT-{M23ki`Q=*Z s=e a}z N[ fVLO LZmD׶ 6G%@@^@:,ujpGjke_,fc5M @$ \i^`mg%.I56(\Q=]"A vx~b{p'Z(å\]6e([Iw5 2(ْAAut_Cngq g,qm(#af}Cء:a'6i vESNYn^=WYVhNchAN@dhJf5ekQrTri!3!t1Njp6Rp7.Eᖒi;ez]pܔAt):%6cYƹ^fahۖWžm 였~-0 '"*@`؎hNN ƞ p#.yte k$`z q` xb;4t Otp {utP6>rKW=:`7)9p l0SP{X'L>7Wa/c/6 q3@7i \ [<_6 <?~_ms?PhAP/30@0p (vO xt{OlJ`F_n/x RQ5Oߦ| fq1T 3Qm{ mnr b ' kpS`*oo ;q bp 5p4z QtaId yp l oop 3p @p 3 5Atpr oCpI`~H[ooQ @B0$ztȞ?jOfooo ?oK`~GFn~NooOЮn~A?>MLAooO+Yi0Fnxboo6Ma>N!,P H*\ȰÅH|Hŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ Jљ*]ʴӧPJ(aիXjʕi`ÊKٳhӪ]˶mϯnʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCmQ鉢S^t]M6Vm͛(Lkݻ4Kx%iAN,WnObiIPO W8]|5#M&g%5P/F(ahU8fhnpq(bb*s#bcŶ0[$8č8Q@)bcPt'ƩwF SJ P` E/ ,A4@>T b#qdcx0DT%АD.h יHA$I8>H">p';y"Rh4l.!OrR Be*c'I, ^9:@ ˃h=T%2̪2lYe2f5߸J\Ԧ8KDG\Zđe:/AHByM4E!σ8Yg!8?b y}R T" I;@{TJZ؅PpƆPi*6*ȣT,hZiE,KLrS<35U@H%2PA-ER:ӕ&%8VSr`MeRcYt@EF Faӎ#@y r%@H#"4vPh(8!B jЀ>@ (@H H *aA%A ZL# $@,$L'.0nj=@!a SlN!XD%n|XA< pw BS4(B,zw ~j 4j`R`hA\68P';J,`-P (vIDtON.: uev%\ wH?aK9Y)NDE $҉&2‡ PaVMLX+!~5c4Blр@~.3Ё`v*y^[@a +=  XCYjK @P`Nָ m_ XB Dd-w Vn oG$Hr@&.5uP$4mvlR "0ȶ2\תh/L\@%+vJT;zou †` (X )͂C Є`- n%S 0t(CJp CZd[\xC, u`3%o[*0"S@w͇>A Pu+ 4E&m56Ltq]Dt Biw$ < Wb)H%)Z g0Gbdbp u,PUN_{N` a QTZ?W0Wе+XD< /Ko#`b*`7?"ׅ@@q9*C 7P>0S@u pHGtr|' ėJ/Pub~J7ç~'_wYe7)0"w7ʰaGPUa H=`0LGT!hp(`pRea[ 8_c Z᷁J n|fw@Wl" ChgJp JDgPTw)qxFpzvHaDU` j^pFyuRp )VT 1 JG %Fehvz*PE{ Xd~V 6 g|S| r |Lkd(nPW ,'h" )U}o%Me j0 8| -HT >ʦLE=@@k 4|@ \  KW"h01[)9K`bK!=]ppe bc m4Ч\71US& 86`8J YYh- ǑĎA{g).϶ʀ5 w"!$x 1{({u  ^OgyfQWFx Ov `{{0 pHNs j0K V HUTq `ZDNT UXmmXu@>EZs@ X]0 ʶOW @I-}J- IYZphh-j` 4h[@>L@DP  E֋-U^%xy5Y4Yud70qt6 `  %+th\ǹ,D +k$xJJpZ"2& @@L,D,G'Qh0P gF QRఫIw,옜`SoTARS>L,u˅ L\ж|=%ЀgL8KV1_PxF,` 1 pc F,pf̍? u 6&Q %"4hP#%ZQI.I0))LMԩOCkT-U Y>j@۴q۲U[]vu]a鎅(N& &kdJgM $Lf'䠌yE,DIqcPRաM LR։pɛN>sѥ*ջNVw{_><ջ ɐd+`c5 > d"ۭ9~J+p:J;qDKB@~&cqpB (Pt1-CO֛jȾ›,4Dp|J,TPJc2K.s1+jӒ'2׬)J2MsN:P<#Ƨon$ȷe2TQX,$P) Sɾ4@8tu:j@K)OGUXc3GHzT`J U4)Q#ǧGH%ֺQBK.OYŊTfncu1VN҇κDԄURD& Ų;m5ԖT;vPVvعTьKdb$)xC>tCKHK.$=A !..,3d QEJ;ֲ4HP钠ӥJf0TS*B!)R:R B ]P}$aO@VDAMl?*BLfaۨ3>KUqKRqJNSfkf(WdZ~B f 0TOpTL(d!b܇2ty^pSt")DƌkZJUZ.c.c%$?D6|` X! %xELc]_P$8?y`` B'`kт% \R ($#", z) a 3`c|bK'>]0SPZ#!~DlQ|@X!h8e* 4 =1w]eR A>1]HE@/Ѐp[fQ@Ob6Ѕ2d|P&3_W  @@Ò1c脍AgABXe];q-sC߼nhH \Pjn!(=IU8[$.! g)eQEE^lp!mJBNP s~q;J^!> ) !ˎP@^՜4! ?ļH } (WWW泃\†Q`0 0c(V~ȃ| _Sy1 aA҄ hx %|M $8@23 o`ȄǐKkzBсH0_AX CaиZ*l"C`5eV cUqk\J ]%a}\ @°6ǐ rّIPR:ex$yQ #7Q@ 5yZ*`pR`8pV%Z@p?Hp$=1 e0Kځ05AZ@-8es'8)1؅ 6e , (1BVxXdB9 {-b5Z2 9k`N؂P207pC"$xRe6d(7(O@DX(ԨC%h-؈h#j4P\?a1 By P1@ 029%* Z4g+ }<2p -H?AL DΪv FȻ#X* G EӔTGy4č[C8G.F;*nJb̙8[Q,f)G+9.dƛ*o JX.`ITAʝIDJ !J89HyJ/; IEht{Ie1P0FtK!3 1ȜʻSKJ8D?vo8Jl bIDioLLď@Kkƴ̙#ˡ˨:5QHS9̨_KMTͬ F![D/L`ĐTLXFc!IL:윖0N3#Q0~ø2p;`>01ck5>4@5*{y1* xALyJPǔ8N%AeǪ  IS,: \hT=`Kh3? Kþ/%I8Q' P,|VA U G1G Jɐl4(ˇxB8S栀(w Y%xDKc2IM3 3?eІiنjE]ˤ!=@E }?N4 ÁGN@3KݙS@:S0xW@&PԖByԂR@0(E[Re} L% EZȅSwM)HP*e XS Z9Xc ?.2Ʉx) *%WNuHPTW]CZD`V (5[?A{ـ\.؞m5 0J0.RX55x)1(e- O`ځТI\ "T#DPKxHTx"B:DMe@*ŁK: XS pTmL%p⸙ׇ -8,ԫ(p8<`+7,)BTH p OcR( v )xc` M- \;?R(F8;L(>|+貝tep `B *Z#8>& 'F(ک-җC 腇P'@p|9`&C>%@ |>h[ߨ)^;饚*x Hcr`aÉ2SP JK؃ťGLps jЭ3 4fz 5KYѰd1h.7K?HG6 {8,._Xր"Ÿe=H&6`)90p-0\#\'EK;ö(7778RU8D0M2RGK5nk8q#U(x/EcX˸Xr֚(nChz=;ׄ5>.(Z@J{f 0KZ+e=H\ex#_`ʼn'`!KmdqK@؇h(KD $h{n3R@>SyxP+S!Ce>!8)qj! 3J';fRkDp(@^QAQb(;5= Wa: e;UESz^fP=[Z@(^z!+/1W>Z-᪶Np:h- P;.PդڹOR!-(LL;5FEX`s~Fhk(eC)?KrcQ8UQSw@F0 x OٕlK3\FBR`K(l(DSL2t 1UCwRCC2yJRLe ;T[;:ZB_ C؏>MZQw1+pxXBG?’hBoOBp?;G!Xx8`4̔ +Rd艈XDI$KpCLIeV{Z(uUee\5CU,xHt 9^.qzGV/ΘNTEȊGtM_ ɍзӵaspTMM;M7׸[hs`L!ć-Dh!2𣲍~ؑ@  EPˑ,A I1 'РB-jO?) X0! h&X·X'2}iS. VYeӒ6Z8ҕlՕ:V,uo_~1i0J 'aRL_Ɩ7g2"ZtHWn5lTkV;ƭ}VA]p} mﵽqǺMj|t7b=;w;z]fcفRE{>YSE7:PTh[T H.ne_\ˉA80Pe')4<(XP ̈M)8 bF長- X},>9g6%_Uڇ%aiII)qVY!x"DPG7}d!+@l % Wej[iQe^~v5Vm bHՍzpFȌK\PC5 hvdvZbZz"Vyݖ bv>4,:kR)"vHpc=cLv2"/\03 O% Zh aTQJ u P)?ر 5.pA#EG>H(0 Qp@>T*"ل|BlaG UqMU78P P0zZ$\ aW9IGZ#0u@-d!q(2.*A`J"P!XVN`2$hJ8W8Fڒ^:39 MEpFG:@ t"nur`~Ghi:K#;%%ju#QG%`L_%+#e,Z"3*֏ ʈ6@Œ҄#Xr9֒ p 1Η/#`Pj3&oZH^$:mSB:Hк k0<@)F`@(hh 4 @`')Q%3bb Lne(xL9x2AiP Z`!$ 0U> #4ٔAPG 6=%y"U`~ A&4#qK+d xyz/gJpS2hA-l,h>"% BT,&L \Z ! ,$x!T'j]  ~hZ: Z,0Q#mee ԝikă<,pCn)NE+2Ɲ k躝As#M-@ bU 9WcjMSU6wZ{c E~p}4 ~'4+:b벢d@JxtnɪZIa I"Fڿ{ț:BJ_$]#IFM^{V_= jE\4Y%F YX[tp `Ye`m Ap}YRUL$uhK  `vGX}hF!BeJqKq4Xdɋm\`M 2wT _uZ [ TzUl!# [ ];ubD$IcHYE*vV ]!~G!aNDMb$ZE$bXqL`X d0!:!|@(](![[x#(}+ H<8\:SH&:Riƽ4c # #>bS(S4d&nV\>ZY"oXEn @JoG՘A^de%~|aAoF@AL øaWA OF7| x/%\ +\0E nRD. H!M@'l"ʀlBY(\(B7RN%Ĩ!Dы .P\*l–|D &ģ̖ AZtE.b`鞪`dD'hʒ2 14'̞Xt*^hE)&]]'8K^XG $A7A+H6d3`MH'wB3hv KX6"A5%HBPYb)"@iBY\d8K  A%ƾ-0W(Rf)C[*   jifDܐхܪAwj,ǖB#V4 B H0AS-Vm%APB'́|X)4Hʺ+@ʾ%ՖAȁ@!BsPn/2g&jBɺz.B,~*P-\A x,L@ïR-ʰPd9bBQ2dB/@K%(C1B2$6TC!B),P*C8@l3ljBbnX)293}emhS`Di!tM HAu1yO;ʜz%Z_$Hm6oA;vsR%L1Hd刔;&3s|ZPc$Z|.+LӽFև+gѢX[sQ[{U>c#o$.I];L%@B h րNf~4M+ӄ/u@oq@#l4Bf+*hB +4fmgRO0@2*4ΠNDe/+2d95lBPA/HȰ(@ @’DI\*Z 4aӾx` "4yGe \/؂ Ro%70Uo Bo-74̹+A N7$؂:ˀx% AޝnG&@϶kf9~a1X1Bnֆ%!k*|E&B2*n>5{1:lY}{>kPCP~D҆p;' {a鵆 L0 ʔ8 LHS!D *QF( ye`!C+LX3 a 󥔛@OaK3\ )6KVR(:$\HR5kҭUd٤#:*mn};Wm`0|xp‹.(rdɓ)H+6 T15m8_Ԡ{~>k5lրUNis(4vbqsr㴕n80_WַS'|x;}zeٷyaÜEGdаk[{:/jS7ۤ2;M@>K/JSCö 4/D  B; +et/˖;<xD> !ɊB%$ BT`|O!%"2,LI7+c( \ *KM ?'+q=A҂W`V29N-g "mH25`HUVp(ṄKRd^GO#!v! :/;Hp ,`Jl6H%vÐ>K5XaZ4` X"tljOÓ\N)6.0^.2[C b䎿cM"dphu3=F^wkR  0.CRJT!Ѐ#!b9D8tp@"H""J`+]e "R?.qi$ĀԈ<j+sdF T i(- $ D(!, &q0`ġ+U` [!lbnʸ!AOM0?Lʐ$% @. "&*10ZC-Mw̝9汏BbA5F46 X!FnA F b΂r! "?(ߢ6T gh^:6\0QD 2!jЉF,rKʰ |U4̛\q-BhB!C<5#x" Ռ" = `'%misC njja "*P q)C 4 CeLHn"10Ҥw~HR>v'UJB(-WW@$ QM@LS,` z0EQBKDaH f 3`, RP0( iC$X3 rCi"mEP5Rq“ ɂ8TMGE93uf m iZ'%DTp Uq}:B4NAbpe-*nr\k!yͬb<\nB "1$6@A‰J [ `݌1@Yza6 C "Js2K0 F`Dra ` EV0 $dBP\p#@a(kUi#%9hx0  Hvfj)p* l2Jsa@5Et DB݊!"~/L : ]Э*dߊjp2W,^ V$` Bl`gkmIrR˺>-g)R - f F6 W]hh%ɧ*RXug1iVm16` > :Hx)~-R!>/DJm7D8G"6s9aH^$\ n& b/>@8Ƅ->q-&jVz qjj0A  !i٢&yQ~|Ž~!p%nh 88UWu !L Aa)ـe`<,LO@, tn(b"6ƧLI) t&l " .la:@#?0! @N 4˝t lcmU8{̘Q +3KJATeD@n@~~ج$`8`R&9Ӈ"qc37#4m/DaA"X%:Ru_K !I*mN여X#\ .)RT&˴o{dwvIUC/4-!x1d gR!8 UaL&Bd~-!p!@G+f`nF{l`.!FaH:n XHC֔.85TY` L$>a HZ .e<)HPN<9X7֐` H-Z h S߻a1:!m, {3=v6գnJrᄀ֮ jV4 "m3hB !< s.XXKv\\{`SkdvqAjĝbJ <0n. f Ec?%(= P ZT΢F=\?6|`a:19/F0s=%Pѩx<$!"AEL'Um!} &1Qɟ]Kح/Aau>x47^ '! AKC@ T`CJ!M#%\;]vdH>ɚ]-RwE  ; 1=J.VD_P L1 A[2fRO >E @A<) 1S"Cڕ޴22pAĵ2qs=ZBa]??+i~wVq•c,m4 R#dpWܚ!PhH2+$bTC!9 Cڭ[nYt:lH.ӐtֵF*W( 94ah;j Bpe VvA2$|"@CBhH #Țxl$T!yN4CBZa,CpM-94MeGuT0ŌǾ} 9ە+[\a7_y1΢cn4ӢW\ ;ٴk1nۈQ#2T,@qjhhKs6?0Î`Aũ?^\ՌJHJ^/;ta'MC]$a;cB( ;4dCfF9ta#  !XX<FxG) 4DCD@@&QI,,E˨L: ˆ'u ~ #4fGu1H:8#[I`EGRhC`!!PfY&j:(硅BiCZ[b:ۣ66'*S')5lL!mSMQezqDfaCBH_дiX:Q͸SyGm!\pAJHAXʐ qA%ѲY` ()$L|t$(2(\ReZ A- 0q+[3q PpYf,4xP$YlvA.l!Y(n*4.fV-fZsgUtb8GhX+,44L8TX'3IV{ [ؑ5{* Ҟ C됳y Э4dRNC8VHSn)qoJ\BCW+.;Tn CaGV(:[| \2:qC!*',73PJ@PG mY )PoaRЎvlRZϲVńA%7Ŵ*FlN@0Tu X#8:hJhVZijZFC2ѐ`LxTuZb@1$ hWb vJ ޠ*X ŀP ,@ZHGl!Ma\@ Y&Fc!1Ɩ=X_41-`# !!~WXJ0Tւ&4O%м7R^>R B (G l 8$ЀLTi꘦ 7@pS@=/KupOy@ 2)6D 5(!Ȃ` -x[ A  @C0ࣶ0T@ d  ا&!V`0`3ɆjV@@'8 -))}C',j d(2ɵYjp .٨&z@Ӕ)jrcR6 UcOե$2481سQk8,q6rӊ'ۦ, I% $e&z$Z *ZMzj d-ۏXJTSF'P[nf0+_Y6"M% xAЀy ^'(%GE^KU$0-}X)S VhsUxx /K^uN"f\@ 7+zIܿZ+nwuy޹Ԣ4fؘ'x},f d!'`/~1 cB9}$_0Pe~pn,[0ql8|Ft (yI^h8hSLo1 'KirK(Zxu-=@gyj+|U8S}u{cRGnlr\5SAL%+B+z@x-&6`& 5+ 1C R 0SF:j`+~X׺\ĭ#vwC^ܾ՝vTnumc. ff̆W(Ԁ d`C@!g|\<5xn!?ETvlw.zqrF3Pf d\ҭ:o*@=$[jNuW h~@&+YύYV쐰wUӄO[1jtnn h{Lo=}`el㽯zF`:#\`2 QJg ~ f <8} ȂX 08;@~ PCLI\ U~{ k !0a#^p pz9>5:`UGw\:q ~ X-1`0Z;=%0|!>pBjiSp)kP&1i`c/[cJOPog/a~@e'eF 0 $|(s؁(Ht  / XrA qvX< p' ! @]dM0 AJ bO $A?$as,G(8L ?0||`9qnFP.*p0%Q=d IP$uKFhQ" /$׶s3 V 30!;NuT#@Bw t Q:0$I (   j06I@#OIu`.RP#1!!-P&y@$P Q0#PT5Ti'iRPwPS # Qq/|r?3F5)PH4|)ŏo `_\vI s=R@[X 0RY /s&p=s<J*P#:X@5;䣦ZAG~^k`F.*HCaXX=i y)3>7Ӄ#]Pz)S> U&+K X0"CC,; zDX^"ԕyPǐnA޵q>zUQz  `B 6@ # iCz @A,F*%#@Oȟ8,+P(~ur; o2aH+CF8 YIdw$I[8CLp  q x2TG uY!jcW.!JG` #&1:2JNT@PP(3|f=nQC{$ ԨI:#{ql@< `Ak#i4J:2GGGi: 0l6J[U`v roOEʠ \`@ p pJ0` %N zxw%$MpnO:wgD ՙ@PD*1vȧr pR3PY#R0oY@coG Z`FHZu iTQP;1 q 0U.>5F3*)&L~ CRoqTBk@sLSs:X.Sui^Te1{Rd%C QP6 S3sK 4am|@ nDm#,`џ$p}9 r -T\а3ZЧxZ9[cSXVFw!h PtkR5BmhE[ s|bm؋T d%] hex6tll57-G:psEf!tagTufri (u_<pߝ`@mefV''樆!i5(%\jڀ a-) G4xH^/wV&ҍse ڟۖGh#.ۧL6'mwP _G4d0!k i5@hy%Я}?^(aWmHy_U^dvfyEi=i>䕲Lj u]mPNpɚDtp_^W1t?w%X*;Zt^a } e4Tezcm(pNcnW[mw~◣&l~vinހ]Rvض4od0nUvu7/K  Kڍ턐9(P*__0~__ J. 8` OqO5P95poVp_0/ !.o"1q +tcACOEonI>8 p  _o|־J9Kog$Pannw/HEXbGJGێz ϐ 0o?^pR2? o>O@O_ /O W@1Q` UAOG/ Q@Vo9, a_߾ O?uc<^e`{^OsqK2:yG)>H F,1"<~lcE$l*bÃ|{B *M.! DT_x\Q$V98CsQ0SyD [m:4a"/_H^QY,5n:#% ztWEbH5$9AV7c1!k? IX%]cQ]E…p:4!n|r}3']ztơOǞ<ԷK^㳗o~`MxZ£@ȓZ47x`E#b <*/2ņB|6e*8bfb:a%&SF8,;͓H>KT ATf0 F58!˲j J(Ӕ$5A|DyJo9jxÔ( ·$jEQVgl krl* 1Kpo=mO?;#KoVܝ^޵v80*YniY-/s$,`t/9DrC? < x :8L'[hbҢEj@RJ( <<2"5*i; j2y >A Ql $03?jN+d"' >豎C&XӋ3i/e-خ}{]VAhCG>?-ת^hYdc AzCZ ۞tolmvo=www>x'(!s\z&H0 ZiL|ՔQLmRˣ:xHjk8{&5W>@\P~n9Rм(ϑ\YZP .LPZ ` 2C =tKp^'?RBE^+EKFʰAI$F@a - XJhXns`fCTKWK8ř%yn`1 CK%HZ`[hT@E%2ئc65ԻT)A0j)*QB / A-|Jw]+M} ,{׾f0倵4  R5L7 '+v ԉPv۾5 `>`UkQʑ xv\L*b=,jsEQe !m *UkIeR]ntݜ$\6Wl92B[V* H+:2ЇFthFOJTg a4P3ijo! 5TUǝrZjjת44IRm]?O+M.# UUT{92"zf8y" , Z`m'2wC7Gw=FW$ FGk3(s5y")|V+$;{\#:#Y,? k+x#of2@n^N7彔=tGQ-lM/s:jxk7ytFp-p`=շvmFunaO93=φyciwM< |(]!>I4-c*~w=w(?Q2Jl3<Ү s{}ш =f< ?}W>}w>?~_?V-Œ?h7~.~}/޿#?#?t k? !,P H*\ȰÅJHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ Jt%H*]ʴӧP3JիXjEztׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟O-)H_驠 02*Dax ^)XbɆM!@$qA`X(W +܅BH1XM(!ɋ@* tP* PX6') F=M $% 1tIf|ihL (6ZS )M0%hbb*IB`@jHQziUër*? ;\%gcbQ䑉`CFCN+HqI bZ ٖЖ^%Q% D"DK^RhflA[ P 靀m)1Eq' ؤ ~]S%B,؂kvG%3'_``v'X`N41'1kK*0RǀvWb"t&(Xfaav@"*t 89 ("D[ qkBLr.JL$B́`,j\i%@ *.N #:$Þ 摒t{ [,)!#q R8`)+Xb7%@,5K~a G!o`Wb-'oDžKK^CaHQQAL^%4郷k8A|vTfc+̉\HÊLuks! P Q!Ƣw.;C L5#PDG ?&Ί<ġTD5k \D/P5L*.17TJRbVсn"u#6qAqU "D! 2\N ;ATє A# Ԉ9PH@tdB&)lA 0yL QVP /  `^$89(#Os2H(2sbbm!I(U:!1M2'PVԡWC&&D BxRx!BģyX::Jҗ)BN •yF="Srڮ Ni䥃R"dp3 Pф@l al5CҐ @Ah$08Jlz n~9 }@4/d .-,qg 0>h5 P '8a 9DŽ -@˶`H?Z 7(Alv.[B  l@$r(.r0ΌVD-NOڐJצt9j]L$.I]h >ӭ%d`)M)3i.u" $Ixt*m1tu00):Y'4``ޡ0i\R8)=AJ!ڵtGS4' L2ru w:A I LrBvJ~xC⛖~1DLjx dx2Hl`9|fGi YDDAp8ѐt8 9NvC2TN <DèK9;73 MQsSf0ٺ^rc늹=sGt띪=&NPkE4mnv4*m;F/F~jxČi9.-كv]h~nJ݅"FkdE0)S iH !G6W"}ĜPx{u@[R8~gD6z^Niޫez3|ϱn =xRЇHzhvp;̀Y " k o(" CM$H~"@]JIu\•,#X(?]!8z#4+@O]l|[::{?8Ds@&U/qs~O[ި&InϰƠqjd3>( dB0 6`Q-PD^P%Hz@ _J0(>SH #Hz8 /0CpS$h(8>fC3Đ&8#P V)'8(+U 2Qk,8*@ R :p Ch7X PS !]x* NxI CdG8| -x @X`3`=$'5h]%HDU@ 0A8>h8x#r f3k0gR\ pR!Iz( @5X} -0#&I#+Fx 9#@V9 [ 0M`tP |P (0G9o@  3Q|8 D>x+ *ɒ#+ vyj@K FЖM@0a 3b85d)P By#9`M&y#{X 9 aHUgoi 9%YF@f&)7C9=iЍfDaN6R&x:r@ 6P6  TUy U e?` "_Y/C"B=8_eTPlSXI+p*L|5` phR4z *cbT0@[2PeO;ڣftcp#E5`@ gJLz6P'PɋLN$: WNpLJ{ yi<ugآW`u 圪Хt]x vN"F bJJm*h$9UhV%K QGO ZJP)Rzz LJJ =pNJɊ`Z ځdhw9suA)1  P p Ы/C0 TZGo%?E$l ySV8Z XrJzP P ry 7W:(бkS `]p, |WNl uQ6 Frn&YgWINުA˴ bXsڜ':+^ tEXuV3}P WXs%@4 Yb\~+$5l pT?=?p: >$ʋ`TnJe۲G q&ZLs*𸃢?tK๻躕dpP/ޔ: py& H;  @ ѐ ` ڐ 6@4@iр ehNb-hD-t H9Nlwj .O JOxʿ@gPVs@l lYOH@  <[P OPvNl0'RkOujubu#l:u ,T>0'K ~`Fk6J3 LbJP7,& r^s}-sɝswGHM_wp6Cp;mR]mvJM-YSm{><(|]6|5bEG^- B%q{p-0oxV_=!I'ڞ{F|2$ 3B?@?~C lKg17-@F|*A~XZYR,:U}H2TDmT!^>>:\ns^v~S` ʐ> p , P rP",H4b`__#v47/>> W ` ^- X e ; }#~@@!^b!8|l pD` ~`Y o8*  ep >$l+S(jHP!o>lnſΏ1/,Oo kj@  `8 ؐ & =g 0 @i PA ̰J1A5nG- HX`K7i[v$捔R(`a؀K|Ic)j,hZ*H E!.tPVK=UkR;ꊅk("z]К9zԪ/%O2e̙5_ޜΡE]Z4WўUͺkƖM{m۸sݛoൃ8F U`dƙ)0UkfjW l ժn5e,n оle-K>O2HIHK $ miRB:6^m36>2[HTFj:>p7'{A?Cn:hVns[]^OvaUNRpD>.q&J1H+O8o:]ݞbTt<^-qAAl)D"A A&lB'|ؓٲ5OB;@7KC!1 G .9Kd\6?(C9C:D8<;br1g8#;` #xl ͳ" =ifs 4Y;:DODP|A)0JRTxWlB a `d@8 /8I0=[KAu EflFg|FC E<[8</aD_1z!{;FvlGwlt]L0l81xg>Xğٕ0Ss3fyGH,@i\" xaHɺL;85G(8b-<ЁbGKH>5IgTPtjk` @@؀s00{L#< =0dH\KlKrS: P77KIj PjY CKEpK ԬLʬh(%b,_LIJ_joHMX?BhDМnLڜC7̌HК.*.)𭖄ML4,3(ݳ DBFP@Һ/\(Ce%4xR1FTNLOT5D9jDAy+.$ KB@7IP, }Pol8\j:\ |49@.ӖE4U'5;()8\NiXy5K Ph Rה〿P0#7DtM:I@*Ye5 7 3DShKA²QÓYOTUj5Yٽ K[͜K_ZmC?҂5-0Z=ZB]mp??؂ZLڭ%5۸[Z[s\p[:+P:Ё)7ЁMPeۯ:}7F[[܈k۳\0`J(0ExӍ1GdZڥ=[HZ1XmF]M^q)`.NK=؃ZLLX(VX_O؃WF}`]\eޮe&MH5W\xѭอ_ ` ccYՃCO%AO6" =p$=`L` b[L8`$`8]xbt&5bAC.8&`b3>c_$x+H[%8.^>8=`x ЮEcE^dD&b^~d[=^۲u25IaFe!䰽dOc5`RaeeQe aZ&edVNQKtdBe`_L\_\m[]6_fhF^ B*-8h漅ZZZ!,x H "4ȰÇ#JHŋ3jȱǏ CIɓ(S(G!0cʜI͛8sɳgP골ѣH*]ʴӧ\zIXjʵׯ`#T K ª]˶۷pݎ@ܻx˷oEs!KÈT@ &UL˘17pIGəCMVrH4ٴװc 8js;7jی{ N8[_μȁ~N1K "Cӫ_=h,O3@ Lk_̰&|! QVhW D(\ .X \g!21 ,(I6PѨtpõ@pP``` @LS  ߭%!~P]&ذ7d)! 08b*Đt!AM4\ ᚂ `bu(\4 xZ HX 0!  ԡpCB0Jad .|b hGKҚc,P66 l4x/Ah# iV$x̸ ,LE :xR (?) }" uhNX5P {e+@VH`> ʄP @ UŞ5b $ >Fo"p(0bf"6p p! ȴ/ ZP DI&ju?iL 8 4 F^q\fT[T0ȍg%OeTd&+cqhaM `$(nV^J@<pBOA^Yl DP:07r'.jSĀDCHD^ L&`!>)A!@FK3"Sc: U%F(؀< Ё32RhW ox)+D ݡ!NJ:=pHGSj΢>-@ xԵ5`T9ӂ4XV0  5 ޴GM׾,Rn`Bl;P? _E|'NPi_>YH6e0(4vshjBf f،3d I`@(0 @ _fjVP 0T.0X Vqe*]3\P\c]C`xWU`@`R!`} p-ᨣr{Jʅp`HP 7gz}탰c) ~paPqm` d,R.Z{n&k8(P2Iղ ]3CwTrGP. p4`rmN.0P BIa,POwc@n6P }t @[ @ N0.0 #@p0UrJ` gpG6g 0CH ,U }EGhdžiZ-LQGO}6 Ef`o B3Zcn$텂CChPJ..s䈖B4@'Lj߲hpnՆ؊%Eht0TrMK+ׇZcUU،hl<3BU"aquuK̨H:X(xWPdICe긏ȏ؏P( + p;8=/b  P f@uD,I&P8Lَ.YQ2TٔbsRT sCYƑ4#Cٕ^ 20䳗t8CA% X`Y6"3Q4H@Pj):@LS "g`)NB3,iaN2p` ZiUٚԂ|I-2&~@:h` ԙ@@b)HQɝy8G&99 0yy Q 22``v8L3M;If0 L @ pj@ &@}8D9 ` Ca ՀyV Z xnx @ @P p%|PA!~9Kh J[i Z~@@@HR 8`Y^FH!~`_2Ey?Х9%p*[ 0 ] s0g@ PZzk xSZ*aا;wyu[PZ& p s :3WbS1@HpyESp̺`ϪJr ) Ι+Je` k`":劤r- Úwꜝ . @iW:_:q~@E0 J; fP\Jz4 +!* 9a ~Pn04 %P+TZQ1{@RBp F6@ P ؠ * 6K2R PR< w)%3 P[s@Up7*Y a D0{Юh d1.@g` N  v "p: vks 㻇`@~@<i >0`f ۼSoG&F;.`{` ڹP*f f g֫bP j,Ps̻ka 8@2H5z뾀YPQ|+$* Wp#+6 ĩFp` 8-Oѝ 5P1^`` WYhޟv мLITVmo9+T.2`vPP`7>e@ɍDpK@>Μy@s u>H+)@C>Ayw3ńdbL21`J B^γ rP㫐|ɳ=wcchvP}Kp:jbkkԘ J PΠ zP) °ҷR ) ؐFzI2h#:p L, \ͯLz7,`; r?m>ªSL3,"~l ; /S{B?z84\~@JnHo  m.H M>  NNƷI ϗPYM`n~2s z q_Ia/W`e7+S!8)qgVip58YH @3 , Yzoa4P/I a}g8piG`Pp) १0 |!G<"YQ% 59sPM.zzUHs¶m㠤P|pa~YJ9U(bhL1Yiծe{6KE4p.,N ,n@Dexj-[d !B3d)cwG+bVpdK#)ʲfFM~vm0mU l` A$nD'yMV(uϒLY.D"c *9TPˮv , [A)Z,&l'*A< @#À N4$Lϳ!PqC@_* E`.tKn^ cHQǎ9h1D)0,CIoA]Ȍ/DKrBSf9P:L#)ȭpB.$AA3N 7 t,1R66N0* Q&Zk  l&*)qQ&DN>9e2FILE֣j)gXCo-\qAB4dUA77e9 ["]ixJC~P$A!/Dx Vxr7>5c=.Yӄ;FC.`@ #r s_ee-tk"\i5-sV9曛:]m5zWceZgi S@K$lv8ȨgF|,;`oAJˎj¯:zO7P>bXn;r7>=!r%o}a>݀GvY4AIzmjEK*mO+ B]3-x+]_ErڹW?ҷ}ן}C~Mƿ{j `xO~1 g@T4@ Lz#R8nCxAЌτ".[!B(?, [P#b豨nhI"LR2]#@=  uPnsP,&hZ@ፅji;}XcI:0d$U ItF8ʐDX9FH1Ld55*Z<$-)I|rlUZ8] |"T<Р"P*Cx@PMp/iz VQF-%"maC f)MGttIXQE}Ц t&De=Hin&Hk͞V%;8P&fQBS"VΠ1@| H kёFIA̘E4FW$RG"PS$>ײ0% -]dI!rIA~&E6nZ"$F=d -Hf"b kB F0  6mNFz*44'ǾS%(OЈ~m $ @IlKبF$60*\q@(G'HB,p$`%XE+&Q$H" u,I ՄL 0 +" ߯F,Fx&&LqM .@Da D8p N\eeiv%4%+TJo]C͎X\1m<зi?Jhh)I(omn?z>iӨHr;t`5%fMr(!DhA @)F+ J4M<\a,l @=LQ uhK4rf!Z|B' 4Ih+1)'YjrY15sqDo|S2v* \-Na-5 X-(`mFpXȯ*WS5$H|b=IItU5Ga<(꼅| *kt%XZTe &x#_6S.kJ$C8jh-h:'p|g3;)n\*/&k;>#a^rSY6WDml>"/7 %#y#˝tG]Szխ~u_kD ~ u$(#qf!h5s{~w]{JB!bx)3<XCSkCK=P21M,1)$ڭk(WXB*_a%-kD: H"9Ea} `F(EK dr2ak؆#m%ˁB(KSFv$N]Hvt6(}~|:yc+XIlL|Lk GWF)'#%fF(XXÂLlǬO 9FAGL1Z>pF૭(M`vB!D0AFr0lJ'6a.Dq7 #]f0.;,~/QZ@ǎGp.[dhtl}qqoNȇtfYKB0kOl'Z¹&dDBF/b"JDaY,7Y*BPrcKSsht>USazEGn(RdqitLIrt6N Wz{:5s'o0Qi9OǰSy9Zjɶɳ5"-AͨF7юz HGJҒTSA(GF<:d H%*JxF<ő7)g3#@GF8: Gv:pHLGjA` 1@Ib  <%xj$-8EMX^Of]&d}r+ rۅ-_P t$@xp D J8H02H ?9)z 0BaK^̊:o@vSy1p;F(F$v:? V_y'DDV=Ew}>B㯟+ "#CJ(+ H xX@ %P 'X kv7-!D;GJ#B"HX4}=JЁ Ž 8{!sĶ||hC 0</1)^S bpoBsв( d At"X rc )(@slzA@"_6G`C!q DIs|\N*{32@COr?4)WTޏN "ON(IvK҈@/)̃3 Be겙%CwY{%0UaFS,@d)єGҙNX~$ԓD@36B@@^|R.3mM]4;ܥgL E#1XB P0EH: NJ@O\D](@|n־s43KJ6:gӏ"jꮜt("UC',DdB [#@9B BOXB `K - #@Z̈,ZhhY 5H!7n ˊ/ ) 6;d8LɊy@x18A@jT 5%>*nF 'm@ZB+QTP#٨[ XYlg W\W xB7?b8P+|U!ˬ `F8ZS €0 a PC%AH+SvZ}!M WK*FΈ3{O(ԝIJ,q΂Pq"#ydFyE 'T.-҉\7Y1FY! a NAV"S@Iyr`%l1NdA/8J? bkh,vG TVNH '/SuY-3VW N!\σ,2 8sqb~Ρ0"S@ũķ`/;Pz8-+o$k;aJfKo=,%F8lhS;?2G|ЕxZu9(T9ȹ=q##W 5s7Yd-Sȡ^e}㼜z{rp|vv7n.+~`5spg0Amrq3mɿtiS伺ϡzO=+g>?lL9w'xms=Xg=k;E4ֻqVfnQ n=en>1_tvܢ_U=>~$P RO&rd)`'K> WV1KD KЃvp\'P=8%r!9HLGSKb~V" "!`n.H.+`2 6f dap n? xqcXzK ȁʦ1O:X=p!)rxOvBPVZ ѴE0hF".xT B>.aB`0t*P/8˘ ghd Ԉ7#,"= ptP p P Q ( @wf:sU@5 (45:a|SS#8`T.K]qp0 SnP7`>7YKe#p "`EH+Вpa8@ 5I\>|` IPH0!p - $i#TTL5Pu8 N 79yEIRS6hcTIL92Nk~@| З|@K987.?YYgK T+@40IpX/LWp `pI#p0 a0$ГIfM0 F Ss h=ɖe  Аe~)@Sht&0)JP0[y+wcM א  ]sp 6ωRm6)sO c6M6@WW`I0Z`E\ z`.uW70`t0\lW-vA6;t0u \ - SX0XIZ 4i ,icW[p@| Wŝ6OI ĝ uYv | pAro+zl5u|H#X0\YzW FC*LlڥX:@  I vIVI/z aEZgzy3|t%TXP ZV y(vQUpw9ܩ5%J]: o6PZ *V=Jfګ @J ǧj2rwJW@"1 ` 6̀v 4)27Gt@9v4E JpYB:0j`0YpH`ݹ7#0p3 0 Wah~Mib0^C@ ()=X:;5` dcK;*JDpyJ f{3ru^PO@ WoE0W|SYb%s #!Kn|f\y+Q~[PPK'@3eph_5 K os)[ fK a++L[<._EDTF^7S]4)ILE{0;n 3 #U=EZ:2 s"+@ex )I: [=K~pB" `Q E¤4Àt WkvpN2/V |@V P FX`kZ/ uF {2kÙ!U@8|,`sr0ZaK IZJ%pb bT[(J0W Y# 0Pұ.E ){Ǥ.|5dlk0CVp:pXG +pl ]@<ś) YPE^=xJsd-?{&y x  ـ [\ `bA-<)0D`zD ɛ }l,rOqMЇ\O cF0에NPO x0W> -T}[pl5+0+ήN/Ọ~Fl@YDvL9Ll^Os+y%V +ʷÄhPnoډX/?p0L;[50<ned0NJlɟ6v _V,OFc4H`CsˣO^u[241&q^B)r3}e#2Acⴢ!0nB0 Cڒλ 3̐<?%u6NNrQ@T YN*K09jājMY\tcω#]ވ!g+}H@blfUfjޞyŕY*9C)4:*=b|Q1a)p89dj+Lw:%Ȁ=@DE%R(` @N ; hD4(05A`A@| :U1-D vH v UPzCZAVhU dg@ HC C\݂fg31kT#kFѹ'E0^`^:8B$`3QBzQH#R&jP7 ) )`G:n ܬ&KL@JJD:AD@ C@ tqC0A@3d?؅|^T̀ ̀e@v|iu21h!Hg"˜%"AT4@И_"t%#a[Z6;!,8׶XU%YX%iIMZFg;]-54 {le`3YMTepƮaIPE ;Hg Ndu`:Cʤp9J &x  jjp1+vd8*M  E )CX i(ĬR ΘNUD?*ٔlpltȳ%IA1paO78& 5y#eS귍4(s9\F(T5.jZ^P2QkMRdaD3,^x Dl;RS!i ^೒d*E]ml"ٝAU?;4:搌NۋWBDR9")>TF@Xˤ@{|3gKCʞv,HRmM 4Q@x[>@` AXT T-Ћ/鄸۩k#; Pn`-R@x8҆rN 'g (n? I1^1׳\qXߩsJ$W.C 2h85]r 1]ssg33s#3`7>'r֪%<TͶrENny{~qEIP1RQ(G(G7pEg5lDz`B/IGV?fх<#z}:t{35~jb|Ԫa~4'm01 `z|~Ϫ|ߥG/{n}~0:oy?˃<3d<8j 9DV  ʐ @Ѓ:@` p@*L P̐@ы  x(*KĐ@6h39•q !(8;x\)q(hÝ87d3S0 *8 C;x K4PA6)xC_D 4@ ( R APAė@ CE9AnEÓDC'L GC_B`,D9 TcT&P&A,Gd 4F@q(`-E0tԈpJY,^$F]DDoÁ(*L?Ȕx9B{\|FDqEn(F*G)!`HA x0CH@U1j%Hqg0GN `@f@S2+8!/h ;8`ʞqP9A7 WJʯ?1SK1K| CL8K>%#ા - (0@$7d!PV˵ @L*˺ \@oXL( C8el MVĉOLhJmx_$mHɈddx̨08N+KXJENk $ U@Oϧ`x-ΞH޴O$T Q`< $LJUV̤!Ѓ\ 8P@-OMDF͔\H$@0cH >;@tlP xXPPIK X(P @ `Hg@Dt7MNPHh (22E؀87|;Ե6SHBE  * ,9pM-.=M Q胥p)P Ă2Xt O %SpԏG#q X`H` NC@{ܘwMT@AhH⌂[x*oH Z\VbSlh-3(ʥ*Eu}Ž>%0פ` XM8̤ % % P&,(8H&8VNa%؁ OH=R`t8Y-LV,-QmLS,=E*Er,9*H0=;pҜkqlІNTm OHt\W7Xa9xI\ p͈} v\L`?eB \ݐ,` Ǔc=UxA5U%X U5P ȄFEP$lMMQXă+E`Qm #@q(q`NHxD8PQRkUYq؆kXܗU*h\Tt C0(((A(8EK>(7!} 'H RA\pVG`]h`aUcЦ0>@ Jh`S[` Rx#Nq98  Z8uvVa`@%u \L|aV P<۳ ̈E;ȐS %m`xŽl ( ŸZ=cq\G@ï (n /dv!݊BPd U b'Ty@7.KE:M@hEcZ>v89 /x:xF% MhiiԊg>2(JA(@jOxiLM`K^]؃@ijj ifݥA[+@jQpM =؃IXL|^.P<ll~衾zk. %Z0X 븆pOlX`5 J>mNme}h=+[0,9A@(0V$PNn^n^Ixʉ5;VʪzNhq A;mnNo^)`î+ lBo.XJ O`oOpWcjnj票hR^ppGv _h xvoqp6\q@' qq o~V q ' or'Z?zT(r,rMfTˀ!,P H*\ȰÅJHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ J4fH*]ʴӧP3իXjݪ(ׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkν`#R:&M*4$XQԩ1}98BG*! < -`A$t{ aa-bb TAQ|a+jt⌐@A *B?\0HQEAvqߓ3X, dJ-=Ў=2\ #+CB\CA90"$#Iy#z‘'f$DK-䈔1AP12't z1 bPҞi% bT'A1TcNbP,r'="̚`bEB> iArd"&d zY_ 4F@0QB)@@bb![KP- ,!J{ +bl/h y!ZmH-r2o,d1tlL%aL): b.*%:O(%*tH4G%"50@zL !`0Et11qͷv,^B…@4@JԎJ6}XKؠ`"q ,o*^R?{b .#fNJg̬8AT~py3d%T(|?o+ 9'qOIO Xl8p TPE J Xw `@B$h0AbB5Dpcq`A"hJ?lCD C`y@BPUdy cCB Q+@{H1V(׸DLv:ن5,mX#(H rP>AIEL/]@ J )AXw8PXi#(c) , FϨ"ThcF*2 v@* KRPT҉`  0862*TaH*t- oU*0C((p KV!QU8Pm ȓZey~2͜9Ev.B8Cw19Bijw5#aA !J%AF0 FmT3 x>ZNjD52M]K/&ѴC-թ>#U#UA  YLp%Y.vURks"8T}d0lI A@WLJf{RuggHHh͗淗-{CT悈 2G$DxlDI`x0I~@H4BBw;mj381͑^bq O d*p@2MCup!&S4 Lԁ %(HIpӞ ߧWW8)Es28\zH X>4DP p ˛!p >J`:2Hq] y7n3#na@H! 6.- \1UDO c~3yt {fPU7 @P&B1R0 W{eja@vpOg c3c`]|;odT2V0kh _E 'P{ {B@bq` '!j zHdxV`7O qS 5A,GRrouds|EJRd؅P%FRQ8o0ChpX}8}Spppq 'Ue5~gX0ЅnpFu\`[ <)fBUZu1@{vVTDWD=]>0b0zLg g{bW%]UgF.PA C":jgBuD_F  }&0~cؑXW u1QHq sv`? ]%]4cVP]8r G8`uZ4}8?`^HD Pv v0 iC# L_daKi J`k n0FZ$ .#` \0ag VYEiDmCsEEY)BVo=9ru]l$wlf` 5jf5B!45VBC9sYAP8ay|j7pvmkhjnzcqr%gvl'5Ǚ_TmdiMUm_dkdֈ eId\ hnUhҹCYLFko9ñn#SئO\;goy)GvJfVqJEXJIt靷 8X$4saWPiWȤkeW0\Gp0t8tyivHaw0}g ƕCwmI*J enh7r0 c]zg JrQnh.=*}5FsIUed`JzsXS9jujsF7`褿b@phЍ#RϰG^ `fϐa9"uI1sɦH80"ul'yu8#@i$qR6jR47x]%yy@\_'H3d ajf=:\#G @Z dH5aJ$",z!rsxn$)@I{j :Lq%:U7VFR. 1F'!_(Y)Y4ˬᩂeƠ@ Q (!<gQ Bu^2{|mJ)!V2''*GH[8pL9d1WT ZA %Qf6Ǣ1Q "4 v{tV 6 RP  WV R\P G$jer$|2I(S 6U;4+d %:ƀaBDZ A5׶11z)ѐ (U %͘P+KEN3y^#oZ!4W(h: Pbfj8ũ3sRpB ڵdU]` DKhO5\N0`BAGbFs 7 &]c0qf9Au JNܧQ: n+bZ;j )/fC&XFiw9ʙwE\GSAZC|4Q kHg@z'l˛^ fc DZf񴤔9g˱m{* Qo vR9LDȧMk6|W-:Ǡl r2d.:DWFM jA;K˻}2 cFF[͏rWOS ptZiqwLfs栵fv Ͷg5^:Akqv -)c_Wl8Gѻrі\I :e o5edϲ!dc'UƐ`G,`\Qv *d9 㪪f;.6+$x_'ձAy})|7+0@~ 9` l 42h41-mdPxe 1^BAlGTS׌XPImdN`[tժ/G5a@P!'̦ ACy?+Y(N^dH(WdF_74vEmeZJ Hm?N[0cY3UoĒ P vQf@[A^>E20 @Ђ^T'ƨ#NƖ 0yhí:˞{j`F~ݗa:)YpEi Ƭb;Xd6'ѝj{hBLٽj f:ڶ̝Fw,ӛ%scζ=Zl^lrjnɓo6lLqz\M:fd6f/gIl~Eɉ.COT6*ߜ}\j`މu$it{Ϝʕ,nbTp8J[%d }^ii?]g*J-ɳ? ti H gEg0Ft Nk8P*h.< 163+XkG4F%} @,AXB1d$O"ox<218`PldjSQNZUYnWaFTu)B i ܦCuj",f t""Dfp)g88 G PBGhOW`Q&Ve Y)$ /dzTsѥO^u!a' " rF5djā(@@Cq`b!h;h4*j"JV$:26ʰ4 2j>J$E[tEc1$3~ .")fRm *`@)ء- {*0S!$Ic0 "Gx*=|" @4TPH a lV@R36YoFH#tRJ[(/#ˇBEјjnET+^e3*-v04,'RD> dfpIML!02čԨ@h*@K<-cɓ !7*tb(ܪ . b ʨؐS=6BUBɘhIi_haaְC %LH!.Lւ qxN,ˉ䴣Z Sva$b-;vBJ䩛tH!!4l&MlxEaFRDCi&Iv͒ a'SŀyNrH8jVƠa lahłp6Jge(Θ XVp5dCqɗI B (/)\pAV:q` R=Kt' uBx8`>ꈞZAݎ>ނ ZZIC@2瑍!=Scyƀ]A rM0VA056iIW0@hhJxi 0H, bM!28Q+X@!D DĶF0N S F؜mp%D|$oԆ*2 eX#a(B}y`O<3-4 s"@(#gl̨(*Y [AGRo oA;d @2fL$ As6d e(RQP,KX,Tn)[Sc19dUhUQ|!YvtMHmڔ i>r$*',g0Gt:rtI>Ӗ#!̊U%Uc 51JЅx_䱝2x:ZQ6f[!IvI\R |g+)}&LQᓕS}4+8=hXY9}hShA5|%Btj(< I8)p*hTXՑ!TP3M?{$bÇ5Aܪ!lW~.5b,R9P#AL0ٯ8Vj45WȘUeT0 %t1cz" hR) * %Q91l 8GddNbZyhUA@H:0 BhbVJB-@#E{/5o7Qiʔ nQȂb)ow CP!Vj5+U X;\ kS ϨFޔaTBNP!YZ\MJTr! j@1R*B@ @ x8h&zPEAl \yj=.@hD I[fxY 09WV( {Ȑ|Ť4Һ3 Dz 2b^<@m4E&&t| %φP\Lc\Ҡhct%Yv5-SZ/k6P )}VH% b|2T7{+3e<#"W<}INpQZj8o\Kp.џ F`-ƈF5*p*x+1ƃ@BO!W@8 ~7hR*H|3(@F`GZ  ^$ `nuKYk!h'$#ōq?+[X9D IiV+5%\ݲ EˆFhpHlLoi p`/ca\2#~[IH A~ (*׈D "2ZUm TY%qx(`x_. j>88!POq!i ڥ10   1&BQvH`Ľ$P= cB## P8#Phy@?`B,+0L%Z";J-; \#ZȐJ1 L G1#`--D+7 +@8"%`AnU9DA怖؃)w3CE*c7?e4(7IQ8jx@fXxaH(5 ogt2$SgT zKHApret<Vh=Ȓ$W,˂ @ $$0̅H|i (+ EqI"Ё9#q,<.lx%(HP/HI5 ,#" ׁ c,](>8(t*KÆRO|a4:52R$B"Y $A4A9A!C2Lyt`ʏR1#R8LIt:+eLMF (0r9e -H64x8K1Mz|2i p2ڤ'93CULA.J&D$0XaoY8c03Q`šcY}'(Pp9 !h/ŒP$`!LHո ;?pL#P#2XS`=Ѐ}) 5 (FQy-NE$H+ (%hQ޴Rĥ6TE.ӂރ 0<!&G36Qc`=0M894&bKP`. T#1l@fܠY!?cɦgΐ Ӓ >pYEb9Zu3&x'tڑq'豅c Årp윘Ҩ;%Sh>B[50^jh m}>Vn.R+Jqe <̉WJ<*&œS':ڹD=QZ IمKӰVR5Nqѯd-J bM/FI+BU 20 f9Hz?1 -"Pʅ #z5\Tz,˕\/'jX*pAXk ӛ3dU'vB%*٭"†šڅ9ν]3/?\7[B])!Tb0K!|/]^@婾\j!A&h.I&pB8:,]6JL`^br ƘZ2"0-].3еiyQtढ#a.yz"AJ Up(a>\P@:``kaaJ琥t+V½%*X߶ #6\A+e2\"a` >T.[..c3"1[&*"E=!&(R[R\lU&宒+aj ߩ@H}mcB=cLdMB_N- \Q?TC҈'U.E11`Myl+UBEҁ _!)bCPH'CJdmfneLslg kWac$ JH?gE™h^岚Cȝ DX;6~[\ZX0Ɩ`Y>(/ Px 65VNHpq)^:Q f.jB8X_؆eHa 8SP~$ΰhL L.軉J/.4?.`S P((ZĽH![H{8H닀^3-3q ?#(JH?12e%GPH<.P lT76TLFMơ;CX.[jpSxUR(sC+ [hڠ3`N0W9'UB 60:d{UYd1P<=kS؁KE-v PfOI1C8H;ѤUd0'ȈU(]<>Dpr-h]Y: 0/p]h^X-Ui,A7θBI/* ^h`ylw[h`w`Zcm$l? ,ʮ62%}&B d@1ڧ(Z]:>n3r^\b]: jX ̵ڍb0j6tRW[DN`?%bq!(*㈈=N h=4a7#s@fh0„($pC "!DXУDŽ G̈1ɔ*#lr#—2gҬiG#Erddǐ@,j(Ҝ`PɄRp +U"( iҙ^n$kmYsaDa߾INߒ@aV!!K8re*5A%2OIMVov:Wbw{z$ڸPu&a3\IN>:d.N#[0􆌯GZSU]yMZ"\c'\nƤZG%oK$5) V݇`G}!fT,tCx={8ExJЁUA iGYV\#B4?IRqe հBb P /Ȉ \}m!ğ V%ӡ2gOBr)b Uz &E _r)إ@SOCrrY`*dZ10DkJ%ژE:LQ@%~Y^: ep b|U2ì LrP{j_uu gEAXP6;1TcA$!L*\0 !2\a W Pȥ8I!׶T,c1Ғ "L5qOv n/f]1E  Aw FG x$A0CqA!H]yGynD)GvF`M'tA |IQPA)~xB7BAo RG2*Q)9hP .~LՁxIkMot0|4-$mp!Fx6xpף$kFDF LO̠ ȉ+P 8@ v@Aǁtu@^D\6f ࡔ &$@ =\` `簂i,X^ ;:Y6!?H@3@GRH KTs|l!>tn1R  X]"(lA- ҋ>g^J`54A$@EB>|2o CqM[Ƹ*H$R@! XUgCW:#[Ӧ:6Y2z ]5B `C"MHHKFW d⚕NMXq-G(@jD̀G7lDc޹n8$ 뀻xIZ-P.F "'#21-FC4`JVn#{(1NPB 06=wSh=x-0'X(R7RMHF$ J Rؾ.-\37AZayDJi^ A';# Aҁ(DB 8 r{EA|1 k7\h øJ.TK+a, F8!Zm|#C 8kϕbg=SB R<@GnFNX QOP%WaCK8yMP\XBګAeR!7"xj@(F $L̸A6p|{7go satrmD6rUP#$XEf=K6|, -̉J bZ!>h+ ҃ w h5ȴŠ.oڠ?^ą}6ؒ0ccЯ.HBR taE,|]VD",,`Ev ?I; ,\8xq[l{!6RCza >^Á~Cps`'ٯ9qzJE _XV^t`(i0r s=Tr;1_ d|3ڈ-|PO S ^ɷɊs(GT@*A $*C@ŔHzAHCX ,*َǜ.UFh*j̨jȪȑjEsr(BBlDhjDJ6gXPEa/ȓiq܄&)Uѽe+jO |hgh |JB\_k6 jꫪd:,駙ǿ>l9׾*_jiikdko eV6@QlEGȪ6bg,nO0>-Fm^-JknP,&wV϶Y$B$y-lٚjEmVPBƨDmr-e^ ^,VF)Ѳuh: j.FӂB7<\S @LʪBK*nlGZ2l… 혲nqT(KFn 龆n6R)ʬ~//Y4jΑR/6oJ1':q oFRj1ϖkiXZp*6/s _I/ l4 Ad6`5 l<ĖJBTW)D1t@%!|1Tq;HC%q:9ؤs:@@%:;@QϲA D2AP 0́ 4*H @Yāxp*4X 0Qċr@.f6@)A#X˔L\'rVo)ooNWA/F/HtO4!qEmA9B,68A8CH 关P@# F9+6F3Gܴ2Y`9(A tfO-ds{dK[@ `jS3}@(d|ϰv@@"( B#ː;AC1㶚'G/G|Jض9Z|o/|I'||/.+9kV$2h/XC5A6@,BFA"3rY(@Id<y[m/n-AB%-U)gksPR)/XAq=$P0ֻ$40,=k}-K<绅,׏pSWqK&v_|tv(CF\޳zD(ȤG\AB0THA{/@je 2M4YB/x*EU/E/9dBSy}O+xTT(Ĵƌb AujՀ-qhǒ2Y Zrp?ZTV7EC̘ e$Jx,2P %^JFfC%PkVZ^k`ÒmZkRhb[cfM;o^oջ@ ;pb ]0_h-6b9'--;Ɗo2;-MsRTE8ON]SHeɏwV M򐾹޸Rm{%JyA yǵ9xs4k0ڭ ϺÎмؒϻ#̴SC*&6Ъ!7 #KB8FIÁ j;jFQ{M$\%o*/,:)(m'!K 1oL#JH2JTKLCB< @Ŭ3l3crE#%u4ѳּtN=qS%Am 3ъ2OS+1tONUHr X|PN]m=GS8VaT95SQM+M tROyDYN, ojj 2\ uW>5_Ra=Vpy:*-4D (4@6coC=bD ``{ kDJYe.ϙ=*9ٞ;-gi>hd=͔)Vk蝓in:mXWz.{ϥ}NJ>gV İzÝ|:lK#.8" QnBQ[,I.a.F. ZU1|c[@\棥al`i,omPj0dxH[f~:5xHX$8[4.q4. =uh f@,`OY"@ .DHAa; ְ `!bl3PDv8 a !5EF*R!D!' @M.p1a |r5` f0d` U $Ԣ@ZO`C :a] HS(B~9BIt SH K2T‡P 1B-& (0Z-rQ< D> m,Eckx2ͥ}@d2 >F!'=101 DF)I\0!kM 1D4\ZqL}e"8s [pZ(j =HCdH]fҢK$%hQuMd&x6El\!,d (e'rq } ynpTjB4@aDc̘]ހ :̐# 4N ` cNuZbW.E`"ȈbrVp%(< @ "i[p`4Fi @ BXA KZ>' yAu@R.yBЄ`l1 )W nn @pIa l{0 ҂$ KhaRMUH |`.D\ E.F$X-ͅ;h *3%H *c@Bm9A4IB_`+^< / plj#Pk0@ʲ]))hyC+ 9oFsA!@0NB1 od-".`-YNrId\$lKRz/F P5Q#\bF$'r4 RE :M@z^A/@h5%8ѪTbJI:C5S !#rc0<" @>IWN-3={ m zA7A6*d sCl` %_<=01%g vú=9(#PᓖȅB>0VcMz_myHO K7pY+ O M1^0+$uÐ@D9 A d^aUb('.q(Lެ-86ZZ1ͷab{ a%XA!{rD PQ `Bl7:ة !xJ0:uxPR/h"( * 65R-6m 3GC@RZP.몈@!ja 4.zᎈZ- X| ȀN2f \,j0 1 |PJ `>@V5 :! n|@'(a!&&- 4J!tL20 jRP$&x+ب^R `͌!6@hPA8A6B!Bv !GR'T~Q0 @8@.)¢m .q怌V!P@!JHTL-\ bH n:Kf0BT\-Z zmޖl@ƌ,` dj&&ClXpwxKE"ě $Ev@ǺbCxЎ@ͶX`fnސ[ILnl-퀺iDs%%L# E91͌IvX7HaF@`KI1: JϦ-4Lalf, \@+D-n:QklS44 "hXMp  *DUhDL H `bPdth+GQ]tH;ItL Foz+HAH@E0H%4CʐF^2NI J4'Zz6:e{"kFnN]iF]1VfadDv@ET^vuC6G:OէCĀ4YȖ  *'(|$@q:$ YUDE uH_ba-ghVXl{c|eg7cu0ViMfcj&j5Vdnc (yl6kffLg5^5O2v0S|Dd^hOdE eh Spϳh_U}o@Vf3Rg6ifjO3s)7d%~tyPhYgvkf^t[ psukeOhr_DsCf_傪UyyΈyWzzz{W{۲i1 w0]iޕkFtwL<~W~~~WWywjSsY׷n x-=}+w#X'+؂W@`;8E`!t@x!4TRX-d!ra ^7!1g؆?\Xux< !wxXlȄ8XG.낯XXz_IeaC^X` DC! aD nޠ ũYؑ fؓ?CYGy~en:cX!`.na b"Ym\ ` >y? "YPG[y!+L\  kΙkم`"7^`#ْyસ٢/3Z7 daLčc 9 h@palX` N p#Yۀ7کׂ J0?tKɹIAmna " mr v+t:uT-0AG zz*۱K >Zz ` >ޙk hqXրh`aL1ـvxC3zz;;X` jڞ!`K"iny-t0Vgkܢ 2!­W4±YLM< ƣ\ʧ`ɇxę̑ک{Q\לͩ\i(R$~ eB0L{`Ej5ىp^G x%`qH|ARP!C@jS~km*M b@!8]&bTz!SZ]t{@| @jVpE"cV!{X _(JE|B!, LbȪdW0M*`k6l mHEZsH\`'| Xq.#pUFB \$@q"$ 2F" R 4! =HJX\+С B<@SH4b iAD W#H!r*&{Gho2PW"].A6ˆUDFUxJ"\ HD(A(Fͽb;U,Ffi+Bڡ5f>HMK X"P1`fcd 5UBӃO$.6H jP3i$KZ rtC؀ g.Р8AGvH=*nz$aAVPȘ hCkQ]^v#\𜻾gwb H遠]BHUܿ\+D?~Eaƍ6͑-p-]5hBޠ)]V="2%P D6@ p r<=q.` 0H`,7 .@`GSjP O z SSn, X[cP^Q&8-z} j`s3,, 6gсE4sQ <%}c%Hbfwyf qx /( t5rxkw Ri Z5 ipv^wE6CpU> LWqCJ<ڂOD(m.%GF}uWX0&T\6tlh^x0|RcTaR҃.x6B[ؖmC#'g4%͈߃xUèm狣Hc˸z4g džͨ(v'pBI4O?48qxh$ŨXbgc rH ) ARǍI7؍Fe iI .0 6 Ψi/qCxI* 9ihEyh{_a66yɎS)VY TOP(gf)BVIjٖnyo%)\ q ] GD?N IP ᘓ/<9C,v3dHAiٙW~탗yINّ٘9Isə(֕aYhsIcYx^)yg)LyK0bV@L9P-Ogo \9VM9h)&iZ`q4epc8Puًjgj׉ zɹ *Zwid~ Ip L\7q e0uk (0 @[ _TxP8g;Z e6BRxaZ zXjZ\ڥ^* 9OI @QTQhڔQF    #i8F A#`@됨I J}*=F@VwH =e382~P 7 [@Y ^pKPEU8P/ک30ȪOe;@ g+@VkgZa0sp _YZ`z:JɗP䜾 ِ ې U4\R Ѱ zʧIP Q C` LE퀲[# /:~,ŲrP8G _|LF|Z3QT_x`LJ/ d gl#MQF }@-C05Z R{j@7\z:ʹ)*P&` ` @e@ рe0ϐ*wM؝d: * C 3D 5j0PJ2'E` =~ c 2kL1؋`+Ps 13kj ) yF j@v9oHǣT:-#29s+>qk%|(L,>(ι-K<$( P k K0 5Ч2] @ XL y8r>z"s+=pX@^You: 8fRW0xXoǘ¨l&ʬʖzN* 3M \p   ـ Q`u @^ neӢ ``[LsPp ?<[(f~ ə)G\8}v J`Cs@H@͇p@4@k 9H  "Cmн  ~aZZ2 |p~ ҇ mW)ʮK@=yFi+̞LM?074,؀ @X ( p 9}Ƽ `k`bD/ /Tj'& ϼΞc-7` 5h Kx~ |Z`ҽ!]_p046Y N@EZ$zA~l GM<ͯ]@zI<<4 A+CL/_*A^<7d.[BG^"n<\n'A] ] ` l~T=0*)&`=1w{Bu4z>eIc nC3.)2`s>E苾ݱ,.%̔&R-@>ꋎ[O~.+`.4P@)봮-#.n8rM[r "Ů&> Y~! kE.s'#!y#>O0IRp i] ~p?$L͉2PB>Dp]f  n~5վ)n 2 & ^6 ?)/gp,~d>-B#/4o 'B.#%Zn 1<, @ y0&@ Ip=6`l 0P ' z`0  U Ba5w- P Y  @'U5%`[   [ WNź@ $'S[? 1 |P(aPb!P|P? )$ZB?\b  0j Pp-aE r8PМU"y@tpPp%?iఊt,iOJ֐3q9tVĩSFL X҈H" }le)2:SK9  uKF؝h(hO?iS+D:{G rH"42`')rc.).I2q3E\([prK*1=H!BJR 4ӈB\+`+Cl:M0.ZJJLcAh`(s;b80Xd9z˒?H㌤S3! 4TO?vC4]8k2!@$Œ.O)]HXRJjJ:0S"0p4#)+HCЃ5}sKQ%1 jCV'pC3B9]Ŏ.U]uF@46 7߷T Mر 6[ J57pV2pd[5Q =V"Tf (Dd7 gp8i %8w>ȜI10Itb %KC $hX %7]R`ԩAء Xms@'h_~)^HSBK U~PbMI"Ks- |&k 7,p頟B Gz |PM} g0B04a0@^O) `3UoMU%YHH ;l v,1.rxЧ5}-AO(1o*8B1qt %y;݄Up0h@CZ 1̱ϑUg,F5mB$h#]dFkd p ;1H!6 `}%9h- H K}6W ZLN(a sS1N@^DհkIEVljN: ]0 $XAh$v-EbЉ LIDgeo7h5cK*_-i)\)5.C{nU !*m'UV%e4#asI Ğ8P26^" j0&sN(-h aTDC~ Z $CFze,/g4i ]&, 8V7+ z11jy՞YNjy?9Gv`'+h-t{*B1?'=5H#4]L5 ^ԅi64!?"6p] Ar0>$?A#:?^s;^0#2hA7Х7I' P<0dP&r:k`BdB'"Ȃ[´ʂ?P׈ P*+ 0b(1?o?P'P)*HC7\Ǔȉ<'*cI|Jq\'<",GHJ8SI(ȀG< KƉȫTE$K4KJdJH{ʪDJZtD,K|Kpix ALD40*<F,KKK,LlM|M،MٜMڴڼM\GҴMՄ$M$ߴH4|ɌY΂̢LJ\M ΂$G$NҜMlO|O˂ϼOOѴOONUP,ϖOPlГlc|Op `MPm %-QB 8- 0N0 GI1 E &' YRE XxR.HP=(-S3ŲG8S5]2 %!.uR)SY(P;S S/\PCEUQ0TD=C(pTKQN}TQD !UL% STQE59WEOPm6U*:AS.U;G9u>V0UX1MS_5eUo Wq%G(Wm=WYR!ea]R -d}R /S;e }T1x`xXA(+,UUmUP؂PQЁ((V=[U[ UJO I"QF8 A=)p;xX q(8V{mR78SFx*zCR: A(:8wXSsEW:M۶=Wse۷u۹[`[[[۹[ ½[Ms%\e\]ǍSn8x%AvT݄==F=@.TF)U[]E]O؃XcLQxW%AHb.?XXF}QU%c9T^B`0.C,H}m%HXI$Hב-Z3QF[Py-T#`!G(إT.!{]E\a.FaNa6~u Xf8bZY![AFX N_PE=R`JX`_bC8F:^'^T>ZȁF .+ՏQ>xbu _E_M2$PMp2x9Z BS Ad,^%^VYf-%S`.u ~Ae^e_e`faeʽaX \F eGZdEu}R0(4UR xPX^=FCԍ cu`X70ߝ=G  @=HBw^\(+ X](sUQV [RA! @R|S1;#-[(j.j>mZ:p#hfv푦Rh<;mU{Խ\`^Qўc;[(c =:&&]5-b9(dPcDmgb8jKlfvǎlɞl~ܗ\ =Zk=f>pFOu|GJxxO0"NJ& (OW>Fl.^n\nn`خXjp5k7=$]`CJ(+UP.-̠.x;#qS6n8]pĻi/%`soပ[q&7qs aqO[rqO[awq!/WrmE$$'rd*%쭞XrurN%'?p62+oڢr%&s7r%sEq;G\5?r=wBC7te\s}d$wpg-l3cmKpmsUR1XsYZ[umu[u`[S*'#u]kNrOu<RUtnvpwq/*R -Sg_RѾ#iw&M쑒q6ƹDeL<ל́]x{R1hWx?xЈxI  )DH1S:,y~Ďxg̛yyDZzNϲ MUXVEo)۟@m] && E m ~]%DJ 1Q#}Wfx!"fD*Tn,N4Id62;mεv 4ކ+kvxlS&ˮ4T.,lIA͋C #K OpKKplB'Ч2(AMA4s@-Dmt}7M#56-P8UБ`n-[Q!,xXH*\ȰÇ#JHbC3jȱǏ CIRcJ\ɲ˗0c ͛8sܩMRJѣH` $HPJJb ׯ`\Eu:m˶۷QXAp˷߿ LÈ+^̸ǐ#KL˘3k̹TCJdó2}lU&԰c,"W Gl!ȓ'lǕKG«[νËOӫ_Ͼ7<"1v;(h& 6F(Vhfv ($h(,0(4h8JePPAE~pK Ury0M6~Ugk٨uL)SB@G4vł`aPoVy5-H_ [F|KP]`At)CZDH@BIvrOu꫰3*+J ;NI9+k챌ly.K DzT+Ŵ!,P H*\ȰCJHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JeH*]ʴӧP3իXjݪ(ׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ PXti*@^hѥUnMR@ eީ+_Μfi*мq$|R0`O| [㗿]_=Iq D$ W@0(ᄜ@ZA]lvadvw$(&[qhb0WݵW Qy%@~D$D4"H*4 Eb5 ɑ 4BM`ZJ X!А1r&DEA4)'T " t$dBDq`c -VP+倊qUR"A!!PM"Hd4$#DBJ!gT@$sK$Mr,3 B/:nS~j _M%I{# g5^XC'#M_J&M_"8& ;IR-iQ0R) lLw3awxm!Se%90< ʹ^DAn#G:nyR3\fpwA2b4F $bY xA |H))Rɯ,`@A(` 0!RLUH8:ŀhRl7.TArd +Ld @Vd !n#&6)QLv DрEƄ$T%M>@0P_|$7mb.'B pp8?(A |.# PjT,Wh_"q H8d gh!c1Ђд0h \u(}Rqb(h;v,l`j;y {* &G:+,>! .\_w"LHf d-. FhE 'OP([ t#$b<Ԏ*g?PSwčЬvxH##vTK JP &I_`*2QEa6Rrr pyRd h+ ƤiL]Qf֔y2)=p';y-iї5$f^Xl]KN=φF=0k.F(F58! .B K|ѧ"BV նv+0Sp [4(hUh 86Fh}fWzlI c@?*QOˋс?d2:f7 uO"#Za JgArQS4-BLX$Ptf"W{FիqT"a-^;Fx#P4v)~ DTUnH>r?@~F*Pu; b^aR?vXg= P  :"X$>0^)vPQx:pTRrH٧x FGp]Mp` U DY9zd*P)!.N6/6.; g0 q egP X %10n@]{ckH_(M Qq0,8(.000=Hj[ 7pbSa1 A͑Gba1ni57 qBIXbp(_8#l4v0FK:Ld+j;5!!)BHKqj@䶣%]7 a9݅^   .LY0明ny^"_\heF(YI*hjl> Xg`HBfhTgwh:AVXq h<@ T3`xf j!91ɋhJZ L%: Y7UGؑ܄YZ[F, Bh H!Qɩ&j^ po/=uM+,*PmmW3щ K!>` 0Qq?Q(`3HGZ P_@IFP U;E `}MS SV;"Q 0ې My!yƧ v^CA#4ZS7ylv`MPo@ |2s@uD`+` VПr{E1{iW P \D_S;)`V4zZEG|m]D y׸ AI+SCsz*|s''AѺ 1 &Rw-•y\]A g@u 9z ; /`{PgW ~7I@ ' ` 8Ȼr2eK8!W @ #ooC:FHe3|u ? QE=!(a1}j>.|16NE`\ J0|P{Ĵ"yk]dz[JbXRшTYʎ ۰>̈!ǫ!\8* ۝bV@;bljTF[KXʐk_1 J˪ӧ7%H\ iǾ bWa:),* %Ʀa̬a xD^Q h[Fx]z}G* Θaa(J[5 #2pO Ȩ̯ܧki|̽,o9KϗA *g"I?֧` \h]$}m{PDgL$7$A/z$ \>2xh1 O1ӒM}j7LSqT#0 7yȵ"1|qQ  qv<r})l~<*E] -esGP ̐ @bH:p#e`"٨X`׎ >0j@H;ڒ\ڜ)Un{Ҋ !ѩ9\ T|,Y$MCu.] +Syȋ)۠ 9cDܱzl@}=pbR@m !c;z2qDnm1!Æ.0i[@,|Qeig# =^F^aj壺俼KȬSQI}a_lJ SŶ9=;5,wfmʹ[VQ'xPFbˈm>Yր]vr+O.aR)Ő-jQzϣeѓbUPSqS92 ~01qdH[\nA]1Ptn 9; J  SENSAO}z^9F4S@ tL[z%bWJP {jJ~q l;$@ItNA x q`pj(2Jb|m]CO*cHbQv mGFm!jQA H+ Pq`y9>umX }bqf!f};Y#GN#0jɂ\ jOehp(^e[ }ຯ qs ]^ >0d;[ Kt{x:aٱZw>-0™=짪Pov 1in rgoX[O\w_AlF4X1o(VA .dC 81@D"2QEMX&%"QI*MHX#H-OA%ZQI!& 꼨&H 8ܩ׫$ jE\#͚'ϞZWXy_MBe:X/j/fnך%3\șV̡E&]iO`o~,u߶bG|!ڠ:&^qY.LX(P݄U32rޓ w"vկgSFAK #Bm̑j{ ʠ,!vI(M&+B OS@Eƿmq Yȕ }21L))%`4H$Ti kRh@^+hr/ߠQ N..+ ɴNFl aIbW#.-ⲠbMȅa!!xb1Z .:  k$5H*hOWVFx"_b;sa l&%!Y)V #Vbf=sΠ+ϮhtZՊDMD߸&*V0D(B(m"P=t~~wcZQAٸ0d6__" "4`O?HA5;" 2(!!0d! A>  L: B'!Ǩʊ%d9FCp-$iBpr $x uaUЁ Y *L H,…yXصV1O =:`H@p"7"WF13hB# |"B@̚aVBF %ݏ .m p$WFQd cCd ;`pAHVrSBt2tdf0Vӊ"B@R$% yz0Immf1P龃h0g2LdJ@`@WnV - Q' bcE l`NT lpE@ laR}`\`)t <5>&N"gT0VQ W2'@^ʌ갡,0`͂Tф+kA` ZZjds&D+:i#X`B gayD *|2*IJH\ E $"n+0Ԣ^#\.[Go:#P7EEp7P\Cn DqpZ-d12q"MD%)F8W#j] Oy[bscR72+f4 +ɽp"B P B KHBwnŠp p=0bD3,C6"$!0B)6VĘ"]8*t 1"c6b`:@ RB(a W*Vh`y1*uOAL+tB:AڮbࡉiՄa(> rI#"}A7ddQg~ J5;!&(!s!6LUvWӭ~ְbT "0E +1AR@8xI Բ|6+,ÂajBp "è 0(@8~ .P8)BqL;6@Rmq fS Yf+1ʭ'=!H%X8 ʐZyͭXҮݰB(='\<P|ըGqpMiS7d@b̀05 •pI(wh xCVdfY}xZj .$%h^ „` E,H'b{]Yv9S<+bbk`!0z, @~Vd(B`N>֩4:]D,N;Mq-kDtkTľb`HX /;P",{ۀ0"e,a=5p+c (B Ȕ٦$[D4cɋ*[(ٞS@o=,ܮۂ: B8As.UH )? B+h4)#d 6<3B9TcC `/t@ؤ 0C%ق6\uJV@ s19= >P`ˁXBH ;@C83BB:1D8K<0sUD.­8b80#< ЂM@FQ~!KDj5o GK<Қc3Q "+Q1M9X# 0H@1(;Kmz*NrM4>8b{ (&E,sHH>pdA`)8[p' RXbVPhRP%HJ|5( N\78p+hI`V-`QȀ:PD24 b#h&| ElHT?ѺJ#O)Q|*40>IQ˚ y)JDHL 0$SHRɼmZȥɜV0;} e7-r$Ut5R'ٶ5ٵŏ@]Y=ŎxWe؍w43ր՛m@j8! ͞E 9.,iH-Rvxxuh8r`v Lv` 6k\B>iN,Z/=LY6# $F#&Z%B47'@ Q X%G #/81I-ۺXء]V58[UMH=!Shhm 2 <Ux8`ih`vHu5i_30U l *,x8Fi"8.*8Ҫ6\J?ƫ 7h.Qc>Ï".)[X/$P@J!x p3F!H,.(ȍ\hRؗ[vVeXT` X"1X(?c 6+:ӀXt(u0`\ȃvH>zb`;K{3%-v;ADNK,z6 'u+ڋDX5U+ů<Ad hKC"`qeW&vOQvY`刅Xiafך^_]<_I8``(YZ}w⍘Lhvо "J@ȅH(-D,Ёsg+PtbC.K;ĎU^Q&M@>L Vי]Pi^VjLav[ؖmEUuiU1i.цHX |qyU8+; h`A8hi8M#e3;7U6\+bGFb\ܸaDJ*n&oUL ^͎DQ* Bd׋Q5m\چY~Wq}TVeV(Mb<y~$FHH?X4]_55HQÒ=u o HFcDK[|[PRˏk:/HK%;)?GGa/?I&-p#:7δNxKVh-[%qSY͘qT'9guaf%ZTu[%)X`]ϊ<(RY2Ɲ601OTkȸ8vk %acQoSM׮HwnZjPY_]Uw}w~e} fm_a[cyWQSٶ^+6FZamX=OB$/Xwۖa[p]w}pu Xy^SryǛyx=ąQ=y7֩m}wxzOWvYdք[ /Xa]͈դߑW\}{'zÿ@ ŧq\~Yşow(+zfx6p|ufP{ҴOo*0@G7}?%3zO_}!ݗU܏'}+U3XЗ3~J~~~W Y$S)ݟB~0-~b(L(pB *l`! Iv"&#Ha< NّfҬ93)&;&B$رJ s o HQh.xQKDǒ*Y/d#ڵfYu)ѕ*1oZzM)ߐ-l1`^B  C Tj3ȲGN]wmv̥ʑgy"XAu; [&$4H} \x,fK6UoGcH:2v'>lJcol&-YA0z@ v1M5]FYͤAvubGz 5(B"FhAP! mh i@ t @[|!Yr!  l<` AD &= Ȁ%Ke xg) (kJ2H$ e L)dAy@wj> >˛0$C( FG'Yhc1Pq*Q@iTg XW̦iA!Pgf#\ު*kYTI|2ŖmƏAK*$gcU֦IX cD18{Ijg %4+&Yy[1~$z|` $BTvJ DŸX3:Q^L*N@lh (lԝrI WE* l(@z]aba :EYGA*L@P@ffaX91AgLHW{ZC%@DDP2AH8;R[q(8 \`nR]sNTx`ń g0Ef8 Biz-˱.a;8HUT'd!DM'~8 BY2jDXKE*4:DTwg`Ȱ ˋhtpUqȤʉw g$gYK vA P9ہ°& 0 PD7u8Brß'<'!dp\ʺ'* Lw&^,CqGL>+P |]c Y&E 89pAE &P@Vk<@U cFXd(9lR@̺°Al|* ",Xpш ѳar_K(%JUaVi !?! ͮma9D /: ]W&:vx ٰ /TAH 'm̑(K b"pj"Yq(Р ڢHq RO*!8E70+m =ѭ(,HфTAyDXoLMiƲ1o2gPAORJUQm"!`+F~4S&-pFz8/>t5 dP :3@e liTAMt@HBA1&SdR{FX)ZjRNM"C$ h [#b j0*Q#i8LPU $'D&x8#HC!%)-:T e" B% R  Pl";PDґLO `}g| R4zeP YM l4e  E*-MY!؂҈zy9kE 9[) ǏxWaC/+Rv^pLꋢ,^ssB҇#P$T`ML+*2l-@dCp*ج5Q[TBB%XH\K1uD TMpEjBY) @C;e ^FQ(/w)0dA܅\|v@?T554q@' Ax(@aաC`M@q8BvB!)"O+ؐ%Tم#v@BÞĉJ\ӑ|A)6$X.b hxe@OL%8I!Yx%`dD!ZasaA@\tjL)d A 7h%^ wH"E% /N,:-"):AtI a6ML87Qnq1 "IށAe4G@?YhB((Fu􉿹+J}gM3vc@pSJ(`PU^K6%r$vo0W^%`%BWYKTV^IuԥmX^nDF[h}P"w"}vYdDi @jGb2aFtg|%itgYLuVkgW'aڦ~((hrh'_t`bf}dzf(B@4Ҹ-Iڄ1 )JJD@\\'stH{FQgNʅdG4eॅ(:(~)hJ()%g''o.e薒d&,x *&lEŸI.Ajd @A"jz"t,ʂ8 Д*~v#r* AꬊpH*~**Aj 耧&rrj(D0BAЁ@pkjk<+빊뺖k+pD>뽶+kkkr(&.lګ@x+ Zت@0kr,vjAqЫ**l^,:&j igȠz|ibj,6$2R&x@-QjBZ*Ժ, &x*hƎ%ڂA@ƖړҪ nm n.묞DAjvt,+Î.閮.nmApZlƪjћɦ-"ab/!`0A0mfN"B`k^/,'C #x%#-p#@'xB0A@l1A/즚怨‚̀+AJ-Dꀼ΀Bj @-ԙ><.#-lZ^ D`A0D |>zjlɂ A+C++>,1q1ױ1DZ1q Dk01 A|j'qgz,l-PϢ-X%V"$#xB(AAT-(_&^ Pl!3¤N,APfm-31)<@B6#$#xAUmD,# <\)4v'O0X%@\M 1lhBB2#;". |V#/yvzG|G0Yl+{4#$#l1>. ƂG$+{1Mĉj@ڊS5??Ip(RT ab_=…LB.j(T(P?D0h ,Erԩђ6 #`,X`yCMK٠CcCS6Tf$\Ib%P[1uT(4Yieӄ,p˖ MA7YDXPJUӠvOZKKd,]v![[IQ͛GV: %L[63^#l E9zyr1w9tөW~{v۹wnP<\tӵmwNx=bJ4PF.Ɂ5brᄒ[k+@b6Zm%.+ k%ͷ` NjfHQ4 h)}E =ԀE$( R1q8X*,ܒ.AAދ`!:m4Y$&%l1CHWC |X {&B BcxP_w uTH@)DiH&䒋G 6$`` 7_px{h Ɩ@&`|h0[)" n@O>X'^wIО*蠄j衈&]"ML@ `@"P`ipUjej꫰"ij뭸뮼+k&6WuLi<Iy(Z%"iE'=Q Q(}AoIVA[Th(' 7G,Wl*JDXB+)"vR̦tps2@)<0JĊ\jz$]zʴE!,xX` H*\ȰÇ#JH"C 3jȱǏ C0ɓ(S\ɲK.cʜIMwLɳϟ@AR**]ʴi&X!괪իX;.)C)YÊKv 4<]˶۷pʝKݻx˷߿ LÈ+^̸ICJhPǘ3?$cKM2iM"\ L9[װI@aظ(D @/^<УKNسkνó5yDSNϞ˟OϿ(h& 6F(Vhfv ($h|pFpbuxP^\0"u[] (֍!l$bqĂ P@RG6EQ9CRx hlp)tix| &C:$C"&>ԀAe&Pa)ΐB"ljX%X2%@*j @*,`!,xX`H*\ȰÇ#JH"3jȱǏ CIɓ(S\ɲa-cʜI͉r¼ɳϟ@3`H*]zSՐ%GJJ*EL$ կ`Ê0سhӪ]˶۷pʝKݻx˷߿ LÈD"BS~K lQ!C.ai``׈I%ہuw_ݩ} Nȓ+_μУKNֳ<)ӫ_Ͼ˟OϿ(h& 6F(Vhf0(¡rD(YEPq,"!7#a9+b#aiD@hu$C;!`eĂKW)dihlp)tiW&B` g" |Dh`1H*7N$=pI Xx)`9TɨMڀ *W@!,xX`%CA˷DC$ՔK'4 HRD+HJG 4ȱ#E<YhɓGDa0`D˘8ک+IΟxJѣH*]ʴӧPJJի`kׯ2+kٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹rhᙩ# xh,ۅ&7B(1h 8]PM,\D`7jj@|((s+9qUËOӫ_Ͼ˟O!;@2 >Ԁ!P Bҁ+HP("hL "@)0"?!,mPFl H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0c\3/BpAJΟ@] JѣH*]ʴӧP:"@TW\d*)]*AٳhPʡid! ۸x˷߿ NÈ+^̸ǐ#_":`̹ϠCMӨS^=hM6hsL[ܽ N!,mPcx @*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cD(͛8s Isϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸s!ݭ#8@T! _μУKNسQP78 ] COѐa8 A+hu~AQOWeHY`cGcd a%lrct~yձP0(4h8@~G TT<%`+!qX*ć#E|r(&^P@CyWitix|y\" `]:9~bk-*F f馜vޜ*ꨤjꩨꪬ꫰Ʒ*무j뭸+I=(Q.b@c0lQ2x@ɲD-r{Ԋ_1Ђ@jHGQ^G<A nd_w,l'M8,"dI8a 'DT%-@* 5s·lQ@;lintr/vignettes/vim-syntastic-still.gif0000644000176200001440000020060714250050336020043 0ustar liggesusersGIF89a ش֪״ٹժصwcLֹNJueֳֺٻsltoSؒeijӵķ֮yu\F[dkWdh̤vkט۶ȷƩƪǷȨc^cƅkY؋rjifjZ^es]Ut]6{ոjtwƺYbZ[cdͶ㰧g^YrtjÝxv鷈|wɚ祆k̨yרQgllv[iuygWeieiieΉzֹjfd释ŚvhgʶҬxeekZiijwwxv{gkthcZǸԨsV^mȳƵ{ʨr]Jr\}뭳bxlwf}l]H寕eed\^ZmgNUbRΖym]47po\eMͫoYعϑϮOc*ͰrלdXJ)m`KΒN}P &#"tQx!6B`S6ne**1e.kY$+Jf/#jf,i՝$jfhhp! NETSCAPE2.0!, HA8ȰaE*aF n\8Ȅ 9>TI#ʖ'] 2cCcRQN7m$PMjiϚBRHvdrҬNi, IO;ӧ(\kU-βv%lߡZK7j]z wEl(Զ9lf*cnƜcz>-9mhg^ͺ5_զk&ZÝsx'mL0`'8եDPItpm*\ #wb%dFӠ>u K|rY/{q0);3+hdO\qmFac(fI:yt  ,0 ء.χjq:Ih4Bmu~vnZq]-wtW5Zmvh{ɻk߿)_73nH_c*,@%H!t8@:1LYT3Ne<ݭysS `^'?nÛ/d"@LBaORju H  @q@ <ֿouSGDt$`rg1  D]MlQ @%1m|hDܫ_Z`g HD^ BŰڅ1o[F-mDaG,(ştpoV*3yt$CO2uٲq!GARV% AEe]8tdIRw eBd( JUlX3LKBt,QgqΡLz3<:HI-J3g:9eCT3'3~tnBԘEfA PBΚj\$ grE.{&H 2a5 !!Y9p+iN z*IaU*?M2TM"ɾ|ԋj/U9n3WUX{9e8; bMwԞZ5vSPWEJ1OIR_.հMlbX˖]9/0 jsX!a]P@ ^O]֬j[Y~dYni균ͬC7VjSnrC S2'n Z%s|` 71a kB 0  :PHpS`1 8pN4#pG4N` B _>`>5 4qY$9AfLCB T0?|L+qh! @*RPsdi*@i]Fl!~vV2ziηrm,?q33 0ZC~Kء\:NF=h`nHA<9%t 5(za@paH`SX- PbcH^ |%PS2 D h' 2OyttP$pllpG D89"Ɇr:e̚YexsTlDW?@$='nl" Ihc? ;W>W?;x!K$  A  X| uA'J^ (jmm w'e\U6pWpYUM`E&4RQ[fxpvP%WIeoox'fZ=^Rq!. D@bDVӱq Q  HW&7[ o@#P qPj-0Fagj3h HR.`Dh`sٷMa^`j1Ai. :Wz'wHX}G^wԀW/Xes%xP[҉(D!;AB2A1i%#7_8v@"7#0%,7L{¸A7O@j$sN@ .0 # Y ` dA @Pj5@(I@ 5ͨqgzXLP7ppG p(}j``# 8Haֈ"oe]!_UDkX*fEuxIx)J/ɀHeegt(7zxEqS1d1FPtM cQyBNRLns0QZ7`[4ɒ6NqM:PgƉ.9 XÔoYoW1(4$.yA_ΡwWg{`O2VkqM+` npIG8x}yx(zZi%9f\;Y<CEPdYpМ/QTiyIYyPM`u^U!DW)Uf/\x~M)Iȗ `nǞfl:y3c.G _!I#ZZz(`Z;)L ySyyFnsnhٚu\;_73z/r.` yF{ w0u=H#/ЖOTJV7>13:e&%Cw=7JAʑ >P@BSK"f`#"?j0h![:R#:1>pch@Z6jp*P@#ҫ% ŧuZ+ڞ~T٬I'= q>>?S>@؜ Hb$PZ``s0p}>s*}16cFd` b`Ja `C0 8ScP sux,b1jP #` (`Z`f 0\6tj<C0 MdH82Kv  U0 PA $p 0 v Ր ԀPA`P na Yx&{5ezZAy ̊I]U6Vc#-b-ggl.V` (GP,hra3V q flu kkp3H Cp H t`lpc. 8p0j G`j0 up <4;n0 djp PPrp 0 [ 0 u` cpþd  uH0Kl)jSKs}9&oE2 ׊pB 5P_ `u`qէG ? #utNPj@ [A u"~ .gfu 0 )kIpp hlr `( m4#``:0  Ð) 6shk  0` PTz  ap-D좣sJךTY*5jŋQLe ,yɰy JPK."%IƔ~*r az<f2@C~1ls'p(P(@Ӳ dvv9D~+Щ  `4RMv0ֽG  lZ} P4Ґ l[0L p P ] q[> h#p ǐ TVoW Y̬kTnz3|{b*.q?p ))āxL@ö)`N9plہl+s-B N(0 U0}b6ԛ11`9Q 栕 *ra,{+x@ w7p³PU F>̶0}"$ `p ׀ i7 ҧΚ@,ϙ\к8K"%ڑM qn= $33.3.c> ѥjsL^uА,!` . V +0(~߉j {\ & jb`v`#)RѰ nh` {w T+ G +Pim93}ͥ Ҡ 0 !k 7"ʙrBN>6RN򞹬)|-㜙=]V1e| I0+B1ą0'OI"C!Vr1p #O »01rаaR%@nľI9Ź3rJmN_1[(2*Ժ3d`c.9y9Wk80=W`uSmϽPJ/L_A_?MdH%,bLJ1+ɐ$ɓ'#|1@6KVT1(ƏD9ͧ>}2]2%Ֆ;edy)ףEZ@`ʪ=; H`6UzCʊCWS^onTm7[閛06n'CH2Q: ;L$M.H%ƞY+-#JSA @8Pဴ; M?0È!j٠S<*2QT|hM(LF3+ˌRE)%E{i"r̎L)%UR u'$C:T[2YD2hWթmYjj[MuU伍Ip'`eJD  DxMK8XoJaF<@!1ް%@`c%P(@:nd#RvTRI^'TՂVlT\Xr]i~5Dpi.VیCjkMmVsRSln)p)miTog 6CJS,UDc7W(!^ 8N4(E$$ed1Fņ3b(XȐu܂Q ȣ!f8رe)oN=f,}uT_ {9WgY6y ƖuIfSY'd2ͶZO TD}LBp_@E1* u*`G0~V6Uz,J|-[OxS¦i fUH0ٴtWNȖЯ֪&+jjUV<asL/yB[0 \dl食oc1#MV[ CM1I_ ¯׎_.C%ѭફ8;1G eOkA . xYRV!_"P@d!3xRz"ZFk'K:Ȥ/3+3hrf9ʤ/ii&UH{)EuB%TsXp,%d7#E btGkaiZJrᘮ`!s檒6"%*+Iѣ~#=IzʭCV"V39<H\6L+-$>"$a9'Q%ZH@ ->@O(!84 dǠ,9< w2ь֚f W3N7$<Eku˚wvPb.pM,"iz+&cK9,̀ \(<Rw!ϕ Gt~\`& \D.=B|8i`K=zZ-Bšv`#݇ɓ7-|RpžBhPiӒ,2!T:>WI>b-ĺc2H9 B Ī.K6Q0 7AC;J= C˰RD4$r< &j_ʃ_+&9C(lJ#x501PʸVwx@:1؜>hCErPC3CD=$,K2s< >cCDCK48JA21DFd`I]J:C"H!8E$[x"(* ȁ:<@D*H'M(v*}I̴:AKAhFHTk$J;uL>\GcIImD5⸹10ʲ@J"$0!=­Ɛu۫>fIFIɔ7Iܽ)KLAjJdˤflya)"&1^pqLrtԠC泠gvdicVإ%jJ`faNk^ۼi[nkf_.NBP+;09gd^{Xg*›ЁU0?ѝRh;H"h1(Pv5+Ihbm^ 2x%f9H5?w `m0.Uim`-JGVfeeklijvۻֳ6hF6~N`kh.[voo6._ijک jN$HM5 Hhu+hl {nK H ^p"H_/6&wpb9p!(f>VHR&5YVpV ofEp W` _a>U9_epe6p=.<,s%o4T%s6jk4t?j^.fsDnP_hGP_:*qj' #HPPJ( nqW `P^?^Mp8UNJ[6`8J`Mv[5P1P4]U@rH`sA B7GkRW, ott~2Q` B7xA/tSN7u>j>}fd\>ttXUt]pimRu4oLW8Цu>˕CDHc鎋.zAg-`cUʅhYK AOxWsto?eJ/'T5{@ox'y}`_|x{k|7tBK:q9Tvet4-WQ饵w:g .UU÷U UUpS2V{ȏ|}'? _?ys/p7}eO`F|~jGu~_~̇u{@PlA&PPT(t`AŊ P0HF<4p ph$Ɣ is\ *"!..8pBC1"&#T/O\R*C%TcѣIJ4jE)P pɦfѦ] hǏ!Rx1נL Ѕ#ȍ|LbF _:a1[`Fbe\ڡgЍ?+%e s|plu&wC4։94Vi\ts\#vl6`~=/>Tkwh TpPBuibfF!N34T1xUSvLi]w}֡H\ j2¥\5UX)v#])wj'8?ndF>xoPvp 7[yPQBBMd [mQ"L(g֠QakR' 7eqˡ砝YfY#Qy& AtGBH$Zu.hfE,̧^{#w{IKAdշ*|pprzd^~1{I{I@QƯ.:ĐA@2"=w"C9 Zխt>">ųtXE5n bYլun]ֵU=4;֑V_kE6;5!N `tʦ+)L+a>!d46i0CKyʞ@ P5rrpT@B:Ws͵ηNیs+&nu]v/2 H ˄,@$(110XPaR8Pa) @Dh+=k<g=CH h! N+,- 51 8QTć:Sh`mAJ޼7}k;Vn*9l=o $uLff% e / *I Bd * AA7O0GU |@*A Mw1h9kȍ,lT `D9W @˜Sq>V~Ġ*wɰ樬Kk ;ys˵y l*Z氵bKq$Q1: )[ӥrbhPd-o>" Zp&4-*[bv{3ױmz>Yv1{ʎi଑DH WCJz>f#-q{yE 5z=#K9|ݛ:Zn=?J_:wt,{,L OB~dj/Eb_CPn. y+.FY5‘~Į:^uY7o-x_|ogrF+6 mm}~0 2 =|Ra xFoљM]>rk+Ώzn{'G~ߗ/׼.{O 뼫gWdTR A{\xZ2&49 Zq$ ^0 4_Mь}_)U` ǠYY(P\%۫AD]BatԶXF}]Q^N!OEV y^ f^`fZ.$.@O2PZ Q5e5T謡 Z`#"$aU%%!~z.&S'L&j)`ù!u8Ą!(`@%\ JA_lчh,A" A)0,"b$5`U,P7z#*~"!!9b:cиcyA<"~DT-.\ p8֘(dI/B9 @Ou_1y_ j!n*֣=j98&_JBJ~SK#(¤<ʤd(bc$&$):B` #I^H *\ BN@d03 d2 9Fa$Px *Ja8$M="[%=+K❤]%Ӂ#][%A:X@z D_]|"2*ֵ EA/(A7$Jʥ\d]$$$) fj`rbk&jjʦjf;ڦo&se?j g0Pc\(! 4DrGB9A < f;LOM[&k&Ijk'l pZngo'}Ze|g\6`}z]/ EFAEG՜8Cydig8f(J":~h'K(zl§rh^>|"cS;As%#N(f+jpidݍڨ(%µ]`iLndn݊6֨55EJE,ܱK՛g"sKf֘Ԏ$*j>i. ⧊wYz($Z2b(@Aި`N& ѧgꢶizR馲j2kcnh2Rk2\rᎂ\*\%j\`4hE*fj8@C̍x`|\auk6~k[&+.k쫹b2̐ibjk,aaj*Zp|-jܠ 蠄 fR˽$AѡkBl-阪Ғ%hrxBCu,d>Di 4G@m9kO,ޮ+Ԗj2$=.! _>jjxzl@:t[lB&TBɶGa 0%i8ü2,m-i.Fo#" 06_ 6-+2pceJP:C6tp2d,$vA \Y9Zܤip"0NY"*lr/n{^Yf?gLRm8/zl:ü #c [8@ hOd.9`ANUFI0< C0!Z l"<"#CA$Sr X2z#2n,6E2z&w4-zP֩[{Vڰo) p[-JЄ4Ei$‚RT`G܊UBX464^7[ Z(+vrU 5WOHo&ONqJkms,4uPHHETMt]&]MhĵV{e|쥾pkG&vnnM-*s-A:ZH9L.p_s\33ɇg[EX3unGT7EH@ wL4gׇh+c3yO#?Orc1- Nq_Oc+[M7H[ ڴG{7tZYœo-%P(!&x#x71x8",x}A|3y$+ B\, C9b"Le}i#:tiэ GT!68L)A .?AȄ 1[P` ć:5VD e\Է}G}ܫ^~S>o 1EA/ExCG 1L79/+*aȈ%8PiCbЃ=lF0ዬ #Ж) $"JF2J“Zd(p(.B̉%12?? MHd Ghl?e&'LBÕ[9{ 'b!z qJh gt1a5@sW'$SAƄx9T3- UQg!F@t\5BT)vY Gt,Yeg^%{9^q!נ`;CC7AF/1`Ib >nb9X+ TW=BP({"D=I)J`”AxºE f+zleуEҾȡ*+rhEjȁ7z[ⰃSjࢄళ@#8HbCEh@l* ‚,e @d%0#&N!f@ވ Tn"5`'(9e?ȏf/wWw5rOx:ó(W/Z 8RZ%A :bǢ1(\ KB9$!C0 ~ȴrJjTP%JiB @ ,tT2 |Ę䓂IlJHt / "DoH!1_+!)-j$((kqp+8@Pi< B% Bbnt#n: j/4W03bQgпGܢFJ#Gh >A62tS5p&(` D  2\@L l-(FQ;X 'y돀n7; v)<==AyB 0eznAn[`d"Lf[BQ׈9bzL3 FZ1r ʡq?BhAxO\( Fbx N0:&v2H eU&@quшTL]48/r C r rzbRB:/5n҈@e*r#B#dvqyn&e."h/ieYT9^s jF(a,M|@FE1H jOKz5 mg막1 8ݠRC2L_ĒOɴS4()O~yҿA̡.=>+@YR[Dum(0Hpl4,H/Pm:ч͹:χd7<Rmt :z1RWGQWXQ-yWre$N6 !L 2`ANDBh>։@9`zAEQXC<1L6{}'tihb7ARܭQ[nAG8L`%K G  rC߃h2TU8asA]PE)p{~m" ChE4.C ΰR& 2pDAP ?tbôVХ͠&"k^Ϣ1')֫:!%%ld0v %" f1᫖ 1R S@hd_ّ(3d^>tFPUl!Cr-XEbB N 9QR8ف%S$*YbbbӡOӑO3mKo=;R#;};Y bmeJP`B`NS#t} \A(1h $*D歇Db lO < :&hAQb%L VrBDPS5E>L@Wڀ^"N^|SԠ^X,ⶫ踌+zAX2Sp~p $\A^0\b^_Ԡp" 袮@ 6@6$ * x j V#tt~CC-f r `zWF t Xw,,S l) "#14G>,/ n j h g"#! Laf 12*` l! \`@Gq^0xO v@qZ 2  @@q )`!C$MP PwTCxj2&(RrQ$/!Q` X$"СcA"l+A,+M* `)}d!f@-Ec;A`(rA':5 2(>rVq/&Vc ,0;0KF=1b D ؃'DN'dS\n.ehR\P@Wa~3ȁ0]es3\eN:S XUb[:Tֈ8`"L@ 7`Ž M"H4u0:@ۆbĔ<@ILLAu#:a*g)d1342grB>t)L4B10g,0` HTB"0rt+;O7)"$pRA$ER"`$#"T"!I-J"4Jp"t$HGL(sg&ɔv2&C (?4tM*+D`ejH;/E!DcGWGeaBP 4OgP`QT:3C35{B0/1Mtv*yiAM@< Nɑ| IFOEa.+/,hRmF[RaUE;4!U`3@ o_@'r 00oY0Q36!TVDU}qU1__{[=n.\Ԡ[3Y0e!u!Z]Z]Rq_W݅5wՑ ?yvu蒴$wsIe&^1t`7scq5tGgeA4l4ldba/C,d+d1lhIdӂhDtf_jgT}j=nR'&x6fԧ,yrJMtjWC|a=lyhHdgF鑆Si@ փbN^e76o`yU/V?5U _1⇥T!]l%v{Wbyu=Ո[Ey}]iw Ƙ؋ YӅX!'"A8x ߤq,vF7C>6bEF)~E#'zn՗|+}S}A Qs@CM8yg]jWX;hv~YCCMG&=6x+feSygPHT9P`_Q朅)[1o8e!r و!ňyYX! w7ǥZ7t7Sf׍[VR 6Vf_jz`[T& (@'`wT̀^zڨ:: ZzyڨU @ :  cڬڭ bvZ˺ Zڮ: z۱#'!*-?{:N۪q6 .AJZg `Yzzjd@/ z$C}b@/E[%AXZ={۲ !~;[Лǻ%\)[; {)qzE2XhA$\B_Zk^A4` @ ~웩kQ :j:!UUS fdL!!/|ƥ @` r qk {ڼ˚ ` HbzI € *@_~  Рū @A`ѿe~|Z9d Dz.]@ ѥ` sA  MLU} ۯ]]۳}Y``` 0f3|:4t`?g l@f@`0\* ATa R] ̫/ # R`t WfasZL@, ^ױlv >e  `l a >`􀰥  ܭ\3`u@ a a> vZ kxAO%:: @@s oܿeךco_wTcC hA86 c!*!=nZrcH{U} \Z N:?`]`>靻hŽ)*(%K A8@DJ3g*2/ԞQHrt4EbGEL8 K!voB MP~ {Pa H =@XHAP)&%:H[.,d> آN DH__ I.ހ,KiHAv>* (a O ğeojv'gF3϶3P-lZbEXBf"ѰFe'^ x5L>`OT4E)\҆*f4K#3Qg^r AgšRF4bWT@F6rjk+z0 pI5@ 7!aAQAE 4/\ @04u RP/+3c`4`$@.KA]D.A2PF( jE9 V @e&'JJ h8QT,$8xƄJH T E Rs(7 Yvt  ʀV`.f*Cft@D  YKO8 >ngjsIu~"(3ZNɢ!!i]!@10[&H*dFB~{ۂؽ&(7Y q@M@/1 %*)Sf(+!kH >B^V)hCS8[E  #eK>JlC҂ѥ @.%-٬; 0L8;./^7k}sg%1Y]w<nXbb~qY4k'#lU, -Eq,K>,^HᥜHBb\'# AmT(2Cꝛ{{T#{7JoF\] HoC +gN&W|Ecݰڙ/@t=i=7|. pjQژ-޸QkagFij.a (-qo(7)ghν읃=q,iCC}d [l$%@n}Y Jw`4Vȱvq\pw kkYfp-K/`cN'dwm h@#`T6$J2o8b&.HoAw]J8> :\68~' e4ew?J5BFe>fd$bXHxikxm>Do`>(og8ņ>1bM(wr8A2؅:X$`[m[X88Bh#xyi8`opN: >`VxX#x 45XmXƇex#1|5|"! p\p rHfq (+ ބ@.fp G; ~#C'e3dAA x>8` `JsRPy0# I{hi[b]<EU𦅹 /H[1iH S8fه=P0-i@b&HtJI&#Y( 5IiXv#H4202I_iN$:8oT$)#P $fc o ;Ѝ$ 80(ٸ8nid Xz3(8?hf3Mo@ @ s `  xJ p 6`O04;P0b ~g 'h;b)Qc!y` Pb ` "` :aP` h3.*p0My[6l@ oP` HS-k0K I0+`#` `pHP ꂛ@q`CP+P}0/:/0 PP,ZCp#03 >S #ФO |PZP3॰~IG1k@/ڋC:40OPɣ(J:X` !:P$|)Vz:yG@J|  J YI#1j 0b+4685 qZJKe:Hx`pZ Eoi bWT~0ȟ̪[o@G`G Fyzwjp QU8 D`ppPAүjJ8:P 8P j]J8PJJ+p+oPPf}Q?Rp : x@IK ##p 9 ` sq!.g1,g&3 o"`hp%4iPp: ;?Wbדk"*b w0 i\ :p@z-pPEACY$R| C@,5 `t[Ц @U(0~ PuIp .K;oI C{  q  zVkE  ;)Uo˽M`[?[ k[Mgز\d FY"|$ )Tu`R 41 IPĊèۿ l .B \,P Qk Ż4zkdC;5~u  @|`lHQr0i0\`c&0,{p ,/ \#ipJZcj5ະ5bp` d}#4A!݌p 6 ( L0 }ݞK od_q ^hq.@MFb_RP((=R)FN)i )j` Q n >=T߽- jp =5# #G0` 4b( f {k]) ]E 1Ee0uI?pHޣM 2MP|yO,`PMN$G ؖ-$nOKZ5r3po-ܹXQPp@ 9.fnkk@5⦐nZ) 6$>`Lr@CAbF 핰@z\쫔,]m!`P?Nž'nJ[0 v= ? ^^#pNߩ4PvI]~% L׀ }zaC#iRIaW(F P$FŁ \\@.  x0E TAń( Q?! K̤0@LP,%eQEH.Y#;@#/Zbk,s !8јBN}ʶp8ă JhaɚEvGL\2JXF| `(, >nd/:ꂥtA%EwU "4hcMЋD^*hA"C:p9am0_;G 0[z!9N kn15 M`,4b :JP\Hk/3 nˍ愃@`"/Hł )ĭ (̳(2 THȱ,PJ:؎KtF[N K4Z.RFBFF`Q-؋q! C2P .B~[mƚXba4FkpdfajhVE M$WK5$\q8[ [ࡒ*dc(*(&)Vu)bE0[|l:Vn@h[n>p&F0B Z֓񈮝40e*j(Ƃb  RXe @j#p%(iZ.aiPh%I `/h,X)aIyOHa/$K+ú=P8by-B /k>.:jZ8m. PlE!H nշbn(Og/o4ܪ_a (@+.;H/a G $p# C0D5gX؜np!*R@ musA6 3jTzPn8F\8Kx8{ g,kϙ[3jai XjO4<_4swt̜M3[WK 7էWÂ%NWf/;_<vݍg<3Po'"nILl #K-ldzHAWy:[-u$:Bse9'dMnBއ6\ʓ@!lN~F0KL: Ԥ]uM..ҞJF.m60K3< VL̒ S rĻ=)T Hv4b/5'pt((e׵ 4@%`JX!U8ڤHA>kK)o-~4;j$a`:lS0#8) I͈8 ^dmJQ}*-C6}N_4 _W5Oo ?>[Pdިpx;%z tIG{aDv=xw._wHVЭa+@(uҨhڀ8苑*0df?[ !V 0YjCڵn+ 6L2CP$`B?(k:(!3G SP 8P:+)(긴G.PQ{#S 0)|8#7B7 I )YC7vzPC'Լ=8:̤2vD5< Q3D({?HqBH\ L)h9*"B˅C@/V4Zƫ5 laxjhjHklpFl[1} PY0MڈbTCVXH 8p78.dDŽPSP2; H 00ЁYByL@)(XcH@Z(:SPMcZZ \$$Idp/8 X~ba .R@JI%H2)ʗ|`KyBe0I2 )Z8Q8J>H?3!>X%pH|@9%JX0ROJH>XKSȈ\I\ChM &Hd<5X)YJ|ˑ=H1 PPJK#X@#8/8.٤ɒ#i%$L˵.JPP|,3Oo˹8]$U? iH сDd̆kxlȆTHl [f `nF#$pcL3R=P3Ⱥ2 Ёҋ + _4Ҹrt;PӐHQ HRPO݈>PV=(Q&@c1?Ё 1.h.x 2.pI, 0?UL2|(UmGP%W>@eZ["Z>mUXR1&ŹhM9d 0J`Ѭ3UtHiXWPk(@P*;F1m$j/<P]j1!^VUHĂ p;)= p\@ h^o'&HCB 6N=P ! %PI HVI)L`@,PXh˩\ʰZ ЂTA:)ٝE0b7р6PP($8P)3j L+)pJ@d:Up X0gx%nP1:R1џ0P:%H NW9#Ё\hy}d P}5%XbAPH?Sy(6P 5Y%E00l.@ H`.6R}ӢD0ʚ$PPK6$iE-EBjS)c{>7Ы Me(T m8ނ@ oo`>&(Xx0Ҋ 0q|?Yi4ȥ(/0GS8H,P@yG1u qАSCCU96hV2Ȃ R5p8 axvL8X (8>[8/`FVX?O@ p|\,hT NK 2 336FdqaLp%qT:p!PL@,,50M6=kNa*;n,nIM5HH%!>B`W|Z UQum$kbe T8*OVxPhkNPFWHq6=< b4{QL훵 Fp;=hG) k. 8gkx[>`:eȗ}yk8-ؤ~3e 3b rp2=HzLj92=Kvؙ=@ +` 1HS V?`;5`cPU#bIsr !b iyU$ pKf !ac8+l -y5?t .H[q?0cL џ $ًN̍$x*``t u+OuKj ˸uވ~`2NxuSjYЁ8L?{_c8>J OȂW(F;?5{0!xv|iͅȏ?8 c!4xܰ- `ı[(܀n_M;KHW/sal@r=ݗ~އPPU™ 7X H\ڈJK0(@4tUnK_DT{)(HgؠaA.P4b(p 8FZU 8qBɨ!PU'C#`Ą kEG 4J Vr 24xU>]X (?Hz /l ++ Pe[[(^4+Z7muJHP(@&5!2-0$pt!>Mqɨ|9lԹ3?$g@.S@tQE`e )47xR{ŷTD D`Ձ Z@ K)qA'_g] TX1XI+ hb P%,0 0%V"eI4WBh`EF U"Х *tI%SyepA $0`b%S`UNPgTvDhu.%"@ XrGGZdXYpg&8fm8"t))$Yګ,E^4mz"f5@q j"y,`m @ۣ̒whlyXFȸ,&mZDEZ2ȴf% Ɲhq5xDk *nH"H$;I(J/PF-KK<.@GMK'9Jz=ԞIfwX engwgvk ))izp8/^v\a*\rN^  Z R+&%W(:/]nʨOzg2Qwn<m̲>.V F7 7AGh^f|ּ T.,&=7yK!74vy[! ['1 r!԰S7$F<A+>y}; .IMkދ-u ^5&¬  -R:@)(VĪoUw."4|X5*s+BBT)9ɞV+gR9 }b:ڽU@j@j+\;w Yi |ū4)1hR;vt_!Xf4פ`D'FD⫕g;];-a)ZSh< I99،@ \"@- M*nJDB 73( L\ s]Ғ*]>)qB$Joe?_ w">(Z`Xk hmCD[Ehaŭ\1pk8k;PD4@pj٨@2( 4ڋtj҄E v=' qN(n,uɆZLB,H5ERjP(5A\ָ3܌iTe0RexBP`5;HA'@bhT .! 5 IGOB8@"HAp kKX{` (x5p`@.06 )T ֎îp`mX hўVQGBTzKh (@ p" l@F=a 3Vp hZX>j&BsHSPNǑ`'@%琕&QY끙D%#6I^F(^TY(NhHJ%TRdBV@5 Dڀ42Tn|0h3X$(KJ$t "N% ARj !(.@A)@F`U6`pYB#YЈ,% i< aTBm^'p]f |@XAEi) tvAbAX@fc&$dA FS\%D i|&dhjp~&- *A]8cx(AT8"ug<'A~"&* 0|W}|`AH(.~^@e82h;bhh5vDzuȂ膆>RʂEJ.A X/p T+X3\5Dd42@ 3_Qe*@B]P_@$ZR!imd  ژiR ib )^ȵ \ԥ0p ur` A) gznl ܍(*8an c#*PgܝJͰn]&  c Ĕ> bn)/]/8AD^Ncrn`8^b#,oU\W+" ! +T2+Dcal .:JA%E/..o\*nR/үڂnɊ,Ӑl3P{$ Eř(@\WROW@NOW%@q/4eK_/r ԢSB0NԀ Z @A)Ђ@z(A+@P@N0IJxp  8@NN w* jT9r@M| '0s (DxupB9rps +T3 ztK<eA(G3w2@xd`4?&0-dij-삮^ .1G!d:Ekd@P-#O45_Fu3W',-AH5w|$GG5icܰu(ˎlcWVX3u%Hf/c1RU  vIy!IzUwWR-ݒP RVF89 T:=c0X5ٕ8_ v0#OVtOLXg $V!A RkeU(LLwˬkLu6k LL̰0ʦwؑiuk{5|? AvNђ6zYvUa R)vz=Iן]O@ ONi{~50}w@BV-fcPO(UmS~C8K88{?9Y__yfyQ(.4(E l4l䚳yojɟ.-a7yHJI9y9hkZkzg{Wt/:~룒uhK)zkixC7GA[yMp(/"<`ت X׋B 8[@[d[[%a :e@\E#@A"P,TÐFi$ c. m `vRHdFt A Vie XrZ*#5?)!\3!`,(@@ټk(ZcB:4ŷ<^@+rF` dShKHV%Zl#q\ N@P7[ s)EjqH z-${PF ` 4.hA"p'Qb(2T.'N~_;KˀieY1ff5;p   @+iD! VCl І6R,jМEgC $@[jn0 @t(0 xF/'vP04p*Ptap:/\sh T .@=hn8AG`\7~1(8ɿaG P`!`bB~\ԠԺtePpԘM{BЖPTXjS e,/ @ǦuD/Cb:(Ѯ "A>hZM.P; ExfW8|Sm.'*[Ój0k7@pƀXЂ0 n@ s.) lJyY=0 G8qԡzEj4h)T4rk24Ŵu|*=a ɤ eu_L2`2d+/]diǞ2@fێ_` hhvXcAba4V26~c˘9J@ 5@4 P8`h@/PV)^"  v3R< h(TLk(X~rb3(LjYB( Cp]:;y>m| `n!F`C| P@%Xх+lXxVAXR JS >"ԁ'C d>Al0! ɝ5QfRk^P` "c(9A'DbBD|,.pOe9*8u%rsD U+sHN]")xdS!'+9fi͈GL@`T3a(Bx0=(/1ZB 1r"(b \bP@7mUE 8`_+yDyJP.Z >V?JB ni\ jB|$j @`LXd}Ԁ XYj\4V !,``$ >bJ hJ\ 0&&(a^ }%26<(d0n_^ b $@[X 6e+ -@&XMj VNP`U8A \a`kj f ZL-K%@x `@ <@ %@"B*G Z q%ڠCr n%$ | n%n@K PY1gb,@\ Q,`T ,l%,"$DfdL B %Z ,9q; @& `OblA!A!2``n| ^G ~.g~ V@8`dȤd&%$W :j +x(+X rnJp`qL%}0ps:qb $p$ܔH j l2$̲ ha N" F:XA HFaHX200`J-):B߆00a`h!B@ @S42P3@tVB r2]2` 7i5_ &-}n` 6t3 9`8F@ Zh;@R`$( /E]`<8 @ 2m2lF6Y2Y(EB !X0m> la ?i-.Fpn?[A$]d-+̾qkn*.@ XԪg.},cx 6/)%QBw J}LJQt$<, *, ;I "+_'ƴNNL@OM?J '9tPLiOQ :$+,*|MMT!;1! RMT 4& M[S./U?cFPTэL"h TO RTM}"$@ҋ+HvU"u%>W[.)5Xt$''U 0Y1JG'ejYIbu[d5VBΑQKJV,P ozS'"Vq[Bfb#0jNcFo; d/O8gB6wcWds#R7e]xeb5,/|p)2,'v4/RyeSGbG8!wvTEF3!ڠ45̘ (,ǪtI6zƺ6҂tS8K8csUF1Z(8,xq avuWX7pwu4@Cs6u,4y7p@^6"7Z8QvkxQv1@TQQ9`Lw_" ,G!`LxN!nYM8ZGRB :  y-R@_"+Nt@VbEYVLAZ 9ڱx6|VXn]X 6szs1dKWoWL7y$f(9 PaJ@)B A "/,2 'M'D,ȘZ_9t[! v b Ѥ *bIU j ه !i"$" RZR" Gp-b(6JgucTƭsB *vł@Uyv  n_뱵WK NO&p%1Ysi `+`2 1#2"#6 *C!4L `l?^ @ 7 7jLUa;:Ne RD@& VfMa%|jaf,tdMAjf<%$%^VPXdBK} J,T,W H d C(>N@wU>B@Wyq *%:6A2r!N< &/`\F E e44)J@Aaa!AOs)Lpq $\`$ȼ@ ``|`'^2ºsvDviޠl¤ T $" tg *a.z d{QJ lA Ȩh2TG az@1k|c~?Mt{離!~[`G F0۠v`L 3!`@`w,aX@ P h۵]`}3z/ګ;/؋Yeogp: f`Z" a霔 l9} @oߴ}*W6,Ò 0M R8#C!88(p xH(HYǓ>!;-PU#Q( HQsK.%B}F`-K+`Jf{Y„ݲq#`1C`.`6 ؍Ǡ/)lqbC(P 9F.*4,p/^zJ\p"9!E`-"x+8fK,Jxʃ8%jpYE.P!j$(R 2` cs/rB4_t7|7#" Nфو yI$"36sc$P <, PU$axt1r/pSCjp \P0KK;bxSĔzME=N)Jkp'=p$ℜ"aXXF5Qj 0P1 6vT :VU4U+Y8K4UfhH&E?.!Ka9_%PTˆ0NrDP( Xv^B֬Xu)A -p-> TX Rb&)֠4|hE` 8B `Vaɭ Z! L ! XJ: $7',][AlK [((FP hA!XnB\YkP,0 %d  }Y@ܬ@/q>tb ` @\RlaC ֋>mVouP-@][- Ubqe/7I[۴$ gs#E=ozʷsT5y*&lyz }H~w_s@oyNmZy2PG-i75ٟi8{vjhφyGy 8A{xl7GEw#An5wIERn ؁l y"hmP|ڷ>wp:(qG7|8wp|ا~B{WO owHg"D(m"WodqmiT zQA5z<;wԷS^7'lb_7;vt؇lWjW(#q<z؆|(&a@(( ݐ ϐ~{v&w(8)*喊uNJu ( -yx!hW'\iGu6(g'LlSX~#&TȄΓ(/qoyA7{nS}WH~طI8pyuXm5U&(weGb78%mvl6 9'qoBF(Ȁy3hy f@,-Hx/R`0s>:=sj%>VЅE8<2o#&]:`x'VX(9=C`c(gkXhܓ&{_)—trwx&LV}T#'tdא|~hsC%'vV+XlFrnJyI7YIb$PpN]C {W ٗF Q}&T\8R4 pNvjS `'u~c! mqC 8qqhȔ>Cp e{('ꋡYuُ& i(v&ը&.$a :@h a) Q  Ԡxq [E m ِ*6+g ( &xP 9PG  ?^Byl!B0CI*` pP2a;D `!ev@,ڲ+⩦PI9gjCpOp H0b]Ы8F6PQp+"@Oz#`~0/2|LiS3`k]$013F0 Ժ]!d# eګq խ{ ~P U (>+?d"[V P+@S 87 k0 ]Py8% ( \1 V+1A@"n7jF$ N/GچIJYSG&PʏRuT Gu+8y+&m)),+ cp @p R aee2P^0gX4 -Py) MԂ )Dd3p WF{ pXPCCDN.DgQR`$[H/ %fd @!`? V }I@6$rH FPX~w0|7[ s@QN \ H+ NZ@ {1Br `cbMw5( RA0R`!` H< M1@C@71\YiPkpP} Q0ȕ`X`Ǐwzl  CP\9KAƓfC8h|Ӥ{i itYZS<˨~a?0?0&)P sj@+cpAaqR»C#eB>B!K'AP`?MZ fq.?ao0l@8; tCYN;u$&` xQ ;T$5w%!- A A:< 1}'L "5$}ư53 =1E}pbB"xh 3SJ @q$)pj9E04opԱ6)PK\L8 GL׀q|1߂"qȹ;SSB /a"uƄzr~˱ TX:GˌaŦyo@8( yz 2 \ G]1 P+[``Pp f|)^'#z3R?Y .aj) -az\?)kX#v1>3I#RP2i|R4bz#1Ek@Fa!0N00^|K:5RX6!S| Ɓ8  k M2<'3e sD40ִjp 27#āQ0H|s~IHD s >|#v6!J0xկ4z mݯ)wQn ( 8;c/8`-C#50)0FAoEK` - HN %N\@H0/rM@ Q ojL0FIPLoD-` P0X E}DJ[m`t %#` .p +QMC6F 5boP <@W t /HEp2[` q/LCo7p(oQqћF.P] a|P 0GO8cpRC 9c>ooM.ntO̚懼,$H@ &,PaC R̸HXؐ&Z50( &J zfH32 @aCYaBT !GE^aa<*Ͱd SPbp"QR0PBHUH?|cgZPɉY|Ä.Jx( j('OnI5[X=·9\=~d\EE ss+}dH кpg4%!8yA VLAqx5~܈c;e `xkEN@aE \۬3ZXA.PZ; ̖:;ùHV JlV!3d ?OÎFc AD]?YX P/pQA(2<% > 3+t9>sOA56AMt>SSBR<;}t2B*"J"x؎h% ǀNm5+2B@J0]Uc[m()a],juՌZUp2\K,1WlhC6 -Du„!5l]}wRy U2A S%dW鵃2 !s8!Rlcl;g?BNVĨSQC}tQæOKӁ"7L ;A{DhRU}H`Mz~qxz @c%ʍ!o܀ϟŜ[q / "`f׍:9wi ](CЅ&t\hC-IMy8LɊvfL+~_.dšiʤ_Ki07}NP)h2ң0UxKRT@mK R0"N,8 AkΫLv~s)KD7-QX!p9 ћH1TQvR0+Ny8fLO & p]cV} Ԁ:&D>㥾J {BJ#STl+I~zm%k;ʔyܥ? rt`-Aܾ U_T T>R*U(UUJQ-D쎬6@*0l Z0 Y`@)`pZU<;)|3"f l\ N+_`@k[E*4F2hZXZQsjpaQB]ɬ٦V!4Q[Ay'Ow(@Za=4G g-ڭM1jK򾙼nkYbח& fQ@ypUC>`%lPԸ5Y͈P `#P,)bEj?C(X?0L #\bnES *RPX P8Dr#8>! b ApЛŁ{vգ 3&| ױܠrxE@EJ,`tK`bN 5F0J(aߦ9@?1ZXa S _Pwd-Pl# 0D<` ע[oOCJH񙻂2w=H9;u3T{7~_^2dȆ_Lc4X/?p aXBY%Aׁe' g9(h8x950958Hx$W [a)@T0 9# ,X@|P-(V иFЃ"`{atȸCP@;F8pP T9PhL)+7H@.+H4CR0 `B:>Gd. \+퀜+2b3*^;24AC<;3p^G@#x= 7S`bHX9*@9JԲ% ɻ99IԸ'H(h+H&A $1AZ-$Nh`"6P؁z;!JJ8J# АȀU)-0JJ0$+PAzϴPͥ$d\"T?+3EP!W򩤚pB(K耽T@xX{*p! Ȅoo`w4(X485`//؋slӖ|8xOg9B؋PHHP{RwJHB 0dC9` K]B"5%JT0.%˚cO+mϽ [8+ hIrQ%к5(9`.SH2Ȃ%R`S.UH#?>@$$݄"]Rp(S܉7DF!̻$J18XPHĈ9{RN[3BE|.3FA#EjE` 2eUfZ2nr2jyCk.P xk[T؀dPJu:hǕ$1"> K5daS )3?$X<=|UA> {T6Kdyx IP3"aQ1<( {t*eK苜OmHЁ8VH0AB50Z`: s ؁:.(S6tLLȎZU@84b&xPYJ6H8P-ȂW 7HL -ɈS8Z-@e7 \ h*]ݢ#R8&Ј+p]1/!)u<FYŎ:VE\żsMJc VNc5N;A+2t᪏(rl@ >hFgpO !GȦ` (p+)Q+K ;H ɾ 7x _p ÐĀ0,#!Zj13\e %FR~}j礭_hZ(]i<..p*lȦ *[,e cT|2{VEm_|i*V]$M $g [-ՔAºu hS,M6'dU2J!>CREFY䔌SLg-W./{G&O1N8ʕwN?9< Q~,yAUvb?oŢ ȭMf|e@^8~W6 ת0 ;Z-2JCIH00`v"Ȱ K'ypΈ0hj2T/-]Kh xwJ ;!Egו".hzUQI/(u)4 H;aFu_PzmoJ`$2SϩF53Bb800;:#2k#к)@8١Q #?c? !P ~ 88iX7!8xP߿& 6`0%N>L)aA53bg DX" #Q , `d͚O5SX|pK o&?>yZI%DM>@xU!&r -A`bŠ><3Js|3$: B-}p(k@CڶDmjq.}ƴ^Ph&фP*$ CtAqyfrh˃ pRo "jZ0¬K0-+.P3k0.ԲRr@ p]9v37G]r7BY3W]yeG8wEGxEx$ވ ؟46 ,L6Dq4*Si@7hLL⊿DԐ 9*@APtXb|ppE1`+r`A"CZl᥇C1 4#P#U_O 6Po F *h+:"Y0Тӥ Z, P&#!5(zP @ . jJ }X2.bCEP@(H H )x8Hnm1[ Xg/ߤ Rp 50B7N[XH7PC0Fn` ([!(v 6 WXr԰&*!x%10)!!*d3/@"%iF4&PfQ r3Xd^&J NUM UFS~dg1T<ڝoC%э64pnu"G\.=i{ڳ+h Tp$X#D^ uuh'l7Ѩ 0(" /GPEWHuc$]b pf R!IO*1 H @@Lj9 ?DFvVPpOh@'=CZHA`" N07!It`v*d|` sE.A`Cd\6 % `XVXX^ 6^S@v*2L$*!͡`+B%]DM s1Op :@h 4ѭN 紉p59- /|2&;QւD 8|XЅԨ^qK~&&0 B+~T HhA[,#0."}}Q5ms&;﹡7z4Σw~G͓X=Y~#"(GHrXF!^_XuH3>l ʰ0T:ܐp DoST^!3x1D/_RPzhp`ѳp]ǔ~*-ဗjtF>%Z@\`ۄ'@b W". a s $i6j)U-L Ӗp>tj0zX:A},0& DLxSHA$<h'@KZ j`|N B%~C tv !oՂ7H@`:4 79 ʠI]7+-|\E.P6[p{~kYY04}lGm-(x;9+;-B d~!kBN kWKGJn iPхDJA3zA:\R?( `HDYC,h D4C6dCh@55Y,<3P!lH]`  p@*\\9]zTB@%@P@ @Ϲ /PM %[tX)hW@@ Bp'!2B@ǿl )  A+X@ d $]su,@-!@#!F% 2'A: A ! <^'lH g!,:%-@02!)tTBi"UFwD a+Ѐ(t'vx:1c6A04-d4 h1&c8ct@2w_sA*32j.T+J$" @40)BsA' Dd 3BpA"f=A}mTxuDyTETMtGvtߡvU_dXw%륟tt eM ࣹGN@^@_:_$X&_bP`4NH@t[U?NsPy%C!xufheIh?D'rfM@%٦m%fBDM iBn^qgq>h~is"<Bئu@ZfwrЀxn:$xڦi#L]&-ިnYN`d.sqXraDof )V"vVjmj2跢kT |r8vԠker`Р^*qgtENrNb-%~(+xgߺk颅Rhijh/TR쾂kݪ~S+>([G6wPߘL()5!"*lW2R[z1B_bT.uVԻmFm,dž_lDy XAgaLƯۯެ$ܥ:+a. . IHHAЭRnyf-*ӶmTzlfM"RZiDpEyoR*Z+R.,/DJmyPSotF(oYNl %Ԯ䐓NmP)T]\)ɀH(@XΉ\/ځ Mjp.+,che%9e # b숎ʀ B\%cKnEm] 囂/r+^՚_Jlj4B1C.wίLoܽqiJ&Z/?..&:(q2F-^a~@"\Ad5455TC60rœx23Pp(IsA *+ 0));s L3sH3ı ́@ `*utJJh]cf`@deD'Xzy `Es] M\I&c X4@epG[EGF|Bdt ۙFw1ƊTor+/֚)әVO+2V+pVSkE5ǚǒ's-(#D欲N"[XYz[rN!NTA Bl@06hZPZH8BZ3LZ*ԕtܥ;o[63 PavTV3eeC Aiwӆ%(.i:Y\Wg/@W'8YО& ЗJs#3qg5ƹ&Imm*Q('؁4ʱxuRUmm+[G.Z0dW_;9W,צDյ] =P[x&<(,B,\3L-PUk04/[q[4Anhfsvk3.=c@]E)U`4%`o+(B_dw6+&$;t@@lcoёmaBnac|] @-#{xBto|e}Y$뀃Xq_G}~8xZnYju󾻧gλF1Kxk 9kz]^tZ(=E Z*`CZ̚5ȗOq@&o\ z_%AA!1%K 7ē(Y a' hU0B@`B:4& *DPHL(z`#^V @Βd XD >` t &aLEu-0`8 S?>&lY$5(A[@dNh 2sJ;M2܀ /c74,v8KⰅk)c$y=!C-o `u Nrg @ NK&8<"'5 A Fn .Kbo6 we^gy:Ncg3C)ܲKkC2̂@ďPZ(h$lGp!--1 @ ;`6Ē<6v-we]afXm5=}?;{av3^V[JoM6ٓK]xLu׷M#CN`fgbF_$3usG@x⊃V^c9h_D 1gW  0jm$d;q. 8 b jH*ol#]ő4Ot3 ^YA$ױ0z{~; Nfx)ŒǺ\\=H_!ƼB|ǭ-*&Ae%:9#ψ G?~?)b~&)PiPW 1άi=i s#[ '}ᩕS!g^ȘP%hAFM sAŴ930kXa )7x ah5K26˓Wisu0W0uRi_ܩp9MfG&/7sme'li"H.E7Qv!P<2:DGL{GȬURP$hRگi<$Ť`1PǤ<^?%lK,9Х.4hIM*UYt|-;ɮzsU]jYJEDu\!/iĦRIlR{Pnd:Tab%^+JwӟMa1KੇP"XZ, DkPH@ⶶmnQ9$r[]m>Uns , 7 Bt>᝭xZUozuxk;׾} p|+`w/yk5Vhwk! b¿UDtR 1pZV?:qE2qkE@+o\Xi*JP 'ŵu$, /DA@ @S@^Ռf7qf8xsg?ρtZ'q!p~>3,@A tx +Wn,8]XxÛOԁPW @V "ynGNA-q:aյ-lcX@ص4#0V른s b_ h PX!4] 83,;t^p|WKpQt;zHaܑW[ k#Vނx:85y8pso=3ⶨ@EezCuD8>x-p!3HL3B PB ` hk #Ѓ<ƌX9ra!@I Iy 0$opNқO b\W<<*nAZ`= ?9 X!-xVY kå =ظ@^6 'f-*ڮ/ P[1`ܷæe1\駁!>>sҠٸ+k=k12Q w+@|:W󀮼ڀ$Ʉ1"*<%,0~8F pv4&~GY+t&GHyI'Iܜ܄4GuJyGtGIIkKITLtJyJJs4KwtKtHH4LtILJtNqt?oM`3W+&bRAAw ; a"SL!0 ͷśJVGrD OoC˖p/+oO WrB S7qIpcp)wqq!o᪅*w owr6sJru_Ju tU3.tCCJ7^脢)S+JV)ˇ]I>Ø 1 R`Ye. n(lad W87ttu|ؗx17r~;7}#+rʭZw~s~!o~8Dv ^W vXp X~g`tk} 6)1y,|)ov\0pL-xp j MeJ!P@fJ` Oe*a `d` 6nM` P@X&l])H~WBf錢w`F%gvXmȍe*0Ü56vGynQWGF(w+e`Y}NxG'yDžݩ'm6^>A^ %.@ r.#+Z)a7`F4,m@#gOR8Jh^ ً|XH$Ǎ(ȒkYYǕ8 yَȝuɜ%ٔȏٗ` 4Z,@ 4ɂ  @`#jϙ_` eTF`] 'ڞ+ړ酜xcǢI\^v9)Z全Ȩ959̙zeZ &zY < ai>G! 0 .]z • ^ݛH5>G%aGUםgÙa9:r  /y]ߏ=> 鯞s=^>߳h߁ؿ`.^gyљ>秝O~Z|`dOS]۸^1A[K^~yV->^ m^#^z3#d b1yQo#_;>Ѝ}Ӽm~S>ҝa>^EIUQaT_Ї ;lintr/vignettes/editors.Rmd0000644000176200001440000001324114457657444015554 0ustar liggesusers--- title: "Editor setup" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Editor setup} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) in_pkgdown <- identical(Sys.getenv("IN_PKGDOWN"), "true") maybe_still <- function(url) { if (in_pkgdown) { url } else { gsub("\\.gif$", "-still.gif", url) } } ``` ```{r, echo = FALSE, results = 'asis'} if (!in_pkgdown) { cat( "Note: This vignette is best viewed [online](https://lintr.r-lib.org/articles/editors.html),", "where we can render full animations of editor flows.\n" ) } ``` ## RStudio lintr lints are automatically displayed in the RStudio Markers pane (RStudio versions \> v0.99.206). ![RStudio Example](rstudio.png "Rstudio Example") In order to show the "Markers" pane in RStudio: Menu "Tools" -\> "Global Options...", a window with title "Options" will pop up. In that window: click "Code" on the left; click "Diagnostics" tab; check "Show diagnostics for R". To lint a source file `test.R` type in the Console `lintr::lint("test.R")` and look at the result in the "Markers" pane. This package also includes two addins for linting the current source and package. To bind the addin to a keyboard shortcut navigate to Tools \> addins \> Browse Addins \> Keyboard Shortcuts. It's recommended to use Alt+Shift+L for linting the current source lint and Ctrl+Shift+Alt+L to code the package. These are easy to remember as you are Alt+Shift+L(int) ;) ## Emacs lintr has [built-in integration](http://www.flycheck.org/en/latest/languages.html#r) with [flycheck](https://github.com/flycheck/flycheck) versions greater than `0.23`. ![Emacs Example](`r maybe_still("emacs.gif")` "Emacs Example") ### Installation lintr is fully integrated into flycheck when using [ESS](http://ess.r-project.org/). See the installation documentation for those packages for more information. ### Configuration You can also configure what linters are used. e.g. using a different line length cutoff. - `M-x customize-option` -\> `flycheck-lintr-linters` -\> `linters_with_defaults(line_length_linter(120))` ## Vim - syntastic lintr can be integrated with [syntastic](https://github.com/vim-syntastic/syntastic) for on-the-fly linting. ![Vim Example](`r maybe_still("vim-syntastic.gif")` "Vim Example") ### Installation Put the file [syntastic/lintr.vim](https://raw.githubusercontent.com/r-lib/lintr/v2.0.1/inst/syntastic/lintr.vim) in `syntastic/syntax_checkers/r`. If you are using [pathogen](https://github.com/tpope/vim-pathogen) this directory is `~/.vim/bundles/syntastic/syntax_checkers/r`. You will also need to add the following lines to your `.vimrc`. ``` vim let g:syntastic_enable_r_lintr_checker = 1 let g:syntastic_r_checkers = ['lintr'] ``` ### Configuration You can also configure what linters are used. e.g. using a different line length cutoff. ``` vim let g:syntastic_r_lintr_linters = "linters_with_defaults(line_length_linter(120))" ``` ## Vim - ALE lintr can be integrated with [ALE](https://github.com/dense-analysis/ale) for on the fly linting. ### Installation lintr is integrated with ALE and requires no additional installation. ### Configuration You can configure what linters are used, e.g. using a different line length cutoff. ``` vim let g:ale_r_lintr_options = "linters_with_defaults(line_length_linter(120))" ``` You can also configure whether `lint` or `lint_package` is used. Set to 1 for `lint_package` and 0 (default) for `lint`. ``` vim let g:ale_r_lintr_lint_package = 1 ``` See `:h ale_r_lintr` for more information. Note that configuration through `.lintr` files are not supported. There is a work around that can be used to read the contents of a `.lintr` file in the root of the working directory. This would allow the use of configuration through `.lintr` files. ``` vim if filereadable(".lintr") let g:ale_r_lintr_options = join(readfile('.lintr')) endif ``` ## Sublime Text 3 lintr can be integrated with [Sublime Linter](https://github.com/SublimeLinter/SublimeLinter) for on-the-fly linting. ![Sublime Example](`r maybe_still("sublime.gif")` "Sublime Example") ### Installation Simply install `sublimeLinter-contrib-lintr` using [Package Control](https://packagecontrol.io/). For more information see [Sublime Linter Docs](http://sublimelinter.readthedocs.io/en/latest/installation.html#installing-via-pc) ### Configuration You can also configure what linters are used. e.g. disabling the assignment linter and using a different line length cutoff. In the SublimeLinter User Settings ```json { "linters": { "lintr": { "linters": "linters_with_defaults(assignment_linter = NULL, line_length_linter(120))" } } } ``` ## Atom lintr can be integrated with [Linter](https://github.com/steelbrain/linter) for on the fly linting. ![Atom Example](atom.png "Atom Example") ### Installation Simply install `linter-lintr` from within Atom or on the command line with: ``` bash apm install linter-lintr ``` For more information and bug reports see [Atom linter-lintr](https://github.com/AtomLinter/linter-lintr). ## Visual Studio Code In Visual Studio Code, [vscode-R](https://github.com/REditorSupport/vscode-R#r-extension-for-visual-studio-code) presents the lintr diagnostics from [languageserver](https://github.com/REditorSupport/languageserver). ![VS Code Example](vscode.png "VS Code Example") ### Installation Installing `languageserver` package in R and `vscode-R` extension in VS Code will enable lintr in VS Code by default or run the following command lines: ``` bash Rscript -e 'install.packages("languageserver")' code --install-extension reditorsupport.r ``` lintr/vignettes/sublime-still.gif0000644000176200001440000027320014250050336016670 0ustar liggesusersGIF89alrwLLLQNQPPPMLQLPMIGHhlugvsleʲRMKCCCWgxzyxѴFJKC<:KQWxSJEgxι|svXjVQLztkKJFXKF{h669{퐍zyh|Ľ{v|{اv966w::CHB;V[fõ39C>B=:9:486.7==4-:9669;859B52¿a^c;DOC5+ǹZWWEKXhb[VdqxihhVMVe[V:5,qqrveWRNdYH9nUIVh̅vhTE/+.qYJȽ...E=CCLg(&)G<7R<5ĵmƺ;GZ46C18RSn~¿xfN̾'#"59J:QhǮzӻ&&&Ҽ:KeHYxT]zеcK7BNt.7C+:L)8JH7'hR=)9V$7]:PsT<'ObžZF-J4(ꤤb/Vqj( /Jݞz.p=wʘBKogyw'i"! NETSCAPE2.0!,Mxb@<*\ȰÇ#J0!E'2jcǏ CI"O\2e˗0cʜK8sygϋ x C*Uz`ӥ>:U7P41#\7ZD،5~{6㊷& 7.\qՋBrNېp^ H4Ǝ"\4خhr,ϠiyEf^ͺװc˞Mgm۸s ˻iYYN3iheȏoּmj,[wË]2xߢ^2?{Pfݖj 8` 2`BR(fᆺavև5%qʍ"\umw\3hx:]أDEd999ߐS)Gf\v`)dihlv ngqfrWVYusuy矧 6ggXw:h>:ڝJ)\i¦t *:j~J-ꪭj뭸@:f̑z*`6F+Ԇ!:砧ugr*yʧ*;頕nˮBiX, o|p/ Gܰ;<1B q #D 8B&[&,`^-Fr[B.9 nE/z dO觯,rY$`8`d.w^!'E )UPDlVvz "nz $79 1NeKF3g _ID I1M#-s DI QI@O}P$zr.pPmЀ!*bv@uiP:TF$N0i(DJi= x\*r@&DT <JSD3^0MuX/H=j$T:eO}0ZG3Qf=iF4bPl~! `Æ <:xsaA`# a+oMc͂7BQ;WKdKJ@XYՖh33l@aP)0P `` & 7<v 5z{jb7{vv=:O&cq= |tMnjXhrFǚid͝+V!Wj5a90Hx)^5OL+/ 7mKvVՀe"2z7<9 Ew6fңu=ߧ=g<ۮpi sxqyKV5; l-[)a7{8)`I,"F~*}O:G1ژ2\;K3:4ˣb3 "vȂ Jx\A=e<x u< s(WO~6GU2cww7KcGZ|(Av,i.4<$qx[)fa `]@W g p Uqw9X:zm3zNg/PYoGs7DCAM{Ji5QB c|GsMC,.ڔl&8~}fICUsP~n %K=ǃxb;XP/@ iDeUGU8Lx#?Ww 8T/`rqqĊXl ]y lrtQh{PK;-gziZ(|x/3\xبXPLx8BӤX|GՅ#wr$أ`X=Xg䄄16EhnfU/ķGxO8?CP֏CfQx䨐"ByאYGc5JBKHDo8lٖRɃ v2'}nvyxz|yRUҗI ){y ~Y 3PivRi ]2@BDxEfBчPn9i[ٙə"ӛ })5a=OŚG4FGT r0ꙗy,ɞy-ٚi=i5|E ^$)QM mh$dzٟ|: *BK_aޡ$!bxKZHjoN;rqQ*9q񩟪+qʩJ, v1䑩BѣժB|#q؊zJyA摮ګ^zjºʬA1%1#Ԫ;$u!:+ yyC$E2EJI,Ho ,4[6{8:<۳>@B;D 3#yG[#K;7{;YTKV{X{TZ{[VH@f[,fekp #Nxڲ}΀b!n#0@jks+s۸k[+ ˹rkj˹+;[뺧K{;+[ë۹, gkж +#[Hq+kPc[+>`k)|; ,4<LF\`, |L(!"2L<'2 1>|7; ><5|C8\)CLlT(R&9\$lZS \aW jL53>zr얪jt:z|Ǘ5~<Ȅ,|ȅȊlȈȎȏɌ,ɔ\ɖ<ɗɘɝɠkJ]%n)mL-΢=!r2 ΰ\G7~3}};ಸP6 I'֠ ڀ 2 ^*`d.fhgn^j={ΣML-M,m]lpJN5.( ְS>fV~ꨞꪾ>^~븞뺾>sMW7p70` ]γΈ./m,߈N5;Ϡ إ`S Q^۷ W:S, ?_ "?$_ b81` e2bp ~}P,ӉN}I߰ ;0T J϶&aSPTqWY `]dr?t_vxz|~?_vuQ ͠ ʠ@+@0G ؾ߱R6|!G C?)@.SϬ7Dk|S@֟ڿ?_߿_oG!xKAY |P!ÄN$X"D 6hņGIӈ D@ҢA ,UibȐ?n6Aѣt1CxV0+^0زgYUZqR W7g% rY0὇ :5mrlL,D4z.6lxPiөOF]zԭ[zkتkvm;6طy৏/9iCWs⹉fvwҲ{~z糣~vc-^ừ#oSϾz돺AkPA[=:`x%d@cfRU:&yI\DkTalkJc (a "2+d*ޚK )1 k2K0Ƹ 2 l3B*'6ajsO>O@-tPB 5PDUtQFuQH#TI)RL3tSM;T2BZ,hjKb C JՉK6 "+fl'srJĸ/IJ@,K0 󶰻(+UᠱǴM8db P-._ &ρ LWυn8b>)3`Xc'1Fc9aG6Ud?mYay3޹3 aJ@VxWYJj5jȒvV׫Ǫ6J}.. [[1nɅݭ^7㼃`^Z KݲG iZɮqʞKs2Wz4+4=32|WrKr7}KGs-?߿os@~nb˛LHa1`k@VjIKF5P ^0Ё.ؖ۲ԁ?@VuɅ sduf67|m} @$`'VqpZbE."/ O%(qBo< Iҕ!I- ۴'©-F/0@ ^lJݺ^֦Ap2hV "f&J"vEec4Y%𕰴,SUҲ=3#-sɱ4B lL OXdz lxf RKSdnCraOLW{Yr0 GW"K]̕%<)O^>SfO欟S0p\, 4k~TGf1˚kXLxx#H+T*{|(Aw8}ڴ NOz>]\MzTOJ ~aǫmO59y-wlFh@k[aiNOln4%?~3';'яnwg2ilxfx& }P\ -e=K)닱;825k)bKȚ5n8 (y=lIcSKX@p;Ț{ 8 #$@3{ @k> 4;H;X SH󵚳9/ma;=ki KS˛S<;'ZC{CSlۣ@Ӯ ?|1/lفg0.x)iCCk:8ΨЎER4ROdS|OWLEXdEYEZYE\^DEE^E_$F`EcFc,emT"XT>R%VEMReUZ}NUPUQuTB]%UJUm:USSm78M,>T<ӘS֩ie+`S)J>68sUTu5QS9S9A-nRTpU[-XdUS];u_Vsd5Y5c%ՉUIeTԇ Y]&]XZ}Wk`EF^7abܦeWu[[]=] J?8 cKWMÝ]4Sq M]c@%U8V9>}U;&%E= vW.S\YXvEeXQf[&STedVfa7=IufuecbNtfSfg=Ko&K]lm\n[Ywng36cQ^gfA{DBgjfgay[6A6mEh ,e}dDK>jhe}~a懎U>]Ό֗xTgJiĪs6hl}Se>fiT|ᛎx.`s>ޓ.蔞~EӘ$1fvhjRN耎gv-`F=jt^v炎}ݭ^cF]Fj9M޾~kcejjNIdnA.D!E2hltҕ1& X>mNm^mnm~m؎mٞmڮm۾mmmmmnn.n6= I5&.lnC*mnoo.o>oNo^ono~oooooooo?Ӌn [=n :nfnp pppqq/qҬ&WpGd6 / Gk2&2 7q!r"/r#?r$Or%_rFd d;r1o A qAi!&os7s8s9s:o'C*rUxqkPwq37pke:t6 6(tOtPuQsq6hV`( @tPe>\s6;` h6X6F4u7gG3(r5`FevlJ'U߾>_i1 1(1d'H6td[ωGv]ofg?`o9(vVLbJH&@7N'98x;.@SBw}+ u _Fh[4ߚf2`E6xzタ o@.xks30z7rnv?wo/0 s 8FGy׺ n=HDy_x((3;V{҄L|7Hv5 ] x7%x:4HπWd}g|@7|/h}VW?40{%|7.|} }~ORnwz_Wk7usDHGN,X@(lP"WF$#Ȍrh+0*IBcʔ& ,PB(H)L,8?HabDF_L 01!tѰE `#F8@&'XZa0b2OV 8 2f-0; (sƋ21&1%LKs0t.d[RVN SujAV‡/n8ʗ3oy"R00Åٷo` V;ֳ_ߣ `>~@*CMTQHz$afɁII'+p)AFE nGO`FR)0Ѡo)[5E jMSgEI*`KZXp1bhőFHK YHo %l1#MILdQ2SQ ghTSQC D!LhvFUYK3-JAK\%sz):*F7]u wA{ė@ :~ Z RQAFGnpDIx xe5ٗDS1cN& %G6d8"H}a/_`!) h,")$f>ŷ)GPkj}K\ h`ox}^ܒ)!Y3qA LpU1i 84L $q:5U[}5VJu);+ܼx_Z6xD; D pSr,k6f,Ђ4mAx-eNƑRqdDBs l w0Qj B $Id<߰X`! 0ie%mZ52J0K,NU-vE0AmPxv A?ZݒFӔᑲ&R5eo \wĶ@=0m42(1`57 ʁwPpAp\`P Y 72:G\`r5@FЏ<r3(]0f0!cEW,N Σ'W t4Y`.+#}ULH,`= fBI:4R 1-9A~TJboG{( yU|G5M ڲK[r'"FL (vBDA6XFl/)`> uaDzoXGu2b2*Rh>f4@pC>Et'# Ѐ2bf Wh`v,yc=l%– J/Ig?it}8 f ),*ԡU9+˰!u;x+Iu |rLf "Mi@wQB=Lt؉Hw$롴ai;*ē66@eĢ򶷾-Q'Z@K%yJ⇪%@5dvp!9C6#kh`ys9|t(KQ#ڵ'f&j.I<}JXs >x+2P X@rXȍ>0y A.N<𖕀%pώO$jmh.ѡ 0#& HvF.`p}>Q.5ofD\hȌ<"i4Cd|q@ 'qETv{D]#0D^Wܓ C\teu6 =xrل,6o5+ʤkP(zL b#"͹stJ P)RJ / wc8(5S6L]4ms[TIUq'S0<`Aǖ\v٠+> QB\4ҋק#p%{X@惑+g܌ )7~ ,<:8ޕ^Ad\5>@:$2dZڽ,刼鋝i|ݾ:ֳnoMe 6{oβ߲*ܯ}y,";EyC ߌu>9A :u7ǦBcif9i/z|Fĺ9, ~7Xf uNfص z؞uTl?>8hy|A~lֳcY꒏@;T\&k̢tg-+rsQTBMrpIB`C_][v$DJdsD$EEb\At AU# W>*H6A@?Gc.X$sqE.GFdq$OOpr,*6x<#>6>5^6 @ <7"v#xh PbXVdYY2QQeH*e$1@zT{!"T W~GDtC%Oab&fX,&cQ*+$4d,cx:Ra 09 hgi aaR a & 䗧m@5%lGt'rDTGdy"%  bG_hXʛԃO-gXX T%GdȂL8C"5GHJhFh" A'e⦇.h ('dr'KƜ)8@B88 )d@ ƼA PM` &Cb bP5aHX-*ftȣee撖^`yar.a<4BAZ, 0X${ Z<A)8<*T*V+FLXK(vk)vVdUqRysm09#H@iL@8)@ L,^ESk#@΀IBrQl.n&CA섛N`@@,CB8t/m)Ȃ:D -k 񒃃B!@-Q9А-ZmG].HúߗM&)zBrJnF`."d^Fl <&N'F )%.j&l-8mjiZ/׆2o8$ke`ArRml+B l–$DP1/ខb,n,)0D[w:΂!qNB&/Thp(mpm#ETBԂb/L$?@)0Z3`tZ~O ܲJ3.q*R~gf.l03l,q& t109h6ǂnF8l'A㢮o0 r(0+`mVp#Q3H2\(@1)#ʦq=jC&'qҧD2(4n@z0Ei8H0 ^ X1 ܀&Z `&ANXs&.^4H4T0 MAQo[gV=p¬) D2ut@ YVFvd6\4oDj-rP pV#E`^qF5t.ς4S+G47&csTostl (Ǝ'QplH/kva^Vckl+ %jrY?&ct<*􀴵[!*{۰^D L%SfOF[ԀG(Zݢb%dN#Y mDYQVl`|p~ зb ɼɍM8-nn?&Zwo;U\FpB[Uzt&|D t-Y1F,i `ܳIyDSL6O8WxZ2\seN@J%CĿ,7% 0Z&& 0ދ9dZK[GR/\ *6哯G{ L@@ XG-g7nb wwSJJ! *puBzcz:tzo5Gyߦ9'l0HANy!9^A~:dz+Ifyy9AL]^0%_߾jW{O\qa+j:[sްBJ"J!d7,OFE< vcw{9ގ g@t@$޼|f'uJ;So{=[5Եt dWr(N>)g|7 Sg;_|++<>FwU`̏҃Jw}F|}x,1:+;{RڒCE!M=ν}=}+>}7>c~G>O>7o>w>臾>韾ߧ#x@}`Z+=2¶}Uu%o~DAK7 (0;?GW?_wg?S?s??˿??D Q>T`D( .X@(E aÎ50dʑ%N#O N2gmK4-<sqK|"j+!z+.2&dr@Ȯ2l PBT^s;.QG[D9wSо40)dRۓ3M<S>E }48/dS7O3P3E# B+FZKEdX5[v$m Ȭ,t:$^܊wJ`*.'Qa+vcUCm}-8^Q wa ˕M:s,DNR oM`Ը}OYGB xoZ/Ds SZog 7qˍT_3d5MeTEz0{Ҙ3]k:1+` @,sUwݻJow'-/ 0`qϽ,_hW6̥ͬ5 s2gp3]NpQyNa wK"aX;'L Ґh5e@%X"nRAԋ^0ǾjUe:{mp gKWXIԕGM~@ĻV@`:4ns  }K \j@ h T´+1KXF$q`!(A| *@m}~˝t /cY_%y0=mN Ф+$HY19!P)pL 8eL '`s T*mk,nPU|QC(:"%)IE~hdz JDm@AX0= RY)Ul%@^7U>f @Z0 i\` buaujJ0x 52BT@>xA`xR (*"#! gJ}`c:xPmhB^RrDHA:E 0~ Hv_VCҶt1 3X-y~d' B>uIKQNK}Dث-msYB',jV[5fLZ:5=8,-d[@(axB0;3dq+c؇)`fD@*k ذ:bfU \<+DiT-&ٞb~RqdWUDM3׼6k;-ZQ TC23KMWA\xi5?^[җmR/PC*:W-m փr.U`̥׼,_ Li58HLXĶМJ:*1yAjT,x!t"2| p.! 6f0'p`Agg@&$P2Ў4 9 2PT@! `rAr[,YM^rg@[ Z|7̓6 hl;)0B dʀoHj=,H%k7taB_ܼ, QƲY:콘gn\7Zь̋l@3^ʒ(O ofwSsX/jG0 Oxp| `$i3O(l󗘂=g0p3 < D80q݀ `Sh /1@S^@KD Ȁ Ȁ/P:8D Pp*qhL1" @@ j@QR!ё"_r % + +>/ R۠Ȥ pQ z2 F @@k( "2-N vrDd2!$q|`21mRR'^02G.(101k0` Cr<$]SE R(גQ|2.28k`@ thhrs =/ +? < 3ڱJk N`,S| 9 QEm`FST *G} 8q`2C~3qPt%* 0T}HTH.`\c@uC#Ws0 5(ϑU@2FM~!-_ $9 rPP+˯XUp0B%/^F7u=)cBehəxq?,QtJ?uV v46V24O1eE7 7,Q#C!j!8Ffp|@S3hӏ6zQ318R#7 /[5aNp@/1Vv 0olujt Amu Е!Pӌ8>V?jxp8 Z͢Xv  . p}@$Bbsh;{ /jv W[WE9P<u2%wm5D$p븓m!9}C zMdV1U9 j#2 3%Q`9b}Wq!~S7 gO')ՀRrIQ1~%i2a`Z|q$`s{h$0 mGN@r55rZ/w "@ 0˃y`*70e4,acˊ1gk:: #zHN{|ڇӢ-``Ƨ6 :b.F\ł\<4B9&[&ZO14AyJ@#M'Tv{O ڝppaSKWut^7Oc @cqXMbnDVI~ĹyⳢpC,}"&qؐ1c Df,hHѦNj"BmqÅ,]0a F$JTPDʥ)~Hh 4DDE74* 2Pi$~2&8aBT _|TBE4#rJ… 5Ge"!rx +Wx""E 81l$2D7LRȒ kxzNOluW1EӘ$!|7$/.ˏ5"Q*B4cEQR[:\L!Ш)JDQK\SHmq# G0 aeY5QDPD 94y6,vXcDy9(V +t< g}I!>aa*PR %sp&dLq#Mȅ@xq|r}Q0c(̀ c"aSMR8UGD &LRkM<Tt0|W04`I 5^Y\F~l)rBgGx7FX*GC|@]>hx{ETMaCxvD"Q{(cD _"cD5E ʚLq іe|>`gq%ׇcRpbUF"p5x rO}a^ P'7_6L JLp '^4Z >iAaQ}E\9H69FKAU()t6AŌ384 iO% *'*TPh"1PK-> pt8C0M>|vBdm )`[R, 2ք>u?!&ETʒ%\geˋMҴ$lfp -?ˏ ew)R\W/~ 9L~+^P2SoCs-Ƞ&58B B `"wNO] fA V:%d /3Ke`zg(Zt;d+XH搀ҝOd /}ij4%1@5mT7e#X? H &0@*u9@LjH/wh^RtX)Ozq=9i"bH PrJ F@J(ajBlIN|NEB@)`A&ӫB5%3Г\A2w)|ꏦVškfsT#jQAor)]1ns~% ?Qa >]2' TCfF`x DVʎ2 O- O@GdPyD)A:X  Em* `h pf.[<:௰*S@=i cEC*R"dtI*z[9%k|X*G0 2+. a"wy&d!> k‹?m0\# `a/%pKL_;_€x1C[+2D!iu$~ejq} L!ϷW=e%W橝m6Բ}.l5fntBY^35\a0-y z /w+S3ԫ &j!bs~~ Z)X(~ h]C:JeՀ /(P`A&N[< ~m}Ȟ0wt1e2ܚ6w}Kt/+/=loS8T( ^@0H uqpX;E6]i}xƸݤE)qtޯg^ku<* 79Nkӟ&ȧKb5ez`,8Knkvp0ofw~]e  c =.LeΰI^rEǜe=m=fsn/қV蓏=B $/c3&5܏>\/?2ߑ!*}£w}~ rw~a)F6$m"`'ym`xQw)w!O-?Wvh}97py1e[H{;"v^$gGyYnlz4@d(}tN0W]2xju^ ^xXrXotX}vn}\xe/hp1x38|qd؂` Á"s8=fU8'XO%>4h6neu6o1t Gum(zux8-yx!?"9&hKNxq1{Hvwi.GQH/40 Xuׇ$kXnʈ1{T25wo5Ww8Yy.㸊' *9o^AF腷G lf4xmH*h?iX[!yj؅F֌X[6}}M8RT)WLJu{-2 @] "uIiXsBf'f8Ari'@,bdmsHHz/XeM%{EƎQGxD(Q o)VI-Qvg]AĈ9dkƖy5YCxh1Ny)Қ7d[shz:ip~ࢗ./w/A;NP/D]2bA,VhIhd@ZLɞ>Fə` ~niq5/:1:3ڜ8)rIpXr{B|yًc9I@crHJ L4{`F¡zt pZ^tɗb/ ʧJj兗:ef, )騵ŨԢd<)yyT֓my*\#gGWi&:kʕz->Z㩫$*r Md(tbnp晢Zhaə*HZX/ AЧ| b5/@؈'x0FJV z[ Xz]8py k|W : [ٰ6ZkZhZ܇!  E;G:1ۦǭ:W@~x7h/ڮ|j]>JI/󒨊kY4 ؗ% <+ 7[_+|:踲+☱-CE{ީ"JҶ)G|Pح;()۪JG2gFJ ^~(BXyvIk3\J]jwzPY+bܕ/p'IHy[uɦ)I;xdwM+|^{˶2Sh!⫿䋇gvzʇ x_9{N;KNS:KWyEeA!"ָ\9Df䚼悼Owzjk.v%VM4LYDҽٻ[Z|A̢Nsa;6tH曶Q8&XlS\Ubtr۹Ȃ);k|-.)`uŴPȮ*IDzXR= jwZ/0ۮP iu HүST8zX vɌ r6L,'+ ɢ{(rh;Ƶ Ț̙|J)gl\ ʍ,ʏ kll,(Ix{Jq:,hnlxg) IP4I|/W}o ( m]k%Բ{1˹z&ϼG0 ;+(iF9;]Lp)g^\sƘ}H  5Cuxb/d?l љ݅h{ckz7)cM.oֻ֑ߔOADP" ,RrE*2d!qbƇDȐǁA`I"Ębˇ|yKB)kJDgK_0# @M p TU^eV ~VlXc֬Y Y:~ I3}~.܆4#A+D4ydA5;(bń-!D-bs1ɼ#?|'A(cB8ڦʣBeh|S2,Y<8ӠQ)WLvy\=8ۢ+ raeL~ү$gyՍ?*;v˄WJN!h.H s ? BQs$d= l9Z:$n+<;/8&S=ɹq:R*z+-ʪj-1"31ӌ@Zc1_X> 0/ @$M(ї  =T8NL/ŐVDμsі)%7% so #X3jQ\\#X"!KkJ=0{\IJAA h`I+2c)J7k">H@ O|=;e (1Pl̐̓Ab}H`"B(%lo=^|nxHy;앭T<ݱwÌVLxaʸljN+LI"TW-N&m"M t߬Nl: wBU<5Oў՛J}zɂd-4|:)Q"|K";VF-3 eDj̓^QKȦnxڹ @K :݂SFm9xMheZ߼=?CS5kDJQ󢇛y6ʸU.TMvVuʪ!FՕx]^6o? QqaLI11bda-PiX4;!Y$,CnTO#4$5jF33fSF=P`i@a(:Xe4xASZ&:rdtXyLʵg+hXipi\y 7=h{? &1H lDzPQǽPqvFst.8cN&umuk@\Ɗ;ĥJW: E#5~'% "ŘUQ]~L#Zx!B.⾫Ddӝ =-gVlL69c@(\T/qbm{"2;E9]3gO)AJPNDp=~gh7 u&@=ZylE2&]8ueA6T7s N3W^»{382'P$\>y}߭=*GىLӽ]T:ྫM|7'F{'Bau9c QrS[t6a>?,4_Vp[M3=_l-6ڷN*H9[ND(oDez_˿d MhϺVYxS7%u 1L/(GO%jzn)e߹=V7۽>QJksDu/|R#&ߌAR3O\?cJ+>_G=J&{]Xֳjh9nAObO|0_&;փ9I{;ً9i >k1 ;&r CC6"{#@k;@K&:((0e+2344@AKS7۫? 6qA647. t .ꊳI  Ѐ#03yb35UaBrB'4 ڣS1 TİrDD@DLS:뺷×;5gDM:2+,^&,s,>?xCؘ3.ҧ ,[hĹ rDNj<Ī@@Ȯ3K$8TTEBԃEj&e_>0Q勔ð0#Z=4,@II; çGJT%V W3nFcơ;;4Ɲ,Fƨ<;쉈DĦ г|A<<$Js4:|kʼlD4;{u+D;KFtFT7@M+7hRH% ),)MzK{LKDs;3DƖzLԄL9D İh 2*{KZ( 6q,h5EUeu %[+ϊ< LOH uL!pʬL89k:#˅"R;$C0%=΁`#}'m-)e&%*R#Չ 8R#08I1m2 3++1ES065mS8}9Su3H=TJeBJGEMmMTPOUIT=5$PU˫ZQK I1@ ` 3`(` b58Vdd=f]Vg}VeVhjV)(VlVkmVom҄qpWsEtVhevuwxyzV{|}~vCU P!KĄ}[eXX_ #"X؉ +S! a(\YKٺ9Y坛X%%UPY_|Y#YeّYӊMZȎ7aIY_P 'k:)Uڐ҈ڪ7aZ빎؝1ڷ S*`ڨZ[؇GrUW]цE\D8[ې#Zu۾]B-ە ܫ\5۹\\w# \׭\ɭ[[ݿ{#ܨZɍ`[\Jᕒ-ާ٭m^e![hݍ/߭^ߐM}] x]Ōe\)Wu%`X\^ P}ZdH^ u~2G+YE^e]R^])u`&*_E]9~`MZZM$a]!ہ*bb!vEaab`*8 P] lMc%)ߕ0;&v2c0c5X>8baN3(4ZdNHVIx9; .VNed!~ ^6!+.G6dC.[#`dcek"Wb g"`ualfxk5f6fgw69;vFc`B`KbV6( XZ%;8fN#Ufi/H6gf_&#@ !7[ PVbR5Ju2^$Ze%ha#f JF_"g& QXX>aH&Xi-1 : (1꣮`\h4Ul璁w]-X< /)Hp.B_FB)`>X#>vhַi"Hh ۰.HI`ViY˦kLav!\E@emm&a飐Fjj͵jnQk6T\l֓Hk4h n">n^Nn\k? t^-)l,`'QugY8387@yO,Єl6F6GHK؂ǽΖ@PBm .fvfZ/q| wr(JHpTA 腆^0Hh[/KV4 `qp.gIoU\NK& Uå5 CSdžT$`5,tq_s=Q6i7H`bR@b_Z3(3h%(v3,68v*np3R/x`v,ngftq? pWcjp zb7sswyrEPo2EXku uw,@jw>f=@q X,vmw*Xbw$ L0w>wGyYxO~GMe~T~qpW.(Pygڊw8J.?y?7vzv8rpgP\7bz38dGִGvPlvvŎ wLnOW)U 1@cQ\RUp6V{& ErlA_K (2, _$ol@B<8 |8~q~F\L(؄kx(YxN5 1S`ĉ 5IyA͇ lT%4hyPE`")LB.8 se yc H32e@xѦ)9w 5ѡggOD["WR?Gď~X!T04 ֆX"Ki0afm鵫B~ث~PP.1KX2)ih@EרX2H;H/>LniKW"?s&/t"8}׬^&ۉ[4{ejm I~ q`h`fIAp3^ T I!H6LBB RM4@^ hDA28@D4STf@0K.i$N&ɤSRYePZɀZv_4lY  0 0lpsI@ $J`H38SUc$0FmL`%\MGOAYH`I4e)P>EERpa] L$ DܚBV0aC,IE"38(EV}t\tkjT4 2xC 0|J$pd >@k TTl`fo`/^\tm;PvmRX! 1Cv\3̄)װ# ʅgJ!dR3$$ nT&o ~h|iv>KĮG@7ŧ)*2Q2A ͉@ó"PXiNQwc,K&0 XTYu= ܋.K74֌ `l`&gqwU`oFO1C1ɷ~EF-F PLo C^'PiOev[gMԾ) =<.ݍGCb/*[B$*=P$00nYbT%U萂4i-Til/sY>`",! hm)0EyC ,l;Z3 HzfŐkRF !+BF9 FI(1 h@9B ZГAYM#-4aC-rDb!Bq DC@EBд&:P*(wC+i%pԴ8@ uK!/RTp8!I`,eBM:l+jA*+WiЃ]X BN&RQy(Gף` x@@;([R&uy{WRH /D@r 8m2Y [$j>hVr")}QY*d4w/=LO4u1> (# p%䰡h ԖdЄxV~R$f p` :jI&z"07ܮR, kg-lcgSӯJN+p[<0ZIT֋ (@Ѕ$ VcI<KZ뎆EPP- zJ\f:--(  Ơޡ1p Q2+b{kDV'jNva#(:R1}(a`%L`mP A1;!.HvJ'{D-HsѪ =0*Յf0C⊳afԷd"& ohAS`# OԢ΁}NjLm}[4@WNNi 6I/|w"lXq:K1WC|e4XcxLNEF3bɀTV?S˔kC^vIf!_,F!bg;jsꑬUBk=XR?|`QZcQZOROm}$v+N@V\)x"uZ0+b&f" >ȀhW8:!\դtG"p[ėH paCuE?!QOHJSCiYVM֤Qp뜍MfL i0əľmP0ȋD SM=R&Aop]eB dZVpG RM$ /a4 JCH8 K@sJQQWGqHEDP@簝QT0μ)B 鄿@OSDB/^"=NtK^՜Z``q]N+-y 0 @I9mChuV z :ymh¥}ۉSVۦ_]X_⩊%ٱdN9jѰfd-;z/Fp-+C EwCA M HM#C!]*QAlqlʬxngW2H"w #oUb'  eW <60pY t@l,њ $ɝ 9h%0B 3aWhuo#@a@! oA lT541Dd@T-JU;ovB PbS3dnb6q S/GWmw^5pmU5 @ AkO[D&vQ^u.D"'6uei\jSo^HWk#wThZAbI7u V3hGE"0-vuX Afuvusre_Tu|@m8&. /bSyk\ywPW5f@-ʑ&}ZK/KUL秓'O(XJ,dH6C 9q?w\Ԫ^aRsBODpz9WW9ۺfpO$uy9 6ue<ȆG5kTGaG:zW5Hϟ|3F-DVWDfDn:ޢOPhcBf'S?qnF{#C c%z/$!7K;|ޯdzL0JW Ïcߙ_o ,B,;W;b0“0>s:ͺG{û.'%'a ޔ;秩<|^:+sP蜑Mi#,>+QVRJxm8&bTnF0ʋ;3$N.6N^֓0լ8rbe)1&<mlZRlBtژ ]hܑ|{ZpnURVt0#2<)xA J<3 k5Roi}5in$a6[!56E1$Ǟ&؝ 0p?`BXK`oӤ,,XGQs%sCO 0ۉ_ѩ6Mtmq}W6cfvҵ..zT])Іwk *^Ғ@Y PBM$ěQܝ8*ǧD`ƹ۝ȡ;,HJK -1̨ qm]0y:)OF A$3D Vl/ݣg-*3v";!g$=%$E?UٗO_=#}?=Ok^'f';OtFG ԩ0*6)4n \̮X`РPp"$a{:fAP! |Po/ob&TONB$ a"+6N lC  )D0tPC$z 7F 7&UÈ0 $p "  =kLcZ 0 iC@+ڄb۶`  )j`m+1f)T`H CBt@  P@hoi0VP>u3 8ab`o# $x ( D&qБIBV P$HV13bIQQ gP"cOw!  = P CG$PN#+Rp"+"QA2 [RSroo'rڔ+rP)6+4-fqfY(t ` @ `  ` 04``f ~ !"/ R@/ej-9!.7 ,w-Q h/WQ$3N`/#-%` 2~@.7 Z $ &s Բ2#P@j2{B5Y2E%q0'M9Q:C7>,;?,>2y:$%Lg / Ó#)2"&# 1"+1 nb$>/K"uBb(;(EM.`&)&lZq j't'Ъ F 0`3 |4GaAt650IPGWB0EAR@ @ An$QJ @6@J ~ΔQ<Ĕ0KaLvQ "I TPKP6R-@#8@ST8t±TSA.<œ:U1 T[2ܳ=aN/Sж+@X!.dU>i5=ׇoE=BE1} * Ap1*n*"`h`C&jEaTd_VA @6t`@ A"!@N 1 R Y1vK'pQ nvTPMqX 9H=Y`b` G8:~ S0AR 29YZeUl72X[mSD ;1>9ٖX`Um5ZKVfo'ulWF!1WǖoT W7mVp1 mnq&!+$'[JI^B-1@-͍& Tj͸_͑_u@ae`8- T "v BS5V` P tKKR+U`IiAkq&$&vp6 t lC G7@jU dd[ 2ATal7_VgUSRYMUu73YﰆsUCuY]aY'vq'nk>:INp5l P2A ZlBPrsՃDtu'bvqg^682NN !~z!83B6|I6k=rt #P8wAFZ U)a9fa1C2A-Q͒<d[WkCÔ-Ox2bTs :oZnS}xW8Y;R#y#S:S{Ync8ftX&@Yy?_؟Yy'-)Ō]R3!gEj \t < 9!j h ci@JAb%aWZw9wd1k n`Rw}VMOYwZA ޠz{{"|~ ] 8T ښޚSX9AW"xauWOu!bfnK2>p//3yA'[%)QqAۡCys_["+%{1\X'Fpu;*uEH0 wGVO`UDaHs v`k/a pY(΀| d7=rV -QY1Z X6;@Qái$]Ƌܝ\Qgɇ7sK(mŢt',xt%'Vy@uڐ 4S `Aāay,OGxG n* |gĔ9 c9R0x6zķ Mn}M<זڶ$F qMO3ٷkb})%M=sVFU]c<.ٛ=ړW![ќ|'++!ΩQS ` x@  R-۠3)Su  f@ .7`3 l6 Bb r qc Buu \8 q7b \u p<̝>m0 b5r3|ٽ<љU ډ>UCڵBB> 8t*pg5(`N zء @!^TQW GֳVkɃ>3{Y0]EF!Z!Ӟaď>?+ d( $BEX$A .^ F?~ )ɓ(Ofɠ˗^ʜI%8iz P9X"B)CyHJ\Á # +5zԥ =!D:!TW .qŠA"}EJUVm4D;``KO?hx[t rE^8tՂWj]MR ҶMqa`P{S5wvmŸWC=4¯yw;;7[v-|9} gBkfF#tI%a҆@N1$L7(bG`FsTBL1Y8PbATXr!(P^r Q5tUǝl1X8nv\I^]N`M闙lf\Hfe>9'g7Dl9՛{]&gAQVZ9"TeTQpر"p֧]*iBJy鍈hc%Ä+mQO&iXa6!N!(bdaJ? TC 욑9JYN*v(ceوl%v&j2.Y.Yie y %y ʗXPg8On}*^.թi}.iw<}Wߖzޡ\3v/f63`3YsVBaQG\;EצFc;k5Q[-Njځ=,2)2׻Yjw_|;Tv j0C\tߋ'yzx?Xޗ+B*MM+yK s{N#]2ab~v~ϭJrr *o_-R̖ f~Km@>i/8仃ɕ}O/XJu8tu0{]/t -r)+pEPIP#׭F >$> -'oka U&eҫa<Bwk /6 ˖:a+|B/&rMa (ۺa7n>l(' ma?'lv)E) *Bre)Aj B2,c";kZ$BNFXErPlS$5B@ `\rҍJ%x-: !ڷ>Ėr$(I ڬ0l>pWHvZy\Kf4jx>xk|Or$哬Vȉըb IT0 St N|i)-*39V I񨧍!$+wiQo1O"4i>HsG}J PKA͕B /EJ@85QVB͖iܔ7i¶s"_iXZ.gEhZ8%/RHah3ܑ- =8|8)T&+/2vRHu*j"j#Ⱥf[Mu3,?7ޮNeeZV$%, Me!(tz- NP%dj{JĀ5bE" 6X=uJ1DFbqHQTc<^ޯaU֬^)bT&iS٫<#vC@ڒYH҈0gL8αw@o\!׸FN%#PAa*h͒ȭ[oW' we)p^ԢPXEkavF 0baN} b jH;.3R}+xOkSٸtSuc2׀ %>w]SUh"b^f$nQ|]@90g`bUk؅k{!wJY`]TsX`&^~E:i؇||C],Ihp:BE37U57jȖxw!vsPq(yQi0fHs3Hw/e w#"\x@00F_E | GFW#W@`dYJ0@ i8s B\5u79I÷X׍wn<gI?+YׇuOc55`U9 Ȑ[hy.vy(EJ gNZR| 0`yIu'/bД.qkx(" 04@GqqYUv`8U)~n+2@)CH9Br v9S`zrm5*Y,iTS!{E{u=` @A'YZ02 w4YuDq9[WANPtė"Xױ}mS6260R9-U*1Ei`8)fiOl@S|/yX+ 򩈸Ȉli2IX[xdHE$ɂDP/ЖH`Eh``H' zF xց=ge:^94 r&Xp$BBِiɲ)#Bo{Q *Qp f  ЉQVk)^(hx &| ɧf`i]pt f*qE D@rp)i৛BojڧzZ Ox{Zܒ >0L}ک9ZV戎HTq:bZШgҚ*z&hc P*ЫskYJ*jQ E k},ě@̖`XD`^E|`<@YQ*Cpؤh8qܗE"qѥ%a*-O~b2PkUJ0ƺ157dе0H#m`Hd&@p hpIPki Y\۵qV ~0q`j^f_ )jp j#'I~@^&c Ko#yd` z04y#dIp _˻4m]{qp)04@oP$\qd0ziCP{(lPI 0+xs p 16p.(k ; iKdF*k2 3!86`C02&ۋѸ4h`Ҹھ)iNog14P>[H-Ȳ39X[ZL3!CaeQpx>4PxS H |/V&Kn00)|\HRp5d>ʙ0A]psЉ, )48P̕-4t$`p&cW] ̵L+|g G݌K!];PL 1˵\"rG0 *˕kY0nj1K25@ q6 CӨ ̕m2 G uνͻP^lEm9̵~ 0qT}4F6/` .mz7iLu^ l9S@ɩ9H5xS\>P kKF]}jYG`3\O{" ̾?HAy ŨP" `f] OR ۘt:_ *`$;@_^8?m ^JO yތ 08 q`CaLcp|' ݖ jz˖@j<" ~U\H f-qux ['R><X@|5wEK.ʁD 'a U><:|V5`G) ?a@)@5`'iP6t:Sz@ف0omːin=y{ p}Pr0aAN^y+'zPHI"q8 XqKEQ{V}X]\}i i#] `-p֥S0ok(y~3.^a>T'^54Kf$@p&;z/MEX@;l|(Gp2p==n,@:]I+tNK$%+ Dpɕ@^@Z$аQ%(E(Cz=2EO=hCd.*;\aG)T.af"N*NrhzD8usdH*uk lI4Y #IHf 49W(KDS'CtVǶw')`A3#“A/:(148 3$hB:|N #+ a8@[C@RL3.ʈ tc 0"KR0!5Ta# d̈́V )iPq !{**R7R# # ‡ ,IqK(K$}BK,HHIT$0aK:IO RUuUV[u`]5VUozW`vX5VXTO5U3-(“)RYH( 0[( 0)|@M5dp\Bzv"/!3N2a #.LᡤKԼ`+1bo6#ZMIp#B·"֫.":q@X%Art;n3ی#4(! H8Dj#q*Ź+rK.EL,IR(CMP:1C3>B"M$hNxd M">i!"LF,UCPtcBLR@d)j?GA AΈȋ&":7ALDx- "!B"x+.xQ&Iuݠ+l 1`JERS "XIp:t! SEûUzxE0Vڪ=ꉚBpD EԨ rqX@ԉyTcWc-+/PlSJH'j&) yH|2Ĩ2*g!hRE S "iP%JHCdT`c\;]y4(!?,8ч]]pQ /Ěd!$]Zۖ2#S6!\_ΰ 6.2e*`Kd>q@UpN`2puN`A[BL8` 06] Tp#$:0~+%T'H,bI!42 @:(4g(o%O$ItPT\#.))n"( ˜`=1lQ"#L{(P-U <L( Q8܆+8%i&U{)Nk0ժ6jWSզzYŰ[<5ao`B .X" ~T'˲ >8 őEHDVp@$G0m ~@jB4|Jۗ)@m\am 1M` $/&`MP[ݠ$)Hfd,D@E'!#H[9}Y!gp y"`}vtL%@&ҚepRtp=׻#ğfpȀbI=eTĠa )8Q%XC p/;GrjkP6k~s|i$3~%|z9ι1-P3#A6@"숵+>;@Ă*6[+_\6^)f 8A냵k;YC?Њ)@wCS;*7#Axa5=Z $))C#58 ; <$@|C "Np|491C3A+Ԋ:=c2CA66H:qA55DSA=ԏ) @3<1M0C4 3DpcMdJS1cR9b<#4S1)=&d.,ȼ IKXĂ "Ӳ-hB3Q2r\" .BF7mt.4829pIȀ;HX1"I7q[JĂ(xLL.xKF;8(US8M͜=$Lιnbٺ_4Ψ6x1\10Y5QM+rND+2亱lKKN&6x2@P̆TIڨLHN6@@XMOq,ȐKUc\bcI5[I+Qbr"'0| ?ՑSWreՑQM-N%@nMpUjVwVxulRLW!eS^l!v-dW@ m׀%u%E;QFDoJvXzkJ5Wc/A S V yMWYYsٙXa،tWdLa~&5}e\Tb,G^me"&wM^%dGB&]XeOW% ^reCdi fh&ogqfUa=4K];nfgJn`NcMV2gKFNf~."}eq}^ngeaf&+cbg`4e'e lIbUej5,"*ܖEc MFL1aV6b)/ߘNang>g6gfhcl`gVF Nn"er~y gjjju|ꯖj.gVj`juNeb]!nVeZ.$yhbcӭn^hjZ.S:.a;ipN.la6&Z>jmZҵȎjNʶc}c-ǖmaRmNVSׅlV5v)vQV,O锎H-le>263ciMynadlNеPthFli~o&o3[&p]onpmQom~&,oJמqX\cS q!F eWnI_m~~k "mNlVd%pVh^o7hPFN-.{mbr3_VxUFVlf(ym:?oѾso0Grs-p',pqİw"s*qefu!Q"2YV6/sؘX!qAeƆc^ Z470/H?R^uE_WG`O]RWs,GNv>s)s* "Q=V[v~&f bg*vB?vGttMNWYnu~vCOuvgEffZT|_[L!=q/n<fv&Oqu!uOX6^p;vk]_d?Vp?mI!|/pop9UtgwLzw`[Ogm~ whm^VwIy&o%z/w{vo{ZVu]_yd6(/zwlg~{_{_ˎ[[~bکzExMa|/EЂ(/|Vuy x(||W{`'&u{oqt~s~N365go~|SWhzh „ &d(С &Rh"ƌ7fT *2!"ɒWBrE "WHf'(iȔDH93 K:QXPbZ(#sȞ&G4ɚ7"(ʖTf='`QEkѯZE [dZVJ.g8Mرc㶴rQ,"ss͝3\l:g;vjd'U`-#'cWH#=Zo_n֩Fsr}ixqFqOaA BDPq4"%GfTn)xQ^s[x^dR/M=`\|UnU$^HuHWVQ[d-`iUOA:LXc]bnJ9iwZraQVhd!U֓&!S]Uz8J\uyIv ]KG[ӀYw >V g\S¦ϵjN[}u**U(ҫ2DZŹ fCj`Jbxab @.&{H3)[B'$D e85a.@@pH ld4 S:AdTLD|E10wP*?0t8|p Pآ8pdOpJG0i q&!onK[G+)+>ؚ\)uŊu~ߩlCY:hY6[ .oiqNcZrbέ} ~pjAeW[&i`%Rzj▛J.BPkQ X1HN8 x>yT7h>9d1PN`BCV20TD g} 4n X)(X0A x, D E87p k}Mb6, Ie#mpr 3jq8?BKp{FdjYKU =sORǍdq\O򟞬.Fʋ%E._!r#jlpӷfOsРG2d%ls]vE.4Rp.yQ! H,QLe  H73p n.AdWF9 5", A#R 6'`\s\!0Td"0C rű!i:P>@+DIZxP.6GpaϷc2y<2YLT0g5+Xy[*$;VvV\d)J2whA%T9MVndZ:ZvՖH,1d,AB!%FfsVZcI^/Kur ¼컈hAK !a (`RD& nR*ABAyO=n;RpJ8Q [@ބAy]b_/Fœ)q|EbMbS]%jaMF4B$ (%DǍI"U-_w]Re_UT"YNT>Tޏ!J/"pDO0VB1*IA`td;t MY;A>$Bm=|*|1l =nUȏ@j2$A'P.D[;zZUXpH`^(bg F.f3JjlP^MPTaQ ,=UuLHMM(WƘO1UXjLxMP"fM(v"#>hmTT$J"RA էT"Z #^b`\!II*dԁ6*@i @dd(h^锎DB%j!:(~D,ܥi.(%^IZZ.޲OaHXiYXUei[VyX .H_!g)@UX9*YAߤ{r!_"&JQ*UAXȋzͨԨ偀F᪺Rrh Y*VVߤa!#a+Z&+-Y{U)GEi,i _.k6+Ur`"hDܷa bV߹RFh# (*%Y`)IL PH˚$^Jrt+Pg~:ƧÂhDĞb_źI) mvz*"-Xb>j&YOEN_lȦ*-ސ-|@d #ɪYvHHͶګdV߾cuz+m!,⒟2-ڪD-^d++^mƆ)规1Rd (y~1([<YZI U nѦՊJ\fE,l!+zh eFa ).^h# c6+K/"b_KnfٮՎ zoo U.rFX*.^*®F/L/Ѷ離n WҢ|0@ZHR0jn,/j ︜YzYbyzP"ob5 30Į5H~B"/p* [Wp0/3-Npսznς&1zbݭ#0$wn-+}1de߬Y kQoȮR.. _?1 q?rĮ2(6(-VN[aQ_2QjL3(Wsq$n~22'S:'2Jq602'%g,\WdQ- ]R.X.#3U,n3BHV@[GkA3V*//aoXL=3>3kX#.,3۰34[??;s4!Gu 0!Gzk– 1?}4VV4i)tBP ,uD_\ȍZD]g /t3._Ovdw^^GNo_ sTn8glg/G\mkmodw13G1Fwotp{p>G~t(64;+wwb}{dw v"u$w]B40g4888Ǹ8׸8縎xSx899'xxA8<7EmVuZ&vaEO|nc.okV61N{* WҞE''#+w5+/4vv~$uzTyX,a;6p_jascKpyt`?`MEd::#CO֮:X(bA^A{(//{/@3;;;W_;KOiO{gK;Ss{s{_kGAHAW{{;|{<'7<ó?|;Ci|=qwfC7owj^)? [x{;G΋rDڇt#1v:Lz2A"Td`Q!ƒ`1(eT0"(#@ NDeaIH2A  dqcGXV29)K$kR':#T(#B) %%r&FXRM ,7yb֭#t+&Y+elطBl'6,6VFZlI$͂ΔYϔeha"s_}u1kl3A%1f>(vvըT*[`9.KDWЪ%않L~:v۹O{`p}zٷwxf6+O<@=4:(BɧXr &h2*^#*+*`A&ʿ@# G β2β @A: 8;!ZJS72MJ(%*- $ˆBTF7 %)b. ʋI2R`t1ȶɈ @"ʳ>""HR-`t (< ƼʢQBEEN!# @ )Z1"ˮ[<cvgjY*@p`뢣$I7 mV8k r+4,N07&~'7!rP50dа$+pDE #ވ7kĺ20aD(쮃)B)LhK" W8 b\:.8-`Gfh*;W\ 4D^͢#VQ#%w oJs}cU :(*XrM.*KD+)u&udJ0*DJZʌ k<,)ioqG3cxhwv}kO(/0b)͞E4L*ThxI`}H^o+5D# { N64\+5iCxCiTHR(Yfc#"" GLQI6cΧ cJ־%pDHL8Qn|iah,/Z `bf q Kw$\yd?&- |=hi(XBUO9 YYR涥a4K J.5 UI@IHPE<G7kC Dpd`bpd5O,=[ޒHu/$]#"|\:8H˱FGbd?:!B4ІlkLnn|D]!3E( hㄓ́8ƮKaWS4BZy#', K]އO!tHoƅJA h[3DTSB9 [V)4VDAlຖ]dA>|Iu;|΃{\6I.I& V J aHH OuEdoKHQN3 Q[U !B , #TtZT XBY\!zo@,lԪ&Q@SO# -Eܚ\Oz-t"1N6vnE+߅; hO+{aTuVJG6<2$Hx gVWkD+<рDDӲ# ΄(qGhC:E &j`p7"`W]STGV5bP lesRoJbZ Tnt+l\a$B/H`-@"^(3x^/if:0Tlt:V;%e]'[Sg{â XkH,h5A҄4 Tpр~X)\;B!IJCk=!/JK3Dhb@!MTF 2C[@#p/P }S:J!@ <h+BA ,F|#̙FQ7nf[QupFAu"+@(Rm #H `MW$im کN>A J7ga~x%ʰ$m0vcɀm(* 3h*t 0/ynEX˚Z@0߆| G!* K /ɀE@0#A :d`Wp w@?`Yp:'QW t>39FrvHf;={9`xֻ齊~fWb H3h kPg]$y%hDz TdH=-jYPofϯ0pMAAD@p@sjV̍ v%~&O !3H@@B fEa & j.# fD@a D0Mh@.nL /e"/x|4Jp0 Bel`||  |%d.X \HR`Bhj@h-8 S {0@/~`+* 2 K H)0J -q j *B o r$~P y rєl3f*6XX l"fb!&/Xbp? i Π DoRm  W. S%*̀<1?@ 31l}@ހ"0dC&XoYͱO%czH^W`***pN& .B%~s , >n8W0 v_"g @ n|̠bK*k |2za8~E@H)`(  l X  ^2^`TͶB uh D` %@m (%@ $ ["x+P 9&SelsV-{PB s"oQ 0;m7sQ !99)9|0 q=3sb ! Xgi * `"]@4'. VPڠ Ƞu `+sR`e,a`7g P`8K4D)b3m@ j ̀?D@?-z}E;-av3JT+ X F'r:aTeh P1!m0M߄AXh %M2T;VOwiYM(=lL6~@IKI PRfIsXgh(@kx`2H_LԊl+F43l @߄d"AR x!Iw!pA 0Fv7A9O@ 63!t 6 lTI0)BWe)HmYOS X!F9A `5[Sր ~ РTl50ؔ~,5R_Tdu2gPϕd?KTaΨXG J"WI6v}Rk(*PcSP8MB &S .XSXH6c9rcDy  p@, @I&j=FV'3\)(] :!I `a, b`KWO`jlHtJId: @nqPu( {:u+uV+V'TFMQE TOnq |`iǮ fvj `6 6nH@k@{B[KM5uYCvkדM/ J8%bSslQ6&ykZk U@p V\eӄl4ku}[\%tyI upR "_ie+PW&2zsuUDA  ^ǀhE,H 8sg@j&nkxeS |F_Y3P_ fSv;&BnmH z`yW!75[|4jxqIX- bCXhh8gFsLIye|!k!@dYYJOyhO%9P5a8S}d"`RX'1:)tY[DP P~xw> Pq ē w~ ಳ( Y `k~ 8@Ւl#wW\V1Y#2* j dzSeT4-Xh`EW5z@HǠD  *OVS@rX~}uWC;w3)Uc߳vP#"F8 :I/bQ & _Kk5:[%Y)!`nZ; VAy}EnYc vp)V\AKW)sP{HSWf৕ ?:qMPKtf%É6+i}lѯ߹lʕN~'5k@ז̱@̥@Y] C"\7gNcO+RGVTv@xUR&=^d Rf!;q daX2 ԠٙKTcI oAW ^SZ{?[Zm 6_  0IS|37zk` s{Yutij?W&-η R3$茩tE &&k)s$"R<sȕ,2:RL„ƑDrCB"l!BD!40PiR&DΔ[\pc# @hA9@|Ä.\"fXae ci!)ӑf HQVPD °Wx,UhG@1#EdbD6(HXV[jأZI)`ҀAJDaNKmrŎ6$GUGѲM"WdHIыD|xZ5ٴmpJ޾?h}5, %GLW\anaza!"f@*b.c/J0h R i'CaŇEʑvk QѠIXYy! >D D $,G58UE]j 5S`QȀt@ C"l!{ST M| Td tjlPPednaBB*UGFK6L `Dc0iF"[r)7CFa9II#4 !FGA 0JCn})ŸA P& :dQtݨZA !AB/F ʀST[5+ IRMbY8PeUFHDjĠS/ȵ# 0_+`E'$q^!b} )&ZJ9k qWH0`X BPeSFCi0<̓ThlAYR2R8mqafdI >ɠ4tM Ȭ͉ jZH!5^JeQh!'"ʈ{.@,/y#jX4#.xBF3XU#&%mx80"<#?vpTPF!3&!J`FKԛ6`#T}ax65T}]M*/~D B= #6Ȁjf%&X .( ,0)_ @H 09`ZAPDUIlXJu6-6 E2d@Rm׾) 9c E0oR(>O -<7YnȈ]OE1 )H( !"y9F8!~P{\ D^ieI!{|;z2b <?qч/ZBa9P,.5b!!u  JUS d1YyyK*s XR|*PELž`jR,I ,:YZA*s63*gK,,E8ȶEjq vgIUސJ>loK UlQ#ʴxcЅ24vPBiʷE f\԰ deL~ے%s.MXSVRp` GH߫py/< 8b`Dpr] 0qlNvUy?Uj<pyl P ax7DhTNX,, RmL6HQr߼a:xoXYNo{iCν!;>d\եa`.[m1]j)wq#*38#veV+1MQ$ER}[9ZuZZحεH|j.NFuCWy^Y-m+ ?$tD|B2v`Ȗjt UqqߒȄtX(h7}w,`x4my~1z968H`*^mddNPZw, :wIdNKgj9r'R3ʭΚ؜X7ԁjc_쐼t]>: =jȆfnx<|Okgjg-١Nys;K>bx/?jN1_z;]穿g/ꪫ~!>ky'O~?ȟ}},w 9 6~{?tBǝOl7ÿCbg~1^v{|z{W{7b7}Hn:cwt>~'w|xvZGv}ƲG^lշlr}I=-ׂ# \x  (V(`|%g}W~68!~ xv_"%"8rDWt烿4p&ȋ}ȏ:H ̸h]^֌h:8iX؏ AWr9{{vȇXEo:O޸ɆG%iDY4s%?Ayxm fz爄bi6q 8tg xɖ4C3S!h G;GqBhL9N8BGvh51Mb[5CX~t)BɐIXi6I^2`Yuɘ Չ]҈ր樔ৄI,癔ɞ靳ْ יG"2 2fi;UɍȚRgPiYwRsyR*ڔٚ<٘J3" S#z:2יSCedYGy#ٜS7(*j I>uqhBD!x$rrAH]\e9gF|Ȥ'*Oڞ+R}~٤yZ| VS Ilj95!1:+ṨӢU:WJ3Yڨ?*skJeRXN9}q*zz "chexh)x:2pQR^uﱤي{`E=Jy8~꫟ȣJlMؗ鹀 gGI|Zڬ)j}:x]F噮( ٮY2p5Z;v▩Yh3私xު {:ii f8p 'zj8GKxJyR:z(ˮ:۩A<ҤU(; +W ǫeۼZ: sEˁR8ܵf18ۚd;,aIP +lNV D@70 Q`Y0 v,N Z`{T`p0e1 7q̑cUHd0H ?@Ljlڌ $@ jKԚLɱ h|8nik*Mk\E$LԉK'N{k=X Tpb(7|]"knLj pw['`epǑPZ `ql!~  D_pirD \p 8 0kCQ>.`<]@1pc"-D ]P_UQM ٍsM`ٴӄj_%MDmš\ް+mm(7,[E f0LͱE uאxiw u   <BD`@:}P8΃!Ϝu2+[Z* [@@!n\ԡ<<L:ȗHhMj]x=]>jk~쵳m &]Z@3sPN 7Z9bl .> )#=p.`U`)bTfTg! 胾թ.Դ n6"u$̯zd*&Ȼ/i֍:ʼ. ^D"Z+ߨ.%'0!hN\Qru g ?ɽ upPb`RԎ 1p^ /, U9@@ B&c@w ؓ]10wlH`3`b+A7 `U '+5[Gzc%I=2x}|LOQNY^ֿï|J_` 0Qm^:q!Ό@@aB HE+RdP"/R7*!9SFH$򕅄?i3H1'փm#aH2tcL0cR"Hу!7 I LjXr_+-ĢdLTFˍ 3Y W`B0)0vCDWttHI5Fh$DċT?n Ce-5B %*֘X8&D5܊C$Q"E[$r%`'qLz܄ט8 ט8gbJ܄9o"M#:țH1#NĪ?;s-Dܤ_=+ SO#:䳰CL*(BK p0 [O< 'ѵ:(/F@4B3\6L3N9猳M 3O=O:2# 8$` $T $4A ha4y?vlAJ '$(c`9J Ռ0 D*ˉd@ D1Dn@&sn+g I΋/B` FHm R" o6 lA)^؃G ,#)P pc15&&3B"0 s*=IJFŽؿ#5M(] M5a$6^Ea}a_fT6HX5!"@r(B[Ⱦ-h 0 ݆<, X`u~(n nh!İ8-kSѠEA[ҴH0ҿsA$}ldQEv'@=364Ȅg#ֶ.n2aSd62)dWNDQD\B߆Ә HB6lGsOs2Ir C6N \$%٧6]!DY=lI1Q De.8"1@5$* ȐoT`ŭ=bzL#+=M gPB+C )t `v9r҂ig HLܜљYRƐg-̋ndjDY8̳IpL=Y&IkR#|FQB1яF R S(E3>QZt* ZFX4 MQT[I"Is$"4TըdR:3MΩqzH6;f3uSwqvRXC$Tg:68]!Z+RMm]$|ICEp 9\rhcGD@5mq"]{Z qv6+{*4"5.d-#8Lt=Ӧđ?X/΅OA{zLcjga1Q񦕳-acgBFuUnt~NiΠ\$lbXD8˥T`-8# W Vx4LGIjE(؝D0,H>U㶻f5!g:S%#cj!pid"n&6AySN_Y ݩ'{WgEtkGFԱ'`/BL7Ez!G"/,aoȀ͡`aNk\j0J[fFك覉B:usKi*QI5LJoB_>>-jz[qbsdfauƻrKO[^^}[oWg\ #Z1{]x;rdDw3ѓdcHsu> ?1p:tQ:8/+C=Sߛ==:ݛ|r< L5@ BY2h2@Ӧ4 2ڳ>>rHY/#|2,P2[蛿  qý@.Z{$2LX#7˻4#S31gh3=0C>R>ms=n"')>;Ed?A:6rL*1eB|FA뽫k+ zLhNlO9Q3ECpEy,z<{|A? @'i@+#p\-\F,GHKdؚ .KxTh|i.k4*Է@\1iڴݼ:4TNMpڄN\$N*FNNF<ONN4NO,O ONdtDOONDNEmOO|N P } O -P=O %OU,Q eQ TPONDQ ^SzS̈́0ǙF73338Q-N-R0 S.S2-S/=.=/U4]0m5S9}S89- =E:>S6SB5B?}8CTDMTGF5IH}TKKSD2iC kR6;7s3׈ XYZ[\]^_`a%b5cEdUeefugV_U$RS]3$5:Vi@ 4),;,S1%/Er]Wsm*WƈW,#HWy{=B !}]Um9#ךdx ؆-X x ' ْ/I 6W~m 1EؚXYٍYm`fHXYE پW5Y-ZaٕĭځmAXUژסmŎ``mX[ث%4Z| 6aU&uJ4 *m.)|܄ #UUZqډaW}2ۄA}b+"9۱Jٖ+ = ϝsX]ՍY}ڿ5ܲJ]5Ѕښ[^X?<޾MYu^ߕe_Ӎ^!B}m'U0}ڋe[دU_^,Yu_0kn]%ŕFBR ծ#\*"_ؚcMc%^b|};x`Y=c̅e: `Lbd]eJcrb1?93^VgZ{cjI6fc^W5[qvg߀f06gMciFJ* hn rQra;hj3l74l$TV*jgN7H]bŨXFp0{e݈QDsgH=aH:@ioU8毎͵erfoDOi=c& Uju2@].d6p0`W;"ehkFqn̎mj&(JdXM@ά|e<\4`QR`1I:?.& S88jd%X%>lvW+nl(-Ճ)H6Xgnl"voNaM_Hj`DXUd!fnra{ lJjnoV&Apvveh>&qsn>k]k`~OkR頬F4aM@UWp&Pm/&h#k'58pjUlnvސ lň1~&Hrls5qN,^ jt&771J\GsԾs*n LqHt9~l@.񋾾0m47to +PpmX8`3`(5MOV!r&Аd`1v^_7(nb&s=G*J&6Q h\.uˠ7]uw`6huϛ!t`*dItJw ]L/Z/ qPPWQO`ww+xojFgvyB'fwk\_@xfˆR'b7v:m9me`lr6mOa/((Hp5 8h76(p?~xX&(2j{ȿ%>0|th{`/o|F&` `%`-h}5`Gʏ<>F K׀"PǁLW60ޯF" ٯH/HXhO|xQd v|QdK  j̠@$ p"J8+\pJp0dDD=W"Fp^`4GJA"g~eK"T%B-1xM4}֛2@ ' ppx<IԁU pRl6"`dQ )F|e"(I204aT)YZ'ĢXHI-ʕ#Ravl+[b*umYBmxa׎H48T!7`;=<湏;Ï/>^zXb 5Q`n%SġBK^t|qS0ap&>~|`$f\B6@ÇX0ȅD` %e@ѣh$>D^ \1%{]&Ox3TLXCm%O!e!f"\y2EGhC dQKO( (CzHGD8PFaPJ*pJ>0B6*Mͦ# {YꅅKCW4 Yȴ>tSYD`oRtB) ȐLh`!n vA@ï44F'j8#IB6L2xڒr=3uaq9/7,iUA)lfe醓c:ym7zc-^ᗶgEXonY̔BONbjQW 4K:0[@P>%CLR\Ѿ(&PC ])TI$Ѧ3, f\rn11311%GM 8fM tzCDJGJ,$*Zys Vf* ADrÇ&T ?);M?>Z 9U C4*J480T` xfӿ|@ Z*U`r*C$xM b 4 /,an"uq,Rp@8!qM.$ L*L>FYG(\ů p/w-„)Wȗ.`%P:K!~Ba;/ ӊpT9nDh^miZS ZCd6IgyMHyR6jsPgquGlٔpGlm|FM;q1QA3RX' ,0"ƪ`n :b I($U֓ ')DG58,rg'*xE@B,!Mfs ^ Vp3鵤h(2!+#',%BHMGUGplD>xM4D<  1,)ĂjX8Uc&PJ!oRS\x1"ࢃEMX8\KXB'(oe.hPWFHBu$kxa KH! dAHWe)*@JՈ{P$26P!W)L蛈L[a Ѳ8ArWrѲ4Rv,HLT5gD Q`[zld&c;~VeFh0ЕUm 0b <&(:BoBSER\8a@ET8Ĺ CK:3l&  =,+mpLiqxTI0 ;‰7BTEL%8 0eC>CE [Љ=x3w^HB 2@Zč!„$uPSWKp(9BUx/JAa%v A93r,VHDGXY @NӤ%#߲,U |Saɞa h*d D#UX(8obwPRXĨ%(4tE]m؈6k Ѡ9'x}*IC Țesn>+"j [j{Q`*Rb:z!~FH}ɀ}IADž "zw d{w06d9,@BE^m^ASlӈN! dLF>^$t$Xq,b0a T]<=VYIQ ](x'h*I9=ilq@PeeQZ]UfxV)QAa\A&Bt $ D4R& LEWx舵}@JșE pN A΢ 3PeA#d ҿd"Kq viޝL T)_+R]t[ ـ]ZЪAR$HVԼԁ$6`RFNWHKM~\hN T$/H!BJ dJ5j 5^A`J՜lhDY ~B`,zJ T&D˖lɑϊ.*PAHc=hXBa E|Ȥ@ebb +q~b PXfV$My0D` ZH&H CHep ֕ʛqNM P.B> |m" <8r_KD\. d͕4M$歋ć̈+(mJtDENTu@ZKp$/F0yQYPH ӀŞi؇nG zǦ\  V,A4FĕF%^,2`^KRP)DE܌BTW}XXixAEI\@j-vc4IAnE)DĚKxHYEp1f pP[,81JȀ&AC6 #nG$hZ Ŭ %J HEE $EhM'_ \ -^EaB$H  {h$elg|p""(_pL $JFHq''q+ # 0 tW/E82 'UHnMnAGNP=7z FtPl&Sx02@;p˱耚E4z $@t%HtďJ_(H2l朴T  hX @' Ҵ8S$P/,ݎp)\6FEI)5o$`!zO۴H`GLdSx5;lGG w{$xbbGcxDŀ/tur w~ r/4٨G@v kcm8.QvEڳGsX@ ,GZ`Wx8eQ5t<ZM8nwxxV&.9ÖdA9 zx >ʶDi'j_w331;Q9w_xz$A>zN\ bg9NzXsowtәos6(J@6T$$kDhg8z}ۺ{uҘn`z!9Ni;qXAwv>/`kҴT,; 7[r ǻChtpvӹFG,+{7{;9nHEl<1oKF{wl0jsY8ykxS/~?=|{OC;7?+|F;}F 埼>8~SL;g;C4r=Ssgw~[Vw 7/?@!Td\(PD" WX|h"R&fPDžHR+)SldÇ RԈQE?9$JWlă4QڴsbFDfQ*  ^i`C,g_zУQ*kC,L}ؽ$DjsKNg,pª;3. 6Xe-׊׉>2·[rwGU^zSR!:y1ƍ ~ș/Ozu׏`{wg@@|.=@3/ *E^]FQv>Db""@˦ ȤhJB +)KLj3 -ɼi.&z>ꏩՄ%z c0xi tȊŒ+P0Ю[0"n  2ɶjz#)8 +*: ?#LƯ ;LKڪpx~v v鱯}{>zǷtg{ۗgxG_z? P{`=].} A~_ʇ0y\^8ؼ҅v7!G9s^hp;  ӡĿ !ЇF,ߠZKU(Q1zF!F F,2A6EiqO'1NdcF2q >294 IG*yd%MpJ0r$&G)Q,,cH,DR4l }IfR)(9{lftq$5yLmslYH\G9#4Cm;t%)ٞXFn(JKpY8OR|@N~"4 <GW\( :]ӡ#ʃVxF'bupL0gR6J3Xh-|^:VO64)@=SJ*, ~*ONr:m9 ѮVBIjI5d?ԑzlMjDzŸNtn-'"W:&JR" 7'XTT45Fo?x;Z FB,YEVљjVP"Ұ m`U;ңMk xҕ-ZW$pQZ7t^ĺpF/2oM$fMTT聢m? LD7x/t[UnqVixkѹo\ )n1KK+2}˔;׵Saw1A3\FvϷn|K1uD'N_X7͵ԙsc:\x0wiW'=W%_{ <'WtGuZ-y4ow$jK-hy~zts޵Y_>͠|0o8s@9<ܖi/`Ѿo39shŐB;:k -S19s2#3)C23 %.5/;..۳.8?F kqQJn53GTJ7%Т֨JrEM2)iBwS8;lJ*iGG),-M1N Hs3!5m4I2,`fԚblGGPH"0"MC"=HsNCUMTtR%/t:NFLՒ"w@QAT%ʩ0E RqY IJQ330gU@q@UGߓGTUOc's2^3|\@WubȦS4u_oPZE+J q76$!rb#M?KvNRO]@5ZG]K8TV ^N4IS퀒.M4cVZ#_dR]C +љM>gghV^WV_UHGUe{dϵeUX2-X5YՊS%D!\lzX{h2t;tsʹLo qc[Mc2ekE6lYvlh#gVhe4suYSp֥4c%)UaQ d˚#^w9#Ok&Nc1+ussi.`r]xEw7ٵV 2w0_QWiu#vxsByUpwRՋʳβnnwwpa4&aTv{Q#;6 74vtTl-ksed r 9o6mvtW!@ zx9(p{7\'w\}Ton[Jl<8]qVsH]sXrs8yi3DOn6vx쎳x_Y9#MQ/2%Eۖ:|Op-B?XoAD6m RSX~]g.});t)u]+~;yiY>[561yE_uyAyؐ#-r6XHVTYX=3hfˑ} J; |=Tx#˾x$HaGn6}w4ψޤԘU 9vفajUa֛؜tUiȗAWYS4M885,pv1 "kѕٙhCkkz7YW땜ssɝcgH53c158waZZ*-ڊz7+NX|Zyaa|OU/ړ1P3~Wٚ"sX-֎XEY-:KӶz9EzVWCy;۰st_}Az:oX4O.mkvY5 皭Zp'ک)R\Sﺇ)z8 1]lrXuy2_:W["͸5Ҳv8CێjǙ%[[ [ћo{WiI7;}qS`έO+#oSU-w: Tw:훽cڔy[l;tswrtYC[ +,[ΦM<8[GrK|}N99Ele;Kߙ{٬ǛB/AASɯwiu}ʯV C39Pĭ(^7^'9^~;I?+#^]>+~3mq~ 0~ko^;~Cki~{^1U>5_^葞E>~~siq>{#~F~~^^cs돾7~b>^>?^^!_%a~ ;%?a']A?C*a? `imkn_m{+w__qg?s_i__a?ÿ??_e_"J .(X …2pać+Z\Fw:z㛏#?I2eǒLtʓ+i,ɒ&J2yi&L@u5jMϙ'qvl3$SFGJygiѡ\REJS*Ray-XiuZq[]yOn`q]klߴL1ٲf99wϞ+O. ͢%}4e֦A۲si{mmvr˙Wnҵ~;AX>ܵ׽<噩/m?~9?^|x8_gYjWj qMx Y d'e #"ejbx!c|<#{f(jXݍ rDzLd)ݔ%h~QxTcb"pcYI~$g69r *Pۋթ Giftɦay%fd`Vji䦒Fh)Rjꥠi( Z>\ZT$j얩Y'贙V;Jkv{Ѯj줝ɑ@|Vxċ@  y)Tp-`q !l.& qB  \ڡM 8XA/2٬nHZ|TN&$[g8m{Q2Nb\Q@FC0d0*C `{! b*SE" Xg($>0"#؝ЂS@oTx1% Ђ|p:%x7`Hx/Kd č9!L`` KFT)) RP iJn-Z09fP%&7KZJ F8AX0>_C[֤ ڏ|XR!륒I{(@lCpWTg~ 7|㙰7` hApHS% I l`xC $B dYDԸE)/p"4r!K !P&` D)npDlH!2!n 1h Ɛ,p'ӄXPV!fM}քvb,BBZRr%l  0`&p5+ JXPV "ЮfvZ?b tEۜ =d֯,[MփTQ\Zf} Xl1vo5ǹaV׹8N|Нc=-i0n3';R7($L`[==.~@Ү@ ZوUDjq' bT@zU^<;j"o V" |a xD!nT  Z/wiIU-` }[PHݚ5EXO  &A) uKd! an[4#=7f} p,&*AIH m|.`xtix2^< + o] }A2`' o+Q   03y wM/^`Dg.@M 3 =E(Dl-?jg7 .O$ x## O{5@zZ XOjp0L} '|z0zp7QH} GuHeiy` [gTy@8P @Hy> NzpdF5@3jsUׇ9d{x?HB4Mcӈډ)%9 ,p "UyyX5+sfI iW)`8p$`7LPHefYSȏU/Z `y`xP_w J'/v(PPN` }/ 91V?$e( tu` 'e ^m2ZG?TyNGZ L*`NifgWsI 8p eФ` hD DC ]P YfD 6떘ЩFgEvY)E1~ffVj@ 2Y9pk)opϖ[lH 0p ʂf`UBxCTic: $pc-I#U|LJ{ju K 0 q!D{`=>`7f(i fŰ4UZyM4a VRYK)` rV PYH٩R7I;=;YpsH }tdJPy:9<$ o OPhN.Y'p` `@ 7yԠ 7 b p s`c0p!*u=*g95@oy(M*47+zʐY@Xj |I. ]gihYg$y`4|9YeVMHq@q`H3hVf*][_Fuհ/WM]=UW XWyftaS`ǻY} =p*7#Կ MW jpU`9MW魱P:kjVb@$,P0Shwt_`V%dHՓF寡 ZP1jp~/0FH WT VsYg@%dd\H. e,]2v|.|B6 ;[EJ'PLpWAH5 ט %l[\(MnjBIl MPGXi5 `7y jt&2@t[.pѪ+=*i`c8 ӎ6J]( PGU {(1J_ Ic<c,bË`t$b+y&`5@u5UBxSZ%@L|OUe`PW)#O(ZU<9a KY Rq@7|6 ,pU{:zJЦ`p6징z iCNx)k {‰` QX59y-{prМo Ys{6k Pyq<CCz|2A+!eG\%Kf!0 z|;'uUСlC` @uL6s ">n$|;i^Etc 8i @+cQp>p J`:cMJL0̼r| W):k:MtDX }|WM _J@V\`GU[C<`qŃ'\@RH$aL  PFE fS0 @ ;#Fs ae X&* bN|@|[o0 3x`Fx3lQ!! H`=< &]G*XPn)%X/BAn R@l8`32(nC;n܀36|J(bQ$30 O%jnk, 8IS&0C`Q h (j,о9`D9 lH @&0 @(UrV2$j'RCY% a+9KPm HY򪍲D;Hh$ $t(;t.5R[iK ՘I@4<:S$BN1N5)sJy7XI*S¤,QIԬ *ZzV5-MYZڴ$LY׶&dukLg*՛<])[ӚWD$+GZV*`Z([ezZ5MrT& ]@JDPvgDe&$ag&6Y[vێJ":%iiZ8}-w)Ѹ~y;\ݴCUEolRĥYMhhؗI@N9^jlՐ${\h ֝,wٻ<"]3N82zek`:Ի$F H_ٍkbEf[X)&" $J~ y]X`%48V|LZb | g Bg>*`%v3fVLl+W.1N|T6Bi96m77'O7\ny>-ٍo4ۆLu^sqj]ö0!sp5f:b/3gkO̧P{Ri.vG3U~vC}xi+:΂"„Wz_=p;SUǞhV=(dLɣ m\ӕDd_5'ƆZlaZܵC%f) *c2sZ>}jV~u8XQDl| 1s?Gc`H5񣿩r~h 9) d8 ))j?䣾C 42 )+;;>[AEkKc?k>ý(W94[?A$"#?3 (+k 3BK.T.qx3 +)ˉC5;@ A,ă?+< â@QB"bBE̵CAA?\B㶉zO;D>$8,T4T@D $>!TDCB8kBZ4U #l߳F-$x$"G"J4{>'}d~taA7G_?~dCrhglD"D<W[H~sȎ\` %\VignetteIndexEntry{Continuous integration} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` You can configure `lintr` to run as part of continuous integration (either for a package or a general project containing R files) in order to automatically check that commits and pull requests do not deteriorate code style. ## For packages First, take special note of the proviso in `?executing_linters` about the need to have your package and its dependencies installed or loaded (e.g. with `pkgload::load_all()`) in order for certain linters (e.g. `object_usage_linter()`) to function as intended. ### GitHub Actions If your package is on GitHub, the easiest way to do this is with GitHub Actions. The workflow configuration files use YAML syntax. The `usethis` package has some great functionality that can help you with workflow files. The most straightforward way to add a `lintr` workflow to your package is to use the [r-lib/actions](https://github.com/r-lib/actions/)'s [`lint` example](https://github.com/r-lib/actions/tree/v2-branch/examples#lint-workflow). To do this with `usethis`, you need to call ```r usethis::use_github_action("lint") ``` This will create a workflow file called `lint.yaml` and place it in the correct location, namely in the `.github/workflows` directory of your repository. This file configures all the steps required to run `lintr::lint_package()` on your package. Alternatively you can use the eponymous [`lint-changed-files.yaml`](https://github.com/r-lib/actions/blob/v2-branch/examples/lint-changed-files.yaml) to only lint any changed files: ```r usethis::use_github_action("lint-changed-files") ``` Comments to the commit or pull request will be printed as [annotations](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-status-checks#types-of-status-checks-on-github) along side the status check on GitHub. If you want the builds to produce an error instead of just a warning, you can set the environment variable `LINTR_ERROR_ON_LINT=true`. This is set by default for both [r-lib/actions](https://github.com/r-lib/actions/)'s `lint.yaml` and `lint-changed-files.yaml`. Note that this will kill the R process in case of a lint. If your project is in a subdirectory and you would like to use GitHub Actions annotations, you can set `options(lintr.github_annotation_project_dir = "path/to/project")` which will make sure that the annotations point to the correct paths. ### Travis CI If you want to run `lintr` on [Travis-CI](https://www.travis-ci.com/), you will need to have Travis install the package first. This can be done by adding the following line to your `.travis.yml` ``` yaml r_github_packages: - r-lib/lintr ``` We recommend running `lintr::lint_package()` as an after_success step in your build process: ``` yaml after_success: - R CMD INSTALL $PKG_TARBALL - Rscript -e 'lintr::lint_package()' ``` If lints are found in the commit or pull request they will be printed on Travis-CI. The environment variable `LINTR_ERROR_ON_LINT` mentioned for GitHub actions also works with Travis CI builds. ## For projects You are not limited to using `lintr` for packages -- you can use it in combination with continuous integration for any other project. ### GitHub Actions If your project is on GitHub, you could take advantage of GitHub Actions and the `usethis` functionality. [r-lib/actions](https://github.com/r-lib/actions/) includes a [`lint-project` example](https://github.com/r-lib/actions/tree/v2-branch/examples#lint-project-workflow), which you can use by calling: ``` r usethis::use_github_action("lint-project") ``` ### Super-Linter `lintr` powers R lints for [Super-Linter](https://github.com/github/super-linter) and [MegaLinter](https://megalinter.io/latest/), which provide a unified linting experience across many languages. Specifically, they execute `lintr::lint()` on the R and R Markdown files included in a given project. lintr/vignettes/creating_linters.Rmd0000644000176200001440000003124214577052532017426 0ustar liggesusers--- title: "Creating new linters" author: "lintr maintainers" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Creating new linters} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- This vignette describes the steps necessary to create a new linter. See the last section for some details specific to writing new linters for `{lintr}`. A good example of a simple linter is the `pipe_call_linter`. ```r #' Pipe call linter #' #' Force explicit calls in magrittr pipes, e.g., #' `1:3 %>% sum()` instead of `1:3 %>% sum`. #' #' @evalRd rd_tags("pipe_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export pipe_call_linter <- function() { xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml2::xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.", type = "warning" ) }) } ``` Let's walk through the parts of the linter individually. ## Writing the linter ## ```r #' Pipe call linter #' #' Force explicit calls in magrittr pipes, e.g., #' `1:3 %>% sum()` instead of `1:3 %>% sum`. ``` Describe the linter, giving it a title and briefly covering the usages that are discouraged when the linter is active. ```r #' @evalRd rd_tags("pipe_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export ``` These lines (1) generate a Tags section in the documentation for the linter^[NB: this is a helper function for generating custom Rd styling. See R/linter_tags.R.]; (2) link to the full table of available linters; and (3) mark the function for export. The most unfamiliar here is probably (1), which can be skipped outside of `lintr` itself. ```r pipe_call_linter <- function() { ``` Next, we define the name of the new linter. The convention is to suffix all linter names with `_linter`. All `_linter` functions are function factories that return a closure that will do the actual linting function. We could define additional parameters that are useful for the linter in this function declaration (see, e.g. `assignment_linter`), but `pipe_call_linter` requires no additional arguments. ```r xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]" ``` Here is the core linter logic. `xpath` is an XPath expression for expressions matching the discouraged usage. `xpath` is saved inside the factory code (as opposed to inside the linter itself) for efficiency. Often, the `xpath` will be somewhat complicated / involve some assembly code using `paste()` or `glue::glue()`[^See `infix_spaces_linter()` for an example of this], in which case it is preferable to execute this code only once when creating the linter; the cached XPath is then re-used on each expression in each file where the linter is run. Let's examine the XPath a bit more closely: ```xpath //expr # global search (//) for 'expr' nodes (R expressions), at any nesting level [ # node[...] looks for any 'node' satisfying conditions in ... preceding-sibling:: # "siblings" are at the same nesting level in XML SPECIAL[ # 'SPECIAL' is the parse token for infix operators like %% or %+% text() = '%>%' # text() returns the string associated with this node ] # and # combine conditions with 'and' * # match any node [1] # match the first such node [self::SYMBOL] # match if the current node is a 'SYMBOL' (i.e., a 'name' in R) ] # ``` Taken together, that means we want to match `expr` nodes preceded by the `%>%` infix operator whose first child node is a `name`. That maps pretty closely to the description of what the `pipe_call_linter` is looking for, but there is subtlety in mapping between the R code you're used to and how they show up in the XML representation. `expr` nodes in particular take some practice to get accustomed to -- use the plentiful XPath-based linters in `lintr` as a guide to get extra practice^[The W3schools tutorials are also quite helpful; see https://www.w3schools.com/xml/xpath_intro.asp]. Note: `xml2` implements XPath 1.0, which lacks some helpful features available in XPath 2.0. ```r Linter(function(source_expression) { ``` This is the closure. It will be called on the `source_expression` variable that contains the top level expressions in the file to be linted. The call to `Linter()` gives this closure the class 'linter' (it also guesses the name of the linter; see `?Linter` for more details). The raw text of the expression is available from `source_file$content`. However, it is not generally possible to implement linters from the raw text -- consider `equals_na_linter`. If we just look for `== NA` in the text of the file, we'll generate many false positives, e.g. in comments (such as `# note: is.na() is the proper way to check == NA`) or inside character literals (such as `warning("don't use == NA to check missingness")`). We're also likely to generate false negatives, for example when `==` and `NA` appear on different lines! Working around these issues using only the un-parsed text in every situation amounts to re-implementing the parser. Therefore it is recommended to work with the tokens from `source_file$parsed_content` or `source_file$xml_parsed_content`, as they are tokenized from the `R` parser. These tokens are obtained from `parse()` and `utils::getParseData()` calls done prior to calling the new linter. `getParseData()` returns a `data.frame` with information from the source parse tree of the file being linted. A list of tokens is available from [r-source/src/main/gram.y](https://github.com/r-devel/r-svn/blob/master/src/main/gram.y#L395-L412). `source_file$xml_parsed_content` uses `xmlparsedata::xml_parse_data()` to convert the `getParseData()` output into an XML tree, which enables writing linter logic in [XPath](https://www.w3schools.com/xml/xpath_intro.asp), a powerful language for expressing paths within the nested XML data structure. Most linters in `lintr` are built using XPath because it is a powerful language for computation on the abstract syntax tree / parse tree. ```r if (!is_lint_level(source_expression, "expression")) { return(list()) } ``` Here, we return early if `source_expression` is not the expression-level object. `get_source_expression()` returns an object that parses the input file in two ways -- once is done expression-by-expression, the other contains all of the expressions in the file. This is done to facilitate caching. Suppose your package has a long source file (e.g., 100s of expressions) -- rather than run linters on every expression every time the file is updated, when caching is activated `lintr` will only run the linter again on expressions that have changed. Therefore, it is preferable to write expression-level linters whenever possible. Two types of exceptions observed in `lintr` are (1) when several or all expressions are _required_ to ensure the linter logic applies (e.g., `conjunct_test_linter` looks for consecutive calls to `stopifnot()`, which will typically appear on consecutive expressions) or (2) when the linter logic is very simple & fast to compute, so that the overhead of re-running the linter is low (e.g., `single_quotes_linter`). In those cases, use `is_lint_level(source_expression, "file")`. ```r xml <- source_expression$xml_parsed_content bad_expr <- xml2::xml_find_all(xml, xpath) ``` `source_expression$xml_parsed_content` is copied to a local variable (this is not strictly necessary but facilitates debugging). Then `xml2::xml_find_all()` is used to execute the XPath on this particular expression. Keep in mind that it is typically possible for a single expression to generate more than one lint -- for example, `x %>% na.omit %>% sum` will trigger the `pipe_call_linter()` twice^[This is particularly important if you want the `message` field in the resulting `Lint()` to vary depending on the exact violation that's found. For `pipe_call_linter()`, the message is always the same. See `assignment_linter()` for an example where the `message` can vary.]. ```r xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.", type = "warning" ) ``` Finally, we pass the matching XML node(s) to `xml_nodes_to_lints()`, which returns `Lint` objects corresponding to any "bad" usages found in `source_expression`. See `?Lint` and `?xml_nodes_to_lints` for details about the arguments. Note that here, the `message` for the lint is always the same, but for many linters, the message is customized to more closely match the observed usage. In such cases, `xml_nodes_to_lint()` can conveniently accept a function in `lint_message` which takes a node as input and converts it to a customized message. See, for example, `seq_linter`. ## Writing linter tests (NB: this section uses the `assignment_linter()` which has simpler examples than `pipe_continuation_linter()`.) `{lintr}` works best inside the `{testthat}` unit testing framework, in particular, `{lintr}` exports `lintr::expect_lint()` which is designed as a companion to other testthat expectations. You can define tests inside separate `test_that` calls. Most of the linters use the same default form. ```r test_that("returns the correct linting", { ``` You then test a series of expectations for the linter using `expect_lint`. Please see `?expect_lint` for a full description of the parameters. The main three aspects to test are: 1. Linter returns no lints when there is nothing to lint, e.g. ```r expect_lint("blah", NULL, assignment_linter()) ``` 2. Linter returns a lint when there is something to lint, e.g. ```r expect_lint("blah=1", rex("Use <-, not =, for assignment."), assignment_linter() ) ``` 3. As many edge cases as you can think of that might break it, e.g. ```r expect_lint("fun((blah = fun(1)))", rex("Use <-, not =, for assignment."), assignment_linter() ) ``` You may want to test that additional `lint` attributes are correct, such as the type, line number, column number, e.g. ```r expect_lint("blah=1", list(message = "assignment", line_number = 1, column_number = 5, type = "style"), assignment_linter() ) ``` Finally, it is a good idea to test that your linter reports multiple lints if needed, e.g. ```r expect_lint("blah=1; blah=2", list( list(line_number = 1, column_number = 5), list(line_number = 1, column_number = 13), ) assignment_linter() ) ``` It is always better to write too many tests rather than too few. ## Other utilities for writing custom linters Besides `is_lint_level()`, `{lintr}` also exports some other helpers generally useful for writing custom linters; these are used a lot in the internals of our own helpers, and so they've been tested and demonstrated their utility already. * `get_r_string()`: Whenever your linter needs to examine the value of a character literal (e.g., whether an argument value is set to some string), use this to extract the string exactly as R will see it. This is especially important to make your logic robust to R-4-style raw strings like `R"-(hello)-"`, which is otherwise difficult to express, for example as an XPath. ## Contributing to `{lintr}` ### More details about writing tests for new `{lintr}` linters The `{lintr}` package uses [testthat](https://github.com/r-lib/testthat) for testing. You can run all of the currently available tests using `devtools::test()`. If you want to run only the tests in a given file use the `filter` argument to `devtools::test()`. Linter tests should be put in the [tests/testthat/](https://github.com/r-lib/lintr/tree/main/tests/testthat) folder. The test filename should be the linter name prefixed by `test-`, e.g. `test-pipe_continuation_linter.R`. ### Adding your linter to the default_linters ## If your linter implements part of the tidyverse style guide you can add it to `default_linters`. This object is created in the file `zzz.R` (this name ensures that it will always run after all the linters are defined). Add your linter name to the `default_linters` list before the `NULL` at the end, and add a corresponding test case to the test script `./tests/testthat/default_linter_testcode.R`. ### Submit pull request ## Push your changes to a branch of your fork of the [lintr](https://github.com/r-lib/lintr) repository, and submit a pull request to get your linter merged into lintr! lintr/vignettes/lintr.Rmd0000644000176200001440000003634414577052532015232 0ustar liggesusers--- title: "Using lintr" author: "Alexander Rosenstock" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using lintr} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This vignette describes how to set up and configure `lintr` for use with projects or packages. ## Running `lintr` on a project Checking an R project for lints can be done with three different functions: - Lint a single file using `lint()`: ``` r lint(filename = "R/bad.R") ``` - Lint a directory using `lint_dir()`: ``` r lint_dir(path = "R") ``` This will apply `lint()` to all R source files matching the `pattern` argument. By default, this means all `.R` files as well as `knitr` formats (e.g. `.Rmd`, `.Rnw`). `lint_dir` is vectorized over `path`, so multiple directories can be linted at the same time. - Lint all relevant directories of an R package using `lint_package()`: ``` r lint_package(path = ".") ``` This will apply `lint_dir()` to all subdirectories usually containing R code in packages: - `R` containing the package implementation. - `tests` containing test code. - `inst` containing sample code or vignettes that will be installed along with the package. - `vignettes` containing package vignettes. - `data-raw` containing code to produce `data` files. For more information about the assumed package structure, see [R Packages](https://r-pkgs.org/structure.html). Note that some linters (e.g. `object_usage_linter()`) require the package to be installed to function properly. `pkgload::load_all()` will also suffice. See `?executing_linters` for more details. ## Configuring linters ### The `.lintr` file The canonical way to configure R projects and packages for linting is to create a `.lintr` file in the project root. This is a file in debian control format (`?read.dcf`), each value of which is evaluated as R code by `lintr` when reading the settings. A minimal `.lintr` file can be generated by running `use_lintr()` in the project directory. Lintr supports per-project configuration of the following fields. - `linters` - see `?linters_with_defaults` for example of specifying only a few non-default linters and `?linters_with_tags` for more fine-grained control. - `exclusions` - a list of filenames to exclude from linting. You can use a named item to exclude only certain lines from a file. - `exclude` - a regex pattern for lines to exclude from linting. Default is "\# nolint" - `exclude_start` - a regex pattern to start exclusion range. Default is "\# nolint start" - `exclude_end` - a regex pattern to end exclusion range. Default is "\# nolint end" - `encoding` - the encoding used for source files. Default inferred from .Rproj or DESCRIPTION files, fallback to UTF-8 ### .lintr File Example Below is an example .lintr file that uses 120 character line lengths, disables `commented_code_linter`, excludes a couple of files. ``` yaml linters: linters_with_defaults( line_length_linter(120), commented_code_linter = NULL ) exclusions: list( "inst/doc/creating_linters.R" = 1, "inst/example/bad.R", "tests/testthat/exclusions-test" ) ``` ### Other configuration options More generally, `lintr` searches for a settings file according to following prioritized list. The first one found, if any, will be used: 1. If `options("lintr.linter_file")` is an absolute path, this file will be used. The default for this option is `".lintr"` or the value of the environment variable `R_LINTR_LINTER_FILE`, if set. 2. A project-local linter file; that is, either 1. a linter file (that is, a file named like `lintr.linter_file`) in the currently-searched directory, i.e. the directory of the file passed to `lint()`; or 2. a linter file in the `.github/linters` child directory of the currently-searched directory. 3. A project-local linter file in the closest parent directory of the currently-searched directory, starting from the deepest path, moving upwards one level at a time. When run from `lint_package()`, this directory can differ for each linted file. 4. A linter file in the user's `HOME` directory. 5. A linter file called `config` in the user's configuration path (given by `tools::R_user_dir("lintr", which = "config")`). If no linter file is found, only default settings take effect (see [defaults](#defaults)). ### Using `options()` Values in `options()`, if they are not `NULL`, take precedence over those in the linter file (e.g. `.lintr`). Note that the key `option_name` in the linter file translates to an R option `lintr.option_name`. For example, `options(lintr.exclude = "# skip lint")` will take precedence over `exclude: # nolint` in the linter file. ### Using arguments to `lint()` The settings can also be passed as arguments to linting functions directly. In case of `exclusions`, these will be combined with the globally parsed settings. Other settings will be overridden. If only the specified settings should be changed, and the remaining settings should be taken directly from the defaults, the argument `parse_settings = FALSE` can be added to the function calls. This will suppress reading of the `.lintr` configuration. This is particularly useful for tests which should not exclude example files containing lints while the package-level `.lintr` excludes those files because the lints are intentional. ### Defaults {#defaults} The default settings of `lintr` are intended to conform to the [tidyverse style guide](https://style.tidyverse.org/). However, the behavior can be customized using different methods. Apart from `lintr.linter_file`, which defaults to `".lintr"`, there are the following settings: ```{r show_default_settings, echo = FALSE} default_settings <- lintr::default_settings default_settings$linters <- "`lintr::default_linters`" default_settings$comment_token <- "(lintr-bot comment token for automatic GitHub comments)" default_settings$exclusions <- "(empty)" make_string <- function(x) { if (inherits(x, "regex")) { paste0("regex: `", x, "`") } else { as.character(x) } } defaults_table <- data.frame( default = vapply(default_settings, make_string, character(1L)), stringsAsFactors = FALSE ) # avoid conflict when loading lintr in echo=TRUE cell below rm(default_settings) knitr::kable(defaults_table) ``` Note that the default `encoding` setting depends on the file to be linted. If an Encoding is found in a `.Rproj` file or a `DESCRIPTION` file, that encoding overrides the default of UTF-8. #### Customizing active linters If you only want to customize some linters, you can use the helper function `linters_with_defaults()`, which will keep all unnamed linters with the default settings. Disable a linter by passing `NULL`. For example, to set the line length limit to 120 characters and globally disable the `whitespace_linter()`, you can put this into your `.lintr`: ``` r linters: linters_with_defaults( line_length_linter = line_length_linter(120L), whitespace_linter = NULL ) ``` By default, the following linters are enabled. Where applicable, the default settings are also shown. ```{r show_linter_defaults, echo = FALSE} library(lintr) # needed here for formalArgs default_linters <- lintr::default_linters linters_with_args <- lapply( setNames(nm = intersect(names(default_linters), lintr::available_linters(tags = "configurable")$linter)), formalArgs ) make_setting_string <- function(linter_name) { args <- linters_with_args[[linter_name]] if (is.null(args)) { return("") } arglist <- vapply(args, function(arg) { env <- environment(default_linters[[linter_name]]) deparse(env[[arg]]) }, character(1L)) paste0(args, " = ", arglist, collapse = ", ") } defaults_table <- data.frame( row.names = names(default_linters), settings = vapply(names(default_linters), make_setting_string, character(1L)), stringsAsFactors = FALSE ) knitr::kable(defaults_table) ``` Another way to customize linters is by specifying tags in `linters_with_tags()`. The available tags are listed below: ```{r show_tags} lintr::available_tags(packages = "lintr") ``` You can select tags of interest to see which linters are included: ```{r show_tag_linters} linters <- lintr::linters_with_tags(tags = c("package_development", "readability")) names(linters) ``` You can include tag-based linters in the configuration file, and customize them further: ```yaml linters: linters_with_tags( tags = c("package_development", "readability"), yoda_test_linter = NULL ) ``` #### Using all available linters The default lintr configuration includes only linters relevant to the tidyverse style guide, but there are many other linters available in `{lintr}`. You can see a list of all available linters using ```{r show_all_linter_names} names(lintr::all_linters()) ``` If you want to use all available linters, you can include this in your `.lintr` file: ```yaml linters: all_linters() ``` If you want to use all available linters *except* a few, you can exclude them using `NULL`: ```yaml linters: all_linters( commented_code_linter = NULL, implicit_integer_linter = NULL ) ``` #### Advanced: programmatic retrieval of linters For some use cases, it may be useful to specify linters by string instead of by name, i.e. `"assignment_linter"` instead of writing out `assignment_linter()`. Beware that in such cases, a simple `get()` is not enough: ```{r programmatic_lintr} library(lintr) linter_name <- "assignment_linter" show_lint <- function(l) { lint_df <- as.data.frame(l) print(lint_df[, c("line_number", "message", "linter")]) } hline <- function() cat(strrep("-", getOption("width") - 5L), "\n", sep = "") withr::with_tempfile("tmp", { writeLines("a = 1", tmp) # linter column is just 'get' show_lint(lint(tmp, linters = get(linter_name)())) hline() this_linter <- get(linter_name)() attr(this_linter, "name") <- linter_name # linter column is 'assignment_linter' show_lint(lint(tmp, linters = this_linter)) hline() # more concise alternative for this case: use eval(call(.)) show_lint(lint(tmp, linters = eval(call(linter_name)))) }) ``` ## Exclusions Sometimes, linters should not be globally disabled. Instead, one might want to exclude some code from linting altogether or selectively disable some linters on some part of the code. > Note that the preferred way of excluding lints from source code is to use the narrowest possible scope and specify exactly which linters should not throw a lint on a marked line. > This prevents accidental suppression of justified lints that happen to be on the same line as a lint that needs to be suppressed. ### Excluding lines of code Within source files, special comments can be used to exclude single lines of code from linting. All lints produced on the marked line are excluded from the results. By default, this special comment is `# nolint`: **file.R** ``` r X = 42L # -------------- this comment overflows the default 80 chars line length. ``` `> lint("file.R")` ```{r show_long_line_lint, echo = FALSE} lint("X = 42L # -------------- this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` **file2.R** ``` r X = 42L # nolint ------ this comment overflows the default 80 chars line length. ``` `> lint("file2.R")` ```{r show_nolint, echo = FALSE} lint("X = 42L # nolint ------ this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` Observe how all lints were suppressed and no output is shown. Sometimes, only a specific linter needs to be excluded. In this case, the *name* of the linter can be appended to the `# nolint` comment preceded by a colon and terminated by a dot. ### Excluding only some linters **file3.R** ``` r X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length. ``` `> lint("file3.R")` ```{r show_long_line_lint_not_skipped, echo = FALSE} lint("X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` Observe how only the `object_name_linter` was suppressed. This is preferable to blanket `# nolint` statements because blanket exclusions may accidentally silence a linter that was not intentionally suppressed. Multiple linters can be specified by listing them with a comma as a separator: **file4.R** ``` r X = 42L # nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length. ``` `> lint("file4.R")` ```{r show_nolint_multiple, echo = FALSE} lint( paste( "X = 42L", "# nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ``` You can also specify the linter names by a unique prefix instead of their full name: **file5.R** ``` r X = 42L # nolint: object_name, line_len. this comment still overflows the default 80 chars line length. ``` `> lint("file5.R")` ```{r show_nolint_abbrev, echo = FALSE} lint( paste( "X = 42L", "# nolint: object_name, line_len. this comment still overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ``` ### Excluding multiple lines of codes If any or all linters should be disabled for a contiguous block of code, the `exclude_start` and `exclude_end` patterns can be used. They default to `# nolint start` and `# nolint end` respectively. `# nolint start` accepts the same syntax as `# nolint` to disable specific linters in the following lines until a `# nolint end` is encountered. ``` r # x <- 42L # print(x) ``` ```{r show_comment_code_lint, echo = FALSE} lint("# x <- 42L\n# print(x)\n", parse_settings = FALSE) ``` ``` r # nolint start: commented_code_linter. # x <- 42L # print(x) # nolint end ``` ```{r show_comment_code_nolint, echo = FALSE} lint("# nolint start: commented_code_linter.\n# x <- 42L\n# print(x)\n# nolint end\n", parse_settings = FALSE ) ``` (No lints) ### Excluding lines via the config file Individual lines can also be excluded via the config file by setting the key `exclusions` to a list with elements corresponding to different files. To exclude all lints for line 1 of file `R/bad.R` and `line_length_linter` for lines 4 to 6 of the same file, one can set ``` r exclusions: list( "R/bad.R" = list( 1, # global exclusions are unnamed line_length_linter = 4:6 ) ) ``` All paths are interpreted relative to the location of the `.lintr` file. ### Excluding files completely The linter configuration can also be used to exclude a file entirely, or a linter for a file entirely. Use the sentinel line number `Inf` to mark all lines as excluded within a file. If a file is only given as a character vector, full exclusion is implied. ``` r exclusions: list( # excluded from all lints: "R/excluded1.R", "R/excluded2.R" = Inf, "R/excluded3.R" = list(Inf), # excluded from line_length_linter: "R/no-line-length.R" = list( line_length_linter = Inf ) ) ``` ### Excluding directories completely Entire directories are also recognized when supplied as strings in the `exclusions` key. For example, to exclude the `renv` folder from linting in a R project using `renv`, set ``` r exclusions: list( "renv" ) ``` This is particularly useful for projects which include external code in subdirectories. lintr/vignettes/rstudio.png0000644000176200001440000015452714250050336015625 0ustar liggesusersPNG  IHDRNV9PLTEضǶywyiݙse|e5<͹Q]S%9YS.O!`>6َFXQӄ)n+xx ]IkCL'tuHߤ(qld sɰZ^ׯT>~tLɸtwaAXmvƄĝwRӃMO͢.ёtٳՔ@\sĮvDe≯5OxpL.4@i??-eTtĦwmi{ɠ\o޲r9vt},puy#NӞv*O9X2'ȊZ#Hn569zЗ|b8gchY9,@zaf??#favp-熪W7 vuf9ˮ N/f}X74'UUfnDb}ظ`rD'v5GT2K J٠ٳK&S|Gᖪ RqڒmvU~'h[N!Hc04V8FHLy T'E8zas0~}tv!7u4JJ=ehu+<-wuoZa商iSM,^wn6Ҳۂ՜炇+zU ^ao5ֻ|گ_P0`4&L'zX$NBcU:\n_AB:I $n7}L~4lMt5rl~ ͯk({zZ'[^rxZ%OB{W=U˞(֟ủt9a6ם@oR'NjL&݉@,OQӠhw'0-iįBQ8t'ZНhb۝h=Ė,k0#St kA ϧWnwr͋Jw;%>^$Mӄfy>ڙ7 ;yzl-I`HfDDR0TN4e2 GLXwhwb>s=Ӱ Nܩ,3^ܽ{wD ͎$&g xVqqN5^nX#Y^,0PA΃,/Wƕ ( M_MrN>̕hcMc]l=< d= [誧O'M F'pJza\;C۽rʥK޼MBdpyS'^׃\8KKD{w+غ,v`B;JX΅dQd]|ZN݉蝐#sr9 6y$46]ƴnT&Wo޿f]Yc` B:au ۀTZiUZO'ZT'΢*R۱XՍPz[w|k$:!,u&;n V t2l`eDrQLIPM2ɬ!뗽\ܼyOv; ZI֠`: ׄ&NDoWjIoY&%NҦ۝Lq8 #՟ȣ[L&S,,̓>f}HCiW܃߆ u nw2,v&ZSlB7 !\ĒwmMdgx6s㄁$`'VCZra /H&ATUM0ݸj%TÚtJIդڕY!;BPZYm+m*/n\)?g.%K&Kf>c; 6O?N͕=:m|>Y\pplbuN+k2MvM_Z/skcwܣg_2z2,l2-9SQQr[g;< #o#ނF:J)t2S l:R! ^^` G%mEդODVԉN:9=L]QVp۰g _6>QNxgNDT~:}.PXI@ϨS&+P'ú:M:`Wܛ#7/N:)VN6BO'rw,ue`w$VF@ }I29˱ r~6 ٬*!{ǓR{Ϣ{=TLN@'ZT";CЬ,u' KH(΄ zU&7 Ϳ7ܛjLL,mr :;OtkJfZ²G׊Q*R.DHѮPv]#y Ic>ds֤fILW} p2x` GN'i>r/nN4ww?+kW9ZZ[-}NST XnIv3E vrT^lR'Yŭ5\` !L`I:8KH@L4BU,6wRΓ-:N}'w/^Nj p޻.ƚ]F3)ϝ6?{f;h\ɠ^5kR'EL餕J'BDICXlR~uołQ;F'pٞC|ήsfc*{'sT>Ii(C,HFtrN'6{M 3بf앲h +ZD=H? |bg&4 MEmPNY~\Q'TCnX{ /'ԣ@'6dљ |?l:*f3G&d}:?/,ØD:mƺMm <܇SvBk3`o@gc[J )q>L80֝m`Х3Ȳ'3~xnc7;7~ȓh(uU_|vm>0jc-9Q[:Wmi>S\NH,v՞4ϊ5hTxnաwpOߺM#_:;TUk-`.Oz|TfµG )[.XTn/}Ҵek kJ?DҢ\xy.9p]MN2͚2 T IKϹ<5jsr,ijGYN/jD\yF|;qYpz$Fj[z{}{>r9N-UEziG;[G8,ܾ g3)VU?Vic]]uZړwѷM窳`BY!"ԝ89,.:׎->Q|$:yxs{<%kq`;[Ӂcc8JDG1mwbbz(:_fWFjk)Z{|geFvI uL?[8r1{Al~Sn)]*N(Tʝ،\\,&'M C[<%86rP؉=wR\N11$dAlXf[]Bb6EZ<[v-b"u\d\1}9;"Yl~N;>v< bjx-r2J^Lg i$Fv$dQɉ$+|kiMk+Ͽ+I:SF;q?fGS~At";1|]T ^S: >K΁7;Nld4ہLGQŐ8TdMnIJhd"+F.&\NԤפoZb;dDXH͔'\nY!ěr'Y;,'C4@@;C#&&w7{^c'=\N~Y2I=F=r\c0ƌۥ8N ;9U!9ɗ;Y1"rrrB yGDA49!Ai%r2d(Q/Y@NEҳ(N8Ύٝl_ wB:;N49DFdT@ov,vݑÝLvȂ7ϝh;;r4qoNX"?;݉< 7NrrbS:Id 9gxN;ё 5)"kv'B# Rf_aAI&^&!;}dp'B(1Fvrr"^. P1wbkd4v4=5V؝FZve;m | D!󼓜y'308 @EInXMPZEdEw.-Q!e)lX`)E+[B2X p_dmV}*٥WoCQ e/;5vX Q,bj@c' N 'MN~ *'\+>)Wrrs'1E&'.X݉NtN 1$2/a)o ~|C-P9 eyHSM;u zΥs'EOt9Ѯrgu'5ܝގɝ8 'Q Nlb?L3w;; V_o"_;9\(ZÝNmU|t'sx}I?ncN[֓^t ۫E$HȺ;IR:rN?)/'ߴilX6b'J_2/  ;yE>EN&O'RzbiƆy';N̖M_:́y'%l[ANgǡN8; -@ '՛v'I͞$K#A\Q /'^+V'Qz'JWwYww a$՝l*va]&_늈&wrX,= ؖ;1E݉/_g*#'ʘCr,W?-@q9V_72Nv9| 'q':;Ǘ 4z9Q1 %;h[N"Ꝕ 3 Q<,W;Z ђIN nLc,X}aAO^Ot=<8;Zf5,SPޝؒH 9N<釲 ^S: &=ԎÅDykG!ARDL NG"j2rH[w$v:{x(SjVNEӉu݉aIe~⛭H1Rd ?4|^uqnVj cԡX$(?8uZqFٹ_+z'ɎbT'ՉptBgwEurg=:`e=t b=pN"hM$Ɍz&Hn) {LT?\Lws$Rzƹ2txR\Xd׉->>_&>>43' `'¯YDtYIˊQsNlY̠Ϧ*Q`rKOuN>D QHm61 (mj)ZkmF?i$6'5SWX5v)Nff|V/xene`ɍLv$r0N2K/h1uRfL'ϧc7R7w2tFD[n"}ou 'qmu·$I['RXnuzN6;u(֪xWJS,m@TNHMΗuC"n:xfa\L'ω)*-DE=-VAqPNPC'aOYhGDYSRdISl#Ӭ􃔴cģ5K$؂pjS'XsQBtwUu2یxWr?IZ+LWiAaDQkgTS3vW#Zrn`z+VGOUPN|Ȯ:!+4RikKbI#LW߃G6â‡EF-EzoIԉ=Ѽ]F G+f#0]jU1$!<'id2Wzdqfqh믳˖W&#G,ݏТ`NxIɲ]'oN8uKj4n3VUpyWe!aFR_j#(5؂ g[CZ LVԉKkGW#ޕ鄼݌w-;u|B(Yrm R,t2ީ`zD(B؇+N̓E'H̞xN.hJJYSlk_7A`ЉdޕЬb |_?(86_]؛WŻZ#5*3}"gLtf,qOg`:a^'6 n׮Nh+Vb HNNli :A-yu9IRk\w:1{'p*ըN+;9ߩG>N{uh]'}!Խu::;4ghgvKZθT'f`T:,w$۰UJfЉ9#5LC'Z zMՉ:#]s`7ۨLGŌNDYR3;N)J!XNL 3;dBC;XdB}~n-X3[ֲBo@VXRl3rAxUֲkcc~I[os݉8~J'\ՉJ^Ia&e['^ՉXy^Δ›ƺYԴ9k67CfhY‚d̖U"=YLVfernɮ .ny~-Fһ3W"*J:j9F{~ZzԎ7gѷVȊǻxu+ks`;oLWeB6{T';K9v] ! N"+Ы:AГ| N8'ɖr͊UC3aYǃV~lNU{oǼVH簚_W'<ݮ;|YF*NNv:͊ut;ɟ8+:A(*>wkW͊5u", N' o{'mVE'SSi|R{BQi{[~߮kurMVesNDN4 U':q/+3[Eo @NɊuӉ;:oVlY~Y)۬X̄UxgV]YN; vNՉf?X; g;t@W'عB(NW'wƖ^tr\࢓~Nc!I_g:ő[7ܫbs=3\g 3Бl}:g ώZbqW'b߽j7[҉I|Kw"5hTl81N5cm'Ku:9;#:uRyJguRrU_C(CG!+fhK'׳L'gcų-:aO 5~k%kjfi1:ͶBj&w ̃l7`,ΗhlŲZj,|t³-$'?8N*겭w^$Bۏj%"A{'NDNɳw5AmO։ي-NDU'kB1^B'ZS'(N|GtЉ Fu+3;I1E{ J5yDh d:ot2`ljՒ*Avs _zE+YxG:I~e{XC ܐU$Y/62=9${[z1;:Vf{t{>>R))qg߫A_ʼjhv&k| @;{c]Xg{U&I50joA$Rח+Kz?iȺL:ELNh7tRȴONTeJ'`W"NƯԪQ2c[rp (jRW`2eLw:ȖpgaPezԉeu~m1{m]܉6%bhK",N:2&TѵKD ; :Qv&捏"c[:1>N:IZ6ɟ+˓8"t\T~Qs'j`'(KWlY:tb낤ܟ0mqڥ>Y}1"҉褮_r;qd'rvX+%Zmn˾*3꾼^>"2?7TΝ9bet|읮t"}|[hpx_%eۮW51'UK%.̎XQ|Nl@'vWK:rj|BU4ӓm_XNdmKM}'J(hr;9XhyzԲZ嫹b4@ u‰bht@'Nl;楓:Il7o߾k>GͷU{n+7ޘZR߭On@C6|J=8{Lq +-?h^\LvGgȍD-گ'wt"'\Ol'j<)^q~#zJtRNѨS1::ڐNNzoNbM<\2KN }ėŁb)ɰ_8Ӊ`bgԇ8::8NjI$5w"e^ejW[drIP׻ʒ-[$f#jG +OoMsNyŖJ/NTcB'>S}mnV䄮XřU tt8凉܈<]XSj`MDX3>#ܖ!3bKݲL9|\7EH~=uJ'^ neCлG^5YN,S9R(uk8_];]U+Fn*ubC]+yxfI3R p^IgvzGgec҉)YȆtT, 2ײkIBitP:5DXٹb=dW,@sO't8e}it3OvVDM";'@CoG O'g;NJ'> 4A''-|9m6:b~*:~3I-oCS'gk5bݵX`Sg*6brvАKX(2@'6٧-ETO% X18aeJ';ӹt&νN'TtNюn:1̽3y^ ݯ:WՉDD侣Eho[5N:wIH3؉RXA'Q I3;Aϱ`G\V|_:u]r8V6w9FN}'`-/7ٳ$_iGI'P7x;Q :iNxĤc듴'ЉOhcf&L8Njv ]nf7\uf-A&:9>y7NrDE'B}<誓0:/it-J'*:;t\:utMI'̡% :iF:A'дtF'N:)tJC?fC *b5t^.H'H:x8TuЉ66ht@: NtNtI.;: L'`@m0wht :`: S:"@3 +;Ьml$N@' @'N @'NDqeݩ8eWw!,Z]x 4\ljղڋ" [mz\u#ƨ #(䚋\o2i3˾oaفy=38 $c)Nb@Ъ:-=ΝF hMD8 b@DGuĉ>[N@8mlbN@6qb6qG6=Fq6qO6qǹ N@@-v''Nz|f4|f nc N@u8 .&qH8X@uI'au+;M#8$[ NqI'P@R ;T'8 '8 Ɖ `:qKKZH q`.k'_Û8I'; vSGX?r(>*<jJė>. 92u.u'CC4> ="՞aB G&n4N wlsX?p4{2=ұ- q|qՃDDw$xc_Kq"y׊1 ͫ.ߡ8ɭm7P3F 5l 4x 7|?NӉ0#Թ,Q4Jq8qq\]]뤟ʩ tyD/LN\v~}#4NM\sʼn8OtR @=礉)F]lkMow\:e9a>>Jq"Q-{D8)bGHCF( TB?Ne;RA+EuDD=4Y|RE4qIWw,A5%Uq 6N'k|jsw3 ׊$#˟ 7k?i0qB + yB+%~*6'C19qF<+G{W)t!rI:DyCJsYJ5*xzފ 0Cs2b|dӔĜ"xRS qJdBuŬ2YSbq vO۫tx틩bR)1,TXlq#*Ϛ7qwUo|_kWn?v6L QMMX(ё2-$N1EqWϻX;T饗|TPdqһn?]u /[W#@ﭝ6W47~YxanmZYͰ:k 3~d=yAuN޲⊛JA_me%o/)d<G1'FvMhR|Pɩŷ̎FG` uUNu|m=05ܷ';N.vJ8Pl[4zdDD9`489ke[,ܠP6Q\<8W^ƌKw^e4yu좳8HxU'SQ;#J)KYv#N-M8'VS%T''?W.z&U>vEDdvs$ v'YKsk okh_?&3si8x5l DD")"B*t[swXPPbծ+z ViTF9 -9\?c0Y`a?~pgyw\d)c/iqG ]'?_t)mbtY;mYxЭB|? ";qgOfBT(u0-XЉ(^+nY? td̻b'C'INoa_|uW*t2*mfy6*ЉRENOpE);yroOOO%:}g[݅o"D<Ǟ 6)uD|woOw"D1lgS&넪ÇXܥ"D.]h2YL]w.X<_,]UauP|7le3:g?ƀN :@'Y)|*T;j>`i? tצB>q;޾N70l~}5gN,kv[о4N"4D`zDuL:izK`$h:*iHkcPߘ3<3i1H'_67ig/Td$Vdّܲ@ -[sG7d}\ܦ*I')I^8\R';% a:Û$ѱMvl#}a4Fٷ2W%fa$a`AHnkKAjke,싊i< ;emtRk "WjH]WoBRN\u9[*F'rIZDUPGYB"dYT'C|ՖH.UM: tːFfic'2 h~f,b&!VR VX:CP~߽ۜNns*5? 9GO5JG SNtqA7.jϝs$/xc ) S[D{>\!I5MHWYus 5G(PɬYqbZjن$36܉>3!d!5nڿ7F'yo оj`=)YO$ [2;yYDmUIAN- ՉMvQ$Bvj.f],2wYop.?cIH},1s!^Н0k'V"^mBؕ' tI^;!uedUQ= #hOSMHNT&l/Zk)}Sad]i0*75Qڵ VԉЂɐRtg&XSA+'.KSV~Z;)DsÔaTӔO}4Z\p3V7M+;=sjP#|`{UԉЂɲ!}HLlu6Wc6G;wIiA!jjFnJǻP,!4n{a徥ӭZ=ɦG7-1}?{Z; :qaه],:..x=:'LN`tgN `+?{Hq#;4vo4r;D\骝 yݩc^-,i/:@0uRKex^kO';;nON,PcU_snx^h.~@0uleNWɎtDi33>kԶkoX\rva":aɉ䊓,= :1zEŜamr|O8ԉqPRzwL7'tDw; t;ZYJߤ+t|EZ_{yǥ\ISG_nDq/v+{;߇I}a킩zf)Qo\6U]m3fNزI~ډU'l|nm6hg'96iъf~"殣37i䦜u\kgS'hɎzʖo1hzD[ȧmVn$.Śء-"̌+Z@6UHX;NV|}#f(>]=#x-qd'ڿq޵D^ ٦ښl"Nf H1AިA^FXUVޠ" +OנXWKt݀.> *lٴi&9w.c{s^9]ۜ?6;X쵉:NFj$Zf$846V^<i@Dm06mNlXUWRD8{~:Mʹtj6|eV'Nf1;z r!0ctQй"p!IˣlhrjSqPb*NNGL Zu?mNGV'cFN!F3R{bVnR}1ͱXAuTV(D,"l"j֌ H :!pBdDN4pޯھaU'u/U'PcYI4j}vk}M*ZY8q6;LDɴtFTىP398vцI6IW:j_v=otue<ٙty¢ͭfm*,OS&4xr&XC6(FEb:Г'ݘ? WNf'%jvx؃;ļTW AN,XfqwjFmnx|h4sFT:;yy-q -:zö"Ҝs%U+N}'(a5X w} $wUgR]m@wOpJf>ZD_IJ͜0+~7kSD}zot} \ߵ;cFCxakWͥy؊o>ĸ^Q)ple >zU`8/'NCc%pOO_5I ~p8a}rxBG8yM5}9qY-;tAk6zS:'8Y>,8Q_NJDvWv;I}VlNyyIN^g{wPLQ+JiE|bNzDV6ULb" X|+hTb?8sMxzr0ܤxGW ×0=4ʀ/}N,0;1TpÞ\ a0S MϊA5ABRzhۊ/j_ՆZzb@!$'g7'wtbW]U8ƏPҽJDKCJj-k[p¨K@PZ_ \17Zu> _ae<*|V6f,FKuo@d[| [:0Zt0F;揙n197uU!L&1%.;ol^SߨTf2  xB" ab&O& c:<(ppB͘'M]ROI"#]lH%zn,1Y_Ne: ҃j'T8afJm,D)8h` ZR߀w[PMOF Sj'M*|0:>=);Scs(F 4|J3y8!X1N|ϵ"88]2cԙo)bRƌ̲(c;'#(2(>15G_V!I!WfnKfg%C_{RD8‰E  jC- #W14'ڙS GrupV'`" #YNdg~f#t$" @f:7$w=ROG3M֔w!h8)]K5S&NT;ԫ Qj3N4䑢19 p0!LSj'"2S rѮ=;hOvd+Ls8! }05vpbhϊ{řɦ%5mq] ۠^TB VVA%U ĊAO匭_GMjz]c5i4μ̳˲4yA}}w>rv6I:[1d]梂%}?TNND鮚j25:yt 6͆&KAN I;!Ur<璚n;d7iXINt NB]3 I^yne4A9  NS2°/lj@M05d}2ګ. d&#x';qʡ0d3-=9Tɪ+5!ZOD;3cbF9?TN#pzr#'Vnb'tu 'jWkf`9>(G$[kZo2Pm( fһMUN7wNNxM05Jz10E]׳Iajb'e$,r(,D|?;1X@d g 2Vl믘4PҜOԭ}g.n}M-oJBҶUGwv<uQ9wv6exاycvߓx:QWcՑ^wۚTNFԨ;;eCk"Fޫ|jd{GS;=UBDQY󸡤2;*'0**grDNNNag6/H`I~CW}zP9Iufۖ4 ԰-ks)tr]Y8+AfR7~('Gt;;0Po"TNU.}E9`j.a܆]|g,y,m†šJjS5'5 '}fI74)91+Šj\NhJH;Aqz =w;TD\}ڶ_QnZiM$azM[򒃝;vݯ;hWxܜ܉?ɗ IDAT%N.s'e4? ;wHjy@z)Q5k?/vhԩSVcUɉv?w9)zMZM94 Ԕ%٭ۋ$( m&/,^sn7h^SƛIaj܉I}S8Jag=sKvN;wGǼIY/BP;6 Wu]lnZo׎ˈ9QRݏ9BOj$4!X VBL#ym&0S}PT (LsR -?ݐ h#>k-ظ:2!OG_r$MQaq-5#t!?LB^2 lrV/μ-v= V'{Gk;H-H' nqOEV9wxmAzboK1oʚ>4V "= E1n^tJ9LJSQNDSo\ϸ%fr232{nP{ }i<.btQwR{?d.ӋQ2‡M3cF9ѐr-֌Ѕ0 0$ ղGEN"?ێܗ39.cZI2YEc斻c&)01Nj: HF+b=!wVtD09 ѤqL'*ռbQN4*Pou%Iʎ[Bޮ qO=Ԍ"IP)-wRVk*CY@bS'QETSz$Z9_nf؁IzCQIb3O\E#{1@tުwx 2VzGd 9i%]*.8K @ٲn;9 pFU 3[jẅ́NGP͎#߿̵:3B YDNqLǰnaxW3HbSMN&Q 5ƐB؉![3BW6:0ȳbT~)K=ecc9BYw{KKA~3(i|z5b ' so,gj) фPp "rQE fS9BB]+&."'8c]]TWST h *7ɉE00P,p̏IN"~ȉ»pPqwN(=)sY1;vHˉMyB%+j7PR9F 4n6 B)蝸T; 1 @xW3B({ʽp!r;$P dXp`;U7^ buBHωU~5Y/'ip9^ӎnTQ9W&r"N4d ^MEF%@.SD0İ1o>fCD cA4/ʥ$?Zx%|DNJˉ9ᅍ#'0nZL;$=Ɔ~C./ZR"R%Tl^aK5 1%1q V|J1BB[C\*JZZ}о!{g9gv~؋@{s~p1e‚xWh6(fre'OjrB[ 2Ii|oXNˊœ]ayw2v68;tni0SȷXU}a:sT;}'$Me b@y%*h@QNpO1Xl"|׮Kq0U{a(:3;xn`Xqɉ3-'̰b+_ bHcZK 3ج0PnM-U棡@+S[qPX܋ܻYSn Q2@Wsa>QXf9QrM[ɓQrMۓ` ֙:ҾRmڲc:ZNiӦD6mZNâM^9Vl*z455Mb&G.pW&geM2YmUөfZ?D1ȚE_E"@gM}UB5 ]kQh+9h8>9ibi'3;X㻇 W(gAu|ÿvLJ[[k.F grYU Vq̼TM4D{.x"U <H (Y=q> i1}Թ&Lhk@R%+(UY[Uoȕ u ~C1㜑d٘Anr27d"+J4g" CdjH !!xuw<|`Xt_fF.BNq  'Ȋr+]Nl_w b8`@\̠u[}SܐDb`gXȊm;(:Dsn։ogyjfR\T/`"+J!b5$WyΠjO֖zr]BN-('j[mgj7ɐG5 H3:]r2'D̜,L^ ˕:Y0?n'"ʉXUFnGd%T\H8sK?QSM'Ks'DVT `9Y/tϗ"/ײcncv*n [GT윐C;/I&w&3ID93Jp:urD1?ET)-6R4#'~"+Ā8+}ŸQYPmjvʉr\rN| Y&f:jx2ngYGΝ|\WkV?1@pX/'pΝ(`*5Y!Ao5ASLLMۼSMB2m`/ʁjz&²qA^xNk{ݻj1c?HN-'O'mIk+'3Ɗ5=Xgeg jߪvj>\cR9)]PrKLHu|蜻!o! Ruď<;&*nMJQ;Hd+;R1@gͿv[sbegȠIq ѻrLv\نz HΦ_~zegII88r2sXD⾓D%;rR)'"w΀ɍ"+J9N&~DVWN ;jNZ "l{rDV r½:@g}'>.3h`gǎ4&c ?` lG"ddoj05,e` mʂj,#@ Vּ*Hɦ"kDXّ`+sZY=k/Iyt*mE/'?7QFatNi9m4-jMˉ6mڴhӦMˉ6mڴhV6m $'cfo ]UX c QakKV,feIT`-Rp6VPS^jY+ =y7KgGdjIZDئԘqx\luh׷;m疖#ނᛣ<4p$#"ovSܛWo|'/pe/Sjێ ?; e jSjfe`b%'Q20R!8Ak3湓L Q! b X/[iW*>{ņ'kx֐inmл‘𮎈ʼnWȄDgbmS+W:0\CB'Phd'8;1좬RVNJ"N[lRWXXKgr2,wZ(;rev7^d7UijNwuD$qv23lhÃbXf/P`zlNughM-XkÉgIqό>wޱ;02X,YGᄵDJ'D@yH#!,DN@ GS=њR8}Q뙿Sv2z^wՒq8;E!p"B"D!B"ND8-"DHNƲ"c"[eH7[N$t*'stTo'?]뀓)o4mNդYw.8IIS[e'"抓i$pqF$>jeZG嶎Q̏.Ylj_D~yt'sw"mPb]fMK#+2;UW[!A}@!*)@! P]밦kեt۱DiODHqk4NO XPJ t+j\9yu15 N>jAwel+bZ8BXr(d%3NehFW]+"q2VbI] 'Wo'P*HmjROP!b0WwN&y6w2n2*yOMT'uFV޻tG.-m f1;ޝи:ЎXDbA_[ Nƕ{[vQB8!;~p֕EM|4DRޅ8bj7%NelK7'W,O+" qrk髼b' Y=Mm8&x1}ȋT,)d" yO9 v ]]Ff62&<4-j ޡ$WY54TuE,Wj |ȴvD섪0N'\aޥֶl'W,_+"pBfN?DGl]A3LߓԣHmj S `d5l'P2/T<~CIWU*4e__Kk$άq[%&WnqҶ epB<ТDw,bxAqbzg^nvbse" '#ӕg#p'X 1R]!GԎbEY01N$Pp &]-X{ѣyN r{q$&WpqY^AKxS1qBe+Jrͧ->2ykT,BRs'VW,_+"p}%HdrAl튍`'Og +g'4N@Tv߼wG}$ٕnM^Iq']FvPO4yBd'H6 N -XuIrp&;MYYFR+/dIYd2+&''b$nW*o+s'ϙԦǟYd 2={er'4$5=]b+w .^𨬇'S8\#zDC@Q(zh(OZjﮩ我F䈉m\b_<3g]pU/3|& j_~u#n#ىAyDOLrh݂-P eWhӹvNL܉4(Y|XЌ..hv2XU,`UקVѦԲ8c7Q*#ũg ʂ1i$;AxםnzX`'<*e ~l'rϵMUg)ê%7;;kq?g'BATTyUf 9ÿ#{܊pX|XЌ^Sd*Y:;Q6jR+yKIEgVIz;(40*tGvƒRV,CjgFvs`d¬; uu' T*ԩn ]w ֝XhHi,Êzf$2w?hDmHe~"e1uR(!0!a'BBBN ;v"$$\IR{߂}Mb%~dOm+`_5[\nfջt{dcr0 =ev /c'Qba#KќN&.ۉZe܋RvQeBB1qpol'SxVkY[~$MHs$?vRXsxa'BO+F2)(b'ѳbG)T߳(960Ӓ]ũ-aNDd4?LAt*VU*@ N4"+DUQ&#VooNi IF*-BRU9z4؛e9w @Yr Yo{狇\(6vNfnݦm XGIۯ˚֬(u6Fdmy{S`W!] eg}YDV+'ҪvPo\%*V]Xjv'NN8T!eV倵7@mdxWЍz|AnmqVGl~9߃<]iX0`JhD%ʳ`;QP;A8!XىMĚbYl*; DdU>s/t ZtHD 4 XLēvh68&zVl15THCň[,BXU,oڭ`p$b0ND_؉P{'} yQ-+/+c'*IFUܢu,*m|4 l'5B90ck0@XZγQMF*-F܆̀H0 GO`;Q0j' ՜}ߜd =i;2_dXΣgX^Y=NYQ^V/?3UGIۦ88VAU݈=X8XqOn2Si1▎tª D5[P0Н(DG0ى-:*B1Cy0)d ٺ0"kfKv/ա!GYQ 7Z2?}01PiOX}Y  dS[bazT2J.Tª("+l=`HȇNNN(s!a%uۣ %Rꅱ=ZlDU΋G6BB1!MO:);v V ;v"$$$DHHH؉!!!ǵ(Y6Y֕p[#d*؉,YYK?›؉67xWuwiPE9 ݖjI c˲.E/z~z[UM92`#J#KiằmJvSH Ea+=Rl͠Z(7֫oux v6L`'1熒d0H/ /g4Zt Umӂ9>-/!qij'Ӕ :)~2qavoXbKGvrBJFp-PNkʠZ/ε.g[(v5+v̝SN2+V3+o=Iw]*jo";axWJX{7z9P'Zn x}-G5_pEL7^n$뙋fT,ܳ/GoÅmvkvr_4SEXIx9\͐X&\)p U7m *Uʔ .NVV v:7gj[8R~8W{DCJ[G杸 vs ܄SΗxDϊ͜?N l Q4RUUMNvmy21R#K]KLsD !܌[ Ť\iX*[>DJ|3sfn1%!"3|;?;gI;10QI+>h<Pwv4gv.섾xxv񮊰ZZc'<˫$}t<|ƪ_U]RiAId3)Yi7c'bFbS} WhI 9ӉD$F۩Vg)$.kw /E03NvMy˸ i k(ymhYPu`̤èo'b~u(:v2Bd};xW:Y\pk0,'@WAds 1ϩ`@deU"u9VۉTklZCO0g; 9ӉE?O}*.C:b:Z'E\Z翟 n' ['2Qu0+6}6rqM;yw?;YLJmUI(PF@b b.NLَf'$vq f+NXc|);ap c`4;q1v2CۉF:Nȏ7`POa%-;IRE㡉Qu[Ʀ;&=7a''Z\@fgǥ"ZVW4~1,9 'v,]k+NagI*-iJV13; T OjΏ̎(fg-fZo&Bh'$UbNd@ubܻnF`gs;k'[\&:C3SEADB;7wԉy֫ҙF1Cy0f[W2+mw4ͣg'b#nl(VMN ^w]bI~^xWh  NTZjwRUI;QZ5jY;IkmYyg)I;#cCDزh'(I¬XweUY;LSa'W6VǞZEsLtQ@dI Dى $Xir'E&횡aBTQ(bt1qpiD2Fc#Sa'ez/`+15E5UqL(w*A;щ=}9w)?IQEPIfŖN3_nm-ŔP ;!@v8.DGv%U=dg'DB%;b0vrd[4N^5N$@DVM v^N]SO<9UvLIT* J6+wֿuv&ΎfwH;T=V;S]TY5ډBJ;tQz 2`6;PTi' (*٬HXě d15E55։گΝ;nu:񇨢Pdb)&ܲ;bh'鵰}rB(DUDV=ˑ0vB+>g˚zDMBasfV 嵓Y{ϘۖZ$uE[g͌ 4Yp–\R5@DV1%^ʙlY5-gvBQ#7MjpU eDYO'j =r,Zuڵ!:9^( *i~=u'-ku'GOs@(j`b݉vB$aVl_fS#&擶 0 >?~Z^$ATQo/]TavRvPh'm'ednNPNB;AP( vB?줮ÂBa'zز^Ϯ@qCiq}6>|ҹΣd b]Zk&ۥ!._BAͱ--C}f;kAh`ӗi'mBlO|v=5#Yv63%MNPI!<K# bĵN]P"ި-l줬e26 񱓻MC^;soɘ;=Cl [[~_+xuf(`"&c?jNf5YgG]l-GpSӝ,`)p:AM㡁-X$k9sA'x?wvIƚc^u"!)cWR7l|7{XhbZ`H sƌcòeWh ډPnɊs-$pGcSyޭw6_w?۹] c'RXZw&7ꝯ΢k𱓳b$'tk-;C&YvIqF RO*'{6~Lj_kزKnAvgᎹ7?xNf'vMѺƦ*f'rfה46mvIU+V/&R)Rf)ngc 36&&iNm!fNT1BwɃ]Ch'h'qz>Sg'V,{O;&;, ^zҿXvNJZr6vЄFg{P,ɡub>R%i'3";챤 IDAT;& .f-e9 Y _}.17f f'sL^vBvJz;)t{I=cNE^H,LNTdJ,`sAcꎇqBNJ@mىΙ-kҸ44%ccSq(DbvpN X4;uš]lRNk]INv/['(pvزNV'3}:w9=6pviec~CLf,ѐWS hc*`/"B &""ȳXᯎIHeVݜK[%[Ӧ&{~{ϻ<{Gǹwχs1NlupXAdbs%N$NIɣGj]q"pBaֵ"?w}u3m& t8gEA!NkI˙Dx$;!+54gyUZs'b 8.ȴVwN`OuEuԾIěIEJ:88is9?K'cs*5w憼ֵ ۷U5[5|rV~ @]]}gxhoPvѓ8ӍWvHb)P?eZ.䵟$pGݲ+H匊dK}g^9n?+}w =',gZuz~Bsnl' '6. q~]Ayk}]0KɪipO\ّ81bů"=ֵ}'qx߉:ʘ;aMwܠz>uq }'4={﹮8DΊ[Nl8Q[brFEwO]Pyϱo w½ ɚN8yXkwBbw/y߉ζrݾb/U6? $s;v~En9s6%/0'=vsr8nhBXzCUui~*;'Tb|6T[D;џ `j")_}ȷxq"#8iQFd#2$Nd|gBweHȐ!CD ''2dȈN+V bUc" ؃8gy(1P [ ̼(g4\Gqga^R5"{SD|Zl%* 6òqN" ?W=f]:gM8$p5ږiS!ʙ'QU[IM:ׇU:{8IYi59SEp;?>ZxFK3I$]O\tϚp"ƞ:Zjϙ'QU⊳ݼbb(,p+ylNrDŽ ;)#8+V*KeWf#<| -gp팜١q7FVjp.NjAXj>jDDM'ծĥَ %.# 7ze^'E\h|jۙWP/Lq|;8+V*%^Kj4G[65ZJq|'8vsȊŴk.( os.ۜڕ\PȞ0闉uGVCɛ SGt:eSa Rä-t{kaVgO;Yҗ1o~/jA[ 5;˗X{٠[б3b= +қ$®X]:X~6NO;YmȠq7FVjp.}'81;m2ئeَ %5+_G ,sNX -ZV;d٪X'Nxŭww z!AQp8Nh.QɒâȻb늞s7ٯfpXG_1,αJ\FTsA VmUg9:Ltu"+;ϻ+V|+o'}'|qqP$qθ~?[ e82 y+<mqt4{Q%N$N&f&2$N$NdȐ!q"C 2dHȐ!CFX8Ibb&k޽{9+F"Y NaoȊJzE\(g<2d2NRu@H)]jyY (N|8Q/ [lMY)3a eȈaM 91bI8UDjBVljj(MH*x|,'-~s2%3]N8-I0B՗87`2R,W?z㲓9LC'ӻb׺بm#Oj8J as[*R1)N|0jAf`x:epYO_=5t΃jb%d*j+& &ɲb@'dј˛MCT9EZ2+2 –qbe)UqĉU־;fHE )9sZаvr `Go3'5<Ą8h TƊiX:XB%4UN=\ɐS}sX8ؙWl٧oG([?6&76fDjfܮqe;..-JE: JD o ڨE_V]eh!ںuMv,h%&~hosy;003:;{s^w oZѫ &7ZTb E'#$ vJ@$Zc`'7-- 0K+ELFz& SBTNj4EXON|Fb]CZ X+p)@T9W.4!HXE5ϧ Idd։{Jq1kC2CN03 !I;AuB / X':?xQYP~x=$Zc' ;"m'5/(jXK&DZy0ۑ Y HErP/ʠ\NXhĐk fM[EQNyKG|kU[NCª٩8|2tDVN<g&QfZΤ_k )n;Am^T}c,Vd1L}`t}y=h'QTwvB _J;0G~)$XN(@ιx{\!  t݉i&Xk:m7:D=%VW4Pbw-kI_:D[t3!\wYfl'ıߌs'< J/YcȇGr]qw*>5Tc4|zBb0M QLXNHӯ0Yl'\8LYvbNX,bXgNdji_cΜI6+V ]}LRO(.SOޭ23OoC{mx{h)@1J;+2ei \NejSX5t ]#~߻$ST>V3r";Qjc FrWݘ4-50tMe|o̸$$jVl+J;AVlg'Ļ+v">F;bA=)|3$D͊ŨI bN{bАQZ |wkɃ)Fsmk,1Φ_yO U!V$*Dn\ӗ8z{v9)(z߰i]N> {0|Նga.t!ksǓxG<=(J\H\rx⦆MSVFH D2("!CE+amFl' f'Qb=vw2C bNZֺs= 4⍳Ӟ:aJ7~Qrx~8??9i=;[:(VBpuۉG4:p <7OqU_%GLjce]}Pob+ mA4l8p`uؿ?"ڰX }gkW50ĕL<=sJK ͍N^-kƩx/}x~KARwŎP+a%B$$ؼsJSXؠΎId[|r]_w,F$aZ}stPzbqVY+p$ZVH n;tvJ+lX~"ֿm iN NJa+lS.|k˓25b'Qb.'~tYK"S?#~ŏ׉WN"K;1κlu'EWr"澤^5ם(H%vb!tI%|. bpL^G(Xai rO Rie/CĵkzgN$Dbyw0*.t݉Nd B|^v0v=+֣=τ!/OG9[SILe{}uxۻ"'Ga"!Xô7OS3cx&' !tv)>bH$F-LNX,gN* b;aXl',vbNX,Sh'qdj6@ QxA!W\ƻF&-)XxbhcNxȄ]Y,VvWVl`XlNxW+nvWVlY= aI(lu%=% ]wFgknj v6Њ +i"~Q_WL؟\ղXPvWVl{30o}Ԩef¶5ױ-D0R+|s^F bѯxWVaWS;İV T k'qcR;y+$b7.IhAEdr}97xW,@^,L'кiA t]oKzn3127ȁwNoڛ؉~5nkѦd~A*FSJav_V\FD>5 bw1U^g!MجY2BI_]/QYwh ^DNA` RсnJhԢ8Im8M1klnfjclhҝs>^=?x=??s2H'/6OU)\NF5c05Ӿ9Eqs9λ3sa<茺D89W;;@ZRq?{x$j31 '!nǧ؎ĭ)'E7݇QhDZ 'Qb04AHa7C#ptIoR@# /UlW~z^x(!c>eu3a|C~BtU,W &(֋DΎhsnJ9AarB0Y 2\&=bOt0}ot`4#ҒcUNf:r5ü "ҁQIDĮ,xB'o*'Wy١L$%(9@ "}?z/kڗ}drYǎ8j9k]1pP$XIIcS6ʩgɾv~rcLRtʥ^ؽQ扤؂?kv{Ng*c]&`+;{|hJ.%mݴ{ ~3=rDD`b0X/;=vI6Hz'Y|3DNgI(HfߋޝOx"Htާ Q>M!'h1+'Qb5OG$anLREV.ޙC<_T\Qj9?P-nh>N)Fjy3LvYJL)`쐶e- <ʉYJ_ R8s%{^Mz4۝,Ҡ(cMDRlAmkW.&;[n)B!'gtWu3>.@WND`b<^X'M& \Riӗ.466'Ok#DD?<"-9$*V,mpj *'Krg  J/4ڴӂܘ h]$+3謊=S)1VRxQΘN%5PnH('q;: gm3f] w0"*j:`qvےBPlA(apȻvB?]('6[T=# HQl>u`Htȉchz73DNBh嬷,)/il/x<[$9c`2?]DN]smd M-zwysǍ&(6 b6|6t2$YgạRi 1H9Y`8"ob*8|XAMft7FT"6 -Z 춨]R@@ RlZ/%% MI%TiE%\򠔠|PBhϜNw%~ tNy93R~T9]QӼhdd- r% Ib8ںtnO^.Ees0S[^Ts JmyKGCez)|_Ƚ2a ͪhcU {w+%BUg&:pR~K>NWRV'#=9J>S8ɚ5B,p RW\б2\B'_bj#Ös2_iJjXzWwι-7L;CG:p6Z;6ob'*島,ى'TǍz_csG$.W,8n(N:QW!2İ*¦^TzJ Qa^^Z8qu.ߕAMgK(NR:-ޔNlQ-oxùm)A::Ym²AJod'WLoAúW8{zPPoaoêл*|qY(N2sYElfBKEqB"gXK\TKܯ‘ ?:Lwma8I'Q\btR09k~p8Y5Hrʤdܰ* Kh?6ӵ+WvaKg^Hq)B[4e[UwYS{EjR / {s|7LfN+ov5'.--ecic'e0qaAĕ~WF(W~tOZsw“OplSɹDu2/3yaֻ=vG_N͡WkcrkDr'm{NdNnVI*;A+zGi_*Uz0Dv8Ȯ7@a-AbN*!kD8A'NNNAAAAAAAA8A8A?GghwIENDB`lintr/vignettes/atom.png0000644000176200001440000007451614250050336015073 0ustar liggesusersPNG  IHDRFPLTEDDDH蟟{{{Ɍl===ʍFDGk"""hbKȌjjʚK}EIfҋk{^WVVbIDߡFƃ٨nK߮뼳亹ʍM[kX}ЕUWʍGn͏PIDFkw֚SעݣO|Jةlإ຃K}ITp‰SZVNjD{]Mݲojsn]\YPDIRȓsH@|R|kj|ޚЭWVošakyg9^rK=ɓzWLT_ʪd]M!AjӜgǃ蛜@ImnnHfJ[%g-ɖgYOhɂ^깴j\20.VSb! %Y˭n$\ێiS{g$Ⲷ~ʥuyUufP~AwH9eLkFl֯/ն LkRPM^K] XŮ,S IDATxLNj}`LY"e@ IC](cJReL-* a<gGI#-BJXi L Q-LZnR} `|w{x PR!(YV*8< Al&Ih#C<-OLiA1+rOpP4]LPb=1 yRbb[OLIYFMɿ h`>9_g8MxoYaj|o$7UMU꺦~Κž',SA[l9j*gx!UM?)kX36HJ=纲z>T8A0}\{o>o<ʙWBw\_d77])3~ t94b~GF5mIkT Ͽ`<#v'Ó]'p?2eB(y^ԡ&˴oAG4ύ)zn^Rgx^ GgS'٩SmT1zFG ~![{u'gm C7 ,UD$SܱTyq90 -M]teSt xz8LZ-:~׈XϏRSg]q<%ֳ);}X!?DtϵI?Ɇ' \8 )ɾ*4-KƩKa(gCxj{,/<"Ů[gF{l~ǩlKOz#o{SStN};Ǔ?rx6c! PjxJ{,+;7$L1NgL;jI~5s ϡ5 s:,JPme255p'@';~+35ÇvVzn@:<&@ ]<bJjKf T:xv:g|`D7H`crKn%'Ncg:jڒ]# uOI?~@=$g?D<ab_>pM0TY2s%\9 XpCR+=U=/ ÔM > x¯xOx@'/ժ|wH%n1FI<6qq|do"1/22zQ(uT! "cHJE*LxWdi/$0TbFxcEP PnU1{|lS~O~w|U՚=Ds/~!6:@C'~!.rrWlBxZINF>KV,E{'a3MpŽ/pȻrT1;ײ(MƓ|J?})56kyՉ_6ȑ];Ru(z#c<7BԘ)}*iewG)O{;Y}ǵx^ O_xx_Wkd$rr A >щ$)ڄהTǶ5L\Vwר.t*r|̈8gGO&[%ٖMO%GO0+xƧ4SK3N\\)j%%zxp}co232Da1S/,) \YyxF =1nݲ^r {w UgJp..V=d_qr~wѓ$:=ka~:3`xbM PDt[AgowTS[ fJQ<xjLX?Y;Cǹi3ĺt [޼Zx.6<_I}eUgeN^><XOmnw95r /r'm0NEϖ+Ts!jBxb!V8~+?Kr/uassNj#:%}5܍$yCg)'LO6-eF?as݋Ou0ƀԤmi z.CPLiS<-> "ǭoZpY=cz8/+^WN7_Nuj< 8vۦ[;V8R:3_<;r 8ײNx:#Ag2&g&z:6"!ׂ%z%].> ه;uӶ{]-g]gti< pH<O@:yglQ}n&h$ۄ7Q2ɖ뚙]tLBeKSOmUlx }<>ƢOX9)(ȍp^9O߳.[C\ n="’9kuR/({bԵ^=\Mݦ1뱑S3OaT$ą%azR yVxfrBQnjBuP&gVǞRrd8<(d8m4O @SPYuf7NJY)^3vF5=YLC;3܆xBų7]H$M{4 =(OD"h_jdO%[#962;?!L@M%|<E-GO1[Q1<ƞˁ'd=1\X7ȖsxB)-2[ > r*RxB*,75jSBO Kq URA4@ M;Xѳ}OMMjTـ235逪O U?&O>~>ip W|xOPU(p[KC܈s_7x6аt;/ }i?{R򒜉N_=%^B\RxJs9;P B^^!1$ B\.Q "dD]ˡr*1;rT:]u"MCwt]hwGYکv:. )/xx?{N</Ó+LxFɓOɂ;xz1pvϙD)/p'h9s|p(x&ѽ' X C`Zx?z= Rk\ʴQ :iIckl, x"|UmY' MD܉4x\S k<]<<~ZGOᔂO0RИ9y pOȁ{zgy 랏;wR[4޲gXO?Ϯ -8Rܓ͕Ix#KioZ%2Z{[v</fo,iQ7fx:}FѯL>dĄm㒓aIJA@˸'CGrF$Z{7?njJSf/ίCe v,<IY_=4 ٬*.H߰4!Gӓ,* v&0pֺl@0w7G\K ܳaeՁ#nFq\!)dq^˲%I>f<ϬeUq5ln5ݿ*P#g}wV[$=KGVZ=E-]Ԣ{^nOxp6o̘''(#<6=nO6Mg4BMOи+- ^)2y4#Ys Β(YLjBHDc^U 4ܖv*!YG IDAT6ZW, xǮ w =`Eߣ_27)bvDeݓO7'W;ڋ{f7³l~lkG3:%&\ dMa08 -CMxZ'Sa"IA@.Rt;Lj'UB-0dHn9'$V3g".Lx}O<'{VΎ{]hKmmxr2ՁHw*4hr->x&oPH{2Fv%C#׆Sn(&s(<=Ơo[>=Z4w^D^'ÎX7R)<Zd.A qx YS h ϧ\\[~vtpr2 &M@(2vنt};B[Ժ0#gqr'-(@yo*yPU ~[= W=`e ը~)Xr3xNhr?O6O9m -;i:tveW1S-kyz?*jI0ĩ=SėtHLO>0caBCY9B˓3}SZ;#r3 םׯ<~7Q]m2[-XH&yc9ۧdNDR9a}"FM҄3JOl6喵|NFXL /O2hҨm.vd=XG3jqLrZ [͕@f(ojup&57qA<=/|Gl\WG1#57 2ֺ/%˷~Y<.ZǑM*dI&ydiIF&yRӤb!5M*byȤb!5M2$v=Jjf{ZEp+xk\Cj#gg3?ih7CSƸ;!Dszgd Jd2yw]n)V(ym7r@PdGlh׆+yVP9psyTږ507RiYCwSy&5m K+%Rcb~HaZCɹ]{@6NH0Lr hR߮\nlM$1@ABbM3 珠Ct](,kOjN BC^TO@/У+&H2,cbL,@8(-HU*nT R))cL͚٣"[~dPC;D6Jy"(I׷cMD#G(3\gH0c:$OhPX"mw 3TjyG+,D( kt:Pؤֹx, lcB][џ6E*E)315xy:seX`CB`,{9OU5bMGXpr`bt婠45I- %ESG^gc*<УJ71P`P7&|8(uMHaG}is2 LuNҶFR3pGD rC ;Fl 5h<#a3)HN{Uv?aL& G&!낒5c51Yb /\lP")E[AK1v) -EmmR ]R+=}Ώ;L6cQgpNU/2񟼎¢[/)9-#t7%t*@tuc2`'}ݾcS%."'I RQ<7.?h\ٚЩ4ճ&HHJ~+7afę 5 [wTw>=u>BG^Ic'm}2Ng17}5z6t"K{fDLB) h8s'#rGa@%7x*|=S#Iڃ3ԨwF lMIOK9#%"boXtĸ9%`.Nw*=S<96x3iv >&FЋ1H5yPRӉ,ѨoM 2Ԩs|`õ3b֩ zR4> X4ÑpܜOt7a*9R|歙i;6x3ix18Nc}[gIM,RRFh8h1 gE"TF.)K(vo9"1 ,1us[ siݜGY4e$|;%75^lQ1&UZ<3͔gp_MۚZI|er`YJx=pqF<ԬWFԾ"{Cp{wR{06/g3srѱaT% {fj(2 ,Ҋ/-7 dr6' F?>^#U=|b'i<7f)V^Vg5朡<5jr֎jr<9>r1x?:(|}45x6> \!Ruhb<''9Z,J+4ܤعӣӒ9D<3I~y8]ćM!s&Y5+O;jqݡ!-3GFME ܋ݹyL%>ыރԇxv==rKWFѻP/s1E_Znd'N^i3ek^30ɨ?"֌PO$ PUɣ&7/~233wZ^8U+KNйQyFB<z6TA29,B[3M i H V^Ih6gVa:֢>,>NY5+FwNaL Q`Dt 7AG79z 2ijPRS}29,B[3XB67ѻgxBYI-ib>T,I#@nV?5t:q0|? Τx:g(O|GϦ]ީܬA=$8ݐ&Yol()_%Y`;SICv2fL92B$#7D"fDoztHi+M͵lq(|3xMAܕsyF |{|{ *3TP!Bx HPM@jjx(T33*W:5w joceW\K|Ҹ~(N9md-{K)_u+_MصrX-R|zĮ05C<\l<%cgqJ$xF]#݋Yx֚T{㙼L f 85uݷEdz'ulfLi( đZ&ihS&5]_x&Ӝ6~p5= l<"™V|I$seTw`P}7;$lvDi(F0RφoZڔHaߥ3/^=KpiT+&gNMdQ^-N k|fYS dPqV >te8[j+=ēvZe5h˴)Q/D3;PjR^T<Yj JlϽGjiSlO))SJ)%%xJIUr<Ԕx )UH)̞RR/9{FKj6WU]}UYBkLNB:vYw+ͷ@+צi {E3|_P 40{FJj6眺7WD<oig=MMxn._0A(q'_z<#'5!5bi&)-"fO@KuHK@oya5|$+ƙ{ȴδ\&nR^DVI̜Ul؉Vp&!Vnncɀ^>*`{¢7c<63 PlgS(u/윩:OsΩ~箑8?ֶ3+- I~͞4q#F Y.]K{N8(TƩqete"*J}'v$sOk=P'Z %'aSva9 ?OثP E5ޱgϵ_@B3OSjϐsuyl 9'Sj̞ёϮ!5a+--~͞4z򜔈];-?Dy8kx[t2IzI}a,@UU8/I̜Ɠ;=# ~N>LKڤO?aC%s]/{KIndnçGfπXa<'휱iwfϨHM 5DIh uRv.o RGRlNdz@rY*[Kl3߫|$p˙VOī'Чp'?_H;0)OثP EcϬ}9ʍQ]ɽ4r <]ĒG휵j͞/;64x$5eO$91뾧I:GRmznhoOTTQC|}x[lP'Ǔ1RW H;0 Rrw {*8zǞPcBu'FgzdU?' sӶUݿ]h{<RnOnz}QSJJ)%%xJII<$RRO!5*8BjJUp<5x )%KΞђpPg?4-%ekK#6X#O;x];Z7%6{FJjfN.sK#6X&VpI4*il<(Es#GT*Th( wԉ%qlڍ×C>imW61nm5xNwCHUI;#1{8aB8(!˨dܑif#!Il5t:;.8#zRlkЛO0Qhro%7{FFjR<'LͰ2M0>34Mn2O`1ִxسYVXc׭?gLYgDfc*wo_{vLvP(:; IؤuIl5b3-U1ֈ_{ϞTc(eJe5;fX:6i#}N1|I=5H~M,k/;<Ў(UQgf|fՁוYP2GMش) 'm H v@ӝeJ4bF:勧p$fHIMB5k$x@+JғpVf?6&k 1 e% + 夢_~|f<I1RRO))SJJ)%񔒪x )URS)T%SHM)=^r,g",ǵ }3s[|w[\ѮOr˝܊،xTSE 7g>Dn5{FJj4l1ijydGfmg4]-OqN]egϨ:^~`1Q$ySowxCd2xp`Oz>J2N&ND4V zB)lMt"Y} 0?wblq>):CMHv^~r:lR3;D{O]<*ֳgN͞4D$-T~$Fu<Υ} KfZ\5Y-=n* }xƻƿw2CԚ]|8=?Rx1G|Eզ_ rKeOMyX *> yXޙܙa2$iBl ide`B4D&K %PHX[S,Wv >EV.[X Zڷ 4sL1sg7sx>&gl5:qz){K+ՏmĊLlD,|+KuճtG |h*I#wf_$c/d\-]?Jd7J@4|1ɛR[k]i"8'WRM^O=Czb7;Q=<'{f*ߨ-#/I!@$_W=K ^>L%IϏ͑$qHZ5E5m'<˓NhMnhDKy7ĹgQD]OOVn="-z?=sES*-{vxyzPp^Vz7@•<U7<ɛҵ^뼴͜({z*yr~9<EsKɖ)EWᮕ)=mt .axTQVϒ?y,-Tr:F$CԶ< nr'h" rk+ӵքάCT$KX \y HF qqSJ 3wFSa WQOK#hp(ؕ [,tTK8$O%:azLiO nr'`̐5S@fjkM@'4cSFRdm gD(L,Mztht*IHY:a(ry~?%Y޶QS*Rߏf#䮪"LTy0ai„ #O&}_2n_xruJ"Y.RsPbȿ<i27h9FIΞyT[CsෑNYqaTJNx,kl><ײS򵆂{ύ\&ю㗓7p]l{lgtZ$M)2epo# (қ]gn]]jHBL%(Opw5z)a͏NXbRE՘7:#Epѐ߮zAu5xn!Βm6AhʩP.'M8E+vJ2'31N5uۖSxfNA~KHls`Q)(]'tL 8y`0󁕰n!Βm6Ahrld'g;A^85)OTQmKgsGq1R*O4Ew_p-sw'_Pt*;)py S&TqR|z|cfx>AAQߑHhUg']“ix$QC yv-D &OM\Cey^S(O{w83kՌ]MM<Ϟ<;Yps̝/j,pଔ'b^&՝~<:5#YW);驞/{,\&d *j_:0ک(7Et*q 'ljy%}岓X= 놔g"@L%${_Ԍx&\j`AwۄˆAY#?u @$ˤ<0)XQyYNɄz"{{ϒ$O4 PH97GZLg͢06Z.q)M (>` +nJ?Iv6?PݔWP)5l%rO/r(CĒM8zIXIe7hcEYDxut\mphzU;1驞#}AzpӿY I?ҵ촉O$d^M\Dl'=TfX'O%b@nu T~' )\T+3B},ɑTgsbQ 8++7Yfo~ Ur"2c_绐gœ3v»xƸ; F&Ly04aӄ ,OCj`yRDӰF&*Y4a oY=KKj|jRmrnjhWN`8zp'4b8" [\4iBgIIͺc\So Z~uJ`s[؎Y?zgMmP=KMj8Γq;?G Vr!&N!S2vY|F!)C'(p~; tFeWa" ~d`뉮t=yb{&I$0"ҢLd2jKvoS "t WYh\Q#r3nK~s* PuVr{ڢ4RsW*!sH&O.=Ŀxڲei˖-O[l} O:g"[}v4h!;-m$$HlvLVSI{@(cWRK^)5T$ OvZulL_f3c2ʉWFƘv}i䭮<шp)$dc:0 &w 'Kq_WӌmqK< A^l ,7G'gc'2Np^v〉0{&Yn O~$;-o`KܢlvLVS^{Hm_ϗFrs[P1RD4a>Znu?z/jdFP?pih݋'"顦lԬ~Suߥs{ie&T.'s\g̓V=@j\㊌$ @pR2}tJ1%.Ҿo5 1gɭ3aE d4sRY%9H3 A]O9Ir%5 UZ/Pǩ4Άvf,+?z |ǸYWDA__*R5 sn;$'fc6R"L@">l\@Sc*Vқ=#5Yf22&ij-睖MO#T&gI%Ęixwwc~x_Jq d0h5 iDHS/HFxdcL%RM^$y1G)$T=gϖ>2H.1O#kw)x$Q/$P H'mq1^H%YK&tgHCTxwk:E3H.ӓQsxr?q)x$|vE뙀D|RZٰF][igϒ=%5csWv~L<B 4e[}G"<.FJ H'ycVY|L=1Og@IM2d22Zo!g2hԛBfihE!᳧ocEf*7*Ƽb̨cZSd,k'ge5zcۓ:~^8ain#.MS1 yVw7|$ mēeI-A/šG8 [%G)Cjj&Xc)jRÒ3yB챯B;Ξ92hŝs_y~k="5gx{Gd&.xb@<6Pf`U!ɬ&>/'aؿgO8ӑÖ||vSq|¹x~;R'1Ol( {7'vA! s.4 x<'vvo&u4KO _АTSy)2Oj0F"5=Pk$&]z:}RLgj3Lf5-y9,l3'wb?q{Cvn+o\;N/*޹l( 獧 6=l ?H) <(S^{yqNΚpd-ziJ~&tj$li ! l FC0!:nF#BB.lU(aҲea =+ɒ^zsexߏygTv뤥IO=|$砪(^1Ot^) =t;OdW{>Rts^QIVkr4gͤxOtP&n@]N"˼{-A2~ZpX`Ad`oSt_??Z\v'G kTЈ,PMX Rړs/SG^"}wv4%>;DgE.LVZ3W[\J˩ (-i-Hnfԓ49jD~˗zju1Cu9Q6'tR}ePٓsz*I,qBu"_3Ps~LXK\f1x;e33"ڿT-=CW.SG6KuvӽOK䪝S:Cv#epUkUvD޹Ey~|_y(8k~.bsWu>b{Dz 6/EW[f)tdf Of̘1<1cx2[x.ԬsY⹀RM'⹐(=!֊̖*-OI0tݺ'V>Fvn馠ތ^>qi<4͝E_gּR؈Jh DMKs'YXAs?\s.ܭ߅GZ7}Xp*v:+h$bK*vi_'ҏݠ}˻n}>w "z*'Qr&fx}mxgCxw2plHFneqnR_j^l 8?wrr83DAAp:ṦHAK3l䖊'bx2[ޓɌyO' ώTED lx]JMj nDB '_-wQENU0z%*kVZuv! YCfc%æH~,HKe!lLvxG枔=f?w9ӌn<y'F{JvAGxjuFp}-{գOhh$99u(U It{:T(,J>ӸD>"ġ>S$050<OO| ?GsOƪPШ 8<+ĩnè(k7"#&J, t:>i$7-]{_:qB(D"fw+0=u٭_)'1 !Fo{ B{JeMC4]8rR{۫gЃMbsS|c0=mIs&P?0[SePSzΪ̽\ oՓ+!z'#;''z”zz޷IENDB`lintr/vignettes/vscode.png0000644000176200001440000015441014250050336015406 0ustar liggesusersPNG  IHDR(<gAMA asRGBPLTE---x77=,,-333%%&<<<:::$$$""#112((('''66<===eeeBBB888 K)), KKKztuuEEE77>444@GMYvmmm+*+&&& |TTTMMMDCD\0--ppqyyy%%$aaa666???HHH~}}PPQ% ///WWW45: )4 $\]`ZZ[0*%+% #(716HaVdt-DC,åMMok@nC [wZ7f=-g6Xk{,p~wRXv"OVQucv zSk';z:# S#CGU|ASꙟ9wov!(:n.O_ < (}:TZ:a#,j#,"k֜0G31õא6[&Ǽ|P?cV! ;:gs.oA#SgЪϣ} ̍#JpE\ah1+X 5uJg\[.GGXN~O`e|C1vdƞok"`j9`@K/06\=fP{udV!XiAf.Ns2ڳFZ߈E8 J )Mw$&c`H A&<>* 9H01oz/SB]W cGй: Cɠ(tkk)ɅX@G0n6,xFPһAAGT]} f:-2l(7l#&];٤98q&ꐊW>dV\N޽_m;~288>~w1L9~)(ݽyؿA¿LwgqJDbJ(?+KR< y AR +0@VZ:'VwV]2M\;eۦǧ~w£Ud6;ltuhnu\w'! Q)lBe꫕}B% Eы ,A, b^4@ @HL7V[dǕzB#h9+LJ\8 .L)i MMLLƀp3o-\PIlƙ~:N~:1xbGG%$r !p+++nE)*K !ՂYY$ pU\3Y\*\L`LJgRV$Kt:R_}_MM}f@x4}iuypn=~z-t/V2_Gq0GI .DR jDNN>g腩LYDXE H4 c@+Jb6d VJuVi$:Ѷo Le@{sy_~zً#$rAD"Z\Z̲Zp~>A8DF^yA $>89r7}8Cr5 o:*cGb#oJ{{ﻉY! ҏ{v~h=܃- Ax51K1ļptxn^:?[o- ) )$C" AJxLfa- -.&-m$[,T4.(1Ч/6?@}ί萺ZÉ/p8s3ָʑ-SF+<);mJRv;>X^bFkO<7ʵ[ܽvzO0d 6?zpg_Wɗ7l'sR1"K(A3zHGDǢhGy! q0L{knZR+imns4mFǁh ,x,leY5(U#/^7!Ï SO,‡__8ssskkswk36 O?,߿h7>rݶDB8 Iaʑ.z]Wnuw{oQ p{WBa}ggucwg?}O:~tP4S` mo?K"%)onw4=D'K'C(mE+A>Q9b|wc'~;?zaWw ̉NY!!tfWUא=k[z@pM{p0+w+E^u[\\XB:v^;}}~OO k5陿hz*~Y9x ۙ*uSQT@/9G^(ƳMF|A`\5z<әHcy\ً@ø#/ (Cs w(@G uF@(Am${'$y!: LH.dmYp~*1ya xqgp@g(iIe:7)R39OM ;~ՙ7}_78 B|%*rS 8K[ep[ҿI'h (MvV=qBgY_0$g0 J ޱzP $@H6)Pժq]5|yC]&!l1~4`g~@X!d.AHP ڞdd OwX&0vc@ߔ ʌ-d*y2`\b>-5D1Q `T-N+bFm% 'BN~3 Bv@8  "EJ|t䉤9%DR"JeJ[@8.@$q ?.v3Jzhe\@kCD;d&@pU)3x:%0H* C K3+ :' 8jd*fQfD)%欂>^^O`21NF>RUuk"('ҩL ʓbYG{ׂPβŖ]fm J*bBR=:mBf\! j7BzQFd6M>X@~VRUоY5$t("-Xo5xF_)}YKdy]Ψk$ .[}7_Ɩ1{v CwnG |o>Y_dڻQe O/zhGQ~h,`v! Sl#S;«φ{z}?ˆ0t~g.ډHfƇ3D5rةd\S" =8ꏅ(nH`t:ULAG,ڟѴ:>2 ֣é#o;HO=ֹ6NeF`;G(vI:ŕ\9GR& @ ٤qn6c`/pˀp4zQ?=~,Ex!f:eMř$a       ZςKY-"֕D[gdY^7+E!A\RXO\.QbY B7&| ܅ 8JW W2€#YXX8 TZ++hp6ym(t\" v)rAϵx4RM9a~%TnVohK4!tyԝ&+u[$mOޠAtQBs5A |~.U N䓌{~=(:/B4hh,^ B0B{5VYf 0h \RC|5 E>B~795y/qcr佩bߛ1Xu/A[7&Cf D*7 I\>_HA /,Bڅ9:/FzA ?3ϱ}z_hc웴K?;lY?x'+ePvV d,{C)ЗKm(ܗb u񃓵8%i_$;qZ75!w=wch5!@!K&WڬP0M\ƨGKvrc4E,QJja9 As Or.\IS`V hw}>0` SQ$(G~^xAcy+ {hnsϕȰҒU}]'SNGN |HUa.]2̬ܵ |MGxNjO*0"l\~[\{uXj!* q#jg;4: sɲOt$Gn)WݮY.۴#- *N5Әt{is1ScβGPnLv!}4s}wgn}zfz&i,$e>u)睏t}Y +9'Ks+ |VP Z)zz&WV/ BbPN"XX.wrfao֌ΐpf,#R6/(TM e ˹c9Tav ӝ|۫KWQYP 2j1 ӝ?ܫ _gtJjX6䆝p>{LA|8q~ `'o3/!:hY0w=K|~{M>SlVq2;ih|b\\As,o^%ۤWr{۴֦QU\(L}8e}8FEAlbY`H5C\}8I,q>}3+Nhhwۙ~ۙy[ʪa6z8!:yg(Krרc= qi+'rO-:>CJʖ2P<[FؽqS $|7~WQ7(>hr[[eZ65U5&xsk IUAz%H߇{^ W)/Gv"LW$1Q@d5{q#8  LUΘk(J2Jx|< _MF"#Yc$ `^h2S4&@:!Ȩ;: ͖N\<%_rGm/r~˥=Gr\~@DR^>N"l'&woR }w8K\:w˝ox,.uԥX"}e=/L_[T BGAڪExAbK8m-Q.*q̡es1* Ǐ5wKBRLޅ^|-yezdhA g!%n~H 20@%idabϱ[vfe1vǭmrOn}k$g4$v $~RQnr/̲2a&rP+ hJs?e> gz@ۨ@D@V (aJ ,*ɀ9dAp8xҸc"\#Mڹ\\\M4;Z5iaHAhKޑ~.2X&UR>|4Ta8Fz+U9XtzZPMXES9'\#Gz>丈9С+T@ؾ,Bȉt3;AMk =~}~Y7lh9F9No&OU8Z -̀\C+* #{@HԄĐ(>؆2 h얧b$ڦ_zx,ŠU@wbƾ$0 `+J>#e0I"BѨ_ &U@nc%gvZ.D J!^9WM={e6qqّϱU[u#ۺ~1gtMnTuRdhEjĈ4-LbBbBM'&7X&uLl*-J869?!$y~w54004=;W qC+ck>DLXmC}Q$f Eʷ?q B\(@%"\R">3n=YH)@֣K}FȠ4* רXOV<-ppC-w=y¯:+MusܼQQ?BN/k{@"&UWd70X虹=vꇽ%cg_"֙~+pE%|y5{R>v}?@"m&Te ;pf6{oa/7]~9Oo779忚K=s%B ]I=jeCm6{rg'Qrf5K{]~[Uu#p^XDm}/{׹_ŵۧ:{d,ƃ=""@#^8KKGY>zvO3֒e=N,߻'k =r8'#Ks3r\yѹ_̃aFwM2f~1G޸r}AypM+گ!Ҥs k\tM,/iQcʍ^^탏SPMMɩqik/N:耶9@N^#XFo!No?ӛY#jae˾ ŧ h"<|M!&CQT_`-JE@Pa\oZb˰Y gǞ>SΒe@m"Y^b!zhFW;F͔~- ٩i-GV<=5P^tǤػl峳Sg3S[bY1)ؘ 1fMa_ gYؙib`;Bu6AQOS.}rog{G7Nd'JwVM iDOؔ()"*hO#~n`Eoٳ̤$u'=]NT5I+KX}W(zMJ"hњ#@{`lT)`cBXV?D-  i%Pkƪ@,i_cN'""f"p1ՓzYQDiw "6AJr,q7Dm)g_=5'\W^M91^"cBn"P!4կqqn3(" 8Q ;FeNyZ,?z"Ȇ]MDQ^!"xh)`"hylb#vϖD"a%\,BRMq!E,^)E @ˋ%$xIr%)C˪ h=bX홒8 %]@*U MyWF%bwy{4BDh7wKK6kk[m(/_e߳'P+@egw_}* `:gHJt֊ U~lp1qVo8`͍Kq585d^07n2766gnlcؽ)HXB"j2_F}:7vmqpI!=RoBcJM%y݋rq sNtl$4;ΕܢMG[ UOP*jS3![{Ql>c2xaZ2L<_s/~"h [k"H3J[[[G*Mq u`AP?.gva2ݞ[ <Ԩo`ws;/5[Mo&ɵWΦ }ū`cr#ulniX--N߇7Dmz > $%o0HM3CVfP*{ޒLM9^ñQ"md8&F* P4ea: IDATl ;$aQ 6f y9Ʀ QR#ތ0fF@@aR- 90`tҔlj+Hg b/U!H:5R?r/NL;kHl`l~P -ȌWyjĺt_KFKE0Ӧd-IܬgOq+eިГ'v膅;jßזKr~/-7kͩ_~0zx2uU] 2Y a@ox S~QSӀv xrhSSf#LgtlXRQLcn?= PB0c,ܼDʾm3Y-+>н%HNp%b)v9A[myOdʖbPJ2^w"0lc?Ny e_8$Y'R|b5ۜV'(7.QSǧvNzv;VVC_i* ?-_)9N{nrjko\ /|/'\-^yX>iuM]O`f5< 95 @@ _!SmiN^FXh(^Dř`\γEn76%Pp!㖨P1plFv].q|Cr<)x"Zs4$U6EɈ7M<M,;K 0mfZ. s)z@ `=xH!It-)?1JuQɊ : nꄃATrǣ')X֍*"<,+};qxcktQx܍% QQ Ax!iZ:?TEoܩALVpenZWN!5CW0Fֻdo4-:"~q19)?,TrM& bL , ŲUdv{;fhpGn]\"xDw~ t BAsYŲIrBJsfC $±G)<M[1zKTc?nvz}@i{Ax8hٸ1|cTYzhtogX2"}FzS618 5Fʊ0{X&b Ϊ_(j7jܶqAi,  M2cQ (¼憬 ɉE" .FA H07)P$SU PEY\&Q5#~ >H4_Oi}m6)htJyхpREX ¢,KE [!Y_4f&fnğ+97Ѫ竴v<ә23󜧆jS: "{0N5R(e10?oAն|k`^a3TkxxNjKLR"`r DÛv"֝R1utikm* F(wΔ8G~u݇wg >mܬnyrP7SyZmo sDñVKW2A_l_SpUsSc"ߝzn^o x- zi8cPq!`@hsn֊fbқBNsӞKLmsFzbSu2#r|2} ٸ= ַl=˂ί@Tz*^-\ϝ}Rㅅu-z3x<5ƉKF]nrqa緽 v01zvK;[[@|]F';]#v /*%)CN8q+a=ݪ2Ѧ+ l(Aج+o'a{~Ajc| ;'27@0^.1@GC6ri ѴhuJկ4ۧ8ᨑ6Y4m&9Uj]VN;]Y 3~UxUafsn8WmN \ &yYE_~R6   R^Bjh.~RˢAWJ`8L(ECTgIcGbNr(GM~Pk8H6n23[[NcDsEFd`T[ v6Q#fjG!#˾ w7\ɲa㤻 | >ɌmҞn k/]޷&Û(P;J1*S3ؿ{"4#5I4AX;HO oY,V٧bѲe5-zDZDm~B|UbHLvV򁀥Su.& 1۠ l*fu}P*Z2w,;QX£KI;w͍ 9۾:s~9r?@-w |-46? (._}&uDci؛WV! 5 .a||q&$N,O~S;_vhBoдvE4fje)Wn^ +6t.AFG<$q~[  Q" : }rtj g Y5mԐgr[D GU޴ӓS"2FHBAG",~XAh"Ӄ a;BݩrBDo}K7f50T.AMlYFQ>ee/}>##0 \Tk'EE1JD!{ ̾4_,R2U%E)EEoda@Nkz:siczL#m/^o p}pBUfbdN"*ǻE8uO y˹s3/-PY\_ʽ5: ng>S_7\iJ5F+'Wh")@XW&V?w/w{K_Kt],T."g  ̶Hш҇a{:Ɔ_tOһQ ΍HHH% pP(CHH([AB W/>Z*{ Я)A_7С===g""yMd+YĻƦn >"؂uiQL fx 1#8 p75Tdf<Jթ9rߞ2# ǭ _d/'o<:o@oqі>^{(&~xA> [DZk1y:bP^#>xwzu֋߿ (tXD!к7OS%*㚯RORQ$x;OI.h_fAn 93bgz#(ycVa$/>vthl>pY\~gH7U:.7x *92FWv^שıw1w1'{%h!+>\76k!uWy>W}wP!u3DӇwkW.ϣy~}{!j?&1Dx0#ߨӛ7l0:67! MG5NWYn2o`QDSi3D@11݀uwSE(ɪ.KC`_aMl╃= @dG;tYoܐzKb)pZo弜 "jk 9RIq&;k韷P:y٭55 U">>נ]+Zaǟ ~L>c{3ӣ#>yRFy堚:04먲v>jpI 2zU3ಞTE2/"\\& zf2>4_=-C`iZr bDIw_15RHN:F@ MYaCgZfo(:e01&KunW1ɿtDjA")bH {?WF@7eNx#XNo9"{)\d3m\,1CL~ LhlgzδAfW+ /:B`úcem;8NN;D_iy#`g5FNmpκpkv9HqP7A}F仧y3,^0 ٔ9^v / " a;b~iR_x16qt|ƭ x7S޻H-\4r4N;|E,Bގc>*-\9]$\M9bÞs}兒BQI3L-+ 1Q>ΥrV_5- l}oL[ABe&bYțv4X3!0l Xn4Ҧmj!?$\Pп{8~ 䵤x:_=~9Q \ W",߭R! <3'8h[R8PlCzI9zl\4pr_A0l=mo>8l^p); :Yrql(b%'yؓ&S7o$W8|I1u#`̐cZ|pJE e'T^! di VWrwzzL˕:_`u ndZ97q&(\Eb= m9y5ijnA΍XBP 야WN/ABf6.P9so B23sN a?s3 텝d ‰LKvP%vdI0\a;yuV+͍^$Ecce( 泚i,B72\0)9HZpeY]&nsRn6w.mWO&D #zn7h.drE\&'3x)."kdڠ 4 y6 8Fp4t[/ں#G@`Vi<|iUx2b!]u61 Vu@x2DȤY!qtW*vN9 #~Y[Q&V c B"x& -LWp):y U)dd <(35>:2d:qBE֓e0~p-i5mpnDy W~2/EcW&6\>ZJ>3׷^Oq}-!A&lЄB.j Ja.3;8JB|^@;@H+CwZQtx[ka*S?/y4#!fp LnVŶhP{Z=v&y`q*8"5\Mpr0 T0pF7Psz rIz+A@Y ̪ͪ4Gx֟M7«F\0|2-B9pVZlJN"Ly/Ic\;"ũMx>ip IDATzMHnbmEݻxbUhsN+ gf/^i:l0cEƻM҆"=AsGNBO3 Mdjn J̀ԶIv4b;x] v (A/E o- $"d>C:>Ex4u壞5ʮme'';!Z0dup2j'J8QBL'%2ͲyxEkAb:+: )VX(NФÅ4edZ+iD#Y^(2"8f!8:LHL[ͪTC}fKVOB?SHJYBdY ]DjXӬOͶ-U/&$+V+#CN`$Ӡb?â;k;)8|>X}' \=@jvX r[X#3b@0d JXZ;8A٠Zr!dr UTYउ9RkAZ@?Q[%[ьOHCiQa=B׻2ß>E[֞ߟYQG g %(?\&^7the񻦋;Z}i3zyVT˷πZҤDm-ЭVH<6u]=)w_EP#sZè -= #凡Iv9n387R+_ #cyI}x\0OCy4O.! tKV㿣E0^ `:GMA8Kz œc;eKaS:)"\N_];Г\y cb=@`zғ*!7z/\mOzrqhc}C64tZbצApx"ae: @}[ZO||fP;KW^M^&*H_~' cGO׶o{ƖC۬ع]N&z7蘎8T-("|WhLlZ2ݛCiz_̀ջv{cjxy}g3 տW]< |UR0[|\9J}s77Opnơ7*~'ۋWSs+jq aqB!8.f=kսrlɍU~,8F坃oVbU8_|==eHh[UWK7hI,MTW2!ljY|W- {7r 1[6jo.6(13=o1N]WP:\CIpsBGVF &l6>@ID b#9Xka [N[؉x7f[UܟO6VS,nSO^=㟷^ժ-[we*rKtTO$gs;0F]<WQ5Q5Sq[WЎ4*E_˼IQ]$xrA0r[Wɵ!KǫtX/\ģ8љ𻴾?@ f@SHZk!]d[j \Q&h =t9FC;tYʎ-^S@i!g>@l^6@>Ax1gj\pU ]-S,v/RWьc#Rh{Z_0x"0g Fu!1(Y8d;s3#=ZUW{(W{ ͸wmxKtJ`Gf7^SmEcnDٽrːүYȰnYԕeb o3hq G#P*q5)>Ms n* j9!e(cۥEۨ`V,;e%k@UЉ(s1h]_(Rr  oj8af-e,X.xʑE$hImQtD7Avq~aQymz!gHaf.$ 6j!Yff@cF12WAB- d\4{]0P& R5U=&a?dZa|BDOWF_`8̨^q.;_IkM(p0. F"F8~"fvSkxV?f+ Yj7bIEݺP1rtTkYVU鐼q#uejX:@ݶ, k+djT7B{-J_RR@+B)Z"!9DD9'/&_r? k %Ws23og{*봚e)=ef^inw?% ?[fs}P?N/R&37#7Qyb)0YEU-4;#nr1FΖ6g u5gЋgp+ǨP FJp)l J(*ՋfrA*2j#?,uTܑ@8s $oJ˟?Y66{il"St"^b9h/IҘ0%b`~oBd?28<.O.D.at^65"*Yݢ9(K9den)BH>J9!%T,/擀䢎=اBXH' Z qhJQ)>EJfk7 BrVq f!Ocı(s2zi~3+vzy*h; j4pP?Q1/P&Z%'l u5!qLI oGeπ0h X VXa +,Xa+P@H رf{BG;fgמo~w?^\"HLJǬ:/ ;'<-|ĬʃIL&P\FW Yĵ@ɯEu\7H*ؽ:,K$;%V пaMɸ:-@M"ŤXDDdK7rC3Tb&r" <Njq[kZ|{$L#UyEQ qQ/nIߔ,N螭OA}Z26IoGgn h)lC֮ט]Ok5#mTuqZW9SULhNjbPR7û[ ͢pZ5fo4.Vǀ6|R k}Y4L t>{Kt ej{f˖j_]!²g̗}tGo k۶ C"Eٓu Y~ٞ-yE%*GFjNX.dpNpu=B 4 /f\2VZc)[+ b@M>X >C LȌpyO)B;0za2#N'wneZx?Tn>N~?šRʚ6ۇ͗ } F9fi =9 \-NC?-lfN?0B;XG_欅Cot zyudCp-oua5t±NZW`RԹ]:?d#sre[~alQ]6Zl0qxb/w2o־-3ctcm(ܕNw|l5@8rFH B$s-0zJ t%k+v{i{*?y 絷0:(h0?.C9OWОƂJ{OFk̢ ̾+Y0%G׻ꗯDD) RIST;T A73[unbrj A+zp|@j]rZwզ'EJ_a͓ME\;+:ж.$_cNUcAH1( K`H\~YPm'S|:^_#@ j=<4@^_Q aC QiSu@P+eҶ8ꛜ7ٓͅfK^a|AB+ \H mٳB?OK4kzleiĒBK[i_Zӂ eiUhŒJ˥@[^_" / Fd9'91F>ߙ-EBfw;v~y<O&Bi-fXC斦^V%Ξ dɑSuӹf X}1{;<8i8uN7ʄiTr;2J}IaMmJe^3O \ۋ7@@myYMkuN5i?SBSe ¶R.jPS}xL cA)3C g4 M@xW+h4||y5֪ŃЃaCXY Wn`]ŽHGK-B<[h "amP&O$T<΂p OC$q:`i/l‚ iu*% 8DB¥ȉ ^PE{i ];IpCIXuwJyè5qT_3}00״^B̃hD_=, >+EsֺWUӯjGZ5:  VUP7?*b@+wz4k7FA]:A2ݣ"+;XjzQUOq ctI'.zY>`UfW{2%n2EniW>cȊ[umwzgǡKo>Wy<>i1 ?_9k~ۘ;bpiOGFNҩrp*E{V7Kbhء0סVs[1O&8 9dGm0/@=VcF.[! DY" S_tDo.MWS{\#ɤlPZD[*=K'l oMusx.#HV| yTdHOֈO@0찛,A'4uXE\Hp3LbbU &%(KFGxew ٠Lf0F)i܇ XLxI`GwO/m10 "#Eʙ uq dq0b.i`g!9uP6xl]Jk5A .ٷW:=RZZ6n:N*z( |Mk1r%n&ě~ ﲇq|JC u[/\[sO@l-$&> ~0_aś6S\~&~ >Us<|doxWbZ")eL̃ hYS؎D1~+>A|T(J;z CfO4~G d%'gsޖŲ4@ `4tv!}|Hruidl7Jbu"bݏl7ةx3X{t `\ |@ )֎>ۜKD[iՋ[Y93BȡեnОTBG$No1Vf,к3k.2a-&OؓdciI NZ=7]Dbrхi5eY vŞTx(tlaO;tNN<Od"p ql=%՜I$a?@Lꂸ9#Ռ8rGĄ79 Fd IĦЭ$i} x}Sqqӆ;#·Sqا"krgbb;MnsEGsI ;Nv_i&gkaebEۓ X`ep+c©GM!orOyls[˫Dmhӗ)hXh[$}iMNlRMW9㵐)4|JHfuSzĉ?gWv.FNoZ8zg@ӈ`AiBh / "p4y1h^>/[( yYsу#²$b/L{Nj1c?rA~[N 0XGQaqLR5BǓ [A0yY$)Ptߢ@~j5dJXT{v':M(;;ӏMk#=g忂W9]@#C1ߊ5B[n@  k3җ5l&u ],x_( v.C._F` a\P$T tJ>5J i >|Q@X;OKT!a'Iڹ F΅`55W]v.㴝 U,癛qw\,P ZΥ7nqv.2sADtc }ԌQ]3B vj4"sAEyqjκjgv.i ETYK))à".A$4bJxs^i]t_ R0N fyp$-lPs0sdnsM0DtsْӆV0xf ^ڂA@{^{;.XVƢ@mpO5n$˱@1B_S:ww7،nI)-e-IVbu>m'B\6{ĥVi};hviqA>l^e asI$ϽdjĀ=S03?εOa o  tdW=<{ܷ6 S1 >‹K' !i6Wh ,8#M_'b`waX[`eyٿ-ָi  #l\xX|H,[ Z0b}d-bn_/؋jt݉)ZuslB}/ ꃰ޶ƺͯ5E:G2Bp U<nn]U9],\fB7f\ug~s64J;]m 4JZp>189k̀:~#pb94rB 5>I't`k  '.͎$? |Y0Khjٶoܜ8t'1ql&aEu`7DP>7RZԳ { o"|-#682ܧ`CK ̦B+%=&X" :W?ȧ9F`GLQc"u} h؉iFz0L4GMgiAYaX Oۧp짵9.g%|\f(C#zY z١a.>\$+ rYA"jhwȊXB%<  - F%dXpj0rAq]T0PZ#B4F\# X| , G^y *Ip˹T0R51%fuZUWWW[&V峳AfWGUo'䐠p|u4i֦FqEq$7(VVmmZoj4M4]2͛lbyMc59pm6s~Ü3 :s8}.?)Ԇٓד_)y+(&NLf1jMW3{‹{/r!IT-ZwF#d KH;$;t{r3-րB*ȱ.鴕e (y`ҁX } YIQ] F `\kX(7*<=7<[-AP$u\JZ! !oax8v;EkHΥ%"G. 74JA'a=w= <|W+un= ^,^HbtNNgRXb8Nr}s7bg . %$frcE̐ѧaӅ.*O<&_ &f1tsC&t50ާ=r.ܤaFﲒrO| NsHɹh]#3F$ qp>+3z.tVAl}헵 ]2>0yړcBO" |?+g;_o{|1.*k>+g3Q?q=77YIU])JrIVG<[~}>p~gfY:#Z׋f7\B9%+)LXr+JzeH.` pd ^u+ǧ<W= kޡc*AO uGr`ǟQO'I_|b?rYOH' *\\ sQ6I.7(P4J$i3Hr$6 }zw2d뫅˲=9L FyIX1 p7F#GSogAPO`&\AG D]'@OV@yÉ'B`3c}$D<%  ?ꂅ.*lAb'ٝquDNBb4V63S ͢*J6yqdd6",풣>z5<rI ұPlQ3sE\ݍA &P 0̹XV[nbU>~ҽrS3[Pn fKrfyT=fϳ11TZujP/"#:Dm}AA6 yŠDך25htg-ӸoCxI^Xp)(? ̚ ͧnfeG͍Kju)2]#v:zpX j2^ѠkvQx*݊M aŦ{cAC!|,ϦGMchi1W&[ԟb4RI@:3XVZ}QikG@XFC@|pZ,D@ z,1l:c^EpW g bS*؊kH{ST;:ٸ:t#⻋3&9݋{MqfVo]x =xG/,3$  ;Dt8+84z-Lͧ{OMC{:h m<L; ', [lZ;_QF;Jz=ɳ ͻY #184;TcG륛X_rQϯg/}JQVСi*JtuJb?V4ឪ{D4͍i ce@I H`ʓhRYWk4BZپB  6uDpwOJmRL:, x3msD=jSXBs+#S0/1[T<О;Y+)_toq:,݋9]5Y>\Q*\=st u# a #~<.{qܳ?nd*w#}KB+ ˽ù{=j_1 )fi0w%Jm'׎*VJbnk3Acbeր KVsRKX`J:h;|كAhNtOBr.gO.G$p C a=ZZ-PA)BI ȫ򪂀A0 K`L0'bnrIc~ {fZ͙tvwמZkըLӽoZ8 ΖZ._iQv XZ ,?[PMM3Qkh CHrZ'z~cλivne/,D?MvӺmt57hfC$yRt񅵱s`ν1[^4?-6USNEhئܯt-s#ZDQ4-s7&즵VkBѨrW4o6VL0Qr{+xv܆s#;ir@YIByw(CVi8ͣ@0.\ \ E\0vd46#ҙz)V_=,Z}ߘo1;֤l:+B/EEI(ͯ$lDH_IҒ7.-19z[bkMn3&o᠝֧$\;Eht/ߚ\Y65fаނ_ s$Z#T?(moI^mΖT4̾ͮCBFPPsnl1'8 =9dQ1v@(%;)ν@ KNU o^&MpkLhJ.̕KJB ˜2] _^}z@pb{bx30 8noo#_1>qU."DJr%8kF YB"eJDİIdn5cqʿN .YRX_ kw2bH$">/2wGGX s/ ЂReAuV<:xG8Qv@g\Kޠv@Ի8>1Eu$nLa. eS `?rVgӿA:m4iLꋀ[1P-I x -qKӿƆF3!H}T?Q_J?F}<'x xQf"jZBA,x+Xn-tww/ܳ|Ya_dd//d[ φC Fq 7=ᩂ=g 'F]ۆP?mT@N(pBXłK8=jA_}G z|̈́^̱<,++;U>`HKa ύM;_?R?1=wvN})xZ:2Sv~zw\sȄNZ2.2j5]=8ASn'P@^BV Ql>$j( ¦\̭H,* D7,:1I@4'*€ɳrdf4lN%J%fi9%Mh1qVb=P'TN@j T `]n ɭ,GZvEp T3cU,e(QUͨajr񺪓灠f;@5ʭ [(o+ʹa̙JmsM6wǷ)%)$QX)dDlT 9&$IL$av|4>ܿf>|dj # aj9 hs `P LJ!mlB GsMΓOPA1XV:W@,bpzbֱѼ\ itHo&crWvOQ Ȣbs=W~F/}(G.傂ROGIOyF'Tpp#˚rҖ_T\q} %a1,,#7+ջ, gF™皵eF euj0&_tߣ.'b7.qb% /@lꠤ U3bF.pǣj6$XwjOeP|ā@e e J1ٚ@nQM\#nìk \ ! q6 !K_C}%5]`ѣxX@p|U;Pcg>Y6LJ(=eZ|jUoObs(KO1B%aZ2Ux $7K.#PCe3zbzXsl}{]m6(0wS:k>k خn0#=rwsQ6:%f*`EQfmŨw HuڱNwfWx9g'UTܑqfWmDkg GNϗR`F0?=d!\-WyM2e?l~8FBN /tg OBwY,!p~d ^6@'~!cQ yWs<ʛQp3DO!N]ބFӳ-S 5 Js A(JXեgQkk$c5ld̎gkW515% , OJWok0L:w}v@a.#\:zQ2BE+˹Gz5D?ѤLk[ƭ'ȚD!#BwS՚QVh'F(VwaѬಙ֌@3"WGh d=A_+ ܭHRp;:H]D}TA ̏j 4 7|DTYE_VFPº**4AFT }W$JU/ FRw&:Q).5'yd>KHe\V9x0apiUP'p@\A!hrG4'Kɚj-R*HeXϻB~ew㸦L5~% n%v oj~_~F:F ~92EP5EI2M^ɩ˩3xL/A)yƟS2ic?F.RSa'Shx[/<[?:8Tt.UΥI Y>ŀǾkȑ䱆L2 r ̀Q Vڊ lv*f*gTyO5~Jeդ!s * ;]£9@j*u,gF EBhb wp`J/;0S͠dpn߾  7Ƅ|9 /#c^w*!‡A(zyjhjsGй,˥|y@L牞*+Y1I1|~ϙwR\3&hqQodTSUN_)Uf--75̣5re $3v\hT2PH~ zR=xU!dze﷐buMTB&;&EūH±k(r:݄X?Ү5jW;+z ω u1ӹnæQԜcqzflWlp#7E$f%' ބ5L_#+GpH5Rm  =X=A*ԨJǤ~R?7jͳ2]/a8 JtB֤;œ^ ۲4’!Z*A"]e 1 9Q/,ݪGU6aIDJ8pKӷD~lvv.5wᰊś'@QvcqeϽjmrg!s R|k%)4^n@&I2 uv b(gzDbcTiYG\{Zvmf#8Nl0K&Jg\6[XVGZb"en/ZPth֖Z5jt'6!8:kEƆu_{ĸZC{?7-N݀p&hC~V}ASZI,:y{y}N;EPT/0PYm覭e+J=yK{WDV3'!2ڗs ),@nA֙M(ӵg1x2^j}PiȐEV5ܕG^`zC?=”F:^ag㖩VCv Ok7/ |՛K#1=3KSEyr3amZpXk ZnISmMbi"tB UJ$D<ܵ*nRP?VH;B0p^*U6 4p/'h ,m^3! |{Q~#C# 9]N*0X Q ('2"hzVP DWح;vb ,YEu@ 'X;^չEfsPWˊH_aHo0tnPK&MiM AMqMQ3郌dޚ[@X2M;!V}~ 82.{ VT%C=:(*"ܶOʭK] ye>~|[h>GLq}؉aW@h]p{\!fr|_rR؋TjPPirm{?Bmx*.97K`w})((қ߸+SZ_if ,et#ɧW]!el/{WF;Պ1} &iW"JDaL68`l2 +K'%"$Oy)B(_[նD0DrWUuWz%,9#kûIY{B>ܻ]!Yh .\_s8Y;|q9oi~0g@hlXjx4> aVRN :7&CH!b15<@ ׅc!unRء k2MuO.щ6p;&3y8%`1S݈)QeNgO&=7ǻfxm;|dPc&qS |OvjqeDY7{ێ*ƟEk-jȣ##EF!즾t\V1.ij#Z2]hu[[ܨwڪڔ.4LԷZ%*Js]Jn\ JFNA_E<¡TPcV;ۧ_賎tĞpk^5|jcebR!0J@ Q`DD11I2E[[65I &$`E@e<)=D_FR5T {+8?6+YJq >4z/W 66ʽ Ehs`$\,CE8.DG%Kհ Rąݤ FII#.M#!86M1Bsaoy"tI()ODa(EKQ"2S" PhX"5I+\!#U:}CFgX |mp1Ƭ_ d=\Ӑ8&lf% @"͓Pݓuǃ`u0UV\1c%HXlLP KDWrDD D܋`|7}If RCB2F`=HX_dۻFBTM{9]0CDE(!oSNQ9!~Co)֧>W VXa +,Xa+@|prs=[oٝ;V@z\zi$9 `agh37H38{!{o'H:Aƕ+W.E@lLg4Vn='?N2ra՛y}K_'|͛'L/$c~@ }%kmKTە!oC n3a?o'8%3J$ ɼo MA fgy6l~'80xsbu|!f22RhwCmhrſ.u(2Cӭ/^Y܁ ,~7^vϛdXHrG(Pɘd"I L% E8(P|)|`<%8Q(8/ Ra `X|Y!}mpDG{p>_ ,䞊t7Bޅ?\J&1*ʒ ~S5t`p@ ۟й17 @)+'I0n;R9,s͐CH_vHZC ed;#{`(9Z`yEAkRpV k~C,Pg;e>˯kaSz>wolq5v&tџsv[VNܭL\w~D[wb\>UtoL_*65~7>%c>nbn|&)#LFJKlza /IRS2 Jm`]$WxitxYw7IXfuI$>b{}vgqn!SGA?&͏Goh~j4ѵݳ촷8SWm>qȆg;t^0vI};=\5-Q  `4å>V  vϬ$r HoUpߦ>5ODٶ5/T{[0h:*\lVƿjP:Kvs@q^=G}.fe0nեOYW4qs,^ע w1V.r H_RX( XfQ VE 6 `H$OLI1͞s"vY9_H{nifƬ5ƌ\j7#;9MWƯmǔ}Dx 7ewEVṃLKM#?WdyWb>΅HA>==bA.x-65-n}֨^wOBDݱ b3ehV>(deVt#b8Ah`ō@P_X Qq@(VehъIo,,4,E4oczc*41]q/bZKWoC}C+@"].hwf+PYkx&.x7If-3V;t-A[ [3o6yY@ISCr,cB Ayt/d Y҇PZA@) \tP:R<^)چ$o`"6^:ش(/55-1#H"^ 9로o!qsGAAaScA^)-]`!꩟{@{P熉W._^IDAT7Buq? o_Δ:LLxV &2$EdT+9AQ }%apP2}1ߛzłC~!:5 @2 0֊4Zaн6b[5 Āk(~@P€DD"+jH 1ax۟{Bxlj.VD*~E8~}&Ғ.ȽH^}C A$g,B ]Ŧ.lHpÒA2DMٹ2& LҰ#eAe_+j" r \M&) qWK gzx<52u>}1=k{AP"$$m{[·aqv3x:X$Sf=tZ>y^9 B}_kƢ<[$}[k( _moEZ\؄^9tbaoUߓP¶@xmE DZڶoY= a?mp{쏬CgjnȱRy*?GFJ^v Β1t2S?7,Zt G~ ˩QÁqiT鼻a vYcuŊAgSs.x=Ň@_WcouŇm-+ f`̂йP ƚ#%- #q&X^]* bLcxlTU|qx0qfZ^lv*ݡbei!~,'@"za„R=B`Y*g Xh1j %A(w0szNǮ}N AjfnJQ @j^L+2Zpp<@L\5=~$ý]Lo_`0V˷MFf3s(Z C5Ǡlҍ <if Qz^4Bgůmy :UU6O4t 9U9N&^LQ|P^F@5MUވ:)|7GWzKK}G H7hgyz>\y'y0ij6ꇲ I0FQ-esstZIm]+U3UvT=k'Af poTJ qgJ@?$Up1eO=^֧I298,ȳ&aW.:xT}@`.K@٢px$z B-nD;1<͖,@[$4x<@\=#nB. v/lpA jiwC2pv00J傃nYR3ER}ܭ{{rnptp=^v݋p8:: F^?,GGN?zۃ7͗7#No\CQo(8"8.!C] o.PzWG#G|пu. Wax{wzǃznavA샫}|yu>4Mܗy/S <4*VW,gWhBz:Lz34aAt2!Rbk?Til0.F~:@! t$aaBfY$S, <@=2$PR,˒x,xb59i\qͪ#Y(C 藙ͪ•:l$TiyV2,XVMt+վ.:tEM^P@mYad` Vz׫$#ESn6b,(6Ah,lf#Y7z˳KRk`LӦ^E4;Z?"$sM8W^ 5N"'. 'de,rdnsi5Nb+㧏gg?@(- WB).Ys0Ccg =!t Ddaq.2L =9QN{#2WiLd^yaN0q0˱,I@_@ֹi&BK >VUviڔӽG 11f=I>8 bKWAIKtE#]$*g!41O!̢2DUe2mC^`|0 B1 4/J0NR4Bs02MHbQ8RPZ TxeH5K&P@AѭC YřAN :h@z8V5ah&8$,nȱ;('E+# 0]#,"KrB#Pt}QL-GˣDd5G1'q `r s酖8%-!i 1/Iv#GtInc\T0bgI)beVڊ}կ&\,LI"],*lDjh5G@)Pxbm8B ,k$Ax?3Z+BDP:D%Gn"MĨ!D{kנ dY V'\hPgDS>6EPʈArЫr/%0:MY&uB>^EQ'(B$T|"zQTbjPXe_g_kGF~]?CW~T ^` "Ve:JpS0&,Y}d9("l: {3?diF`$RbѡFK 5ҝP #zs* ?U(& "2sDh"$IQdS@`0O"tHv L!D M&G"(ϋCX1 26s(WB9At#x fkԊP7~V,VLumWL]{+*>l!E}9[D:l(k ٖ94I,6q^c?` %74!E~S#g}/0"6v@3p}},1vϜ&:2\qSoݱ,{,BpZVe uLl HPGrW' fsVZ5'D8h&ӑ݇*1iK];ae[oVw\U˲K=nv[)?}:>Yk 静ķӐ-lt`wq'nnnpՠNau^5k7 f.N+R$D"E1b-±X|*!rVSbV2<f7{@ؑ,1L>Kqp^, ֢ddR,*J:ˁ"}@(3yIENDB`lintr/vignettes/emacs-still.gif0000644000176200001440000010730514250050336016322 0ustar liggesusersGIF89apĭܜ}Ɗ㌄{Ļvj}q}t|~uwį8s{|mz~{u~{u|{fzvtw՜ԻtZv˽㙣zƫsgtwɩԄ~zׇƹz̻甌ȋvӻÊ~tŽzu÷ؓƸ~r˼|ֹſįø_twÆ|mʷ͹σ|uzvǔ󍢧ζȮ㡟usjǶիpPVXFʴqP2穭+ɐ=ǏmFs뎢Ƥר覸X}OUғ`bήPtlUJώnpħ>֯򳙐dla_iihrhxl.' ! NETSCAPE2.0!,px A8Ȱ0X@ć3JhǏ1Hrɓ%S\B0cʜI͛8[9pė<qP:iФPJ:*իVbAey_kE]}ȉ5t3l JcJ LxkÈ cBBZE2@Ŋ%;`50dX9up٠2Ş-*L虣(;)Pý #ȑ++baİ߫G6SzV@LA3-]IM \_L ipR | eiPS8ht0܅f!prx#1وaChY!Au!hgT,Иc1}lD.)@ŘMbˏ.c/,E89Yu[* #tPDh 2n쐊`q&pJABG6D2d( M7ӬlN4|9L6$4s&lͳ<3 8.o,xtYIiql"P$mSc -*hDVZSt(2H'o8r*´+0f ؄Â1p% ,H}`F@/1%PW S'O𥻯)ЁDr滀mxoK=6Hz,XW D!;!BHT@ Atep4bk1,]}#NّDś PAjX fOjS j рw+"v?\pC!yCA OZJ3;-Tu6|!_ܕVrauHʱsD"8!{Tj4EuGD?to z6udM$B |㉛=w=(RA+e[Ca./`s`/0@@xϠ CÚ k(z FO"T@A %Pr`@[2E|R|'lķU|U65mf{c}Q0](p0p -0  ` p  րM,0KjNT e`pD İQ( $ʲm'Ђ/ [p<rWIp&Tz5|/$w'XaV|,,cD hLpAX)P`C@ ) DpsP'yc xQAO XCpcfLd 4a3reHC  xp ؎Ў ̰8 x x  X3D  ُސ^ TaS!7\)R!_fGa; ( 5y9 :(B8i0i59C7 D̠ؒLI@9I`Z9!y`w6A]G%&LStZid^ (P dt\bRjp~`#|;}.F~ 4xy ])6YyPgH?d|)OU!a[[xr'qQ6B`g}aAnl]y(M1HC - u1os@AB%~1^tsPKz pH)p[1g{EshŖ!R Gr %j:0ijB0v.* Lehy]i%4f@kkr#lJ @؁B7r@[o *# LB 巙y_}`lICsj1.UƤr`taR12O$8awQL腖+Rq s3/ w .@.ח 0 {Vupg:F< 0p@02 IVppqAp ~qF @]SťJ pj`d0,T*qG> s{P  hڨ@.pD|*8hd9)g ~YrmcyA{0ub.h AuVu :V},TsuTk`5PV56 WZu Y@ 0`p CGs5! Lfz|r Z @~ V#y@pǞI\\6PVs1=2PCZ0W _IR~IS k)six76{m*PzhuD1 x;G 0/[ v4p* WzuP 峲]}02E=!|R!3wfJa:@W5m#@;Hj˯2x]YiPh[⠀ҸoL7z^q7$ d O0D'Z!mNU'!A0-yxћk0Etq 61^P W #񆽎p =м!P=Ojs )sY"IZ)u%Nu5 I*m1cmyЫ ,؂^|g L Jmi>;Y*Wls,l )8XVBPpЛo\%ixyc s p7qf7+!$&tBE\`;a| OX ` (蚯1}pŜ%(wf̱fy!Ctg̉h1$H{6O2`Il i XGWQPR` ) Q |(C6 E" @/`'` 嘐ɐXy@ ʰϼk1{*ȰLL VX]T *ܼN=OX_m Ncݖ)crU9 ܰVyX]$ _OV__şŎd JZz֞ rӦ])!%Y\);KF}IFd>*"Zb^`Dc)Pmqg `2gp=ۭ۝M mbQ"W",q r!ȈvߨYyӋFfD.ibѪa1ZLm;30f]5uAj] q` >EBh'=dt;b$ڐElуtc@xOpI^guϫ0[78ي${#Zt跿EPoͤa ::YWwb<eMAg=} -Bl 7 a `B@ENllBd(W=Y?.0=%*m % u H=P.~Qr|1MФj4 ]M`ʤ]]wmFal̞)mgLWוP~bǞ.>~]N6!oܐ-z֢^Rcc)%21Y·ִ/8?EhNPg0$?yq pSpջg[0ȮVq&  P: Q - Bd'R ō&M\fkdG„* ,S0\o^Hމ_[`q UW }_P Og YvK/UzYO^۬  T",@B5O&S QALjD CT$'Ȑc8M7kOAy)TQI.S> UU@!R66덽W^V*}FÅ-8B$  H_$jh2qr! "Y&Q%OVXe-g gПQ k֬$:ֵ@W+T=xٞu bP@0&fyѥ;^:T5yǶryr%|!e2>W߼rҲe+P+z̺㌄J9"X:?sC53=pä*@ ta'Fcb̆L3B; K $H#DrINnqт #wytnоQX"6ÇD2sNR J ̭f&$n+#" &8)x484PI- >vȚWcUY$Ϭb@%TYyjB b B`f0E!jwv>*q£CaF]#H\3Xq)UHUI~P ކr4 cyJX/b2yc,S|q 9fmXEHf]bd+fKpG'_Vzu`b,'-a'C6&0#3и +xXCㆵ0GB`CqEC` 2 +yjo6Q`r3q(Z"EHTT\Yb Ԝ -py$ =iāSJ4E 䩧PVNHB8 hÒ%NgIU17}1w+&Tzt =d1*dSC$w4`XR0 `>TBF>)h6@"%1BGI–O1dHECB.; @uTRĈ &`~ &A|MF(b ̚~FE`!^ B( 6`ymLg?RCvҝep[jKx$+@II&]p4(N@F#c(xX8k Q9? 9H$Lx%B\w#8 ьjX3io<5J&7YMqV3ވF2IL_ ~ħ>' ̇@3 ,i*H2ZPm8D TFG(GqQf2f'K)}jNAQ .?DQ[R4pY"M` gVC?ƴ/*W=OBtSƧx4aT (F <^">Yf6Q 6A"la hU}jeçbU`k] 5C(j8Q ҒH3@QqpЩ))h i4)ZR&,B34ؔ* 1lp"X1Z5xP`Їs,c8pon[ C)Ea!F7*VV%n#S}(װFk# gXcjP'x@ @Z4kfh)r/8  <8aLU*| 4 D"b. #o|T HT26Ar$A.;~S.ج1tE9R $XlC=*  r*s36u0agPȅ4R *dҤrm'*]hg8q#4$ >=uO6xgbz;2NК]Vj Y/xVC K 32 uVg:.E Zu> *ao:_d"Dj=&\Q?0W_'9¤nq $yiǛ^ӿsDڜ3S0h?s@?}qѹNh>:P({@+&7*5hC=z5 c=DAA% #+8+r?MX;A :),*AC,= TB8,)n8@Ŀ s @,B4 5t.@3ދ(@6D5=!B=!\A +py: &1+(DNLZd4,.<$@hP#XAEgl270B 8>07\[|ƋAD,up|xw0(p| %Grܿq @G|ȇ|xGu"#)|tj$HG^>dxdˈHVPF@ȌH|Fb)DlH|AX HI1ǃIxJCJIJdJEJ|K>dIGI<ʮ\|Ktʉư Kp 4˸dFŤn! 74`tԸPŜ |Kpy5`K*LLOBHTHx|Htp HM|lY,G.9!K|Mtx\+L[ι|M>-LۄXx%9NlˤLN$Lټ)JNCO[@,[ϭloĬ #XEul̗Ю0D|K$PP)`bD6+'>؅"v^RLF`dM7[Cx!P6%!KգxamH.CW?p0Z90=Ѕ (Y/^4[(!cVf쑲Rօ%H5-Pb%@e#(e H$$N]VfEPh0@e;Y`N]dP^Z*M881X#La1 :`?;.Sޭ%:`Zȵ%(J 5`^]0p%PA{/p.+M& CHgmW\$PZe9#02H, =:@ZۜށV@X1}VjB(R%Q>,0Xc%H 6G03jh]\0Nd퍅=~5(181jۃ..Ol۶\.H/[ mK([[6X؝ d鳕!Ѐ/+`.MJKp&\|b1.\m$&5H_N-H[H(+4 }^].6^\Ѐր X.`%.@yh9P?ΦZw]otl҃i~llA +H^zZoQnڃ`11c ֖[ Ph>d-w^u%\q5_j $_IȅBF<&= g_o]^8kxfL>ٙKp82.6q%mp^r+z"&m󬄠:]-_{"t*4NiHPle7 T`5fV|)`|s9ps'PPVGqen𩗑u1߁+FUH垶xu8e&Eivkk4Sf{#1}֧gz~%[;n*r]؅,Lv⾀m} hKH9ibxe . X2$80IFFFa /F,iRS_^9D4.qJScfM ,ZǠLznPi!ҤHqX%$qr*M-N2FcI(4qp% \- Dxft@U , 4vQ(%rKe^(,miWNзuױamմwiV%$)%akʁ,{賙K=ғFs{43F.r(03BD oo7/Cz-CM&%oA_$!VMI|Q GUw")`RC1aC 4x5⸣9#A DydI*$MYhDd}h1T)V y?jcF. S9k %1Hn(e'|i *Ɇt+*(6,J:CBzf2:ꦨ*Xj !ʩ:+bꩶ&:,*,2P -f-j{-v-.嚫mߪ{..;/{/ 0|+bH@TAndLC;˷ۆ-01 G *# M4w $FG:LnE ~]tˣt46h2 nxLS,FEm@l[0]Q~ܰ!pS@uX~9n|S<Ƭnz╫+terD-N"/N _|RoK?=C -!Ԋ@@aH4Ս?q ŧo pl 8F\Glcua\d5~"W4PZ`DưW,/!pE}1%s!' .&>p1NYGS$u@Q8PhCmAp n>@bV0q&f i x%ޡC- T(K >O%tm^`a\p*)C;@x?Ԏ~<Pʈ * @ b<Ct"Rsb؃?)x3&6N(A! pN`.YRBӄbkf%FY#pQj @&mAI$xd z3nJdN HE&n@O8!"R44a̶AzEPHu+"PEdB<΀rh,dv BQ Ȃ>lֵub_&8!{(a Jl _ư D@&VpO&`]"v3M(*ᢟa$mfIW%L*n[Z`z,l`N@/ X !$\xJ@QT#"pݖ 'X(ar!N\e$ If6I>3!)! PR^Y$-8`|H&C@#31nBp-w )M$ AZ0P& iADpc D_Pd@$9vjpBG 0ruc"\]\ X/~@U%b,p!@!js Ec,1`K1# IegsZHCr,jRWԔ\<͚d:#׺u݆3쀬P:zZF3m: mHuƳͱwZPcլq<9muYv],Y% 49I|v9vfM >)}W'6I(]-mG8K7C.֝#?err$GRD|+nanT1sʃr[=ϹҥB/:Etުҫ:֯NqM]Gz3sDO^Ŏ`YƲ.w _Dvd7`'~SǜoڦS/oR@u'Wņ E.w` hAmqx#rvq}(4(qMQvx G*؀0epgop|N@Z<͚qU>O mm ܧ 7G||p9P_YA2H[d޸2( D4(Q`0 tF0P<z@4 VO\ (hQ $zY d͵"Uؤ\̠b\ Q︎@|г L`L0L.t4/ Z(9(C7ܐ;A `9 59 ͜2 xC3C4F;84 D1@30ݕ7B /34C-J1 BTtBBc y   \@w}MG݅^2^"B$`9p~i5Y/;@@$BucDзhATUd I7,b"ކ7fu@\ŸA%%E`1Cǐ8q 5@;2"p A 26$&(C8d57 &00&aQ>B[BM9B$~Uh@ fL1)NF!AB҄2 R A&<=((cFB]jZ*8]ٞ?T+4~]AzyB)A7Q\OYFUDW'?ge$\A?ѓL~rvq`Y^1Dd(4yT +b "@@M^#D<h5Б WyF2ee^&@b) LbA4$ԟP|@ 8۫lpfmDZ¼rj㈕5aV'jԂke@DrhU)y" ؙ M-❅!LµF BPBB@FD:j}I_: N: ,l!Bδ؆ͺ t`l$@լZfyy:C (.^/ C:Cgr1D9il)FgP2,†^B8}˅x @N @-- .!Qb -. !gtWD&`mA^-hJE!4*^(WV@UL@: #ӶOznAԠAp< nʁ(|q=Չ W󾓍ƀHT46ʎ fThF7x"\o4 @bu@\V! ]22]K3pF @34  &12 /35$12 =n%pR 'AZ`~]HY:.D@@S6;Οwh x)dѱ8%OǒOKB1mDdt-L <) -4At#Q@Ņ؁پDVL?әPx`93P7o$ԵeY5^Q-s3e3]|'߹Z?$585A}3A|ݿ8ieF]}^#5=8xMa؅Oy(f_~c8ٜ#Ƚox~z z85xx^۝x8xCm/889ͭ2%0 mx4yøg89cy9ńMQˀݍ4d cKO@(#ou+*W6͡3̐P ךa A}K]e po,yǹ /6QbDVJ A(!:X ^q im@L4AEVe5+w'9 xrr O^T{Sˌ7P"e _= 0hX+ )y!vZ5@." wB`k˦o t4֑\ ١ (4(s$ƻ`Sm8C7]Y J%B\@+}!mlN hI"z!}x|e6ol#/85Rb(L&,8C@[)*z5mo9% C3 b00$A2OԸ 6nLRDJm\O>+%;BkScn>mPSY- %tO q џ#}$k>c>^$"${#PHbeʐ}$~A \KeقD_,4HBv!$!} RS\*R̀]B!#A[ph^pXCю(a @>X &'HKDTpUVn`y0AaɎB1cfu JA7n(Pc@!sШ[qJ1µXaE88hq_`K=DIi-.Mcb=@ӭ6hv5@0{.I; P-gOQGe&BmI~Y=(o5g(\B%d`\ "JS $> Rc]J%8KvBTƸa$ F, !o89uѠ: <o9& ®R, 5+4+(=9^.:Ǥ[\BL`-z2,!,j۰5*j!ʫ*RTfg&tq _fa<"(ikzCda`k,8 D$%:+5&4 re/ ! " 4 (dR޵pٙ+- $'`FQ='.zN2R(aنЂTvdfb6Yg"hj\=+A(rVKF66@# ڔ.B)@g ZL-( Shb_d ,֠1d\Xc؎P^+[tEЀNhs@BdYl\L1L*\W$幱Uj I>gh}#h714!YP@Kw|eJqKHXo[845(P#G b/t+5[H3 ҈ޜ LdN2C$ ~Ą$mдM -#BHxE8 #68g?R>ϩaF|B4)Wd+Kx[ N!TDGG8@ W\  a.s:!*=f#8 d1f C8bc q(SD^f,cap>9p&q4CWG 2(w 9.Z " F1ZZfs < \FDF\"x1)=xCM(PDsP luhk$r,j@C 608fZ ".plR!)"ۈⁿ5 GA eC0P k٭R`A@:0Sr;(ۮ !UGEb ! F 0B '3bʀI081> V.5Q涆 Yv ic f$K5 *c+I?˖s`MBD10gD>B" EMҳ@^mn4!n3|@:Zs#uK*p3  m^Ե%! jϚ@4倀K?.1E}6 r[ rdxZiU1N|Kϐ>v5琠0 oNWJ]w7wӹǩX1(Af*Cq  *e+|,W's9 b>CwҙIk?} =_Kȣv{f׎]UWܕu|R+;1vl?<=x?Qmq}Ny"燯 ˝\+Z|\# @_ePÇd9{ols㈞bF?⛝?$ђW˽Kv~x4_|EOlc bL7M; ?G#$Bp@8h` .vvN`-l_XI(bN,6/P(06!n(DPI>B$8Hi` PJ>DgF8$E`hEJd,$L=23pF`2OB1D.,F )@,{2-a 0"꣔Ơ>܎O0ES.*.Á ~ R@xf!FO8z!bh"\Z 6 l&A"q@ dHhe2nA@q3:\^#4ob ^¬Gi2lP'q0"$(;8POTaM%OCa"Vq#` G2@q2|h ΌVP6@Q4EtaE @ )$zP ̸!1ǀZefuq3j"HD5"Ku 71 <'W.JPAVl8`QT`@O(5$CdT;ׯi܇Q)j˞cN CT$U;K c"VRj @h*.Z-XK NRoT`x7i]D+rZus>P CGUT` ԕ\5oA]!3x4 V^9jD C>bS'ƄrRqATb, aE7KbA~*Ʀm,tim`ƀؤZbalAVLzIDh*@  =7&!B>A)@*ڏ N| Z(l!G7ytX),' ǖ:Ik2XSkAD%D/+ ȶt.ʬҪ&Ebt`R l#` tkӒ %`A] 9k'""Ěk:8KN,;mdF#lvłDkxK݇S2'l`܊p @17b  hz~b6| y8a$\.ƶ88p< b`W8P\jg娡Ȳ!0i  )Ɏa@.Ȍ!B I Roΐ贩ldQ5nBnJRf9r8f$ d- 9Z g&S+LpU Ii3RbO~l*J/$ ^<mqYZyR!GOR\Zbڦ_ld "NZxlVWo^:ϐmrZ:vz^yJzu~: )C^:af!"QԚ i x6qAzbQ[ zRN`7(< |0Y6a!;/$ڋJ[IE%`hzNb f)CUB YEi:BAqjP !v2ЯRla! ȁX ̛ f0ך_+(v+[p:BQ6ۍqr #[pl -g;" 3a=: ^<`R;9V7L;ͣ@KS)?p<ħ6%U7e|c ;553-[Co8pI4`javF7KA{ԢuYnf>of n9~ z\ HSHmJV> h4HFh*|3]7QhM 1!:ph=LJ "B\ 16i*w"`W)gII N]R xrBYFd9m\n7p1_Xsl3\7q]x`C5I̱)X#9ȲGr FAFlkPDH i.(R('D)#l%AK8@pBVQ g;Q`,cHYŀI'?5hLQ+˒q@+hi{kOޔyԣJBֻ*v*P`˺hStK0לCx,V1JS7J%mRMԇX2N#(PPaۺ*&iX5)`>P8q#r`lBQuB  XboZ^2fzVMkOnްk""Coy[Y+kW5v09෾r!t X dW6>hi!G9X qd **<%lSy(=а !hP$%Qڛ"8=g:י̺D@fU eJd F&Cc: K`i7VF8ЀU}&&:g2YAV*8`K;DֈdJxHŽ8E҅"~QDoܱf[[pkFf'OցSʛbs0ldߒ"+4\κgDR^(7eam3AS$,PG3~w@İ @(ՠ?NhJ:RFV0G*L;e6!̦7g$=cQ ":A&!nA[*WqABpFp-潓cX44!l'=@lT'pw- hV4b'u6R@8V`Ne@f6`͈6V= N'șc9 mm5&`aec9ΰqؚ@0Y b%]Ʊ󞬫@Ĭ26 x\7Za4,w/aU66^yf' zwG qנuc {7DX*}Aƀtt)`|=% ,@,`ʠ}'T 162Od P Oud2 ` p =rAO9!v7wkqrHߥ f6C+r2r&efe v!`e`('_"{XpcP gg wq/u9xN* s v1 ِ ;2s p$ф"@2Ra ҧtUfGrvjx-=u$as'1P90C01Wd( s#O <1Ї-w#33||'32<-? 2d3#48c@]3 ޠ? 6YX ?c Xq`0y'b6 jE<؃yC9a c >W:3:9;":X-*P;;Rc aBVtN@6#@q>v??̗~anėxyU"K9SS_eVvf2i1G.pD U+F+4Q?HKuII 0 r0TiP 7j9-g @=jYF{o6>B^w?v >kS,Fr:eH=s@F!mCw˗y<1|BKU@<+rp;p :o:upkd8yKm 3:=p[7;l04 ~eqX`DPk9cտp&_٫<:G3\Px*8X% iQ Z90) ` t @ ʰ,h ӗUH>y'|-uQ6a~wx Ha6V gXh A"8Ub J @02h Uum`:[5;i'o%R2"~`)Ƿ&Sq xp<=!t/1;K ~42wL wK=` a9vz0{ nw'8 ;;79F ^,[,uGrU4r<ƣ#PH)9$CE`a.pi;焰 Dtg`t,ZxZ(K(Rm|uWX-2RaHnp@}⻪w+a>b(Fl@ļ>$"@ 0`aZF Pҵ0>O6GI8=ˍ/rɡ63">éb rv =ӈA ;q* R߸5:Ґ E"P 9&4C Ў.3@01}P%xma- 6#0x'Q4i #@|xᎡPDF -1 = 7P lG 5 R dQ~ɛI1un4 *e@6]b=PӴnf#b6ǜτ OY W |:F `F;;֐R;S@$ !^/jpF@ګ0p$vyЙ5֓C#F?8$=#C$ P1c>./!Z<_>ZQ1{=P:9iUDL_OU$?pw=+_GmR3َ=L%TF?u* :p."zñOy S@װgZ^!UIYf IճI;JsN)wQvŁQ '3K2֐eaOo"ğ?M"U0M"@u +?![NaD+)*ZLL,fD֔V zTM1k!~@`)4a>V lX! * E1ҡ\ƉlRgM} 4PwʔsҢD )@jf)TNpp*uƁ*ݪl،4` Uט{7/[H}0>Z먠ʗY,}+6hd{J{ ND9)Q-Τ^X̮4ݼ{N4RL(StlP8 f". HH"`aI![?G,`#sMǏ@h/Tϼ% `? /B&BC̣CKDQDIC.y@#n@k$CkLmaGLp1@G0Q%T*fI1b$J 6ZL`N?)]`O|/8!DEg294q<*׋, PD Jka9d@}Ct!ꔔ<РT;P@9!WDOP#%$s5E[N) eYE5T^K-,h 3#dQC6.DM@WEO>vP-g,NN4ntYeY^$X桱¤xM^qk$) f+@N4$lkn!,SX=>'DҪ qc !L_ \2rKp]@̚¡s\ciYǜ/0\WO)& oN(o]̦k I p;y8fL!DGC .ă-jqC$#h4cI_D8O{/젿4;_88 !bPJDfx*LlAp 9 u|9"k!.S;toTIW>eQ3&'ib $CS(|\Q.=cǃpF$XP~=Ⱦh!:E A;`5'Q!\(M2@/k r C`AAg C!wN@܈FDkDcΐ Hְ+G@l [%x^` DDT`#1XP(`4Z" EYbDCXF9E K' .P':.5 7@ .028hO"(A@,6p%8 qdL4L #u'F6,"EE08?x @h+Ĵ,"0mbH=M֨#?AP k*p =NQ=*J4M쀤>}&jhrUo:զ/}AZE'&*8TZ- YHH%PP L`짊oAԙ7^jav)9t8B.;.Q{s!Bz!i8Q3K9m1t˖$ ћQ>RSf/MRhfGA" 0`@PLR*% ֬UNR -g4rxIHg0Pè94qt@Kp@x@yufPYIEwU$8-;û!#z1 ?xɧ 5`DZMFѠh:@#(E#ڣB=nڝSn oX+l|(No f`hql]A8ky؉ܼUP p+e%lĤ֢hhs-窆SApwpYRw"< ?M߽ss@mϝ=[L3[{H(X7)쪱,;[L$R3(s_2[9k_[=X`"32<'ϭc&A5E!XFy; W}^f(kpXFMvX@baZnS]̮ "N{jix֩Cڵ-`Ew Lb(v'Fh@"fDXX`{8~ 9+`jI{,w.ʲD;R8’7+8)i۰K@^?1 h7R8QML+nT P>M0fᗌ`Xqvov[\[a i`h$c`sx!#F/@Z`=k&1Hvw]p6Iqv.rg5Pn=j.^-;8&F1d2E˄lp fCd-Fjn{&PE7X m누ю%aPXM醆Q^imXaiIŎnh %͕XRt*7-k`/C1t +6uPYlnVĂ>F᮰ y݉*?$v{5rU42P!!tFEarE(ÖtbG.hGi@b0pa/ KehM_e^%IEb_#_`Hni[ƒ1Q4͸Ԇԥ[aPI\jNe X`}GQLL h&Pܙ)ͺJ'snM\&BTLlqiRn(~)b5VUU)6u j+X/ Ru)Z'u ͑>uXu`WX"QxjⱋUHϷb{ q֯WǮ]"l:^v#GaWj>O=?sՁc}؟H,s=ɇ`.x`naII`hyX"&vhp8(-֘#p ؀GCx",Fh?6xaRD:I$ZE(ebm&^~&굧%"f.٦W9IIzxq♧,FY؋9 ɹ'}hmY(Jb*%@uiv):*ae*ejjZj $,? ?Nr?P%(X *VWkO:݂Km0d,Bm;z+b*T3C 3pCpS\c|sq"*̲ &C̯3Ӽl:OJ> g=i}DdK!5D:H{t$ K\!baIU `sIgk"Is't kDA1FU9P-D5 h [A Ep!OL ٠Tv: Ҿ#As"8#"$  l4spa55@$ldCCpXQwl-F첶YXmWe7L8qwf Sx^_6‰e t$(M"vE%ipmA6 H wu 0e+IAp@DMAqJ^H' 6!D ‹\|bR hGChl`A% 4XjP xD `|'Pad /D bIN ">9*-B8pА! O{A,0bPK bP$v+Lxd&*q"4m^74pg؟Gp,  {We|` 3 .% E`Cva<Xc @ XC5b Z W}kؤذ0⦂E^:1|]Ma ) Qİ" <0 e!(A(  ʙEDnPX@DԨ63xC8a bFO m?<$ a-bp*^X KHzs (Ο?qa@gC l(1Z \v0B" ؀J{Ĕ 6[.6!t(렻&,Tg,WCP|Op{N ](8H@rqK Ģ Uhtꋪf @n@竸I#/0d3~9pa7:Y ؅>`$bH"/")9EaR"gCB u`(l U '+OT `< %[ ̔s?|yiRP3U48.s0tf˿E/hCp:n[zpR48UhP@Q="D x=5H.* 9q.!dM@C]T'œ6R (>1 ˊ,GÕ3 b"+5)[6I9h184Ȃ\^.Ht%A%²W)lb[`E/.E}P P0h8|,PCԐ4]OtP!x|[}VɄ_: Lߌx#tԓPe(DIwKRSPp``* @9O:UX/FcDwQ AK\Md@pAIE &Q;1pHK"S|<# 0_('$!E/!F,!QM`DPO"bTT}E@%>"` "1Fk` PS RdӈePSH HLO/;ʀܜŲuMb,?@c0 6Ĥ;DM|TDj⽍CE >QA;c;JE4F$Db$IFV?D}V=v$e=dI$K6KԁJLd?ƅA eQQFRQQ QB%S"TR eU&XUrR^aSRXeW5QZeXe[eTe[Q]e^^\Zn%_` &aZ*a2&b&&YL8M9N&ORtd XeVfgi&yjJilMm&m&ό&ppn&ofq.gsgrs2tF t'ujv&t"'iNK:z'v'wsuygv{g{uxy}i'}~hy{feB*萄@L'rrFhfJ.(D@;lintr/R/0000755000176200001440000000000014600122247011601 5ustar liggesuserslintr/R/with.R0000644000176200001440000002234714577052532012723 0ustar liggesusers#' Modify lintr defaults #' #' Modify a list of defaults by name, allowing for replacement, deletion and addition of new elements. #' #' @param ... arguments of elements to change. If unnamed, the argument is automatically named. #' If the named argument already exists in `defaults`, it is replaced by the new element. #' If it does not exist, it is added. If the value is `NULL`, the element is removed. #' @param defaults named list of elements to modify. #' @return A modified list of elements, sorted by name. To achieve this sort in a platform-independent way, two #' transformations are applied to the names: (1) replace `_` with `0` and (2) convert [tolower()]. #' #' @seealso #' - [linters_with_defaults] for basing off lintr's set of default linters. #' - [all_linters] for basing off all available linters in lintr. #' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. #' - [available_linters] to get a data frame of available linters. #' - [linters] for a complete list of linters available in lintr. #' #' @examples #' # custom list of undesirable functions: #' # remove `sapply` (using `NULL`) #' # add `cat` (with an accompanying message), #' # add `print` (unnamed, i.e. with no accompanying message) #' # add `source` (as taken from `all_undesirable_functions`) #' my_undesirable_functions <- modify_defaults( #' defaults = default_undesirable_functions, #' sapply = NULL, "cat" = "No cat allowed", "print", all_undesirable_functions[["source"]] #' ) #' #' # list names of functions specified as undesirable #' names(my_undesirable_functions) #' @export modify_defaults <- function(defaults, ...) { if (missing(defaults) || !is.list(defaults) || !all(nzchar(names2(defaults)))) { stop("`defaults` must be a named list.") } vals <- list(...) nms <- names2(vals) missing_index <- !nzchar(nms, keepNA = TRUE) if (any(missing_index)) { nms[missing_index] <- guess_names(..., missing_index = missing_index) } to_null <- vapply(vals, is.null, logical(1L)) if (!all(nms[to_null] %in% names(defaults))) { bad_nms <- setdiff(nms[to_null], names(defaults)) is_are <- if (length(bad_nms) > 1L) "are" else "is" warning( "Trying to remove ", glue_collapse(sQuote(bad_nms), sep = ", ", last = " and "), ", which ", is_are, " not in `defaults`." ) } is.na(vals) <- nms == vals defaults[nms] <- vals res <- defaults[!vapply(defaults, is.null, logical(1L))] res <- res[platform_independent_order(names(res))] res } #' Create a tag-based linter configuration #' #' Make a new list based on all linters provided by `packages` and tagged with `tags`. #' The result of this function is meant to be passed to the `linters` argument of `lint()`, #' or to be put in your configuration file. #' #' @param ... Arguments of elements to change. If unnamed, the argument is automatically named. #' If the named argument already exists in the list of linters, it is replaced by the new element. #' If it does not exist, it is added. If the value is `NULL`, the linter is removed. #' @inheritParams available_linters #' #' @return A modified list of linters. #' @seealso #' - [linters_with_defaults] for basing off lintr's set of default linters. #' - [all_linters] for basing off all available linters in lintr. #' - [available_linters] to get a data frame of available linters. #' - [linters] for a complete list of linters available in lintr. #' #' @examples #' # `linters_with_defaults()` and `linters_with_tags("default")` are the same: #' all.equal(linters_with_defaults(), linters_with_tags("default")) #' #' # Get all linters useful for package development #' linters <- linters_with_tags(tags = c("package_development", "style")) #' names(linters) #' #' # Get all linters tagged as "default" from lintr and mypkg #' if (FALSE) { #' linters_with_tags("default", packages = c("lintr", "mypkg")) #' } #' @export linters_with_tags <- function(tags, ..., packages = "lintr", exclude_tags = "deprecated") { if (!is.character(tags) && !is.null(tags)) { stop("`tags` must be a character vector, or NULL.") } tagged_linters <- list() for (package in packages) { pkg_ns <- loadNamespace(package) ns_exports <- getNamespaceExports(pkg_ns) available <- available_linters(packages = package, tags = tags, exclude_tags = exclude_tags) if (nrow(available) > 0L) { if (!all(available$linter %in% ns_exports)) { missing_linters <- setdiff(available$linter, ns_exports) stop( "Linters ", glue_collapse(sQuote(missing_linters), sep = ", ", last = " and "), " advertised by `available_linters()` but not exported by package ", package, "." ) } linter_factories <- mget(available$linter, envir = pkg_ns) linters <- Map( call_linter_factory, linter_factory = linter_factories, linter_name = names(linter_factories), MoreArgs = list(package = package) ) tagged_linters <- c(tagged_linters, linters) } } modify_defaults(..., defaults = tagged_linters) } #' Create a linter configuration based on all available linters #' #' @inheritParams linters_with_tags #' #' @examples #' names(all_linters()) #' #' @seealso #' - [linters_with_defaults] for basing off lintr's set of default linters. #' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. #' - [available_linters] to get a data frame of available linters. #' - [linters] for a complete list of linters available in lintr. #' @export all_linters <- function(packages = "lintr", ...) { linters_with_tags(tags = NULL, packages = packages, ...) } #' Create a linter configuration based on defaults #' #' Make a new list based on \pkg{lintr}'s default linters. #' The result of this function is meant to be passed to the `linters` argument of `lint()`, #' or to be put in your configuration file. #' #' @param defaults,default Default list of linters to modify. Must be named. #' @inheritParams linters_with_tags #' @examplesIf requireNamespace("withr", quietly = TRUE) #' # When using interactively you will usually pass the result onto `lint` or `lint_package()` #' f <- withr::local_tempfile(lines = "my_slightly_long_variable_name <- 2.3", fileext = "R") #' lint(f, linters = linters_with_defaults(line_length_linter = line_length_linter(120))) #' #' # the default linter list with a different line length cutoff #' my_linters <- linters_with_defaults(line_length_linter = line_length_linter(120)) #' #' # omit the argument name if you are just using different arguments #' my_linters <- linters_with_defaults(defaults = my_linters, object_name_linter("camelCase")) #' #' # remove assignment checks (with NULL), add absolute path checks #' my_linters <- linters_with_defaults( #' defaults = my_linters, #' assignment_linter = NULL, #' absolute_path_linter() #' ) #' #' # checking the included linters #' names(my_linters) #' #' @seealso #' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. #' - [all_linters] for basing off all available linters in lintr. #' - [available_linters] to get a data frame of available linters. #' - [linters] for a complete list of linters available in lintr. #' @export linters_with_defaults <- function(..., defaults = default_linters) { dots <- list(...) if (missing(defaults) && "default" %in% names(dots)) { warning( "'default' is not an argument to linters_with_defaults(). Did you mean 'defaults'? ", "This warning will be removed when with_defaults() is fully deprecated." ) defaults <- dots$default nms <- names2(dots) missing_index <- !nzchar(nms, keepNA = TRUE) if (any(missing_index)) { names(dots)[missing_index] <- guess_names(..., missing_index = missing_index) } dots$default <- NULL dots <- c(dots, list(defaults = defaults)) return(do.call(modify_defaults, dots)) } modify_defaults(..., defaults = defaults) } #' @rdname linters_with_defaults #' @export with_defaults <- function(..., default = default_linters) { lintr_deprecated("with_defaults", "linters_with_defaults or modify_defaults", "3.0.0") # to ease the burden of transition -- default = NULL used to behave like defaults = list() now does if (is.null(default)) default <- list() linters_with_defaults(..., defaults = default) } #' @keywords internal #' @noRd call_linter_factory <- function(linter_factory, linter_name, package) { linter <- tryCatch( linter_factory(), error = function(e) { stop("Could not create linter with ", package, "::", linter_name, "(): ", conditionMessage(e)) } ) # Otherwise, all linters would be called "linter_factory". attr(linter, "name") <- linter_name linter } #' @keywords internal #' @noRd guess_names <- function(..., missing_index) { args <- as.character(eval(substitute(alist(...)[missing_index]))) # foo_linter(x=1) => "foo" # var[["foo"]] => "foo" # strip call: foo_linter(x=1) --> foo_linter # NB: Very long input might have newlines which are not caught # by . in a perl regex; see #774 args <- re_substitutes(args, rex("(", anything), "", options = "s") # strip extractors: pkg::foo_linter, var[["foo_linter"]] --> foo_linter args <- re_substitutes(args, rex(start, anything, '["' %or% "::"), "") re_substitutes(args, rex('"]', anything, end), "") } lintr/R/absolute_path_linter.R0000644000176200001440000000215314457657444016162 0ustar liggesusers#' Absolute path linter #' #' Check that no absolute paths are used (e.g. "/var", "C:\\System", "~/docs"). #' #' @param lax Less stringent linting, leading to fewer false positives. #' If `TRUE`, only lint path strings, which #' #' * contain at least two path elements, with one having at least two characters and #' * contain only alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation #' #' @examplesIf getRversion() >= "4.0" #' # Following examples use raw character constant syntax introduced in R 4.0. #' #' # will produce lints #' lint( #' text = 'R"--[/blah/file.txt]--"', #' linters = absolute_path_linter() #' ) #' #' # okay #' lint( #' text = 'R"(./blah)"', #' linters = absolute_path_linter() #' ) #' #' @evalRd rd_tags("absolute_path_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [nonportable_path_linter()] #' @export absolute_path_linter <- function(lax = TRUE) { path_linter_factory( path_function = function(path) { is_absolute_path(path) && is_valid_long_path(path, lax) }, message = "Do not use absolute paths." ) } lintr/R/is_numeric_linter.R0000644000176200001440000000675514577052532015467 0ustar liggesusers#' Redirect `is.numeric(x) || is.integer(x)` to just use `is.numeric(x)` #' #' [is.numeric()] returns `TRUE` when `typeof(x)` is `double` or `integer` -- #' testing `is.numeric(x) || is.integer(x)` is thus redundant. #' #' NB: This linter plays well with [class_equals_linter()], which can help #' avoid further `is.numeric()` equivalents like #' `any(class(x) == c("numeric", "integer"))`. #' #' @examples #' # will produce lints #' lint( #' text = "is.numeric(y) || is.integer(y)", #' linters = is_numeric_linter() #' ) #' #' lint( #' text = 'class(z) %in% c("numeric", "integer")', #' linters = is_numeric_linter() #' ) #' #' # okay #' lint( #' text = "is.numeric(y) || is.factor(y)", #' linters = is_numeric_linter() #' ) #' #' lint( #' text = 'class(z) %in% c("numeric", "integer", "factor")', #' linters = is_numeric_linter() #' ) #' #' @evalRd rd_tags("is_numeric_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export is_numeric_linter <- function() { # TODO(michaelchirico): this should also cover is.double(x) || is.integer(x) # TODO(#1636): is.numeric(x) || is.integer(x) || is.factor(x) is also redundant # TODO(michaelchirico): consdier capturing any(class(x) == "numeric/integer") # here directly; currently we rely on class_equals_linter() also active # TODO(michaelchirico): also catch inherits(x, c("numeric", "integer")) is_numeric_expr <- "expr[1][SYMBOL_FUNCTION_CALL[text() = 'is.numeric']]" is_integer_expr <- "expr[1][SYMBOL_FUNCTION_CALL[text() = 'is.integer']]" # testing things like is.numeric(x) || is.integer(x) or_xpath <- glue(" //OR2 /parent::expr[ expr/{is_numeric_expr} and expr/{is_integer_expr} and expr/{is_numeric_expr}/following-sibling::expr[1] = expr/{is_integer_expr}/following-sibling::expr[1] ] ") # testing class(x) %in% c("numeric", "integer") # TODO(michaelchirico): include typeof(x) %in% c("integer", "double") class_xpath <- " //SPECIAL[ text() = '%in%' and following-sibling::expr[ expr/SYMBOL_FUNCTION_CALL[text() = 'c'] and count(expr/STR_CONST) = 2 and count(expr) = 3 ] and preceding-sibling::expr/expr/SYMBOL_FUNCTION_CALL[text() = 'class'] ] /parent::expr " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content or_expr <- xml_find_all(xml, or_xpath) or_lints <- xml_nodes_to_lints( or_expr, source_expression = source_expression, lint_message = paste( "is.numeric(x) is the same as is.numeric(x) || is.integer(x).", "Use is.double(x) to test for objects stored as 64-bit floating point." ), type = "warning" ) class_expr <- xml_find_all(xml, class_xpath) if (length(class_expr) > 0L) { class_strings <- c( get_r_string(class_expr, "expr[2]/expr[2]/STR_CONST"), get_r_string(class_expr, "expr[2]/expr[3]/STR_CONST") ) is_lintable <- "integer" %in% class_strings && "numeric" %in% class_strings class_expr <- class_expr[is_lintable] } class_lints <- xml_nodes_to_lints( class_expr, source_expression = source_expression, lint_message = paste( 'is.numeric(x) is the same as class(x) %in% c("integer", "numeric").', "Use is.double(x) to test for objects stored as 64-bit floating point." ), type = "warning" ) c(or_lints, class_lints) }) } lintr/R/declared_functions.R0000644000176200001440000000071114510656222015563 0ustar liggesusersdeclared_s3_generics <- function(x) { xpath <- paste0( # Top level expression which "/exprlist/expr", # Assigns to a symbol "[./LEFT_ASSIGN|EQ_ASSIGN]", "[./expr[FUNCTION or OP-LAMBDA]]", "[./expr/SYMBOL]", # Is a S3 Generic (contains call to UseMethod) "[.//SYMBOL_FUNCTION_CALL[text()='UseMethod']]", # Retrieve assigned name of the function "/expr/SYMBOL/text()" ) as.character(xml_find_all(x, xpath)) } lintr/R/expect_named_linter.R0000644000176200001440000000316714577052532015760 0ustar liggesusers#' Require usage of `expect_named(x, n)` over `expect_equal(names(x), n)` #' #' [testthat::expect_named()] exists specifically for testing the [names()] of #' an object. [testthat::expect_equal()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = 'expect_equal(names(x), "a")', #' linters = expect_named_linter() #' ) #' #' # okay #' lint( #' text = 'expect_named(x, "a")', #' linters = expect_named_linter() #' ) #' #' lint( #' text = 'expect_equal(colnames(x), "a")', #' linters = expect_named_linter() #' ) #' #' lint( #' text = 'expect_equal(dimnames(x), "a")', #' linters = expect_named_linter() #' ) #' #' @evalRd rd_tags("expect_named_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_named_linter <- function() { xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'names']] and (position() = 1 or preceding-sibling::expr[STR_CONST]) ] /parent::expr " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_function <- xp_call_name(bad_expr) lint_message <- sprintf("expect_named(x, n) is better than %s(names(x), n)", matched_function) xml_nodes_to_lints(bad_expr, source_expression = source_expression, lint_message, type = "warning") }) } lintr/R/unnecessary_concatenation_linter.R0000644000176200001440000001037114577052532020563 0ustar liggesusers#' Unneeded concatenation linter #' #' Check that the [c()] function is not used without arguments nor with a single constant. #' #' @param allow_single_expression Logical, default `TRUE`. If `FALSE`, one-expression #' usages of `c()` are always linted, e.g. `c(x)` and `c(matrix(...))`. In some such #' cases, `c()` is being used for its side-effect of stripping non-name attributes; #' it is usually preferable to use the more readable [as.vector()] instead. #' [as.vector()] is not always preferable, for example with environments #' (especially, `R6` objects), in which case `list()` is the better alternative. #' #' @examples #' # will produce lints #' lint( #' text = "x <- c()", #' linters = unnecessary_concatenation_linter() #' ) #' #' lint( #' text = "x <- c(TRUE)", #' linters = unnecessary_concatenation_linter() #' ) #' #' lint( #' text = "x <- c(1.5 + 2.5)", #' linters = unnecessary_concatenation_linter(allow_single_expression = FALSE) #' ) #' #' # okay #' lint( #' text = "x <- NULL", #' linters = unnecessary_concatenation_linter() #' ) #' #' # In case the intent here was to seed a vector of known size #' lint( #' text = "x <- integer(4L)", #' linters = unnecessary_concatenation_linter() #' ) #' #' lint( #' text = "x <- TRUE", #' linters = unnecessary_concatenation_linter() #' ) #' #' lint( #' text = "x <- c(1.5 + 2.5)", #' linters = unnecessary_concatenation_linter(allow_single_expression = TRUE) #' ) #' #' @evalRd rd_tags("unnecessary_concatenation_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unnecessary_concatenation_linter <- function(allow_single_expression = TRUE) { # nolint: object_length_linter. stopifnot( is.logical(allow_single_expression), length(allow_single_expression) == 1L ) msg_empty <- paste( "Unneeded concatenation without arguments.", 'Replace the "c" call by NULL or, whenever possible,', "vector() seeded with the correct type and/or length." ) msg_const <- 'Unneeded concatenation of a constant. Remove the "c" call.' non_constant_cond <- "SYMBOL or (expr and not(OP-COLON and count(expr[SYMBOL or expr]) != 2))" pipes <- setdiff(magrittr_pipes, "%$%") to_pipe_xpath <- glue(" ./preceding-sibling::*[1][ self::PIPE or self::SPECIAL[{ xp_text_in_table(pipes) }] ] ") if (allow_single_expression) { zero_arg_cond <- glue("count(expr) = 1 and not( {to_pipe_xpath} / preceding-sibling::expr[ {non_constant_cond} ])") one_arg_cond <- glue("count(expr) = 2 and not(expr[2][ {non_constant_cond} ])") } else { zero_arg_cond <- glue("count(expr) = 1 and not( {to_pipe_xpath} )") one_arg_cond <- "count(expr) = 2 and not(expr[2]/SYMBOL[text() = '...'])" path_to_non_constant <- glue("./expr[2][ {non_constant_cond} ]") msg_const_expr <- paste( 'Unneeded concatenation of a simple expression. Remove the "c" call,', 'replacing with "as.vector" if using "c" to string attributes, e.g. in converting an array to a vector.' ) } call_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'c'] /parent::expr /parent::expr[ not(EQ_SUB) and ( {xp_or(zero_arg_cond, one_arg_cond)} ) ] ") num_args_xpath <- "count(./expr) - 1" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content c_calls <- xml_find_all(xml, call_xpath) # bump count(args) by 1 if inside a pipeline num_args <- as.integer(xml_find_num(c_calls, num_args_xpath)) + as.integer(!is.na(xml_find_first(c_calls, to_pipe_xpath))) # NB: the xpath guarantees num_args is 0, 1, or 2. 2 comes # in "a" %>% c("b"). # TODO(michaelchirico): can we handle this all inside the XPath with reasonable concision? is_unneeded <- num_args <= 1L c_calls <- c_calls[is_unneeded] num_args <- num_args[is_unneeded] msg <- ifelse(num_args == 0L, msg_empty, msg_const) if (!allow_single_expression) { is_single_expression <- !is.na(xml_find_first(c_calls, path_to_non_constant)) msg[is_single_expression] <- msg_const_expr } xml_nodes_to_lints( c_calls, source_expression = source_expression, lint_message = msg ) }) } lintr/R/matrix_apply_linter.R0000644000176200001440000001113014577052532016022 0ustar liggesusers#' Require usage of `colSums(x)` or `rowSums(x)` over `apply(x, ., sum)` #' #' [colSums()] and [rowSums()] are clearer and more performant alternatives to #' `apply(x, 2, sum)` and `apply(x, 1, sum)` respectively in the case of 2D #' arrays, or matrices #' #' @examples #' # will produce lints #' lint( #' text = "apply(x, 1, sum)", #' linters = matrix_apply_linter() #' ) #' #' lint( #' text = "apply(x, 2, sum)", #' linters = matrix_apply_linter() #' ) #' #' lint( #' text = "apply(x, 2, sum, na.rm = TRUE)", #' linters = matrix_apply_linter() #' ) #' #' lint( #' text = "apply(x, 2:4, sum)", #' linters = matrix_apply_linter() #' ) #' #' @evalRd rd_tags("matrix_apply_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export matrix_apply_linter <- function() { # mean() and sum() have very different signatures so we treat them separately. # sum() takes values to sum over via ..., has just one extra argument and is not a generic # mean() is a generic, takes values to average via a single argument, and can have extra arguments # # Currently supported values for MARGIN: scalar numeric and vector of contiguous values created by : (OP-COLON) sums_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'apply'] /parent::expr /following-sibling::expr[ NUM_CONST or OP-COLON/preceding-sibling::expr[NUM_CONST]/following-sibling::expr[NUM_CONST] and (position() = 2) ] /following-sibling::expr[ SYMBOL[text() = 'sum'] and (position() = 1) ] /parent::expr " # Since mean() is a generic, we make sure that we only lint cases with arguments # supported by colMeans() and rowMeans(), i.e., na.rm means_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'apply'] /parent::expr /following-sibling::expr[ NUM_CONST or OP-COLON/preceding-sibling::expr[NUM_CONST]/following-sibling::expr[NUM_CONST] and (position() = 2) ] /following-sibling::expr[ SYMBOL[text() = 'mean'] and (position() = 1) ] /parent::expr[ count(expr) = 4 or (count(expr) = 5 and SYMBOL_SUB[text() = 'na.rm']) ] " xpath <- glue("{sums_xpath} | {means_xpath}") # This doesn't handle the case when MARGIN and FUN are named and in a different position # but this should be relatively rate var_xpath <- "expr[position() = 2]" margin_xpath <- "expr[position() = 3]" fun_xpath <- "expr[position() = 4]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) var <- xml_text(xml_find_all(bad_expr, var_xpath)) fun <- xml_text(xml_find_all(bad_expr, fun_xpath)) fun <- tools::toTitleCase(fun) margin <- xml_find_all(bad_expr, margin_xpath) narm_val <- xml_text( xml_find_first(bad_expr, "SYMBOL_SUB[text() = 'na.rm']/following-sibling::expr") ) recos <- Map(craft_colsums_rowsums_msg, var, margin, fun, narm_val) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = sprintf("Use %1$s rather than %2$s", recos, get_r_string(bad_expr)), type = "warning" ) }) } craft_colsums_rowsums_msg <- function(var, margin, fun, narm_val) { if (is.na(xml_find_first(margin, "OP-COLON"))) { l1 <- xml_text(margin) l2 <- NULL } else { l1 <- xml_text(xml_find_first(margin, "expr[1]")) l2 <- xml_text(xml_find_first(margin, "expr[2]")) } # See #1764 for details about various cases. In short: # - If apply(., 1:l2, sum) -> rowSums(., dims = l2) # - If apply(., l1:l2, sum) -> rowSums(colSums(., dims = l1 - 1), dims = l2 - l1 + 1) # - This last case can be simplified to a simple colSums() call if l2 = length(dim(.)) # - dims argument can be dropped if equals to 1. This notably is the case for matrices if (is.null(l2)) { l2 <- l1 } # We don't want warnings when converted as NAs l1 <- suppressWarnings(as.integer(re_substitutes(l1, "L$", ""))) l2 <- suppressWarnings(as.integer(re_substitutes(l2, "L$", ""))) if (!is.na(narm_val)) { narm <- glue(", na.rm = {narm_val}") } else { narm <- "" } if (identical(l1, 1L)) { reco <- glue("row{fun}s({var}{narm}, dims = {l2})") } else { reco <- glue( "row{fun}s(col{fun}s({var}{narm}, dims = {l1 - 1}), dims = {l2 - l1 + 1})", " or ", "col{fun}s({var}{narm}, dims = {l1 - 1}) if {var} has {l2} dimensions" ) } # It's easier to remove this after the fact, rather than having never ending if/elses reco <- gsub(", dims = 1", "", reco, fixed = TRUE) reco } lintr/R/if_not_else_linter.R0000644000176200001440000000621714577052532015611 0ustar liggesusers#' Block statements like if (!A) x else y #' #' `if (!A) x else y` is the same as `if (A) y else x`, but the latter is #' easier to reason about in the `else` case. The former requires #' double negation that can be avoided by switching the statement order. #' #' This only applies in the simple `if/else` case. Statements like #' `if (!A) x else if (B) y else z` don't always have a simpler or #' more readable form. #' #' It also applies to [ifelse()] and the package equivalents #' `dplyr::if_else()` and `data.table::fifelse()`. #' #' @param exceptions Character vector of calls to exclude from linting. #' By default, [is.null()], [is.na()], and [missing()] are excluded #' given the common idiom `!is.na(x)` as "x is present". #' #' @examples #' # will produce lints #' lint( #' text = "if (!A) x else y", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "if (!A) x else if (!B) y else z", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "ifelse(!is_treatment, x, y)", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "if (!is.null(x)) x else 2", #' linters = if_not_else_linter(exceptions = character()) #' ) #' #' # okay #' lint( #' text = "if (A) x else y", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "if (!A) x else if (B) z else y", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "ifelse(is_treatment, y, x)", #' linters = if_not_else_linter() #' ) #' #' lint( #' text = "if (!is.null(x)) x else 2", #' linters = if_not_else_linter() #' ) #' #' @evalRd rd_tags("if_not_else_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export if_not_else_linter <- function(exceptions = c("is.null", "is.na", "missing")) { if_xpath <- glue(" //IF[following-sibling::ELSE[not(following-sibling::expr[IF])]] /following-sibling::expr[1][ OP-EXCLAMATION and not(expr[expr[SYMBOL_FUNCTION_CALL[{ xp_text_in_table(exceptions) }]]]) ] ") ifelse_xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)} ] /parent::expr /parent::expr[expr[ position() = 2 and OP-EXCLAMATION and not(expr[ OP-EXCLAMATION or expr/SYMBOL_FUNCTION_CALL[{ xp_text_in_table(exceptions) }] ]) ]] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content if_expr <- xml_find_all(xml, if_xpath) if_lints <- xml_nodes_to_lints( if_expr, source_expression = source_expression, lint_message = paste( "In a simple if/else statement,", "prefer `if (A) x else y` to the less-readable `if (!A) y else x`." ), type = "warning" ) ifelse_expr <- xml_find_all(xml, ifelse_xpath) ifelse_call <- xp_call_name(ifelse_expr) ifelse_lints <- xml_nodes_to_lints( ifelse_expr, source_expression = source_expression, lint_message = sprintf( "Prefer `%1$s(A, x, y)` to the less-readable `%1$s(!A, y, x)`.", ifelse_call ), type = "warning" ) c(if_lints, ifelse_lints) }) } lintr/R/redundant_equals_linter.R0000644000176200001440000000361514577052532016660 0ustar liggesusers#' Block usage of `==`, `!=` on logical vectors #' #' Testing `x == TRUE` is redundant if `x` is a logical vector. Wherever this is #' used to improve readability, the solution should instead be to improve the #' naming of the object to better indicate that its contents are logical. This #' can be done using prefixes (is, has, can, etc.). For example, `is_child`, #' `has_parent_supervision`, `can_watch_horror_movie` clarify their logical #' nature, while `child`, `parent_supervision`, `watch_horror_movie` don't. #' #' @examples #' # will produce lints #' lint( #' text = "if (any(x == TRUE)) 1", #' linters = redundant_equals_linter() #' ) #' #' lint( #' text = "if (any(x != FALSE)) 0", #' linters = redundant_equals_linter() #' ) #' #' # okay #' lint( #' text = "if (any(x)) 1", #' linters = redundant_equals_linter() #' ) #' #' lint( #' text = "if (!all(x)) 0", #' linters = redundant_equals_linter() #' ) #' #' @evalRd rd_tags("redundant_equals_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [outer_negation_linter()] #' @export redundant_equals_linter <- function() { xpath <- " (//EQ | //NE) /parent::expr /expr[NUM_CONST[text() = 'TRUE' or text() = 'FALSE']] /parent::expr " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) op <- xml_text(xml_find_first(bad_expr, "*[2]")) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( "Using", op, "on a logical vector is redundant.", "Well-named logical vectors can be used directly in filtering.", "For data.table's `i` argument, wrap the column name in (), like `DT[(is_treatment)]`." ), type = "warning" ) }) } lintr/R/undesirable_operator_linter.R0000644000176200001440000000550414577052532017531 0ustar liggesusers#' Undesirable operator linter #' #' Report the use of undesirable operators, e.g. \code{\link[base:ns-dblcolon]{:::}} or #' [`<<-`][base::assignOps] and suggest an alternative. #' #' @param op Named character vector. `names(op)` correspond to undesirable operators, #' while the values give a description of why the operator is undesirable. #' If `NA`, no additional information is given in the lint message. Defaults to #' [default_undesirable_operators]. To make small customizations to this list, #' use [modify_defaults()]. #' #' @examples #' # defaults for which functions are considered undesirable #' names(default_undesirable_operators) #' #' # will produce lints #' lint( #' text = "a <<- log(10)", #' linters = undesirable_operator_linter() #' ) #' #' lint( #' text = "mtcars$wt", #' linters = undesirable_operator_linter(op = c("$" = "As an alternative, use the `[[` accessor.")) #' ) #' #' # okay #' lint( #' text = "a <- log(10)", #' linters = undesirable_operator_linter() #' ) #' lint( #' text = 'mtcars[["wt"]]', #' linters = undesirable_operator_linter(op = c("$" = NA)) #' ) #' #' lint( #' text = 'mtcars[["wt"]]', #' linters = undesirable_operator_linter(op = c("$" = "As an alternative, use the `[[` accessor.")) #' ) #' #' @evalRd rd_tags("undesirable_operator_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export undesirable_operator_linter <- function(op = default_undesirable_operators) { if (is.null(names(op)) || !all(nzchar(names(op))) || length(op) == 0L) { stop("'op' should be a non-empty named character vector; use missing elements to indicate default messages.") } # infix must be handled individually below; non-assignment `=` are always OK operator_nodes <- infix_metadata$xml_tag_exact[ infix_metadata$string_value %in% setdiff(names(op), "%%") & !infix_metadata$xml_tag %in% c("EQ_SUB", "EQ_FORMALS") ] is_infix <- startsWith(names(op), "%") if (any(is_infix)) { operator_nodes <- c(operator_nodes, sprintf("SPECIAL[text() = '%s']", names(op)[is_infix])) } if (length(operator_nodes) == 0L) { stop("Did not recognize any valid operators in request for: ", toString(names(op))) } xpath <- paste(paste0("//", operator_nodes), collapse = " | ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_op <- xml_find_all(xml, xpath) operator <- xml_text(bad_op) lint_message <- sprintf("Operator `%s` is undesirable.", operator) alternative <- op[operator] has_alternative <- !is.na(alternative) lint_message[has_alternative] <- paste(lint_message[has_alternative], alternative[has_alternative]) xml_nodes_to_lints(bad_op, source_expression, lint_message, type = "warning") }) } lintr/R/extraction_operator_linter.R0000644000176200001440000000520114577052532017406 0ustar liggesusers#' Extraction operator linter #' #' Check that the `[[` operator is used when extracting a single element from an object, #' not `[` (subsetting) nor `$` (interactive use). #' #' @details #' #' There are three subsetting operators in R (`[[`, `[`, and `$`) and they interact differently #' with different data structures (atomic vector, list, data frame, etc.). #' #' Here are a few reasons to prefer the `[[` operator over `[` or `$` when you want to extract #' an element from a data frame or a list: #' #' - Subsetting a list with `[` always returns a smaller list, while `[[` returns #' the list element. #' #' - Subsetting a named atomic vector with `[` returns a named vector, while `[[` returns #' the vector element. #' #' - Subsetting a data frame (but not tibble) with `[` is type unstable; it can return #' a vector or a data frame. `[[`, on the other hand, always returns a vector. #' #' - For a data frame (but not tibble), `$` does partial matching (e.g. `df$a` will subset #' `df$abc`), which can be a source of bugs. `[[` doesn't do partial matching. #' #' For data frames (and tibbles), irrespective of the size, the `[[` operator is slower than `$`. #' For lists, however, the reverse is true. #' #' @examples #' # will produce lints #' lint( #' text = 'iris["Species"]', #' linters = extraction_operator_linter() #' ) #' #' lint( #' text = "iris$Species", #' linters = extraction_operator_linter() #' ) #' #' # okay #' lint( #' text = 'iris[["Species"]]', #' linters = extraction_operator_linter() #' ) #' #' @references #' - Subsetting [chapter](https://adv-r.hadley.nz/subsetting.html) from _Advanced R_ (Wickham, 2019). #' #' @evalRd rd_tags("extraction_operator_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export extraction_operator_linter <- function() { constant_nodes_in_brackets <- paste0("self::", c("expr", "OP-PLUS", "NUM_CONST", "STR_CONST")) xpath <- glue(" //OP-DOLLAR[not(preceding-sibling::expr[1]/SYMBOL[text() = 'self' or text() = '.self'])] | //OP-LEFT-BRACKET[ not(following-sibling::expr[1]/descendant::*[not({xp_or(constant_nodes_in_brackets)})]) and not(following-sibling::OP-COMMA) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_exprs <- xml_find_all(xml, xpath) msgs <- sprintf("Use `[[` instead of `%s` to extract an element.", xml_text(bad_exprs)) xml_nodes_to_lints( bad_exprs, source_expression = source_expression, lint_message = msgs, type = "warning" ) }) } lintr/R/implicit_assignment_linter.R0000644000176200001440000000746714577052532017375 0ustar liggesusers#' Avoid implicit assignment in function calls #' #' Assigning inside function calls makes the code difficult to read, and should #' be avoided, except for functions that capture side-effects (e.g. [capture.output()]). #' #' @param except A character vector of functions to be excluded from linting. #' @param allow_lazy logical, default `FALSE`. If `TRUE`, assignments that only #' trigger conditionally (e.g. in the RHS of `&&` or `||` expressions) are skipped. #' @param allow_scoped Logical, default `FALSE`. If `TRUE`, "scoped assignments", #' where the object is assigned in the statement beginning a branch and used only #' within that branch, are skipped. #' #' @examples #' # will produce lints #' lint( #' text = "if (x <- 1L) TRUE", #' linters = implicit_assignment_linter() #' ) #' #' lint( #' text = "mean(x <- 1:4)", #' linters = implicit_assignment_linter() #' ) #' #' # okay #' lines <- "x <- 1L\nif (x) TRUE" #' writeLines(lines) #' lint( #' text = lines, #' linters = implicit_assignment_linter() #' ) #' #' lines <- "x <- 1:4\nmean(x)" #' writeLines(lines) #' lint( #' text = lines, #' linters = implicit_assignment_linter() #' ) #' #' lint( #' text = "A && (B <- foo(A))", #' linters = implicit_assignment_linter(allow_lazy = TRUE) #' ) #' #' lines <- c( #' "if (any(idx <- x < 0)) {", #' " stop('negative elements: ', toString(which(idx)))", #' "}" #' ) #' writeLines(lines) #' lint( #' text = lines, #' linters = implicit_assignment_linter(allow_scoped = TRUE) #' ) #' #' @evalRd rd_tags("implicit_assignment_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' #' @export implicit_assignment_linter <- function(except = c("bquote", "expression", "expr", "quo", "quos", "quote"), allow_lazy = FALSE, allow_scoped = FALSE) { stopifnot(is.null(except) || is.character(except)) if (length(except) > 0L) { exceptions <- xp_text_in_table(except) xpath_exceptions <- glue("SYMBOL_FUNCTION_CALL[ not({exceptions}) ]") } else { xpath_exceptions <- "SYMBOL_FUNCTION_CALL" } # The walrus operator `:=` is also `LEFT_ASSIGN`, but is not a relevant operator # to be considered for the present linter. assignments <- paste( "//LEFT_ASSIGN[text() != ':=']", "//RIGHT_ASSIGN", sep = " | " ) xpath <- glue(" ({assignments}) /parent::expr[ preceding-sibling::*[2][self::IF or self::WHILE] or parent::forcond or preceding-sibling::expr/{xpath_exceptions} or parent::expr/*[1][self::OP-LEFT-PAREN] ] ") if (allow_lazy) { xpath <- paste0(xpath, "[not(ancestor::expr/preceding-sibling::*[self::AND2 or self::OR2])]") } if (allow_scoped) { # force 2nd preceding to ensure we're in the loop condition, not the loop expression in_branch_cond <- "ancestor::expr[preceding-sibling::*[2][self::IF or self::WHILE]]" xpath <- paste0( xpath, # _if_ we're in an IF/WHILE branch, lint if the assigned SYMBOL appears anywhere later on. glue("[not({in_branch_cond}) or expr[1]/SYMBOL = {in_branch_cond}/parent::expr/following::SYMBOL]") ) } Linter(function(source_expression) { # need the full file to also catch usages at the top level if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content bad_expr <- xml_find_all(xml, xpath) lint_message <- paste( "Avoid implicit assignments in function calls.", "For example, instead of `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`." ) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) }) } lintr/R/methods.R0000644000176200001440000001333714577052532013412 0ustar liggesusers#' @export format.lint <- function(x, ...) { if (requireNamespace("crayon", quietly = TRUE)) { color <- switch(x$type, warning = crayon::magenta, error = crayon::red, style = crayon::blue, crayon::bold ) emph <- crayon::bold } else { # nocov start color <- identity emph <- identity # nocov end } paste0( emph( x$filename, ":", as.character(x$line_number), ":", as.character(x$column_number), ": ", sep = "" ), color(x$type, ": ", sep = ""), "[", x$linter, "] ", emph(x$message), "\n", # swap tabs for spaces for #528 (sorry Richard Hendricks) chartr("\t", " ", x$line), "\n", highlight_string(x$message, x$column_number, x$ranges), "\n" ) } #' @export print.lint <- function(x, ...) { cat(format(x)) invisible(x) } markdown <- function(x, info, ...) { cat( sep = "", "[", x$filename, ":", as.character(x$line_number), ":", as.character(x$column_number), ":", "]", "(", file.path( "https://github.com", info$user, info$repo, "blob", info$commit, x$filename ), "#L", x$line_number, ")", " ", "*", x$type, ":", "* ", "[", x$linter, "] ", "**", x$message, "**\n", "```r\n\U200B", # we use a zero width unicode character here so that Github # does not strip the leading whitespace x$line, "\n", highlight_string(x$message, x$column_number, x$ranges), "\n```\n" ) invisible(x) } #' @export format.lints <- function(x, ...) { paste(vapply(x, format, character(1L)), collapse = "\n") } #' @export print.lints <- function(x, ...) { use_rstudio_source_markers <- lintr_option("rstudio_source_markers", TRUE) && requireNamespace("rstudioapi", quietly = TRUE) && rstudioapi::hasFun("sourceMarkers") github_annotation_project_dir <- lintr_option("github_annotation_project_dir", "") if (length(x) > 0L) { inline_data <- x[[1L]][["filename"]] == "" if (!inline_data && use_rstudio_source_markers) { rstudio_source_markers(x) } else if (in_github_actions()) { github_actions_log_lints(x, project_dir = github_annotation_project_dir) } else { if (in_ci() && settings$comment_bot) { info <- ci_build_info() lint_output <- trim_output( paste0( collapse = "\n", capture.output(invisible(lapply(x, markdown, info, ...))) ) ) github_comment(lint_output, info, ...) } lapply(x, print, ...) } if (isTRUE(settings$error_on_lint)) { quit("no", 31L, FALSE) # nocov } } else if (use_rstudio_source_markers) { # Empty lints: clear RStudio source markers rstudio_source_markers(x) } invisible(x) } trim_output <- function(x, max = 65535L) { # if x is less than the max, just return it if (length(x) <= 0L || nchar(x) <= max) { return(x) } # otherwise trim x to the max, then search for the lint starts x <- substr(x, 1L, max) re <- rex( "[", except_some_of(":"), ":", numbers, ":", numbers, ":", "]", "(", except_some_of(")"), ")", space, "*", or("style", "warning", "error"), ":", "*", except_some_of("\r\n"), newline, except_some_of("\r\n"), newline, except_some_of("\r\n"), newline, except_some_of("\r\n"), newline, except_some_of("\r\n"), newline ) lint_starts <- re_matches(x, re, global = TRUE, locations = TRUE)[[1L]] # if at least one lint ends before the cutoff, cutoff there, else just use # the cutoff last_end <- lint_starts[NROW(lint_starts), "end"] if (!is.na(last_end)) { substr(x, 1L, last_end) } else { x } } #' @export names.lints <- function(x, ...) { vapply(x, `[[`, character(1L), "filename") } #' @export split.lints <- function(x, f = NULL, ...) { if (is.null(f)) f <- names(x) splt <- split.default(x, f) for (i in names(splt)) class(splt[[i]]) <- "lints" return(splt) } #' @export as.data.frame.lints <- function(x, row.names = NULL, optional = FALSE, ...) { # nolint: object_name. (row.names, #764) data.frame( filename = vapply(x, `[[`, character(1L), "filename"), line_number = vapply(x, `[[`, integer(1L), "line_number"), column_number = vapply(x, `[[`, integer(1L), "column_number"), type = vapply(x, `[[`, character(1L), "type"), message = vapply(x, `[[`, character(1L), "message"), line = vapply(x, `[[`, character(1L), "line"), linter = vapply(x, `[[`, character(1L), "linter"), stringsAsFactors = FALSE ) } as_tibble.lints <- function(x, ..., # nolint: object_name_linter. .rows = NULL, .name_repair = c("check_unique", "unique", "universal", "minimal"), rownames = NULL) { stopifnot(requireNamespace("tibble", quietly = TRUE)) tibble::as_tibble(as.data.frame(x), ..., .rows = .rows, .name_repair = .name_repair, rownames = rownames) } as.data.table.lints <- function(x, keep.rownames = FALSE, ...) { # nolint: object_name_linter. stopifnot(requireNamespace("data.table", quietly = TRUE)) data.table::setDT(as.data.frame(x), keep.rownames = keep.rownames, ...) } #' @export `[.lints` <- function(x, ...) { attrs <- attributes(x) x <- unclass(x) x <- x[...] attributes(x) <- attrs x } #' @export summary.lints <- function(object, ...) { filenames <- vapply(object, `[[`, character(1L), "filename") types <- factor(vapply(object, `[[`, character(1L), "type"), levels = c("style", "warning", "error") ) tbl <- table(filenames, types) filenames <- rownames(tbl) res <- as.data.frame.matrix(tbl, stringsAsFactors = FALSE, row.names = NULL) res$filenames <- filenames %||% character() nms <- colnames(res) res[order(res$filenames), c("filenames", nms[nms != "filenames"])] } lintr/R/semicolon_linter.R0000644000176200001440000000570514577052532015314 0ustar liggesusers#' Semicolon linter #' #' Check that no semicolons terminate expressions. #' #' @param allow_compound Logical, default `FALSE`. If `TRUE`, "compound" #' semicolons (e.g. as in `x; y`, i.e., on the same line of code) are allowed. #' @param allow_trailing Logical, default `FALSE`. If `TRUE`, "trailing" #' semicolons (i.e., those that terminate lines of code) are allowed. #' #' @examples #' # will produce lints #' lint( #' text = "a <- 1;", #' linters = semicolon_linter() #' ) #' #' lint( #' text = "a <- 1; b <- 1", #' linters = semicolon_linter() #' ) #' #' lint( #' text = "function() { a <- 1; b <- 1 }", #' linters = semicolon_linter() #' ) #' #' # okay #' lint( #' text = "a <- 1", #' linters = semicolon_linter() #' ) #' #' lint( #' text = "a <- 1;", #' linters = semicolon_linter(allow_trailing = TRUE) #' ) #' #' code_lines <- "a <- 1\nb <- 1" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = semicolon_linter() #' ) #' #' lint( #' text = "a <- 1; b <- 1", #' linters = semicolon_linter(allow_compound = TRUE) #' ) #' #' code_lines <- "function() { \n a <- 1\n b <- 1\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = semicolon_linter() #' ) #' #' @evalRd rd_tags("semicolon_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export semicolon_linter <- function(allow_compound = FALSE, allow_trailing = FALSE) { msg_trailing <- "Trailing semicolons are not needed." msg_compound <- "Compound semicolons are discouraged. Replace them by a newline." if (allow_compound && allow_trailing) { stop("At least one of `allow_compound` or `allow_trailing` must be FALSE, otherwise no lints can be generated.") } else if (allow_compound && !allow_trailing) { # lint only trailing xpath <- "//OP-SEMICOLON[not(@line1 = following-sibling::*[1]/@line1)]" msg <- msg_trailing # nolint: object_usage. (usage in linter, since need_detection == FALSE) need_detection <- FALSE } else if (!allow_compound && allow_trailing) { # lint only compound xpath <- "//OP-SEMICOLON[@line1 = following-sibling::*[1]/@line1]" msg <- msg_compound # nolint: object_usage. (usage in linter, since need_detection == FALSE) need_detection <- FALSE } else { # lint all xpath <- "//OP-SEMICOLON" need_detection <- TRUE } compound_xpath <- "self::*[@line1 = following-sibling::*[1]/@line1]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content bad_exprs <- xml_find_all(xml, xpath) if (need_detection) { is_trailing <- is.na(xml_find_first(bad_exprs, compound_xpath)) msg <- ifelse(is_trailing, msg_trailing, msg_compound) } xml_nodes_to_lints( bad_exprs, source_expression = source_expression, lint_message = msg ) }) } lintr/R/unnecessary_placeholder_linter.R0000644000176200001440000000360314577052532020220 0ustar liggesusers#' Block usage of pipeline placeholders if unnecessary #' #' The argument placeholder `.` in magrittr pipelines is unnecessary if #' passed as the first positional argument; using it can cause confusion #' and impacts readability. #' #' This is true for forward (`%>%`), assignment (`%<>%`), and tee (`%T>%`) operators. #' #' @examples #' # will produce lints #' lint( #' text = "x %>% sum(., na.rm = TRUE)", #' linters = unnecessary_placeholder_linter() #' ) #' #' # okay #' lint( #' text = "x %>% sum(na.rm = TRUE)", #' linters = unnecessary_placeholder_linter() #' ) #' #' lint( #' text = "x %>% lm(data = ., y ~ z)", #' linters = unnecessary_placeholder_linter() #' ) #' #' lint( #' text = "x %>% outer(., .)", #' linters = unnecessary_placeholder_linter() #' ) #' #' @evalRd rd_tags("unnecessary_placeholder_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unnecessary_placeholder_linter <- function() { # TODO(michaelchirico): handle R4.2.0 native placeholder _ as well xpath <- glue(" //SPECIAL[{ xp_text_in_table(magrittr_pipes) }] /following-sibling::expr[ expr/SYMBOL_FUNCTION_CALL and not(expr[ position() > 2 and descendant-or-self::expr/SYMBOL[text() = '.'] ]) ] /expr[2][ SYMBOL[text() = '.'] and not(preceding-sibling::*[1][self::EQ_SUB]) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( "Don't use the placeholder (`.`) when it's not needed,", "i.e., when it's only used as the first positional argument in a pipeline step." ), type = "warning" ) }) } lintr/R/addins.R0000644000176200001440000000222014577052532013176 0ustar liggesusers# nocov start addin_lint <- function() { if (!requireNamespace("rstudioapi", quietly = TRUE)) { stop("'rstudioapi' is required for add-ins.") } filename <- rstudioapi::getSourceEditorContext() if (filename$path == "") { return("Current source has no path. Please save before continue") } config_file <- (get("find_config", asNamespace("lintr")))(filename$path) if (length(config_file) == 0L) { config_linters <- NULL } else { config <- read.dcf(config_file, all = TRUE) config_linters <- config[["linters"]] } linters <- if (length(config_linters) == 0L) { message("No configuration found. Using default linters.") default_linters } else { eval(parse(text = config_linters)) } lintr::lint(filename$path, linters = linters) } addin_lint_package <- function() { if (!requireNamespace("rstudioapi", quietly = TRUE)) { stop("'rstudioapi' is required for add-ins.") } project <- rstudioapi::getActiveProject() project_path <- if (is.null(project)) getwd() else project if (is.null(project)) message("No project found, passing current directory") lintr::lint_package(project_path) } # nocov end lintr/R/linter_tags.R0000644000176200001440000001743514577052532014265 0ustar liggesusers#' Get Linter metadata from a package #' #' `available_linters()` obtains a tagged list of all Linters available in a package. #' #' @param packages A character vector of packages to search for linters. #' @param tags Optional character vector of tags to search. Only linters with at least one matching tag will be #' returned. If `tags` is `NULL`, all linters will be returned. See `available_tags("lintr")` to find out what #' tags are already used by lintr. #' @param exclude_tags Tags to exclude from the results. Linters with at least one matching tag will not be returned. #' If `except_tags` is `NULL`, no linters will be excluded. Note that `tags` takes priority, meaning that any #' tag found in both `tags` and `exclude_tags` will be included, not excluded. #' #' @section Package Authors: #' #' To implement `available_linters()` for your package, include a file `inst/lintr/linters.csv` in your #' package. #' The CSV file must contain the columns 'linter' and 'tags', and be UTF-8 encoded. #' Additional columns will be silently ignored if present and the columns are identified by name. #' Each row describes a linter by #' #' 1. its function name (e.g. `"assignment_linter"`) in the column 'linter'. #' 2. space-separated tags associated with the linter (e.g. `"style consistency default"`) in the column 'tags'. #' #' Tags should be snake_case. #' #' See `available_tags("lintr")` to find out what tags are already used by lintr. #' #' @return #' `available_linters` returns a data frame with columns 'linter', 'package' and 'tags': #' #' \describe{ #' \item{linter}{A character column naming the function associated with the linter.} #' \item{package}{A character column containing the name of the package providing the linter.} #' \item{tags}{A list column containing tags associated with the linter.} #' } #' #' @examples #' lintr_linters <- available_linters() #' #' # If the package doesn't exist or isn't installed, an empty data frame will be returned #' available_linters("does-not-exist") #' #' lintr_linters2 <- available_linters(c("lintr", "does-not-exist")) #' identical(lintr_linters, lintr_linters2) #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [available_tags()] to retrieve the set of valid tags. #' @export available_linters <- function(packages = "lintr", tags = NULL, exclude_tags = "deprecated") { if (!is.character(packages)) { stop("`packages` must be a character vector.") } if (!is.null(tags) && !is.character(tags)) { stop("`tags` must be a character vector.") } if (!is.null(exclude_tags) && !is.character(exclude_tags)) { stop("`exclude_tags` must be a character vector.") } # any tags specified explicitly will not be excluded (#1959) exclude_tags <- setdiff(exclude_tags, tags) # Handle multiple packages if (length(packages) > 1L) { return(do.call(rbind, lapply(packages, available_linters, tags = tags, exclude_tags = exclude_tags))) } csv_file <- system.file("lintr", "linters.csv", package = packages) if (!file.exists(csv_file)) { return(empty_linters()) } available <- utils::read.csv(csv_file, encoding = "UTF-8", as.is = TRUE) if (!validate_linter_db(available, packages)) { return(empty_linters()) } build_available_linters(available, packages, tags, exclude_tags) } build_available_linters <- function(available, package, tags, exclude_tags) { available_df <- data.frame( linter = available[["linter"]], package, stringsAsFactors = FALSE ) available_df$tags <- strsplit(available[["tags"]], split = " ", fixed = TRUE) if (!is.null(tags)) { matches_tags <- vapply(available_df$tags, function(linter_tags) any(linter_tags %in% tags), logical(1L)) available_df <- available_df[matches_tags, ] } if (!is.null(exclude_tags)) { matches_exclude <- vapply(available_df$tags, function(linter_tags) any(linter_tags %in% exclude_tags), logical(1L)) available_df <- available_df[!matches_exclude, ] } # Due to removal of deprecated linters in the returned data frame, there can be gaps in row numbers. # To avoid this inconsistency, regenerate row names. rownames(available_df) <- NULL available_df } #' Make sure we always return a valid data frame #' #' `data.frame` constructors don't handle zero-row list-columns properly, so supply `tags` afterwards. #' @noRd empty_linters <- function() { empty_df <- data.frame(linter = character(), package = character(), stringsAsFactors = FALSE) empty_df$tags <- list() empty_df } validate_linter_db <- function(available, package) { # Check that the csv file contains two character columns, named 'linter' and 'tags'. # Otherwise, fallback to an empty data frame. if (!all(c("linter", "tags") %in% colnames(available))) { warning( "`linters.csv` must contain the columns 'linter' and 'tags'.\nPackage '", package, "' is missing ", paste0("'", setdiff(c("linter", "tags"), names(available)), "'", collapse = " and "), "." ) return(FALSE) } nrow(available) > 0L } #' @rdname available_linters #' #' @description #' `available_tags()` searches for available tags. #' #' @return `available_tags` returns a character vector of linter tags used by the packages. #' @export #' @examples #' available_tags() available_tags <- function(packages = "lintr") { platform_independent_sort(unique(unlist(available_linters(packages = packages, exclude_tags = NULL)[["tags"]]))) } # nocov start #' Generate Rd fragment for the Tags section of a linter #' #' @param linter_name Name of the linter to generate Rd code for. #' #' @noRd rd_tags <- function(linter_name) { linters <- available_linters(exclude_tags = NULL) tags <- platform_independent_sort(linters[["tags"]][[match(linter_name, linters[["linter"]])]]) if (length(tags) == 0L) { stop("tags are required, but found none for ", linter_name) } c( "\\section{Tags}{", paste0("\\link[=", tags, "_linters]{", tags, "}", collapse = ", "), "}" ) } #' Generate Rd fragment for the Linters section of a tag #' #' @param tag_name Name of the tag to generate Rd code for. #' #' @noRd rd_linters <- function(tag_name) { linters <- available_linters(tags = tag_name) tagged <- platform_independent_sort(linters[["linter"]]) if (length(tagged) == 0L) { stop("No linters found associated with tag ", tag_name) } c( "\\section{Linters}{", paste0("The following linters are tagged with '", tag_name, "':"), "\\itemize{", paste0("\\item{\\code{\\link{", tagged, "}}}"), "}", # itemize "}" # section ) } #' Generate Rd fragment for the main help page, listing all tags #' #' @noRd rd_taglist <- function() { linters <- available_linters(exclude_tags = NULL) # don't count tags on deprecated linters to the counts of other tags linters$tags <- lapply(linters$tags, function(x) if ("deprecated" %in% x) "deprecated" else x) tag_table <- table(unlist(linters[["tags"]])) tags <- platform_independent_sort(names(tag_table)) # re-order tag_table <- tag_table[tags] c( "\\section{Tags}{", "The following tags exist:", "\\itemize{", vapply(tags, function(tag) { paste0("\\item{\\link[=", tag, "_linters]{", tag, "} (", tag_table[[tag]], " linters)}") }, character(1L)), "}", # itemize "}" # section ) } #' Generate Rd fragment for the main help page, listing all linters #' #' @noRd rd_linterlist <- function() { linters <- available_linters() linter_names <- platform_independent_sort(linters[["linter"]]) c( "\\section{Linters}{", "The following linters exist:", "\\itemize{", vapply(linter_names, function(linter_name) { tags <- platform_independent_sort(linters[["tags"]][[match(linter_name, linters[["linter"]])]]) paste0("\\item{\\code{\\link{", linter_name, "}} (tags: ", toString(tags), ")}") }, character(1L)), "}", # itemize "}" # section ) } # nocov end lintr/R/indentation_linter.R0000644000176200001440000003241514577052532015636 0ustar liggesusers#' Check that indentation is consistent #' #' @param indent Number of spaces, that a code block should be indented by relative to its parent code block. #' Used for multi-line code blocks (`{ ... }`), function calls (`( ... )`) and extractions (`[ ... ]`, `[[ ... ]]`). #' Defaults to 2. #' @param hanging_indent_style Indentation style for multi-line function calls with arguments in their first line. #' Defaults to tidyverse style, i.e. a block indent is used if the function call terminates with `)` on a separate #' line and a hanging indent if not. #' Note that function multi-line function calls without arguments on their first line will always be expected to have #' block-indented arguments. #' If `hanging_indent_style` is `"tidy"`, multi-line function definitions are expected to be double-indented if the #' first line of the function definition contains no arguments and the closing parenthesis is not on its own line. #' #' ```r #' # complies to any style #' map( #' x, #' f, #' additional_arg = 42 #' ) #' #' # complies to "tidy" and "never" #' map(x, f, #' additional_arg = 42 #' ) #' #' # complies to "always" #' map(x, f, #' additional_arg = 42 #' ) #' #' # complies to "tidy" and "always" #' map(x, f, #' additional_arg = 42) #' #' # complies to "never" #' map(x, f, #' additional_arg = 42) #' #' # complies to "tidy" #' function( #' a, #' b) { #' # body #' } #' ``` #' @param assignment_as_infix Treat `<-` as a regular (i.e. left-associative) infix operator? #' This means, that infix operators on the right hand side of an assignment do not trigger a second level of #' indentation: #' ```r #' # complies to any style #' variable <- a %+% #' b %+% #' c #' #' # complies to assignment_as_infix = TRUE #' variable <- #' a %+% #' b %+% #' c #' #' # complies to assignment_as_infix = FALSE #' variable <- #' a %+% #' b %+% #' c #' ``` #' #' @examples #' # will produce lints #' code_lines <- "if (TRUE) {\n1 + 1\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter() #' ) #' #' code_lines <- "if (TRUE) {\n 1 + 1\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter() #' ) #' #' code_lines <- "map(x, f,\n additional_arg = 42\n)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter(hanging_indent_style = "always") #' ) #' #' code_lines <- "map(x, f,\n additional_arg = 42)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter(hanging_indent_style = "never") #' ) #' #' # okay #' code_lines <- "map(x, f,\n additional_arg = 42\n)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter() #' ) #' #' code_lines <- "if (TRUE) {\n 1 + 1\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = indentation_linter(indent = 4) #' ) #' #' @evalRd rd_tags("indentation_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - #' #' @export indentation_linter <- function(indent = 2L, hanging_indent_style = c("tidy", "always", "never"), assignment_as_infix = TRUE) { paren_tokens_left <- c("OP-LEFT-BRACE", "OP-LEFT-PAREN", "OP-LEFT-BRACKET", "LBB") paren_tokens_right <- c("OP-RIGHT-BRACE", "OP-RIGHT-PAREN", "OP-RIGHT-BRACKET", "OP-RIGHT-BRACKET") infix_tokens <- setdiff(infix_metadata$xml_tag, c("OP-LEFT-BRACE", "OP-COMMA", paren_tokens_left)) no_paren_keywords <- c("ELSE", "REPEAT") keyword_tokens <- c("FUNCTION", "OP-LAMBDA", "IF", "FOR", "WHILE") xp_last_on_line <- "@line1 != following-sibling::*[not(self::COMMENT)][1]/@line1" hanging_indent_style <- match.arg(hanging_indent_style) find_indent_type <- switch(hanging_indent_style, tidy = build_indentation_style_tidy(), always = build_indentation_style_always(), never = function(change) "block" ) if (isTRUE(assignment_as_infix)) { suppressing_tokens <- c("LEFT_ASSIGN", "EQ_ASSIGN", "EQ_SUB", "EQ_FORMALS") xp_suppress <- glue("preceding-sibling::{suppressing_tokens}[{xp_last_on_line}]") restoring_tokens <- c("expr[SYMBOL_FUNCTION_CALL]", "OP-LEFT-BRACE") xp_restore <- glue("preceding-sibling::{restoring_tokens}") # match the first ancestor expr that is either # * a suppressing token (<- or =) or # * a restoring token (braces or a function call) # suppress the indent if the matched ancestor is a suppressing token infix_condition <- glue(" and not(ancestor::expr[{xp_or(c(xp_suppress, xp_restore))}][1][{xp_or(xp_suppress)}]) ") } else { infix_condition <- "" } xp_block_ends <- paste0( "number(", paste( c( glue("self::{paren_tokens_left}/following-sibling::{paren_tokens_right}/preceding-sibling::*[1]/@line2"), glue(" self::*[{xp_and(paste0('not(self::', paren_tokens_left, ')'))}] /following-sibling::SYMBOL_FUNCTION_CALL /parent::expr /following-sibling::expr[1] /@line2 "), glue(" self::*[ {xp_and(paste0('not(self::', paren_tokens_left, ')'))} and not(following-sibling::SYMBOL_FUNCTION_CALL) ] /following-sibling::*[not(self::COMMENT)][1] /@line2 ") ), collapse = " | " ), ")" ) global_nodes <- function(nodes) paste0("//", nodes, collapse = "|") xp_indent_changes <- paste( c( glue("//{paren_tokens_left}[not( @line1 = following-sibling::expr[ @line2 > @line1 and ({xp_or(paste0('descendant::', paren_tokens_left, '[', xp_last_on_line, ']'))}) ]/@line1 )]"), glue("({ global_nodes(infix_tokens) })[{xp_last_on_line}{infix_condition}]"), glue("({ global_nodes(no_paren_keywords) })[{xp_last_on_line}]"), glue(" ({ global_nodes(keyword_tokens) }) /following-sibling::OP-RIGHT-PAREN[ {xp_last_on_line} and not(following-sibling::expr[1][OP-LEFT-BRACE]) ] ") ), collapse = " | " ) xp_multiline_string <- "//STR_CONST[@line1 < @line2]" Linter(function(source_expression) { # must run on file level because a line can contain multiple expressions, losing indentation information, e.g. # #> fun( # a) # comment # # will have "# comment" as a separate expression if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content # Indentation increases by 1 for: # - { } blocks that span multiple lines # - ( ), [ ], or [[ ]] calls that span multiple lines # + if a token follows (, a hanging indent is required until ) # + if there is no token following ( on the same line, a block indent is required until ) # - binary operators where the second arguments starts on a new line indent_levels <- re_matches( source_expression$file_lines, rex(start, any_spaces), locations = TRUE )[, "end"] expected_indent_levels <- integer(length(indent_levels)) is_hanging <- logical(length(indent_levels)) indent_changes <- xml_find_all(xml, xp_indent_changes) for (change in indent_changes) { change_type <- find_indent_type(change) change_begin <- as.integer(xml_attr(change, "line1")) + 1L change_end <- xml_find_num(change, xp_block_ends) if (isTRUE(change_begin <= change_end)) { to_indent <- seq(from = change_begin, to = change_end) expected_indent_levels[to_indent] <- find_new_indent( current_indent = expected_indent_levels[to_indent], change_type = change_type, indent = indent, hanging_indent = as.integer(xml_attr(change, "col2")) ) is_hanging[to_indent] <- change_type == "hanging" } } in_str_const <- logical(length(indent_levels)) multiline_strings <- xml_find_all(xml, xp_multiline_string) for (string in multiline_strings) { is_in_str <- seq( from = as.integer(xml_attr(string, "line1")) + 1L, to = as.integer(xml_attr(string, "line2")) ) in_str_const[is_in_str] <- TRUE } # Only lint non-empty lines if the indentation level doesn't match. bad_lines <- which(indent_levels != expected_indent_levels & nzchar(trimws(source_expression$file_lines)) & !in_str_const) if (length(bad_lines) > 0L) { # Suppress consecutive lints with the same indentation difference, to not generate an excessive number of lints is_consecutive_lint <- c(FALSE, diff(bad_lines) == 1L) indent_diff <- expected_indent_levels[bad_lines] - indent_levels[bad_lines] is_same_diff <- c(FALSE, diff(indent_diff) == 0L) bad_lines <- bad_lines[!(is_consecutive_lint & is_same_diff)] lint_messages <- sprintf( "%s should be %d spaces but is %d spaces.", ifelse(is_hanging[bad_lines], "Hanging indent", "Indentation"), expected_indent_levels[bad_lines], indent_levels[bad_lines] ) lint_lines <- unname(as.integer(names(source_expression$file_lines)[bad_lines])) lint_ranges <- cbind( pmin(expected_indent_levels[bad_lines] + 1L, indent_levels[bad_lines]), # If the expected indent is larger than the current line width, the lint range would become invalid. # Therefor, limit range end to end of line. pmin( pmax(expected_indent_levels[bad_lines], indent_levels[bad_lines]), nchar(source_expression$file_lines[bad_lines]) + 1L ) ) Map( Lint, filename = source_expression$filename, line_number = lint_lines, column_number = indent_levels[bad_lines], type = "style", message = lint_messages, line = unname(source_expression$file_lines[bad_lines]), # TODO(AshesITR) when updating supported R version to R >= 4.1: # replace by ranges = apply(lint_ranges, 1L, list, simplify = FALSE) ranges = lapply( seq_along(bad_lines), function(i) { list(lint_ranges[i, ]) } ) ) } else { list() } }) } find_new_indent <- function(current_indent, change_type, indent, hanging_indent) { switch(change_type, suppress = current_indent, hanging = hanging_indent, double = current_indent + 2L * indent, block = current_indent + indent ) } build_indentation_style_tidy <- function() { paren_tokens_left <- c("OP-LEFT-BRACE", "OP-LEFT-PAREN", "OP-LEFT-BRACKET", "LBB") paren_tokens_right <- c("OP-RIGHT-BRACE", "OP-RIGHT-PAREN", "OP-RIGHT-BRACKET", "OP-RIGHT-BRACKET") xp_last_on_line <- "@line1 != following-sibling::*[not(self::COMMENT)][1]/@line1" xp_inner_expr <- "preceding-sibling::*[1][self::expr and expr[SYMBOL_FUNCTION_CALL]]/*[not(self::COMMENT)]" # double indent is tidyverse style for function definitions # triggered only if the closing parenthesis of the function definition is not on its own line and the opening # parenthesis has no arguments behind it. # this allows both of these styles: # #> function( #> a, #> b) { #> body #> } # #> function( #> a, #> b #> ) { #> body #> } xp_is_double_indent <- " parent::expr[(FUNCTION or OP-LAMBDA) and not(@line1 = SYMBOL_FORMALS/@line1)] /OP-RIGHT-PAREN[@line1 = preceding-sibling::*[not(self::COMMENT)][1]/@line2] " xp_suppress <- paste( glue(" self::{paren_tokens_left}[ @line1 = following-sibling::{paren_tokens_right}/{xp_inner_expr}[position() = 1]/@line1 ]/following-sibling::{paren_tokens_right}[ @line1 > {xp_inner_expr}[position() = last() - 1]/@line2 ]"), collapse = " | " ) xp_is_not_hanging <- paste( c( glue( "self::{paren_tokens_left}/following-sibling::{paren_tokens_right}[@line1 > preceding-sibling::*[1]/@line2]" ), glue("self::*[{xp_and(paste0('not(self::', paren_tokens_left, ')'))} and {xp_last_on_line}]") ), collapse = " | " ) function(change) { if (length(xml_find_first(change, xp_is_double_indent)) > 0L) { "double" } else if (length(xml_find_first(change, xp_suppress)) > 0L) { "suppress" } else if (length(xml_find_first(change, xp_is_not_hanging)) == 0L) { "hanging" } else { "block" } } } build_indentation_style_always <- function() { paren_tokens_left <- c("OP-LEFT-BRACE", "OP-LEFT-PAREN", "OP-LEFT-BRACKET", "LBB") paren_tokens_right <- c("OP-RIGHT-BRACE", "OP-RIGHT-PAREN", "OP-RIGHT-BRACKET", "OP-RIGHT-BRACKET") xp_last_on_line <- "@line1 != following-sibling::*[not(self::COMMENT)][1]/@line1" xp_is_not_hanging <- paste( c( glue(" self::{paren_tokens_left}[{xp_last_on_line}]/ following-sibling::{paren_tokens_right}[@line1 > preceding-sibling::*[1]/@line2] "), glue("self::*[{xp_and(paste0('not(self::', paren_tokens_left, ')'))} and {xp_last_on_line}]") ), collapse = " | " ) function(change) { if (length(xml_find_first(change, xp_is_not_hanging)) == 0L) { "hanging" } else { "block" } } } lintr/R/get_source_expressions.R0000644000176200001440000005722014577052532016547 0ustar liggesusers#' Parsed sourced file from a filename #' #' This object is given as input to each linter. #' #' @details #' The file is read using the `encoding` setting. #' This setting is found by taking the first valid result from the following locations #' #' 1. The `encoding` key from the usual lintr configuration settings. #' 2. The `Encoding` field from a Package `DESCRIPTION` file in a parent directory. #' 3. The `Encoding` field from an R Project `.Rproj` file in a parent directory. #' 4. `"UTF-8"` as a fallback. #' #' @param filename the file to be parsed. #' @param lines a character vector of lines. #' If `NULL`, then `filename` will be read. #' @return A `list` with three components: #' \describe{ #' \item{expressions}{a `list` of #' `n+1` objects. The first `n` elements correspond to each expression in #' `filename`, and consist of a list of 9 elements: #' \itemize{ #' \item{`filename` (`character`)} #' \item{`line` (`integer`) the line in `filename` where this expression begins} #' \item{`column` (`integer`) the column in `filename` where this expression begins} #' \item{`lines` (named `character`) vector of all lines spanned by this #' expression, named with the line number corresponding to `filename`} #' \item{`parsed_content` (`data.frame`) as given by [utils::getParseData()] for this expression} #' \item{`xml_parsed_content` (`xml_document`) the XML parse tree of this #' expression as given by [xmlparsedata::xml_parse_data()]} #' \item{`content` (`character`) the same as `lines` as a single string (not split across lines)} #' } #' #' The final element of `expressions` is a list corresponding to the full file #' consisting of 6 elements: #' \itemize{ #' \item{`filename` (`character`)} #' \item{`file_lines` (`character`) the [readLines()] output for this file} #' \item{`content` (`character`) for .R files, the same as `file_lines`; #' for .Rmd or .qmd scripts, this is the extracted R source code (as text)} #' \item{`full_parsed_content` (`data.frame`) as given by #' [utils::getParseData()] for the full content} #' \item{`full_xml_parsed_content` (`xml_document`) the XML parse tree of all #' expressions as given by [xmlparsedata::xml_parse_data()]} #' \item{`terminal_newline` (`logical`) records whether `filename` has a terminal #' newline (as determined by [readLines()] producing a corresponding warning)} #' } #' } #' \item{error}{A `Lint` object describing any parsing error.} #' \item{lines}{The [readLines()] output for this file.} #' } #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) #' get_source_expressions(tmp) #' @export get_source_expressions <- function(filename, lines = NULL) { source_expression <- srcfile(filename, encoding = settings$encoding) # Ensure English locale for terminal newline and zero-length variable warning messages old_lang <- set_lang("en") on.exit(reset_lang(old_lang)) source_expression$lines <- if (is.null(lines)) { read_lines(filename) } else { lines } # Only regard explicit attribute terminal_newline=FALSE as FALSE and all other cases (e.g. NULL or TRUE) as TRUE. # We don't use isFALSE since it is introduced in R 3.5.0. terminal_newline <- !identical(attr(source_expression$lines, "terminal_newline", exact = TRUE), FALSE) e <- NULL source_expression$lines <- extract_r_source( filename = source_expression$filename, lines = source_expression$lines, error = function(e) lint_rmd_error(e, source_expression) ) names(source_expression$lines) <- seq_along(source_expression$lines) source_expression$content <- get_content(source_expression$lines) parsed_content <- get_source_expression(source_expression, error = function(e) lint_parse_error(e, source_expression)) if (inherits(e, "lint") && (is.na(e$line) || !nzchar(e$line) || e$message == "unexpected end of input")) { # Don't create expression list if it's unreliable (invalid encoding or unhandled parse error) expressions <- list() } else { top_level_map <- generate_top_level_map(parsed_content) xml_parsed_content <- safe_parse_to_xml(parsed_content) expressions <- lapply( X = top_level_expressions(parsed_content), FUN = get_single_source_expression, parsed_content, source_expression, filename, top_level_map ) if (!is.null(xml_parsed_content) && !is.na(xml_parsed_content)) { expression_xmls <- lapply( xml_find_all(xml_parsed_content, "/exprlist/*"), function(top_level_expr) xml2::xml_add_parent(xml2::xml_new_root(top_level_expr), "exprlist") ) for (i in seq_along(expressions)) { expressions[[i]]$xml_parsed_content <- expression_xmls[[i]] } } # add global expression expressions[[length(expressions) + 1L]] <- list( filename = filename, file_lines = source_expression$lines, content = source_expression$lines, full_parsed_content = parsed_content, full_xml_parsed_content = xml_parsed_content, terminal_newline = terminal_newline ) } list(expressions = expressions, error = e, lines = source_expression$lines) } lint_parse_error <- function(e, source_expression) { # R >= 4.3.0 if (inherits(e, "parseError")) { return(lint_parse_error_r43(e, source_expression)) } message_info <- re_matches( e$message, rex( except_some_of(":"), ":", capture(name = "line", digits), ":", capture(name = "column", digits), ":", space, capture(name = "message", anything), "\n" ) ) if (!is.na(message_info$line)) { return(lint_parse_error_r42(message_info, source_expression)) } # an error that does not use R_ParseErrorMsg lint_parse_error_nonstandard(e, source_expression) } #' Ensure a string is valid for printing #' #' Helper to ensure a valid string is provided as line where necessary. #' Handles two cases: #' #' 1. NA lines, as generated by running outside of code blocks in Rmd documents #' 2. invalid string lines, as generated by invalid encoding settings #' #' [nchar()] can detect both cases: 1. returns `NA_integer_`, 2. errors #' #' @param line Possibly misencoded or NA line #' #' @return `line` if it's a valid non-NA string, otherwise an empty string. #' #' @noRd fixup_line <- function(line) { nchars <- tryCatch(nchar(line, type = "chars"), error = function(e) NA_integer_) if (is.na(nchars)) { "" } else { line } } #' Convert an R >= 4.3.0 classed parseError with metadata into a lint #' #' @param e A parse error of class `parseError` generated by R >= 4.3.0 #' @param source_expression The source expression that generated the parse error #' #' @return A [Lint()] based on the metadata attached to `e`. #' #' @noRd lint_parse_error_r43 <- function(e, source_expression) { msg <- re_substitutes(e$message, rex(" (", except_some_of(")"), ")", end), "") line_number <- e$lineno column <- e$colno substr(msg, 1L, 1L) <- toupper(substr(msg, 1L, 1L)) msg <- paste0(msg, ".") if (inherits(e, "invalidMBCS")) { msg <- paste(msg, "Is the encoding correct?") } if (column == 0L) { line_number <- line_number - 1L } if (line_number < 1L || line_number > length(source_expression$lines)) { # Safely handle invalid location info line_number <- 1L column <- 1L } line <- fixup_line(source_expression$lines[[line_number]]) if (column == 0L || column > nchar(line) + 1L) { column <- nchar(line) + 1L } Lint( filename = source_expression$filename, line_number = line_number, column_number = column, type = "error", message = msg, line = line ) } #' Convert a R < 4.3.0 standard parse error message into a lint #' #' @param message_info Match of the structured parse error message regex, matched in [lint_parse_error()] #' @param source_expression The source expression that generated the parse error #' #' @return A [Lint()] based on text mining of the error message captured by `message_info`, #' #' @noRd lint_parse_error_r42 <- function(message_info, source_expression) { line_number <- as.integer(message_info$line) column_number <- as.integer(message_info$column) # If the column number is zero it means the error really occurred at the # end of the previous line if (column_number %==% 0L) { line_number <- line_number - 1L line <- fixup_line(source_expression$lines[[line_number]]) column_number <- nchar(line) + 1L } else { line <- fixup_line(source_expression$lines[[line_number]]) } Lint( filename = source_expression$filename, line_number = line_number, column_number = column_number, type = "error", message = message_info$message, line = line ) } #' Convert a R < 4.3.0 non-standard parse error message into a lint #' #' Uses a hand-crafted regex and some heuristics to find location information, falling back to Line 1, Column 1 if all #' attempts fail. #' #' @param e A parse error that doesn't fit the standard `message_info` regex used for most parse errors in R < 4.3.0 #' @param source_expression The source expression that generated the parse error #' #' @return A [Lint()] based on trying to extract information from the error message of `e`. #' #' @noRd lint_parse_error_nonstandard <- function(e, source_expression) { if (grepl("invalid multibyte character in parser at line", e$message, fixed = TRUE)) { # nocov start: platform-specific l <- as.integer(re_matches( e$message, rex("invalid multibyte character in parser at line ", capture(name = "line", digits)) )$line) # Invalid encoding in source code return( Lint( filename = source_expression$filename, line_number = l, column_number = 1L, type = "error", message = "Invalid multibyte character in parser. Is the encoding correct?", line = fixup_line(source_expression$lines[[l]]) ) ) # nocov end } else if (grepl("invalid multibyte string, element", e$message, fixed = TRUE)) { # Invalid encoding, will break even re_matches() below, so we need to handle this first. return( Lint( filename = source_expression$filename, line_number = 1L, column_number = 1L, type = "error", message = "Invalid multibyte string. Is the encoding correct?", line = "" ) ) } else if (grepl("repeated formal argument", e$message, fixed = TRUE)) { # nocov start: only throws on certain versions; tested on L389 matches <- re_matches( e$message, rex( "repeated formal argument '", capture(name = "symbol", anything), "' on line ", capture(name = "line", digits) ) ) sym <- matches$symbol l <- as.integer(matches$line) # Repeated formal argument 'sym' on line l return( Lint( filename = source_expression$filename, line_number = l, column_number = 1L, type = "error", message = sprintf("Repeated formal argument '%s'.", sym), line = fixup_line(source_expression$lines[[l]]) ) ) # nocov end } # Hand-crafted regex to parse all error messages generated by the R parser code for R < 4.3. # The error messages can be found in src/main/gram.c and src/main/character.c in the R code. # This code produces a list of all possible error messages: # # nolint start: commented_code_linter. # parser_files <- c("src/main/gram.c", "src/main/character.c") # # lines <- unlist(lapply( # file.path("https://raw.githubusercontent.com/wch/r-source/trunk", parser_files), # readLines # )) # error_calls <- grep("error(_(", lines, fixed = TRUE, value = TRUE) # error_formats <- trimws(gsub("^.*error\\(_\\(\"(.+)\".+", "\\1", error_calls)) # error_formats <- unique(error_formats) # nolint end parse_error_rx <- rex( start, capture(anything, name = "msg_1"), or(" at ", " on ", " ("), "line ", capture(digits, name = "line"), maybe(")"), capture(anything, name = "msg_2"), end ) if (grepl(parse_error_rx, e$message, perl = TRUE)) { # nocov start: only throws on certain versions. Tested on L396: Nul character not allowed rx_match <- re_matches( e$message, parse_error_rx ) l <- as.integer(rx_match$line) # Sometimes the parser "line" runs one past the last line l <- pmin(l, length(source_expression$lines)) msg <- paste0(rx_match$msg_1, if (nzchar(rx_match$msg_2)) " ", rx_match$msg_2, ".") substr(msg, 1L, 1L) <- toupper(substr(msg, 1L, 1L)) return( Lint( filename = source_expression$filename, line_number = l, column_number = 1L, type = "error", message = msg, line = source_expression$lines[[l]] ) ) # nocov end } message_info <- re_matches( e$message, rex( single_quotes, capture(name = "name", anything), single_quotes, anything, double_quotes, capture(name = "starting", anything), double_quotes ) ) loc <- re_matches(source_expression$content, rex(message_info$starting), locations = TRUE) line_location <- loc[!is.na(loc$start) & !is.na(loc$end), ] if (nrow(line_location) == 0L) { if (grepl("attempt to use zero-length variable name", e$message, fixed = TRUE)) { # empty symbol: ``, ``(), ''(), ""(), fun(''=42), fun(""=42), fun(a=1,""=42) loc <- re_matches( source_expression$content, rex( "``" %or% list(or("''", '""'), any_spaces, "(") %or% list(or("(", ","), any_spaces, or("''", '""'), any_spaces, "=") ), options = "multi-line", locations = TRUE ) loc <- loc[!is.na(loc$start) & !is.na(loc$end), ] if (nrow(loc) > 0L) { line_location <- loc[1L, ] } } else { # nocov start return( Lint( filename = source_expression$filename, line_number = 1L, column_number = 1L, type = "error", message = e$message, line = "" ) ) # nocov end } } newline_locs <- get_newline_locs(source_expression$content) line_number <- which(newline_locs >= line_location$start)[1L] - 1L column_number <- line_location$start - newline_locs[line_number] Lint( filename = source_expression$filename, line_number = line_number, column_number = column_number, type = "error", message = e$message, line = source_expression$lines[[line_number]] ) } lint_rmd_error <- function(e, source_expression) { message_info <- re_matches( e$message, rex( except_some_of(":"), ":", capture(name = "line", digits), ":", capture(name = "column", digits), ":", space, capture(name = "message", anything), "\n" ) ) line_number <- as.integer(message_info$line) column_number <- as.integer(message_info$column) Lint( filename = source_expression$filename, line_number = line_number, column_number = column_number, type = "error", message = message_info$message, line = source_expression$lines[line_number] ) } get_single_source_expression <- function(loc, parsed_content, source_expression, filename, top_level_map) { line_nums <- parsed_content$line1[loc]:parsed_content$line2[loc] expr_lines <- source_expression$lines[line_nums] names(expr_lines) <- line_nums content <- get_content(source_expression$lines, parsed_content[loc, ]) id <- parsed_content$id[loc] pc <- parsed_content[which(top_level_map == id), ] list( filename = filename, line = parsed_content[loc, "line1"], column = parsed_content[loc, "col1"], lines = expr_lines, parsed_content = pc, xml_parsed_content = xml2::xml_missing(), content = content ) } get_source_expression <- function(source_expression, error = identity) { parse_error <- FALSE parsed_content <- tryCatch( parse( text = source_expression$content, srcfile = source_expression, keep.source = TRUE ), error = error ) # TODO: Remove when minimum R version is bumped to > 3.5 # # This needs to be done twice to avoid a bug fixed in R 3.4.4 # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16041 parsed_content <- tryCatch( parse( text = source_expression$content, srcfile = source_expression, keep.source = TRUE ), error = error ) if (inherits(parsed_content, c("error", "lint"))) { assign("e", parsed_content, envir = parent.frame()) parse_error <- TRUE } # Triggers an error if the lines contain invalid characters. parsed_content <- tryCatch( nchar(source_expression$content, type = "chars"), error = error ) if (inherits(parsed_content, c("error", "lint"))) { # Let parse errors take precedence over encoding problems if (!parse_error) assign("e", parsed_content, envir = parent.frame()) return() # parsed_content is unreliable if encoding is invalid } source_expression$parsed_content <- parsed_content fix_octal_escapes(fix_eq_assigns(fix_tab_indentations(source_expression)), source_expression$lines) } get_newline_locs <- function(x) { newline_search <- re_matches(x, rex("\n"), locations = TRUE, global = TRUE)[[1L]]$start c( 0L, if (!is.na(newline_search[1L])) newline_search, nchar(x) + 1L ) } # Fix column numbers when there are tabs # getParseData() counts 1 tab as a variable number of spaces instead of one: # https://github.com/wch/r-source/blame/e7401b68ab0e032fce3e376aaca9a5431619b2b4/src/main/gram.y#L512 # The number of spaces is so that the code is brought to the next 8-character indentation level e.g.: # "1\t;" --> "1 ;" # "12\t;" --> "12 ;" # "123\t;" --> "123 ;" # "1234\t;" --> "1234 ;" # "12345\t;" --> "12345 ;" # "123456\t;" --> "123456 ;" # "1234567\t;" --> "1234567 ;" # "12345678\t;" --> "12345678 ;" # "123456789\t;" --> "123456789 ;" # "1234567890\t;" --> "1234567890 ;" fix_tab_indentations <- function(source_expression) { parse_data <- getParseData(source_expression) if (is.null(parse_data)) { return(NULL) } tab_cols <- gregexpr("\t", source_expression[["lines"]], fixed = TRUE) names(tab_cols) <- seq_along(tab_cols) matched_lines <- vapply(tab_cols, function(line_match) !is.na(line_match[1L]) && line_match[1L] > 0L, logical(1L)) if (!any(matched_lines)) { return(parse_data) } tab_cols <- tab_cols[matched_lines] fix_columns <- c("line1", "line2", "col1", "col2") parse_data[, fix_columns] <- fix_tab_columns(parse_data[, fix_columns], tab_cols) parse_data } fix_tab_columns <- function(parse_content, tab_cols) { dat <- cbind( c(parse_content$line1, parse_content$line2), c(parse_content$col1, parse_content$col2) ) lines <- as.integer(names(tab_cols)) for (i in seq_along(tab_cols)) { is_curr_line <- dat[, 1L] == lines[[i]] if (!any(is_curr_line)) { next } tab_col <- tab_cols[[i]] line_tab_offsets <- tab_offsets(tab_col) for (j in seq_along(tab_col)) { is_line_to_change <- is_curr_line & dat[, 2L] > tab_col[[j]] if (any(is_line_to_change)) { dat[is_line_to_change, 2L] <- dat[is_line_to_change, 2L] - line_tab_offsets[[j]] } } } dat } tab_offsets <- function(tab_columns) { cum_offset <- 0L vapply( tab_columns - 1L, function(tab_idx) { offset <- 7L - (tab_idx + cum_offset) %% 8L # using a tab width of 8 characters cum_offset <<- cum_offset + offset offset }, integer(1L), USE.NAMES = FALSE ) } # This function wraps equal assign expressions in a parent expression so they # are the same as the corresponding <- expression fix_eq_assigns <- function(pc) { if (is.null(pc) || any(c("equal_assign", "expr_or_assign_or_help") %in% pc$token)) { return(pc) } eq_assign_locs <- which(pc$token == "EQ_ASSIGN") # check whether the equal-assignment is the final entry if (length(eq_assign_locs) == 0L || tail(eq_assign_locs, 1L) == nrow(pc)) { return(pc) } prev_locs <- vapply(eq_assign_locs, prev_with_parent, pc = pc, integer(1L)) next_locs <- vapply(eq_assign_locs, next_with_parent, pc = pc, integer(1L)) expr_locs <- prev_locs != lag(next_locs) expr_locs[is.na(expr_locs)] <- TRUE id_itr <- max(pc$id) true_locs <- which(expr_locs) n_expr <- length(true_locs) supplemental_content <- data.frame( line1 = integer(n_expr), col1 = integer(n_expr), line2 = integer(n_expr), col2 = integer(n_expr), id = integer(n_expr), parent = integer(n_expr), token = character(n_expr), terminal = logical(n_expr), text = character(n_expr), stringsAsFactors = FALSE ) for (i in seq_len(n_expr)) { start <- true_locs[i] # TODO(michaelchirico): vectorize this loop away. the tricky part is, # this loop doesn't execute on most R versions (we tried 3.6.3 and 4.2.0). # so it likely requires some GHA print debugging -- tedious :) end <- true_locs[i] j <- end + 1L # nocov start: only runs on certain R versions while (j <= length(expr_locs) && !expr_locs[j]) { end <- j j <- j + 1L } # nocov end prev_loc <- prev_locs[start] next_loc <- next_locs[end] id_itr <- id_itr + 1L supplemental_content[i, ] <- list( pc[prev_loc, "line1"], pc[prev_loc, "col1"], pc[next_loc, "line2"], pc[next_loc, "col2"], id_itr, pc[eq_assign_locs[true_locs[i]], "parent"], "expr", # R now uses "equal_assign" FALSE, "" ) new_parent_locs <- c( prev_locs[start:end], eq_assign_locs[start:end], next_locs[start:end], next_loc ) pc[new_parent_locs, "parent"] <- id_itr } rownames(supplemental_content) <- supplemental_content$id res <- rbind(pc, supplemental_content) res[order(res$line1, res$col1, res$line2, res$col2, res$id), ] } step_with_parent <- function(pc, loc, offset) { id <- pc$id[loc] parent_id <- pc$parent[loc] with_parent <- pc[pc$parent == parent_id, ] with_parent <- with_parent[order(with_parent$line1, with_parent$col1, with_parent$line2, with_parent$col2), ] loc <- which(with_parent$id == id) which(pc$id == with_parent$id[loc + offset]) } prev_with_parent <- function(pc, loc) step_with_parent(pc, loc, offset = -1L) next_with_parent <- function(pc, loc) step_with_parent(pc, loc, offset = 1L) top_level_expressions <- function(pc) { if (is.null(pc)) { return(integer(0L)) } which(pc$parent <= 0L) } # workaround for bad parse data bug for octal escapes # https://bugs.r-project.org/show_bug.cgi?id=18323 fix_octal_escapes <- function(pc, lines) { # subset first to prevent using nchar() on MBCS input is_str_const <- pc$token == "STR_CONST" str_const <- pc[is_str_const, ] str_const_mismatch <- str_const$col2 - str_const$col1 != nchar(str_const$text) - 1L if (!any(str_const_mismatch)) { return(pc) } str_const <- str_const[str_const_mismatch, ] out <- character(nrow(str_const)) single_line <- str_const$line1 == str_const$line2 out[single_line] <- substr( lines[str_const$line1[single_line]], str_const$col1[single_line], str_const$col2[single_line] ) for (ii in which(!single_line)) { out[ii] <- paste( c( substring(lines[str_const$line1[ii]], str_const$col1[ii]), if (str_const$line1[ii] < str_const$line2[ii] - 1L) { lines[(str_const$line1[ii] + 1L):(str_const$line2[ii] - 1L)] }, substr(lines[str_const$line2[ii]], 1L, str_const$col2[ii]) ), collapse = "\n" ) } pc$text[is_str_const][str_const_mismatch] <- out pc } lintr/R/assignment_linter.R0000644000176200001440000001044014577052532015464 0ustar liggesusers#' Assignment linter #' #' Check that `<-` is always used for assignment. #' #' @param allow_cascading_assign Logical, default `TRUE`. #' If `FALSE`, [`<<-`][base::assignOps] and `->>` are not allowed. #' @param allow_right_assign Logical, default `FALSE`. If `TRUE`, `->` and `->>` are allowed. #' @param allow_trailing Logical, default `TRUE`. If `FALSE` then assignments aren't allowed at end of lines. #' @param allow_pipe_assign Logical, default `FALSE`. If `TRUE`, magrittr's `%<>%` assignment is allowed. #' #' @examples #' # will produce lints #' lint( #' text = "x = mean(x)", #' linters = assignment_linter() #' ) #' #' code_lines <- "1 -> x\n2 ->> y" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = assignment_linter() #' ) #' #' lint( #' text = "x %<>% as.character()", #' linters = assignment_linter() #' ) #' #' # okay #' lint( #' text = "x <- mean(x)", #' linters = assignment_linter() #' ) #' #' code_lines <- "x <- 1\ny <<- 2" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = assignment_linter() #' ) #' #' # customizing using arguments #' code_lines <- "1 -> x\n2 ->> y" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = assignment_linter(allow_right_assign = TRUE) #' ) #' #' lint( #' text = "x <<- 1", #' linters = assignment_linter(allow_cascading_assign = FALSE) #' ) #' #' writeLines("foo(bar = \n 1)") #' lint( #' text = "foo(bar = \n 1)", #' linters = assignment_linter(allow_trailing = FALSE) #' ) #' #' lint( #' text = "x %<>% as.character()", #' linters = assignment_linter(allow_pipe_assign = TRUE) #' ) #' #' @evalRd rd_tags("assignment_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - #' @export assignment_linter <- function(allow_cascading_assign = TRUE, allow_right_assign = FALSE, allow_trailing = TRUE, allow_pipe_assign = FALSE) { trailing_assign_xpath <- paste( collapse = " | ", c( paste0("//LEFT_ASSIGN", if (allow_cascading_assign) "" else "[text() = '<-']"), if (allow_right_assign) paste0("//RIGHT_ASSIGN", if (allow_cascading_assign) "" else "[text() = '->']"), "//EQ_SUB", "//EQ_FORMALS", if (!allow_pipe_assign) "//SPECIAL[text() = '%<>%']" ), "[@line1 < following-sibling::expr[1]/@line1]" ) xpath <- paste(collapse = " | ", c( # always block = (NB: the parser differentiates EQ_ASSIGN, EQ_SUB, and EQ_FORMALS) "//EQ_ASSIGN", # -> and ->> are both 'RIGHT_ASSIGN' if (!allow_right_assign) "//RIGHT_ASSIGN" else if (!allow_cascading_assign) "//RIGHT_ASSIGN[text() = '->>']", # <-, :=, and <<- are all 'LEFT_ASSIGN'; check the text if blocking <<-. # NB: := is not linted because of (1) its common usage in rlang/data.table and # (2) it's extremely uncommon as a normal assignment operator if (!allow_cascading_assign) "//LEFT_ASSIGN[text() = '<<-']", if (!allow_trailing) trailing_assign_xpath, if (!allow_pipe_assign) "//SPECIAL[text() = '%<>%']" )) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) if (length(bad_expr) == 0L) { return(list()) } operator <- xml_text(bad_expr) lint_message_fmt <- rep("Use <-, not %s, for assignment.", length(operator)) lint_message_fmt[operator %in% c("<<-", "->>")] <- "%s can have hard-to-predict behavior; prefer assigning to a specific environment instead (with assign() or <-)." lint_message_fmt[operator == "%<>%"] <- "Avoid the assignment pipe %s; prefer using <- and %%>%% separately." if (!allow_trailing) { bad_trailing_expr <- xml_find_all(xml, trailing_assign_xpath) trailing_assignments <- xml2::xml_attrs(bad_expr) %in% xml2::xml_attrs(bad_trailing_expr) lint_message_fmt[trailing_assignments] <- "Assignment %s should not be trailing at the end of a line." } lint_message <- sprintf(lint_message_fmt, operator) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "style") }) } lintr/R/outer_negation_linter.R0000644000176200001440000000422214577052532016337 0ustar liggesusers#' Require usage of `!any(x)` over `all(!x)`, `!all(x)` over `any(!x)` #' #' `any(!x)` is logically equivalent to `!any(x)`; ditto for the equivalence of #' `all(!x)` and `!any(x)`. Negating after aggregation only requires inverting #' one logical value, and is typically more readable. #' #' @examples #' # will produce lints #' lint( #' text = "all(!x)", #' linters = outer_negation_linter() #' ) #' #' lint( #' text = "any(!x)", #' linters = outer_negation_linter() #' ) #' #' # okay #' lint( #' text = "!any(x)", #' linters = outer_negation_linter() #' ) #' #' lint( #' text = "!all(x)", #' linters = outer_negation_linter() #' ) #' #' @evalRd rd_tags("outer_negation_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export outer_negation_linter <- function() { # NB: the double negation is a bit hairy, but it's what we need to check if # _all_ of the inputs to any(..., na.rm=na.rm) are negated, i.e., there are # _not_ any entries that are _not_ negated. IINM that's what we're stuck # with in xpath if we want to guarantee a condition on _all_ # coming after any( and before na.rm= . # NB: requirement that count(expr)>1 is to prevent any() from linting # e.g. in magrittr pipelines. xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'any' or text() = 'all'] /parent::expr[following-sibling::expr] /parent::expr[ not(expr[ position() > 1 and not(OP-EXCLAMATION) and not(preceding-sibling::*[1][self::EQ_SUB]) ]) ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_call <- xp_call_name(bad_expr) inverse_call <- ifelse(matched_call == "any", "all", "any") lint_message <- paste( sprintf("!%s(x) is better than %s(!x).", inverse_call, matched_call), "The former applies negation only once after aggregation instead of many times for each element of x." ) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/utils.R0000644000176200001440000002123714577052532013105 0ustar liggesusers`%||%` <- function(x, y) { if (is.null(x) || length(x) == 0L || (is.atomic(x[[1L]]) && is.na(x[[1L]]))) { y } else { x } } `%==%` <- function(x, y) { identical(x, y) } `%:::%` <- function(p, f) { get(f, envir = asNamespace(p)) } flatten_lints <- function(x) { x <- flatten_list(x, class = "lint") class(x) <- "lints" x } # any function using unlist or c was dropping the classnames, # so need to brute force copy the objects flatten_list <- function(x, class) { res <- list() itr <- 1L assign_item <- function(x) { if (inherits(x, class)) { res[[itr]] <<- x itr <<- itr + 1L } else if (is.list(x)) { lapply(x, assign_item) } } assign_item(x) res } fix_names <- function(x, default) { nms <- names(x) if (is.null(nms)) { nms <- default } else { nms[nms == ""] <- default } names(x) <- nms x } linter_auto_name <- function(which = -3L) { call <- sys.call(which = which) nm <- paste(deparse(call, 500L), collapse = " ") regex <- rex(start, one_or_more(alnum %or% "." %or% "_" %or% ":")) if (re_matches(nm, regex)) { match <- re_matches(nm, regex, locations = TRUE) nm <- substr(nm, start = 1L, stop = match[1L, "end"]) nm <- re_substitutes(nm, rex(start, alnums, "::"), "") } nm } auto_names <- function(x) { nms <- names2(x) missing <- nms == "" if (!any(missing)) { return(nms) } default_name <- function(x) { if (is_linter(x)) { attr(x, "name", exact = TRUE) } else { paste(deparse(x, 500L), collapse = " ") } } defaults <- vapply(x[missing], default_name, character(1L), USE.NAMES = FALSE) nms[missing] <- defaults nms } # The following functions is from dplyr names2 <- function(x) { names(x) %||% rep("", length(x)) } safe_parse_to_xml <- function(parsed_content) { if (is.null(parsed_content)) { return(xml2::xml_missing()) } tryCatch( xml2::read_xml(xmlparsedata::xml_parse_data(parsed_content)), # use xml_missing so that code doesn't always need to condition on XML existing error = function(e) xml2::xml_missing() ) } get_content <- function(lines, info) { lines[is.na(lines)] <- "" if (!missing(info)) { if (is_node(info)) { info <- lapply(stats::setNames(nm = c("col1", "col2", "line1", "line2")), function(attr) { as.integer(xml_attr(info, attr)) }) } lines <- lines[seq(info$line1, info$line2)] lines[length(lines)] <- substr(lines[length(lines)], 1L, info$col2) lines[1L] <- substr(lines[1L], info$col1, nchar(lines[1L])) } paste0(collapse = "\n", lines) } logical_env <- function(x) { res <- as.logical(Sys.getenv(x)) if (is.na(res)) { return(NULL) } res } # from ?chartr rot <- function(ch, k = 13L) { p0 <- function(...) paste(c(...), collapse = "") alphabet <- c(letters, LETTERS, " '") idx <- seq_len(k) chartr(p0(alphabet), p0(c(alphabet[-idx], alphabet[idx])), ch) } try_silently <- function(expr) { suppressWarnings( suppressMessages( try(expr, silent = TRUE) ) ) } # imitate sQuote(x, q) [requires R>=3.6] quote_wrap <- function(x, q) paste0(q, x, q) # interface to work like options() or setwd() -- returns the old value for convenience set_lang <- function(new_lang) { old_lang <- Sys.getenv("LANGUAGE", unset = NA) Sys.setenv(LANGUAGE = new_lang) # nolint: undesirable_function. Avoiding {withr} dep in pkg. old_lang } # handle the logic of either unsetting if it was previously unset, or resetting reset_lang <- function(old_lang) { if (is.na(old_lang)) { Sys.unsetenv("LANGUAGE") } else { Sys.setenv(LANGUAGE = old_lang) # nolint: undesirable_function. Avoiding {withr} dep in pkg. } } #' Create a `linter` closure #' #' @param fun A function that takes a source file and returns `lint` objects. #' @param name Default name of the Linter. #' Lints produced by the linter will be labelled with `name` by default. #' @return The same function with its class set to 'linter'. #' @export Linter <- function(fun, name = linter_auto_name()) { # nolint: object_name. if (!is.function(fun) || length(formals(args(fun))) != 1L) { stop("`fun` must be a function taking exactly one argument.", call. = FALSE) } force(name) class(fun) <- c("linter", "function") attr(fun, "name") <- name fun } read_lines <- function(file, encoding = settings$encoding, ...) { terminal_newline <- TRUE lines <- withCallingHandlers( readLines(file, warn = TRUE, ...), warning = function(w) { if (grepl("incomplete final line found on", w$message, fixed = TRUE)) { terminal_newline <<- FALSE invokeRestart("muffleWarning") } } ) lines_conv <- iconv(lines, from = encoding, to = "UTF-8") lines[!is.na(lines_conv)] <- lines_conv[!is.na(lines_conv)] Encoding(lines) <- "UTF-8" attr(lines, "terminal_newline") <- terminal_newline lines } # nocov start # support for usethis::use_release_issue(). Make sure to use devtools::load_all() beforehand! release_bullets <- function() { } # nocov end # see issue #923 -- some locales ignore _ when running sort(), others don't. # we want to consistently treat "_" < "n" = "N" platform_independent_order <- function(x) order(tolower(gsub("_", "0", x, fixed = TRUE))) platform_independent_sort <- function(x) x[platform_independent_order(x)] #' Extract text from `STR_CONST` nodes #' #' Convert `STR_CONST` `text()` values into R strings. This is useful to account for arbitrary #' character literals valid since R 4.0, e.g. `R"------[hello]------"`, which is parsed in #' R as `"hello"`. It is quite cumbersome to write XPaths allowing for strings like this, #' so whenever your linter logic requires testing a `STR_CONST` node's value, use this #' function. #' NB: this is also properly vectorized on `s`, and accepts a variety of inputs. Empty inputs #' will become `NA` outputs, which helps ensure that `length(get_r_string(s)) == length(s)`. #' #' @param s An input string or strings. If `s` is an `xml_node` or `xml_nodeset` and `xpath` is `NULL`, #' extract its string value with [xml2::xml_text()]. If `s` is an `xml_node` or `xml_nodeset` #' and `xpath` is specified, it is extracted with [xml2::xml_find_chr()]. #' @param xpath An XPath, passed on to [xml2::xml_find_chr()] after wrapping with `string()`. #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' tmp <- withr::local_tempfile(lines = "c('a', 'b')") #' expr_as_xml <- get_source_expressions(tmp)$expressions[[1L]]$xml_parsed_content #' writeLines(as.character(expr_as_xml)) #' get_r_string(expr_as_xml, "expr[2]") # "a" #' get_r_string(expr_as_xml, "expr[3]") # "b" #' #' # more importantly, extract strings under R>=4 raw strings #' @examplesIf getRversion() >= "4.0.0" #' tmp4.0 <- withr::local_tempfile(lines = "c(R'(a\\b)', R'--[a\\\"\'\"\\b]--')") #' expr_as_xml4.0 <- get_source_expressions(tmp4.0)$expressions[[1L]]$xml_parsed_content #' writeLines(as.character(expr_as_xml4.0)) #' get_r_string(expr_as_xml4.0, "expr[2]") # "a\\b" #' get_r_string(expr_as_xml4.0, "expr[3]") # "a\\\"'\"\\b" #' #' @export get_r_string <- function(s, xpath = NULL) { if (is_node(s) || is_nodeset(s)) { if (is.null(xpath)) { s <- xml_text(s) } else { s <- xml_find_chr(s, sprintf("string(%s)", xpath)) } } # parse() skips "" elements --> offsets the length of the output, # but NA in --> NA out is.na(s) <- !nzchar(s) out <- as.character(parse(text = s, keep.source = FALSE)) is.na(out) <- is.na(s) out } #' str2lang, but for xml children. #' #' [xml2::xml_text()] is deceptively close to obviating this helper, but it collapses #' text across lines. R is _mostly_ whitespace-agnostic, so this only matters in some edge cases, #' in particular when there are comments within an expression (`` node). See #1919. #' #' @noRd xml2lang <- function(x) { x_strip_comments <- xml_find_all(x, ".//*[not(self::COMMENT or self::expr)]") str2lang(paste(xml_text(x_strip_comments), collapse = " ")) } is_linter <- function(x) inherits(x, "linter") is_tainted <- function(lines) { inherits(tryCatch(nchar(lines), error = identity), "error") } #' Check that the entries in ... are valid #' #' @param dot_names Supplied names, from [...names()]. #' @param ref_calls Functions consuming these `...` (character). #' @param ref_help Help page to refer users hitting an error to. #' @noRd check_dots <- function(dot_names, ref_calls, ref_help = as.character(sys.call(-1L)[[1L]])) { valid_args <- unlist(lapply(ref_calls, function(f) names(formals(f)))) is_valid <- dot_names %in% valid_args if (all(is_valid)) { return(invisible()) } stop( "Found unknown arguments in ...: ", toString(dot_names[!is_valid]), ".\n", "Check for typos and see ?", ref_help, " for valid arguments.", call. = FALSE ) } lintr/R/actions.R0000644000176200001440000000105114250050336013361 0ustar liggesusersin_github_actions <- function() { identical(Sys.getenv("GITHUB_ACTIONS"), "true") } # Output logging commands for any lints found github_actions_log_lints <- function(lints, project_dir = "") { for (x in lints) { if (nzchar(project_dir)) { x$filename <- file.path(project_dir, x$filename) } file_line_col <- sprintf( "file=%s,line=%s,col=%s", x$filename, x$line_number, x$column_number ) cat(sprintf( "::warning %s::%s,[%s] %s\n", file_line_col, file_line_col, x$linter, x$message ), sep = "") } } lintr/R/zzz.R0000644000176200001440000003451114577052532012601 0ustar liggesusers#' Default linters #' #' @description List of default linters for [lint()]. Use #' [linters_with_defaults()] to customize it. Most of the default linters #' are based on [the tidyverse style guide](https://style.tidyverse.org/). #' #' The set of default linters is as follows (any parameterized linters, e.g., `line_length_linter` use their default #' argument(s), see `?` for details): #' #' @evalRd rd_linters("default") #' @seealso [linters] for a complete list of linters available in lintr. #' #' @export default_linters <- modify_defaults( defaults = list(), assignment_linter(), brace_linter(), commas_linter(), commented_code_linter(), cyclocomp_linter(), equals_na_linter(), function_left_parentheses_linter(), indentation_linter(), infix_spaces_linter(), line_length_linter(), object_length_linter(), object_name_linter(), object_usage_linter(), paren_body_linter(), pipe_continuation_linter(), quotes_linter(), semicolon_linter(), seq_linter(), spaces_inside_linter(), spaces_left_parentheses_linter(), T_and_F_symbol_linter(), trailing_blank_lines_linter(), trailing_whitespace_linter(), vector_logic_linter(), whitespace_linter() ) #' Default undesirable functions and operators #' #' Lists of function names and operators for [undesirable_function_linter()] and [undesirable_operator_linter()]. #' There is a list for the default elements and another that contains all available elements. #' Use [modify_defaults()] to produce a custom list. #' #' @details #' The following functions are sometimes regarded as undesirable: #' #' * [attach()] modifies the global search path. Use roxygen2's @importFrom statement in packages, or `::` in scripts. #' * [browser()] pauses execution when run and is likely a leftover from debugging. It should be removed. #' * [debug()] traps a function and causes execution to pause when that function is run. It should be removed. #' * [debugcall()] works similarly to [debug()], causing execution to pause. It should be removed. #' * [debugonce()] is only useful for interactive debugging. It should be removed. #' * [detach()] modifies the global search path. Detaching environments from the search path is rarely necessary in #' production code. #' * [ifelse()] isn't type stable. Use an `if`/`else` block for scalar logic, or use #' `dplyr::if_else()`/`data.table::fifelse()` for type stable vectorized logic. #' * [.libPaths()] permanently modifies the library location. Use [withr::with_libpaths()] for a temporary change #' instead. #' * [library()] modifies the global search path. Use roxygen2's @importFrom statement in packages, or `::` in scripts. #' * [loadNamespace()] doesn't provide an easy way to signal failures. Use the return value of [requireNamespace()] #' instead. #' * [mapply()] isn't type stable. Use [Map()] to guarantee a list is returned and simplify accordingly. #' * [options()] permanently modifies the session options. Use [withr::with_options()] for a temporary change instead. #' * [par()] permanently modifies the graphics device parameters. Use [withr::with_par()] for a temporary change #' instead. #' * [require()] modifies the global search path. Use roxygen2's @importFrom statement in packages, and [library()] #' or `::` in scripts. #' * [sapply()] isn't type stable. Use [vapply()] with an appropriate `FUN.VALUE=` argument to obtain type stable #' simplification. #' * [setwd()] modifies the global working directory. Use [withr::with_dir()] for a temporary change instead. #' * [sink()] permanently redirects output. Use [withr::with_sink()] for a temporary redirection instead. #' * [source()] loads code into the global environment unless `local = TRUE` is used, which can cause unexpected #' behavior. #' * [substring()] should be replaced by [substr()] with appropriate `stop=` value. #' * [Sys.setenv()] permanently modifies the global environment variables. Use [withr::with_envvar()] for a temporary #' change instead. #' * [Sys.setlocale()] permanently modifies the session locale. Use [withr::with_locale()] for a temporary change #' instead. #' * [trace()] traps a function and causes execution of arbitrary code when that function is run. It should be removed. #' * [undebug()] is only useful for interactive debugging with [debug()]. It should be removed. #' * [untrace()] is only useful for interactive debugging with [trace()]. It should be removed. #' #' The following operators are sometimes regarded as undesirable: #' #' * \code{\link[base:ns-dblcolon]{:::}} accesses non-exported functions inside packages. Code relying on these is #' likely to break in future versions of the package because the functions are not part of the public interface and #' may be changed or removed by the maintainers without notice. #' Use public functions via `::` instead. #' * [`<<-`][base::assignOps] and `->>` assign outside the current environment in a way that can be hard to reason #' about. Prefer fully-encapsulated functions wherever possible, or, if necessary, assign to a specific environment #' with [assign()]. Recall that you can create an environment at the desired scope with [new.env()]. #' #' @format A named list of character strings. #' @rdname default_undesirable_functions #' @export all_undesirable_functions <- modify_defaults( defaults = list(), attach = "use roxygen2's @importFrom statement in packages, or `::` in scripts. attach() modifies the global search path", browser = "remove this likely leftover from debugging. It pauses execution when run", debug = paste( "remove this likely leftover from debugging.", "It traps a function and causes execution to pause when that function is run" ), debugcall = paste( "remove this likely leftover from debugging.", "It traps a function and causes execution to pause when that function is run" ), debugonce = paste( "remove this likely leftover from debugging.", "It traps a function and causes execution to pause when that function is run" ), detach = paste( "avoid modifying the global search path.", "Detaching environments from the search path is rarely necessary in production code" ), ifelse = paste( "use an `if`/`else` block for scalar logic,", "or use dplyr::if_else()/data.table::fifelse() for type-stable vectorized logic" ), .libPaths = paste( "use withr::with_libpaths() for a temporary change", "instead of permanently modifying the library location" ), library = paste( "use roxygen2's @importFrom statement in packages and `::` in scripts,", "instead of modifying the global search path" ), loadNamespace = "use the return value of requireNamespace() instead to provide an easy way to signal failures", mapply = "use Map() to guarantee a list is returned and simplify accordingly", options = "use withr::with_options() for a temporary change instead of permanently modifying the session options", par = "use withr::with_par() for a temporary change instead of permanently modifying the graphics device parameters", require = paste( "use roxygen2's @importFrom statement in packages and library() or `::` in scripts,", "instead of modifying the global search path" ), sapply = "use vapply() with an appropriate `FUN.VALUE=` argument to obtain type-stable simplification", setwd = "use withr::with_dir() for a temporary change instead of modifying the global working directory", sink = "use withr::with_sink() for a temporary redirection instead of permanently redirecting output", source = paste( "manage dependencies through packages.", "source() loads code into the global environment unless `local = TRUE` is used,", "which can cause hard-to-predict behavior" ), substring = "use substr() with appropriate `stop=` value.", Sys.setenv = "use withr::with_envvar() for a temporary change instead of permanently modifying global environment variables", Sys.setlocale = "use withr::with_locale() for a temporary change instead of permanently modifying the session locale", trace = paste( "remove this likely leftover from debugging.", "It traps a function and causes execution of arbitrary code when that function is run" ), undebug = paste( "remove this likely leftover from debugging.", "It is only useful for interactive debugging with debug()" ), untrace = paste( "remove this likely leftover from debugging.", "It is only useful for interactive debugging with trace()" ), structure = "Use class<-, names<-, and attr<- to set attributes" ) #' @rdname default_undesirable_functions #' @format NULL #' @export default_undesirable_functions <- all_undesirable_functions[names(all_undesirable_functions) %in% c( "attach", "browser", "debug", "debugcall", "debugonce", "detach", ".libPaths", "library", "mapply", "options", "par", "require", "sapply", "setwd", "sink", "source", "structure", "Sys.setenv", "Sys.setlocale", "Sys.unsetenv", "trace", "undebug", "untrace", NULL )] #' @rdname default_undesirable_functions #' @format NULL #' @export all_undesirable_operators <- modify_defaults( defaults = list(), ":::" = paste( "It accesses non-exported functions inside packages. Code relying on these is likely to break in", "future versions of the package because the functions are not part of the public interface and may be", "changed or removed by the maintainers without notice. Use public functions via :: instead." ), "<<-" = paste( "It assigns outside the current environment in a way that can be hard to reason about.", "Prefer fully-encapsulated functions wherever possible, or, if necessary, assign to a specific", "environment with assign(). Recall that you can create an environment at the desired scope with", "new.env()." ), "->>" = paste( "It assigns outside the current environment in a way that can be hard to reason about.", "Prefer fully-encapsulated functions wherever possible, or, if necessary, assign to a specific", "environment with assign(). Recall that you can create an environment at the desired scope with", "new.env()." ) ) #' @rdname default_undesirable_functions #' @format NULL #' @export default_undesirable_operators <- all_undesirable_operators[names(all_undesirable_operators) %in% c( ":::", "<<-", "->>", NULL )] #' Default lintr settings #' #' @description #' The default settings consist of #' #' - `linters`: a list of default linters (see [default_linters()]) #' - `encoding`: the character encoding assumed for the file #' - `exclude`: pattern used to exclude a line of code #' - `exclude_start`, `exclude_end`: patterns used to mark start and end of the code block to exclude #' - `exclude_linter`, `exclude_linter_sep`: patterns used to exclude linters #' - `exclusions`: a list of exclusions, see [exclude()] for a complete description of valid values. #' - `cache_directory`: location of cache directory #' - `comment_token`: a GitHub token character #' - `comment_bot`: decides if lintr comment bot on GitHub can comment on commits #' - `error_on_lint`: decides if error should be produced when any lints are found #' #' There are no settings without defaults, i.e., this list describes every valid setting. #' #' @examples #' # available settings #' names(default_settings) #' #' # linters included by default #' names(default_settings$linters) #' #' # default values for a few of the other settings #' default_settings[c( #' "encoding", #' "exclude", #' "exclude_start", #' "exclude_end", #' "exclude_linter", #' "exclude_linter_sep", #' "exclusions", #' "error_on_lint" #' )] #' #' @seealso [read_settings()], [default_linters] #' @aliases settings config lintr-config lintr-settings .lintr #' @export default_settings <- NULL # TODO(R>=3.6.0): Just use sys.source() directly. Note that we can't # write a wrapper that only passes keep.parse.data=FALSE on R>3.5.0 # (without doing some wizardry to evade R CMD check) because # there is a check for arguments not matching the signature which # will throw a false positive on R3.5.0. Luckily the argument # defaults on R>=3.6.0 are dictated by global options, so we can use # that for the wrapper here rather than doing some NSE tricks. sys_source <- function(...) { old <- options(keep.source.pkgs = FALSE, keep.parse.data.pkgs = FALSE) on.exit(options(old)) sys.source(...) } settings <- new.env(parent = emptyenv()) # nocov start .onLoad <- function(libname, pkgname) { op <- options() op_lintr <- list( lintr.linter_file = Sys.getenv("R_LINTR_LINTER_FILE", ".lintr") ) toset <- !(names(op_lintr) %in% names(op)) if (any(toset)) options(op_lintr[toset]) # R>=3.6.0: str2expression, str2lang # R>=4.0.0: deparse1 # R>=4.1.0: ...names backports::import(pkgname, c("deparse1", "...names")) base_ns <- getNamespace("base") backports_ns <- getNamespace("backports") lintr_ns <- getNamespace(pkgname) for (base_fun in c("str2lang", "str2expression")) { if (!exists(base_fun, base_ns)) { assign(base_fun, get(base_fun, backports_ns), lintr_ns) } } utils::assignInMyNamespace("default_settings", list( linters = default_linters, encoding = "UTF-8", exclude = rex("#", any_spaces, "nolint"), exclude_next = rex("#", any_spaces, "nolint next"), exclude_start = rex("#", any_spaces, "nolint start"), exclude_end = rex("#", any_spaces, "nolint end"), exclude_linter = rex( start, any_spaces, ":", any_spaces, capture( name = "linters", zero_or_more(one_or_more(none_of(",.")), any_spaces, ",", any_spaces), one_or_more(none_of(",.")) ), "." ), exclude_linter_sep = rex(any_spaces, ",", any_spaces), exclusions = list(), cache_directory = R_user_dir("lintr", "cache"), comment_token = Sys.getenv("GITHUB_TOKEN", unset = NA) %||% rot( paste0( "0n12nn72507", "r6273qnnp34", "43qno7q42n1", "n71nn28" ), 54L - 13L ), comment_bot = logical_env("LINTR_COMMENT_BOT") %||% TRUE, error_on_lint = logical_env("LINTR_ERROR_ON_LINT") %||% FALSE )) reset_settings() if (requireNamespace("tibble", quietly = TRUE)) { registerS3method("as_tibble", "lints", as_tibble.lints, asNamespace("tibble")) } if (requireNamespace("data.table", quietly = TRUE)) { registerS3method("as.data.table", "lints", as.data.table.lints, asNamespace("data.table")) } } # nocov end lintr/R/xml_nodes_to_lints.R0000644000176200001440000001024714577052532015647 0ustar liggesusers#' Convert an XML node or nodeset into a Lint #' #' Convenience function for converting nodes matched by XPath-based #' linter logic into a [Lint()] object to return. #' #' @details #' The location XPaths, `column_number_xpath`, `range_start_xpath` and `range_end_xpath` are evaluated using #' [xml2::xml_find_num()] and will usually be of the form `"number(./relative/xpath)"`. #' Note that the location line number cannot be changed and lints spanning multiple lines will ignore `range_end_xpath`. #' `column_number_xpath` and `range_start_xpath` are assumed to always refer to locations on the starting line of the #' `xml` node. #' #' @inheritParams lint-s3 #' @param xml An `xml_node` object (to generate one `Lint`) or an #' `xml_nodeset` object (to generate several `Lint`s), e.g. as returned by #' [xml2::xml_find_all()] or [xml2::xml_find_first()] or a #' list of `xml_node` objects. #' @param source_expression A source expression object, e.g. as #' returned typically by [lint()], or more generally #' by [get_source_expressions()]. #' @param lint_message The message to be included as the `message` #' to the `Lint` object. If `lint_message` is a character vector the same length as `xml`, #' the `i`-th lint will be given the `i`-th message. #' @param column_number_xpath XPath expression to return the column number location of the lint. #' Defaults to the start of the range matched by `range_start_xpath`. See details for more information. #' @param range_start_xpath XPath expression to return the range start location of the lint. #' Defaults to the start of the expression matched by `xml`. See details for more information. #' @param range_end_xpath XPath expression to return the range end location of the lint. #' Defaults to the end of the expression matched by `xml`. See details for more information. #' #' @return For `xml_node`s, a `lint`. For `xml_nodeset`s, `lints` (a list of `lint`s). #' @export xml_nodes_to_lints <- function(xml, source_expression, lint_message, type = c("style", "warning", "error"), column_number_xpath = range_start_xpath, range_start_xpath = "number(./@col1)", range_end_xpath = "number(./@col2)") { if (length(xml) == 0L) { return(list()) } if (is_nodeset_like(xml)) { lints <- .mapply( xml_nodes_to_lints, dots = list(xml = xml, lint_message = lint_message), MoreArgs = list( source_expression = source_expression, type = type, column_number_xpath = column_number_xpath, range_start_xpath = range_start_xpath, range_end_xpath = range_end_xpath ) ) class(lints) <- "lints" return(lints) } else if (!is_node(xml)) { stop( "Expected an xml_nodeset, a list of xml_nodes, or an xml_node, instead got an object of class(es): ", toString(class(xml)) ) } type <- match.arg(type, c("style", "warning", "error")) line1 <- xml_attr(xml, "line1") col1 <- xp_find_location(xml, range_start_xpath) if (is.na(col1)) { warning("Could not find range start for lint. Defaulting to start of line.") col1 <- 1L } lines <- source_expression[["lines"]] if (is.null(lines)) lines <- source_expression[["file_lines"]] if (xml_attr(xml, "line2") == line1) { col2 <- xp_find_location(xml, range_end_xpath) if (is.na(col2)) { warning("Could not find range end for lint. Defaulting to width 1.") col2 <- col1 } } else { col2 <- nchar(lines[[line1]]) } column_number <- xp_find_location(xml, column_number_xpath) if (is.na(column_number)) { warning("Could not find location for lint. Defaulting to start of range.") column_number <- col1 } Lint( filename = source_expression$filename, line_number = as.integer(line1), column_number = column_number, type = type, message = lint_message, line = lines[[line1]], ranges = list(c(col1, col2)) ) } is_node <- function(xml) inherits(xml, "xml_node") is_nodeset <- function(xml) inherits(xml, "xml_nodeset") is_nodeset_like <- function(xml) { is_nodeset(xml) || (is.list(xml) && all(vapply(xml, is_node, logical(1L)))) } lintr/R/backport_linter.R0000644000176200001440000001526014577222272015126 0ustar liggesusers#' Backport linter #' #' Check for usage of unavailable functions. Not reliable for testing r-devel dependencies. #' #' @param r_version Minimum R version to test for compatibility #' @param except Character vector of functions to be excluded from linting. #' Use this to list explicitly defined backports, e.g. those imported from the `{backports}` package or manually #' defined in your package. #' #' @examples #' # will produce lints #' lint( #' text = "trimws(x)", #' linters = backport_linter("3.0.0") #' ) #' #' lint( #' text = "str2lang(x)", #' linters = backport_linter("3.2.0") #' ) #' #' # okay #' lint( #' text = "trimws(x)", #' linters = backport_linter("3.6.0") #' ) #' #' lint( #' text = "str2lang(x)", #' linters = backport_linter("4.0.0") #' ) #' #' @evalRd rd_tags("backport_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export backport_linter <- function(r_version = getRversion(), except = character()) { r_version <- normalize_r_version(r_version) backport_blacklist <- backports[r_version < R_system_version(names(backports))] backport_blacklist <- lapply(backport_blacklist, setdiff, except) names_xpath <- "//SYMBOL | //SYMBOL_FUNCTION_CALL" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } if (all(r_version >= R_system_version(names(backports)))) { return(list()) } xml <- source_expression$xml_parsed_content all_names_nodes <- xml_find_all(xml, names_xpath) all_names <- xml_text(all_names_nodes) # not sapply/vapply, which may over-simplify to vector # rbind makes sure we have a matrix with dimensions [n_versions x n_names] # so that colSums() works to tell us which names are in an unavailable version # rbind not cbind because R is column-major --> which() below will be in column order needs_backport <- do.call(rbind, lapply(backport_blacklist, function(nm) all_names %in% nm)) bad_idx <- colSums(needs_backport) > 0L # which(arr.ind) returns things in the same order as which() needs_backport_version_idx <- ((which(needs_backport) - 1L) %% length(backport_blacklist)) + 1L lint_message <- sprintf( paste( "%s (R %s) is not available for dependency R >= %s.", "Use the `except` argument of `backport_linter()` to configure available backports." ), all_names[bad_idx], names(backport_blacklist)[needs_backport_version_idx], r_version ) xml_nodes_to_lints( all_names_nodes[bad_idx], source_expression = source_expression, lint_message = lint_message, type = "warning" ) }) } normalize_r_version <- function(r_version) { rx_release_spec <- rex( start, "release" %or% list("oldrel", maybe("-", digits)) %or% "devel", end ) if (is.character(r_version) && re_matches(r_version, rx_release_spec)) { # Support devel, release, oldrel, oldrel-1, ... if (r_version == "oldrel") { r_version <- "oldrel-1" } all_versions <- names(backports) minor_versions <- unique(re_substitutes(all_versions, rex(".", digits, end), "")) version_names <- c("devel", "release", paste0("oldrel-", seq_len(length(minor_versions) - 2L))) if (!r_version %in% version_names) { # This can only trip if e.g. oldrel-99 is requested stop("`r_version` must be a version number or one of ", toString(sQuote(version_names))) } requested_version <- minor_versions[match(r_version, table = version_names)] available_patches <- all_versions[startsWith(all_versions, requested_version)] selected_patch <- which.max(as.integer( substr(available_patches, start = nchar(requested_version) + 2L, stop = nchar(available_patches)) )) r_version <- R_system_version(available_patches[selected_patch]) } else if (is.character(r_version)) { r_version <- R_system_version(r_version, strict = TRUE) } else if (!inherits(r_version, "R_system_version")) { stop("`r_version` must be a R version number, returned by R_system_version(), or a string.") } if (r_version < "3.0.0") { warning("It is not recommended to depend on an R version older than 3.0.0. Resetting 'r_version' to 3.0.0.") r_version <- R_system_version("3.0.0") } r_version } # Sources: # devel NEWS https://cran.rstudio.com/doc/manuals/r-devel/NEWS.html # release NEWS https://cran.r-project.org/doc/manuals/r-release/NEWS.html # NB: character() entries are needed for oldrel specifications backports <- list( # NB: if present, R devel needs to be ahead of all other versions `4.3.0` = c("as.Rconcordance", "matchConcordance", "R_compiled_by", "array2DF"), `4.2.1` = "findCRANmirror", `4.2.0` = c(".pretty", ".LC.categories", "Sys.setLanguage()"), `4.1.3` = character(), `4.1.0` = c("numToBits", "numToInts", "gregexec", "charClass", "checkRdContents", "...names"), `4.0.5` = character(), `4.0.0` = c( ".class2", ".S3method", "activeBindingFunction", "deparse1", "globalCallingHandlers", "infoRDS", "list2DF", "marginSums", "proportions", "R_user_dir", "socketTimeout", "tryInvokeRestart" ), `3.6.3` = character(), `3.6.0` = c( "asplit", "hcl.colors", "hcl.pals", "mem.maxNsize", "mem.maxVsize", "nullfile", "str2lang", "str2expression", "update_PACKAGES" ), `3.5.3` = character(), `3.5.0` = c("...elt", "...length", "askYesNo", "getDefaultCluster", "isFALSE", "packageDate", "warnErrList"), `3.4.4` = character(), `3.4.0` = c( "check_packages_in_dir_details", "CRAN_package_db", "debugcall", "hasName", "isS3stdgeneric", "strcapture", "Sys.setFileTime", "undebugcall" ), `3.3.3` = character(), `3.3.0` = c( ".traceback", "chkDots", "curlGetHeaders", "endsWith", "grouping", "isS3method", "makevars_site", "makevars_user", "Rcmd", "sigma", "startsWith", "strrep", "validEnc", "validUTF8" ), `3.2.5` = character(), `3.2.0` = c( ".getNamespaceInfo", "check_packages_in_dir_changes", "debuggingState", "dir.exists", "dynGet", "extSoftVersion", "get0", "grSoftVersion", "hsearch_db", "isNamespaceLoaded", "lengths", "libcurlVersion", "returnValue", "tclVersion", "toTitleCase", "trimws" ), `3.1.3` = "pcre_config", `3.1.2` = "icuGetCollate", `3.1.1` = c(".nknots.smspl", "promptImport"), `3.1.0` = c("agrepl", "anyNA", "changedFiles", "cospi", "fileSnapshot", "find_gs_cmd", "sinpi", "tanpi"), `3.0.3` = "La_version", `3.0.2` = c("assertCondition", "assertError", "assertWarning", "getVignetteInfo"), `3.0.0` = c( ".onDetach", "bitwAnd", "bitwNot", "bitwOr", "bitwShiftL", "bitwShiftR", "bitwXor", "check_packages_in_dir", "cite", "citeNatbib", "clearPushBack", "packageName", "process.events", "provideDimnames", "quartz.save", "rep_len" ) ) lintr/R/pipe_continuation_linter.R0000644000176200001440000000470514577052532017052 0ustar liggesusers#' Pipe continuation linter #' #' Check that each step in a pipeline is on a new line, or the entire pipe fits on one line. #' #' @evalRd rd_tags("pipe_continuation_linter") #' #' @examples #' # will produce lints #' code_lines <- "1:3 %>%\n mean() %>% as.character()" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = pipe_continuation_linter() #' ) #' #' code_lines <- "1:3 |> mean() |>\n as.character()" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = pipe_continuation_linter() #' ) #' #' # okay #' lint( #' text = "1:3 %>% mean() %>% as.character()", #' linters = pipe_continuation_linter() #' ) #' #' code_lines <- "1:3 %>%\n mean() %>%\n as.character()" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = pipe_continuation_linter() #' ) #' #' lint( #' text = "1:3 |> mean() |> as.character()", #' linters = pipe_continuation_linter() #' ) #' #' code_lines <- "1:3 |>\n mean() |>\n as.character()" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = pipe_continuation_linter() #' ) #' #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export pipe_continuation_linter <- function() { # Where a single-line pipeline is nested inside a larger expression # e.g. inside a function definition), the outer expression can span multiple lines # without throwing a lint. pipe_node <- glue("SPECIAL[{ xp_text_in_table(magrittr_pipes) }]") preceding_pipe <- glue("preceding-sibling::expr[1]/descendant::*[self::{pipe_node} or self::PIPE]") xpath <- glue(" (//PIPE | //{pipe_node})[ parent::expr[@line1 < @line2] and {preceding_pipe} and ( preceding-sibling::expr[1]/descendant-or-self::expr/@line2 = following-sibling::expr[1]/descendant-or-self::expr/@line1 or @line1 = {preceding_pipe}/@line1 ) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content pipe_exprs <- xml_find_all(xml, xpath) pipe_text <- xml_text(pipe_exprs) xml_nodes_to_lints( pipe_exprs, source_expression = source_expression, lint_message = sprintf( "`%s` should always have a space before it and a new line after it, unless the full pipeline fits on one line.", pipe_text ), type = "style" ) }) } lintr/R/repeat_linter.R0000644000176200001440000000177014577052532014602 0ustar liggesusers#' Repeat linter #' #' Check that `while (TRUE)` is not used for infinite loops. #' #' @examples #' # will produce lints #' lint( #' text = "while (TRUE) { }", #' linters = repeat_linter() #' ) #' #' #' # okay #' lint( #' text = "repeat { }", #' linters = repeat_linter() #' ) #' #' @evalRd rd_tags("repeat_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export repeat_linter <- function() { xpath <- "//WHILE[following-sibling::expr[1]/NUM_CONST[text() = 'TRUE']]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content lints <- xml_find_all(xml, xpath) xml_nodes_to_lints( lints, source_expression = source_expression, lint_message = "Use 'repeat' instead of 'while (TRUE)' for infinite loops.", range_start_xpath = "number(./@col1)", range_end_xpath = "number(./following-sibling::*[3]/@col2)" ) }) } lintr/R/ifelse_censor_linter.R0000644000176200001440000000424414577052532016141 0ustar liggesusers#' Block usage of `ifelse()` where `pmin()` or `pmax()` is more appropriate #' #' `ifelse(x > M, M, x)` is the same as `pmin(x, M)`, but harder #' to read and requires several passes over the vector. #' #' The same goes for other similar ways to censor a vector, e.g. #' `ifelse(x <= M, x, M)` is `pmin(x, M)`, #' `ifelse(x < m, m, x)` is `pmax(x, m)`, and #' `ifelse(x >= m, x, m)` is `pmax(x, m)`. #' #' @examples #' # will produce lints #' lint( #' text = "ifelse(5:1 < pi, 5:1, pi)", #' linters = ifelse_censor_linter() #' ) #' #' lint( #' text = "ifelse(x > 0, x, 0)", #' linters = ifelse_censor_linter() #' ) #' #' # okay #' lint( #' text = "pmin(5:1, pi)", #' linters = ifelse_censor_linter() #' ) #' #' lint( #' text = "pmax(x, 0)", #' linters = ifelse_censor_linter() #' ) #' #' @evalRd rd_tags("ifelse_censor_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export ifelse_censor_linter <- function() { xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)} ] /parent::expr /following-sibling::expr[ (LT or GT or LE or GE) and expr[1] = following-sibling::expr and expr[2] = following-sibling::expr ] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_call <- xp_call_name(bad_expr) operator <- xml_find_chr(bad_expr, "string(expr[2]/*[2])") match_first <- !is.na(xml_find_first(bad_expr, "expr[2][expr[1] = following-sibling::expr[1]]")) optimizer <- ifelse((operator %in% c("<", "<=")) == match_first, "pmin", "pmax") first_var <- rep_len("x", length(match_first)) second_var <- rep_len("y", length(match_first)) first_var[!match_first] <- "y" second_var[!match_first] <- "x" xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = sprintf( "%s(x, y) is preferable to %s(x %s y, %s, %s).", optimizer, matched_call, operator, first_var, second_var ), type = "warning" ) }) } lintr/R/package_hooks_linter.R0000644000176200001440000001654714577052532016130 0ustar liggesusers#' Package hooks linter #' #' Check various common "gotchas" in [.onLoad()], [.onAttach()], [.Last.lib()], and [.onDetach()] #' namespace hooks that will cause `R CMD check` issues. See Writing R Extensions for details. #' #' 1. `.onLoad()` shouldn't call [cat()], [message()], [print()], [writeLines()], [packageStartupMessage()], #' [require()], [library()], or [installed.packages()]. #' 2. `.onAttach()` shouldn't call `cat()`, `message()`, `print()`, `writeLines()`, [library.dynam()], #' `require()`, `library()`, or `installed.packages()`. #' 3. `.Last.lib()` and `.onDetach()` shouldn't call [library.dynam.unload()]. #' 4. `.onLoad()` and `.onAttach()` should take two arguments, with names matching `^lib` and `^pkg`; #' `.Last.lib()` and `.onDetach()` should take one argument with name matching `^lib`. #' #' @examples #' # will produce lints #' lint( #' text = ".onLoad <- function(lib, ...) { }", #' linters = package_hooks_linter() #' ) #' #' lint( #' text = ".onAttach <- function(lib, pkg) { require(foo) }", #' linters = package_hooks_linter() #' ) #' #' lint( #' text = ".onDetach <- function(pkg) { }", #' linters = package_hooks_linter() #' ) #' #' # okay #' lint( #' text = ".onLoad <- function(lib, pkg) { }", #' linters = package_hooks_linter() #' ) #' #' lint( #' text = '.onAttach <- function(lib, pkg) { loadNamespace("foo") }', #' linters = package_hooks_linter() #' ) #' #' lint( #' text = ".onDetach <- function(lib) { }", #' linters = package_hooks_linter() #' ) #' #' @evalRd rd_tags("package_hooks_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export package_hooks_linter <- function() { bad_msg_calls <- c("cat", "message", "print", "writeLines") bad_calls <- list( .onLoad = c(bad_msg_calls, "packageStartupMessage"), .onAttach = c(bad_msg_calls, "library.dynam") ) bad_msg_call_xpath_fmt <- " (//FUNCTION | //OP-LAMBDA) /parent::expr[preceding-sibling::expr/SYMBOL[text() = '%s']] //SYMBOL_FUNCTION_CALL[%s] " bad_call_xpaths <- vapply( seq_along(bad_calls), function(ii) sprintf(bad_msg_call_xpath_fmt, names(bad_calls)[ii], xp_text_in_table(bad_calls[[ii]])), character(1L) ) names(bad_call_xpaths) <- names(bad_calls) make_bad_call_lint_message <- function(expr, hook) { call_name <- xml_text(expr) lint_message <- sprintf("Don't use %s() in %s().", call_name, hook) lint_message[call_name == "packageStartupMessage"] <- "Put packageStartupMessage() calls in .onAttach(), not .onLoad()." lint_message[call_name == "library.dynam"] <- "Put library.dynam() calls in .onLoad, not .onAttach()." lint_message } # lints here will hit the function , # this path returns to the corresponding namespace hook's name ns_calls <- xp_text_in_table(c(".onLoad", ".onAttach", ".onDetach", ".Last.lib")) hook_xpath <- sprintf("string(./ancestor::expr/expr/SYMBOL[%s])", ns_calls) load_arg_name_xpath <- " (//FUNCTION | //OP-LAMBDA) /parent::expr[ preceding-sibling::expr/SYMBOL[text() = '.onAttach' or text() = '.onLoad'] and ( count(SYMBOL_FORMALS) != 2 or SYMBOL_FORMALS[ (position() = 1 and not(starts-with(text(), 'lib'))) or (position() = 2 and not(starts-with(text(), 'pkg'))) ] ) ] " library_require_xpath <- " (//FUNCTION | //OP-LAMBDA) /parent::expr[preceding-sibling::expr/SYMBOL[text() = '.onAttach' or text() = '.onLoad']] //*[1][ (self::SYMBOL or self::SYMBOL_FUNCTION_CALL) and (text() = 'require' or text() = 'library' or text() = 'installed.packages') ] " bad_unload_call_xpath <- " (//FUNCTION | //OP-LAMBDA) /parent::expr[preceding-sibling::expr/SYMBOL[text() = '.Last.lib' or text() = '.onDetach']] //SYMBOL_FUNCTION_CALL[text() = 'library.dynam.unload'] " unload_arg_name_xpath <- " (//FUNCTION | //OP-LAMBDA) /parent::expr[ preceding-sibling::expr/SYMBOL[text() = '.onDetach' or text() = '.Last.lib'] and ( count(SYMBOL_FORMALS) != 1 or SYMBOL_FORMALS[not(starts-with(text(), 'lib'))] ) ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content # inherits: source_expression, bad_call_xpaths bad_msg_call_lints <- function(xml, hook) { bad_expr <- xml_find_all(xml, bad_call_xpaths[[hook]]) lint_message <- make_bad_call_lint_message(bad_expr, hook) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") } # (1) improper messaging calls shouldn't be used inside .onLoad()/.onAttach() onload_bad_msg_call_lints <- bad_msg_call_lints(xml, ".onLoad") onattach_bad_msg_call_lints <- bad_msg_call_lints(xml, ".onAttach") # (2) .onLoad() and .onAttach() should take two arguments, with names matching ^lib and ^pkg load_arg_name_expr <- xml_find_all(xml, load_arg_name_xpath) load_arg_name_message <- sprintf( "%s() should take two arguments, with the first starting with 'lib' and the second starting with 'pkg'.", xml_find_chr(load_arg_name_expr, hook_xpath) ) load_arg_name_lints <- xml_nodes_to_lints(load_arg_name_expr, source_expression, load_arg_name_message, type = "warning") # (3) .onLoad() and .onAttach() shouldn't call require(), library(), or installed.packages() # NB: base only checks the SYMBOL_FUNCTION_CALL version, not SYMBOL. library_require_expr <- xml_find_all(xml, library_require_xpath) library_require_bad_call <- xml_text(library_require_expr) library_require_hook <- xml_find_chr(library_require_expr, hook_xpath) library_require_message <- character(length(library_require_bad_call)) is_installed_packages <- library_require_bad_call == "installed.packages" library_require_message[is_installed_packages] <- sprintf("Don't slow down package load by running installed.packages() in %s().", library_require_hook) library_require_message[!is_installed_packages] <- sprintf("Don't alter the search() path in %s() by calling %s().", library_require_hook, library_require_bad_call) library_require_lints <- xml_nodes_to_lints(library_require_expr, source_expression, library_require_message, type = "warning") # (4) .Last.lib() and .onDetach() shouldn't call library.dynam.unload() bad_unload_call_expr <- xml_find_all(xml, bad_unload_call_xpath) bad_unload_call_message <- sprintf( "Use library.dynam.unload() calls in .onUnload(), not %s().", xml_find_chr(bad_unload_call_expr, hook_xpath) ) bad_unload_call_lints <- xml_nodes_to_lints(bad_unload_call_expr, source_expression, bad_unload_call_message, type = "warning") # (5) .Last.lib() and .onDetach() should take one arguments with name matching ^lib unload_arg_name_expr <- xml_find_all(xml, unload_arg_name_xpath) unload_arg_name_message <- sprintf( "%s() should take one argument starting with 'lib'.", xml_find_chr(unload_arg_name_expr, hook_xpath) ) unload_arg_name_lints <- xml_nodes_to_lints(unload_arg_name_expr, source_expression, unload_arg_name_message, type = "warning") return(c( onload_bad_msg_call_lints, onattach_bad_msg_call_lints, load_arg_name_lints, library_require_lints, bad_unload_call_lints, unload_arg_name_lints )) }) } lintr/R/trailing_whitespace_linter.R0000644000176200001440000000462014577052532017344 0ustar liggesusers#' Trailing whitespace linter #' #' Check that there are no space characters at the end of source lines. #' #' @param allow_empty_lines Suppress lints for lines that contain only whitespace. #' @param allow_in_strings Suppress lints for trailing whitespace in string constants. #' #' @examples #' # will produce lints #' lint( #' text = "x <- 1.2 ", #' linters = trailing_whitespace_linter() #' ) #' #' code_lines <- "a <- TRUE\n \nb <- FALSE" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = trailing_whitespace_linter() #' ) #' #' # okay #' lint( #' text = "x <- 1.2", #' linters = trailing_whitespace_linter() #' ) #' #' lint( #' text = "x <- 1.2 # comment about this assignment", #' linters = trailing_whitespace_linter() #' ) #' #' code_lines <- "a <- TRUE\n \nb <- FALSE" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = trailing_whitespace_linter(allow_empty_lines = TRUE) #' ) #' #' @evalRd rd_tags("trailing_whitespace_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export trailing_whitespace_linter <- function(allow_empty_lines = FALSE, allow_in_strings = TRUE) { Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } res <- re_matches( source_expression$file_lines, rex(blanks, end), locations = TRUE ) if (isTRUE(allow_empty_lines)) { bad_lines <- which(res$start > 1L) } else { bad_lines <- which(!is.na(res$start)) } if (isTRUE(allow_in_strings) && !is.null(source_expression$full_xml_parsed_content)) { all_str_consts <- xml_find_all(source_expression$full_xml_parsed_content, "//STR_CONST") start_lines <- as.integer(xml_attr(all_str_consts, "line1")) end_lines <- as.integer(xml_attr(all_str_consts, "line2")) is_in_str <- vapply(bad_lines, function(ln) any(start_lines <= ln & ln < end_lines), logical(1L)) bad_lines <- bad_lines[!is_in_str] } lapply( bad_lines, function(line) { Lint( filename = source_expression$filename, line_number = line, column_number = res$start[[line]], type = "style", message = "Trailing whitespace is superfluous.", line = source_expression$file_lines[[line]], ranges = list(c(res$start[[line]], res$end[[line]])) ) } ) }) } lintr/R/shared_constants.R0000644000176200001440000002612314577052532015306 0ustar liggesusersrx_non_active_char <- rex(none_of("^${(.*+?|[\\")) rx_static_escape <- local({ rx_char_escape <- rex(or( group("\\", none_of(alnum)), group("\\x", between(xdigit, 1L, 2L)), group("\\", between("0":"7", 1L, 3L)), group("\\u{", between(xdigit, 1L, 4L), "}"), group("\\u", between(xdigit, 1L, 4L)), group("\\U{", between(xdigit, 1L, 8L), "}"), group("\\U", between(xdigit, 1L, 8L)) )) rx_trivial_char_group <- rex( "[", or( any, group("\\", none_of("dswDSW")), # character classes, e.g. \d are enabled in [] too if perl = TRUE rx_char_escape ), "]" ) rex(or( capture(rx_char_escape, name = "char_escape"), capture(rx_trivial_char_group, name = "trivial_char_group") )) }) rx_static_token <- local({ rex(or( rx_non_active_char, rx_static_escape )) }) rx_unescaped_regex <- paste0("(?s)", rex(start, zero_or_more(rx_non_active_char), end)) rx_static_regex <- paste0("(?s)", rex(start, zero_or_more(rx_static_token), end)) rx_first_static_token <- paste0("(?s)", rex(start, zero_or_more(rx_non_active_char), rx_static_escape)) #' Determine whether a regex pattern actually uses regex patterns #' #' Note that is applies to the strings that are found on the XML parse tree, #' _not_ plain strings. This is important for backslash escaping, which #' happens at different layers of escaping than one might expect. So testing #' this function is best done through testing the expected results of a lint #' on a given file, rather than passing strings to this function, which can #' be confusing. #' #' @param str A character vector. #' @return A logical vector, `TRUE` wherever `str` could be replaced by a #' string with `fixed = TRUE`. #' @noRd is_not_regex <- function(str, allow_unescaped = FALSE) { # need to add single-line option to allow literal newlines if (allow_unescaped) { !grepl(rx_unescaped_regex, str, perl = TRUE) } else { grepl(rx_static_regex, str, perl = TRUE) } } #' Compute a fixed string equivalent to a static regular expression #' #' @param static_regex A regex for which `is_not_regex()` returns `TRUE` #' @return A string such that `grepl(static_regex, x)` is equivalent to #' `grepl(get_fixed_string(static_regex), x, fixed = TRUE)` #' #' @noRd get_fixed_string <- function(static_regex) { if (length(static_regex) == 0L) { return(character()) } else if (length(static_regex) > 1L) { return(vapply(static_regex, get_fixed_string, character(1L))) } fixed_string <- "" current_match <- regexpr(rx_first_static_token, static_regex, perl = TRUE) while (current_match != -1L) { token_type <- attr(current_match, "capture.names")[attr(current_match, "capture.start") > 0L] token_start <- max(attr(current_match, "capture.start")) if (token_start > 1L) { fixed_string <- paste0(fixed_string, substr(static_regex, 1L, token_start - 1L)) } consume_to <- attr(current_match, "match.length") token_content <- substr(static_regex, token_start, consume_to) fixed_string <- paste0(fixed_string, get_token_replacement(token_content, token_type)) static_regex <- substr(static_regex, start = consume_to + 1L, stop = nchar(static_regex)) current_match <- regexpr(rx_first_static_token, static_regex, perl = TRUE) } paste0(fixed_string, static_regex) } #' Get a fixed string equivalent to a regular expression token #' #' This handles two cases: converting a "trivial" character group like `[$]` to `$`, #' and converting an escaped character like `"\\$"` to `$`. Splitting a full expression #' into tokens is handled by [get_fixed_string()]. #' #' @noRd get_token_replacement <- function(token_content, token_type) { if (token_type == "trivial_char_group") { token_content <- substr(token_content, start = 2L, stop = nchar(token_content) - 1L) if (startsWith(token_content, "\\")) { # escape within trivial char group get_token_replacement(token_content, "char_escape") } else { token_content } } else { # char_escape token if (re_matches(token_content, rex("\\", one_of("^${}().*+?|[]\\<>=:;/_-!@#%&,~")))) { substr(token_content, start = 2L, stop = nchar(token_content)) } else { eval(parse(text = paste0('"', token_content, '"'))) } } } # some metadata about infix operators on the R parse tree. # xml_tag gives the XML tag as returned by xmlparsedata::xml_parse_data(). # r_string gives the operator as you would write it in R code. # styler: off infix_metadata <- data.frame(stringsAsFactors = FALSE, matrix(byrow = TRUE, ncol = 2L, c( "OP-PLUS", "+", "OP-MINUS", "-", "OP-TILDE", "~", "GT", ">", "GE", ">=", "LT", "<", "LE", "<=", "EQ", "==", "NE", "!=", "AND", "&", "OR", "|", "AND2", "&&", "OR2", "||", "LEFT_ASSIGN", "<-", "LEFT_ASSIGN", ":=", "LEFT_ASSIGN", "<<-", "RIGHT_ASSIGN", "->", "RIGHT_ASSIGN", "->>", "EQ_ASSIGN", "=", "EQ_SUB", "=", # in calls: foo(x = 1) "EQ_FORMALS", "=", # in definitions: function(x = 1) "PIPE", "|>", "SPECIAL", "%%", "OP-SLASH", "/", "OP-STAR", "*", "OP-COMMA", ",", "OP-CARET", "^", "OP-CARET", "**", "OP-AT", "@", "OP-EXCLAMATION", "!", "OP-COLON", ":", "NS_GET", "::", "NS_GET_INT", ":::", "OP-LEFT-BRACE", "{", "OP-LEFT-BRACKET", "[", "LBB", "[[", "OP-LEFT-PAREN", "(", "OP-QUESTION", "?", "OP-DOLLAR", "$", NULL ))) # styler: on names(infix_metadata) <- c("xml_tag", "string_value") # utils::getParseData()'s designation for the tokens wouldn't be valid as XML tags infix_metadata$parse_tag <- ifelse( startsWith(infix_metadata$xml_tag, "OP-"), quote_wrap(infix_metadata$string_value, "'"), infix_metadata$xml_tag ) # treated separately because spacing rules are different for unary operators infix_metadata$unary <- infix_metadata$xml_tag %in% c("OP-PLUS", "OP-MINUS", "OP-TILDE") # high-precedence operators are ignored by this linter; see # https://style.tidyverse.org/syntax.html#infix-operators infix_metadata$low_precedence <- infix_metadata$string_value %in% c( "+", "-", "~", ">", ">=", "<", "<=", "==", "!=", "&", "&&", "|", "||", "<-", ":=", "<<-", "->", "->>", "=", "%%", "/", "*", "|>" ) # comparators come up in several lints infix_metadata$comparator <- infix_metadata$string_value %in% c("<", "<=", ">", ">=", "==", "!=") # these XML nodes require checking the text() to disambiguate multiple operators using the same tag infix_metadata$ambiguous_tag <- infix_metadata$xml_tag %in% infix_metadata$xml_tag[duplicated(infix_metadata$xml_tag)] infix_metadata$xml_tag_exact <- infix_metadata$xml_tag infix_metadata$xml_tag_exact[infix_metadata$ambiguous_tag] <- sprintf( "%s[text() = '%s']", infix_metadata$xml_tag_exact[infix_metadata$ambiguous_tag], infix_metadata$string_value[infix_metadata$ambiguous_tag] ) # functions equivalent to base::ifelse() for linting purposes ifelse_funs <- c("ifelse", "if_else", "fifelse") object_name_xpath <- local({ # search ancestor:: axis for assignments of symbols for # cases like a$b$c. We only try to lint 'a' since 'b' # and 'c' might be beyond the user's control to name. # the tree structure for 'a$b$c <- 1' has 'a' # at the 'bottom' of the list; it is distinguished # from 'b' and 'c' by not having '$' as a sibling. # search parent:: axis for assignments of strings because # the complicated nested assignment available for symbols # is not possible for strings, though we do still have to # be aware of cases like 'a$"b" <- 1'. xp_assignment_target_fmt <- paste0( "not(parent::expr[OP-DOLLAR or OP-AT])", "and %1$s::expr[", " following-sibling::LEFT_ASSIGN", " or preceding-sibling::RIGHT_ASSIGN", " or following-sibling::EQ_ASSIGN", "]", "and not(%1$s::expr[", " preceding-sibling::OP-LEFT-BRACKET", " or preceding-sibling::LBB", "])" ) glue(" //SYMBOL[ {sprintf(xp_assignment_target_fmt, 'ancestor')} ] | //STR_CONST[ {sprintf(xp_assignment_target_fmt, 'parent')} ] | //SYMBOL_FORMALS ") }) # Remove quotes or other things from names strip_names <- function(x) { x <- re_substitutes(x, rex(start, some_of(quote, "`", "%")), "") x <- re_substitutes(x, rex(some_of(quote, "`", "<", "-", "%"), end), "") x } #' Pull out symbols used in glue strings under the current sub-tree #' #' Required by any linter (e.g. [object_usage_linter()] / [unused_imports_linter()]) #' that lints based on whether certain symbols are present, to ensure any #' symbols only used inside glue strings are also visible to the linter. #' #' @param expr An XML AST #' @param interpret_glue Logical, if `FALSE` return nothing. #' @return A character vector of symbols (variables, infix operators, and #' function calls) found in glue calls under `expr`. #' @noRd extract_glued_symbols <- function(expr, interpret_glue) { if (!isTRUE(interpret_glue)) { return(character()) } # TODO support more glue functions # Package glue: # - glue_sql # - glue_safe # - glue_col # - glue_data # - glue_data_sql # - glue_data_safe # - glue_data_col # # Package stringr: # - str_interp # NB: position() > 1 because position=1 is glue_call_xpath <- " descendant::SYMBOL_FUNCTION_CALL[text() = 'glue'] /parent::expr /parent::expr[ not(SYMBOL_SUB[text() = '.envir' or text() = '.transform']) and not(expr[position() > 1 and not(STR_CONST)]) ] " glue_calls <- xml_find_all(expr, glue_call_xpath) glued_symbols <- new.env(parent = emptyenv()) for (glue_call in glue_calls) { # TODO(michaelchirico): consider dropping tryCatch() here if we're more confident in our logic parsed_call <- tryCatch(xml2lang(glue_call), error = unexpected_glue_parse_error, warning = unexpected_glue_parse_error) parsed_call[[".envir"]] <- glued_symbols parsed_call[[".transformer"]] <- glue_symbol_extractor # #1459: syntax errors in glue'd code are ignored with warning, rather than crashing lint tryCatch(eval(parsed_call), error = glue_parse_failure_warning) } names(glued_symbols) } unexpected_glue_parse_error <- function(cond) { stop("Unexpected failure to parse glue call, please report: ", conditionMessage(cond)) # nocov } glue_parse_failure_warning <- function(cond) { warning( "Evaluating glue expression while testing for local variable usage failed: ", conditionMessage(cond), "\nPlease ensure correct glue syntax, e.g., matched delimiters.", call. = FALSE ) NULL } glue_symbol_extractor <- function(text, envir, data) { symbols <- tryCatch( all.vars(parse(text = text), functions = TRUE), error = function(...) NULL, warning = function(...) NULL ) for (sym in symbols) { assign(sym, NULL, envir = envir) } "" } magrittr_pipes <- c("%>%", "%!>%", "%T>%", "%$%", "%<>%") purrr_mappers <- c( "map", "walk", "map_raw", "map_lgl", "map_int", "map_dbl", "map_chr", "map_vec", "map_df", "map_dfr", "map_dfc" ) lintr/R/deprecated.R0000644000176200001440000000114714577052532014043 0ustar liggesusers#' Deprecated functions #' #' Functions that have been deprecated and replaced by newer ones. They will be removed in an #' upcoming version of \pkg{lintr} and should thus not be used anymore. #' @noRd NULL lintr_deprecated <- function(old, new = NULL, version = NULL, type = "Function") { msg <- c( c(type, " ", old, " was deprecated"), if (length(version) > 0L) { c(" in lintr version ", version) }, ". ", if (length(new) > 0L) { c("Use ", new, " instead.") } ) msg <- paste0(msg, collapse = "") warning(msg, call. = FALSE, domain = NA) } lintr/R/function_argument_linter.R0000644000176200001440000000440514577052532017047 0ustar liggesusers#' Function argument linter #' #' @description #' Check that arguments with defaults come last in all function declarations, #' as per the tidyverse design guide. #' #' Changing the argument order can be a breaking change. An alternative to changing the argument order #' is to instead set the default for such arguments to `NULL`. #' #' @examples #' # will produce lints #' lint( #' text = "function(y = 1, z = 2, x) {}", #' linters = function_argument_linter() #' ) #' #' lint( #' text = "function(x, y, z = 1, ..., w) {}", #' linters = function_argument_linter() #' ) #' #' # okay #' lint( #' text = "function(x, y = 1, z = 2) {}", #' linters = function_argument_linter() #' ) #' #' lint( #' text = "function(x, y, w, z = 1, ...) {}", #' linters = function_argument_linter() #' ) #' #' lint( #' text = "function(y = 1, z = 2, x = NULL) {}", #' linters = function_argument_linter() #' ) #' #' lint( #' text = "function(x, y, z = 1, ..., w = NULL) {}", #' linters = function_argument_linter() #' ) #' #' @evalRd rd_tags("function_argument_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export function_argument_linter <- function() { xpath <- " (//FUNCTION | //OP-LAMBDA) /following-sibling::EQ_FORMALS[1] /following-sibling::SYMBOL_FORMALS[ text() != '...' and not(following-sibling::*[not(self::COMMENT)][1][self::EQ_FORMALS]) ] " used_in_missing_xpath <- " text() = following-sibling::expr[last()]//expr[expr/SYMBOL_FUNCTION_CALL[text() = 'missing']]/expr[2]/SYMBOL/text() " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) uses_missing <- xml_find_lgl(bad_expr, used_in_missing_xpath) missing_note <- ifelse(uses_missing, " Consider setting the default to NULL and using is.null() instead of using missing()", "") xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste0("Arguments without defaults should come before arguments with defaults.", missing_note), type = "style" ) }) } lintr/R/unused_import_linter.R0000644000176200001440000001073014577052532016213 0ustar liggesusers#' Check that imported packages are actually used #' #' @inheritParams object_usage_linter #' @param allow_ns_usage Suppress lints for packages only used via namespace. #' This is `FALSE` by default because `pkg::fun()` doesn't require `library(pkg)`. #' You can use [requireNamespace("pkg")][requireNamespace()] to ensure a package is #' installed without loading it. #' @param except_packages Character vector of packages that are ignored. #' These are usually attached for their side effects. #' #' @examples #' # will produce lints #' code_lines <- "library(dplyr)\n1 + 1" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unused_import_linter() #' ) #' #' code_lines <- "library(dplyr)\ndplyr::tibble(a = 1)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unused_import_linter() #' ) #' #' # okay #' code_lines <- "library(dplyr)\ntibble(a = 1)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unused_import_linter() #' ) #' #' code_lines <- "library(dplyr)\ndplyr::tibble(a = 1)" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unused_import_linter(allow_ns_usage = TRUE) #' ) #' #' @evalRd rd_tags("unused_import_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unused_import_linter <- function(allow_ns_usage = FALSE, except_packages = c("bit64", "data.table", "tidyverse"), interpret_glue = TRUE) { # Get dataset names lazy-loaded by imported packages get_datasets <- function(pkg) { results <- utils::data(package = pkg)$results items <- results[, "Item"] # e.g. 'state.abb (state)' in 'datasets' gsub("\\s*\\([^)]*\\)$", "", items) } import_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'library' or text() = 'require'] /parent::expr /parent::expr[ expr[2][STR_CONST] or not(SYMBOL_SUB[ text() = 'character.only' and following-sibling::expr[1][NUM_CONST[text() = 'TRUE'] or SYMBOL[text() = 'T']] ]) ] " xp_used_symbols <- paste( "//SYMBOL_FUNCTION_CALL[not(preceding-sibling::NS_GET)]", "//SYMBOL[not( parent::expr/preceding-sibling::expr[last()]/SYMBOL_FUNCTION_CALL[text() = 'library' or text() = 'require'] )]", "//SPECIAL", sep = " | " ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content import_exprs <- xml_find_all(xml, import_xpath) if (length(import_exprs) == 0L) { return(list()) } imported_pkgs <- xml_find_chr(import_exprs, "string(expr[STR_CONST|SYMBOL])") # as.character(parse(...)) returns one entry per expression imported_pkgs <- as.character(parse(text = imported_pkgs, keep.source = FALSE)) used_symbols <- unique(c( xml_text(xml_find_all(xml, xp_used_symbols)), extract_glued_symbols(xml, interpret_glue = interpret_glue) )) is_used <- vapply( imported_pkgs, function(pkg) { # Skip excepted packages and packages that are not installed if (pkg %in% except_packages || !requireNamespace(pkg, quietly = TRUE)) { return(TRUE) } package_exports <- getNamespaceExports(pkg) # functions dataset_exports <- get_datasets(pkg) # datasets any(package_exports %in% used_symbols) || any(dataset_exports %in% used_symbols) }, logical(1L) ) # TODO(michaelchirico): instead of vectorizing over packages, # xml_find_all SYMBOL_PACKAGE namespaces and check imported_pkgs %in% is_ns_used <- vapply( imported_pkgs, function(pkg) { ns_usage <- xml_find_first(xml, paste0("//SYMBOL_PACKAGE[text() = '", pkg, "']")) !identical(ns_usage, xml2::xml_missing()) }, logical(1L) ) is_unused <- !is_used if (allow_ns_usage) { is_unused[is_ns_used] <- FALSE } import_exprs <- import_exprs[is_unused] unused_packages <- get_r_string(import_exprs, xpath = "expr[STR_CONST | SYMBOL]") lint_message <- ifelse( is_ns_used[is_unused][unused_packages], paste0( "Package '", unused_packages, "' is only used by namespace. ", "Check that it is installed using loadNamespace() instead." ), paste0("Package '", unused_packages, "' is attached but never used.") ) xml_nodes_to_lints(import_exprs, source_expression, lint_message, type = "warning") }) } lintr/R/T_and_F_symbol_linter.R0000644000176200001440000000417314577052532016201 0ustar liggesusers#' `T` and `F` symbol linter #' #' Avoid the symbols `T` and `F`, and use `TRUE` and `FALSE` instead. #' #' @examples #' # will produce lints #' lint( #' text = "x <- T; y <- F", #' linters = T_and_F_symbol_linter() #' ) #' #' lint( #' text = "T = 1.2; F = 2.4", #' linters = T_and_F_symbol_linter() #' ) #' #' # okay #' lint( #' text = "x <- c(TRUE, FALSE)", #' linters = T_and_F_symbol_linter() #' ) #' #' lint( #' text = "t = 1.2; f = 2.4", #' linters = T_and_F_symbol_linter() #' ) #' #' @evalRd rd_tags("T_and_F_symbol_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export T_and_F_symbol_linter <- function() { # nolint: object_name. symbol_xpath <- "//SYMBOL[ (text() = 'T' or text() = 'F') and not(parent::expr[OP-DOLLAR or OP-AT]) ]" assignment_xpath <- "parent::expr[following-sibling::LEFT_ASSIGN or preceding-sibling::RIGHT_ASSIGN or following-sibling::EQ_ASSIGN]" usage_xpath <- sprintf("%s[not(%s)]", symbol_xpath, assignment_xpath) assignment_xpath <- sprintf("%s[%s]", symbol_xpath, assignment_xpath) replacement_map <- c(T = "TRUE", F = "FALSE") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } bad_usage <- xml_find_all(source_expression$xml_parsed_content, usage_xpath) bad_assignment <- xml_find_all(source_expression$xml_parsed_content, assignment_xpath) make_lints <- function(expr, fmt) { symbol <- xml_text(expr) lint_message <- sprintf(fmt, replacement_map[symbol], symbol) xml_nodes_to_lints( xml = expr, source_expression = source_expression, lint_message = lint_message, type = "style", column_number_xpath = "number(./@col2 + 1)", # mark at end range_end_xpath = "number(./@col2 + 1)" # end after T/F for easy fixing ) } c( make_lints(bad_usage, "Use %s instead of the symbol %s."), make_lints(bad_assignment, "Don't use %2$s as a variable name, as it can break code relying on %2$s being %1$s.") ) }) } lintr/R/trailing_blank_lines_linter.R0000644000176200001440000000354414577052532017475 0ustar liggesusers#' Trailing blank lines linter #' #' Check that there are no trailing blank lines in source code. #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' # will produce lints #' f <- withr::local_tempfile(lines = "x <- 1\n") #' readLines(f) #' lint( #' filename = f, #' linters = trailing_blank_lines_linter() #' ) #' #' # okay #' f <- withr::local_tempfile(lines = "x <- 1") #' readLines(f) #' lint( #' filename = f, #' linters = trailing_blank_lines_linter() #' ) #' #' @evalRd rd_tags("trailing_blank_lines_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export trailing_blank_lines_linter <- function() { Linter(function(source_expression) { blanks <- re_matches( source_expression$file_lines, rex(start, any_spaces, end) ) line_number <- length(source_expression$file_lines) lints <- list() while (line_number > 0L && (is.na(blanks[[line_number]]) || isTRUE(blanks[[line_number]]))) { if (!is.na(blanks[[line_number]])) { lints[[length(lints) + 1L]] <- Lint( filename = source_expression$filename, line_number = line_number, column_number = 1L, type = "style", message = "Trailing blank lines are superfluous.", line = source_expression$file_lines[[line_number]] ) } line_number <- line_number - 1L } if (identical(source_expression$terminal_newline, FALSE)) { # could use isFALSE, but needs backports last_line <- tail(source_expression$file_lines, 1L) lints[[length(lints) + 1L]] <- Lint( filename = source_expression$filename, line_number = length(source_expression$file_lines), column_number = (nchar(last_line) %||% 0L) + 1L, type = "style", message = "Missing terminal newline.", line = last_line ) } lints }) } lintr/R/quotes_linter.R0000644000176200001440000000336714577052532014646 0ustar liggesusers#' Character string quote linter #' #' Check that the desired quote delimiter is used for string constants. #' #' @param delimiter Which quote delimiter to accept. Defaults to the tidyverse #' default of `"` (double-quoted strings). #' #' @examples #' # will produce lints #' lint( #' text = "c('a', 'b')", #' linters = quotes_linter() #' ) #' #' # okay #' lint( #' text = 'c("a", "b")', #' linters = quotes_linter() #' ) #' #' code_lines <- "paste0(x, '\"this is fine\"')" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = quotes_linter() #' ) #' #' # okay #' lint( #' text = "c('a', 'b')", #' linters = quotes_linter(delimiter = "'") #' ) #' #' @evalRd rd_tags("quotes_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export quotes_linter <- function(delimiter = c('"', "'")) { delimiter <- match.arg(delimiter) if (delimiter == '"') { quote_regex <- rex( start, zero_or_one(character_class("rR")), single_quote, any_non_double_quotes, single_quote, end ) lint_message <- "Only use double-quotes." } else { quote_regex <- rex( start, zero_or_one(character_class("rR")), double_quote, any_non_single_quotes, double_quote, end ) lint_message <- "Only use single-quotes." } Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } string_exprs <- xml_find_all(source_expression$xml_parsed_content, "//STR_CONST") is_bad <- re_matches(xml_text(string_exprs), quote_regex) xml_nodes_to_lints(string_exprs[is_bad], source_expression, lint_message) }) } lintr/R/expect_true_false_linter.R0000644000176200001440000000360614577052532017023 0ustar liggesusers#' Require usage of `expect_true(x)` over `expect_equal(x, TRUE)` #' #' [testthat::expect_true()] and [testthat::expect_false()] exist specifically #' for testing the `TRUE`/`FALSE` value of an object. #' [testthat::expect_equal()] and [testthat::expect_identical()] can also be #' used for such tests, but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = "expect_equal(x, TRUE)", #' linters = expect_true_false_linter() #' ) #' #' lint( #' text = "expect_equal(x, FALSE)", #' linters = expect_true_false_linter() #' ) #' #' # okay #' lint( #' text = "expect_true(x)", #' linters = expect_true_false_linter() #' ) #' #' lint( #' text = "expect_false(x)", #' linters = expect_true_false_linter() #' ) #' #' @evalRd rd_tags("expect_true_false_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_true_false_linter <- function() { xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[position() <= 2 and NUM_CONST[text() = 'TRUE' or text() = 'FALSE']] /parent::expr " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) # NB: use expr/$node, not expr[$node], to exclude other things (especially ns:: parts of the call) call_name <- xp_call_name(bad_expr, condition = "starts-with(text(), 'expect_')") truth_value <- xml_find_chr(bad_expr, "string(expr/NUM_CONST[text() = 'TRUE' or text() = 'FALSE'])") lint_message <- sprintf( "expect_%s(x) is better than %s(x, %s)", tolower(truth_value), call_name, truth_value ) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/keyword_quote_linter.R0000644000176200001440000001323114577052532016216 0ustar liggesusers#' Block unnecessary quoting in calls #' #' Any valid symbol can be used as a keyword argument to an R function call. #' Sometimes, it is necessary to quote (or backtick) an argument that is #' not an otherwise valid symbol (e.g. creating a vector whose names have #' spaces); besides this edge case, quoting should not be done. #' #' The most common source of violation for this is creating named vectors, #' lists, or data.frame-alikes, but it can be observed in other calls as well. #' #' Similar reasoning applies to extractions with `$` or `@`. #' #' @examples #' # will produce lints #' lint( #' text = 'data.frame("a" = 1)', #' linters = keyword_quote_linter() #' ) #' #' lint( #' text = "data.frame(`a` = 1)", #' linters = keyword_quote_linter() #' ) #' #' lint( #' text = 'my_list$"key"', #' linters = keyword_quote_linter() #' ) #' #' lint( #' text = 's4obj@"key"', #' linters = keyword_quote_linter() #' ) #' #' # okay #' lint( #' text = "data.frame(`a b` = 1)", #' linters = keyword_quote_linter() #' ) #' #' lint( #' text = "my_list$`a b`", #' linters = keyword_quote_linter() #' ) #' #' @evalRd rd_tags("keyword_quote_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export # TODO(michaelchirico): offer a stricter version of this that # requires backticks to be used for non-syntactic names (i.e., not quotes). # Here are the relevant xpaths: # //expr[expr[SYMBOL_FUNCTION_CALL]]/SYMBOL_SUB[starts-with(text(), '`')] # //expr[expr[SYMBOL_FUNCTION_CALL]]/STR_CONST[{is_quoted(text())}] keyword_quote_linter <- function() { # Check if a string could be assigned as an R variable. # # See [make.names()] for the description of syntactically valid names in R. is_valid_r_name <- function(x) make.names(x) == x # NB: xml2 uses xpath 1.0 which doesn't support matches() for regex, so we # have to jump out of xpath to complete this lint. # It's also a bit tough to get the escaping through R and then xpath to # work as intended, hence the rather verbose declaration here. quote_cond <- xp_or( "starts-with(text(), '\"')", "starts-with(text(), '`')", 'starts-with(text(), "\'")' ) # SYMBOL_SUB for backticks, STR_CONST for quoted names call_arg_xpath <- glue(" //SYMBOL_FUNCTION_CALL /parent::expr /parent::expr /*[(self::SYMBOL_SUB or self::STR_CONST) and {quote_cond}] ") # also exclude $ or @, which are handled below assignment_candidate_cond <- " not(OP-DOLLAR or OP-AT) and (STR_CONST or SYMBOL[starts-with(text(), '`')]) " assignment_xpath <- glue(" (//EQ_ASSIGN | //LEFT_ASSIGN[text() != ':=']) /preceding-sibling::expr[{ assignment_candidate_cond }] | //RIGHT_ASSIGN/following-sibling::expr[{ assignment_candidate_cond }] ") extraction_xpath <- " (//OP-DOLLAR | //OP-AT)/following-sibling::STR_CONST | //OP-DOLLAR/following-sibling::SYMBOL[starts-with(text(), '`')] | //OP-AT/following-sibling::SLOT[starts-with(text(), '`')] " no_quote_msg <- "Use backticks to create non-syntactic names, not quotes." clarification <- "i.e., if the name is not a valid R symbol (see ?make.names)." Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content call_arg_expr <- xml_find_all(xml, call_arg_xpath) invalid_call_quoting <- is_valid_r_name(get_r_string(call_arg_expr)) call_arg_lints <- xml_nodes_to_lints( call_arg_expr[invalid_call_quoting], source_expression = source_expression, lint_message = paste("Only quote named arguments to functions if necessary,", clarification), type = "warning" ) assignment_expr <- xml_find_all(xml, assignment_xpath) invalid_assignment_quoting <- is_valid_r_name(get_r_string(assignment_expr)) # NB: XPath is such that there is exactly 1 node per match, making xml_children() ideal. # xml_child() gets it wrong for 0 (an error) and >1 match. assignment_to_string <- xml_name(xml2::xml_children(assignment_expr)) == "STR_CONST" string_assignment_lints <- xml_nodes_to_lints( assignment_expr[assignment_to_string & !invalid_assignment_quoting], source_expression = source_expression, lint_message = no_quote_msg, type = "warning" ) assignment_lints <- xml_nodes_to_lints( assignment_expr[invalid_assignment_quoting], source_expression = source_expression, lint_message = paste("Only quote targets of assignment if necessary,", clarification), type = "warning" ) extraction_expr <- xml_find_all(xml, extraction_xpath) invalid_extraction_quoting <- is_valid_r_name(get_r_string(extraction_expr)) extraction_of_string <- xml_name(extraction_expr) == "STR_CONST" string_extraction_lints <- xml_nodes_to_lints( extraction_expr[extraction_of_string & !invalid_extraction_quoting], source_expression = source_expression, lint_message = no_quote_msg, type = "warning" ) extraction_expr <- extraction_expr[invalid_extraction_quoting] extractor <- xml_find_chr(extraction_expr, "string(preceding-sibling::*[1])") gen_extractor <- ifelse(extractor == "$", "[[", "slot()") extraction_lints <- xml_nodes_to_lints( extraction_expr, source_expression = source_expression, lint_message = paste( "Only quote targets of extraction with", extractor, "if necessary,", clarification, "Use backticks to create non-syntactic names, or use", gen_extractor, "to extract by string." ), type = "warning" ) c(call_arg_lints, string_assignment_lints, assignment_lints, string_extraction_lints, extraction_lints) }) } lintr/R/condition_message_linter.R0000644000176200001440000000520614577052532017012 0ustar liggesusers#' Block usage of `paste()` and `paste0()` with messaging functions using `...` #' #' @description #' This linter discourages combining condition functions like [stop()] with string concatenation #' functions [paste()] and [paste0()]. This is because #' #' - `stop(paste0(...))` is redundant as it is exactly equivalent to `stop(...)` #' - `stop(paste(...))` is similarly equivalent to `stop(...)` with separators (see examples) #' #' The same applies to the other default condition functions as well, i.e., [warning()], [message()], #' and [packageStartupMessage()]. #' #' @examples #' # will produce lints #' lint( #' text = 'stop(paste("a string", "another"))', #' linters = condition_message_linter() #' ) #' #' lint( #' text = 'warning(paste0("a string", " another"))', #' linters = condition_message_linter() #' ) #' #' # okay #' lint( #' text = 'stop("a string", " another")', #' linters = condition_message_linter() #' ) #' #' lint( #' text = 'warning("a string", " another")', #' linters = condition_message_linter() #' ) #' #' lint( #' text = 'warning(paste("a string", "another", sep = "-"))', #' linters = condition_message_linter() #' ) #' #' @evalRd rd_tags("condition_message_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export condition_message_linter <- function() { translators <- c("packageStartupMessage", "message", "warning", "stop") xpath <- glue(" //SYMBOL_FUNCTION_CALL[ ({xp_text_in_table(translators)}) and not(preceding-sibling::OP-DOLLAR or preceding-sibling::OP-AT) ] /parent::expr /following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'paste' or text() = 'paste0']] and not(SYMBOL_SUB[text() = 'collapse']) ] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) sep_value <- get_r_string(bad_expr, xpath = "./expr/SYMBOL_SUB[text() = 'sep']/following-sibling::expr/STR_CONST") bad_expr <- bad_expr[is.na(sep_value) | sep_value %in% c("", " ")] outer_call <- xp_call_name(bad_expr) inner_call <- xp_call_name(bad_expr, depth = 2L) lint_message <- paste( "Don't use", inner_call, "to build", outer_call, "strings.", "Instead use the fact that these functions build condition message strings from their input", '(using "" as a separator). For translatable strings, prefer using gettextf().' ) xml_nodes_to_lints(bad_expr, source_expression = source_expression, lint_message = lint_message, type = "warning") }) } lintr/R/library_call_linter.R0000644000176200001440000000410714577052532015756 0ustar liggesusers#' Library call linter #' #' Force library calls to all be at the top of the script. #' #' @param allow_preamble Logical, default `TRUE`. If `FALSE`, #' no code is allowed to precede the first `library()` call, #' otherwise some setup code is allowed, but all `library()` #' calls must follow consecutively after the first one. #' @examples #' # will produce lints #' lint( #' text = " #' library(dplyr) #' print('test') #' library(tidyr) #' ", #' linters = library_call_linter() #' ) #' #' lint( #' text = " #' library(dplyr) #' print('test') #' library(tidyr) #' library(purrr) #' ", #' linters = library_call_linter() #' ) #' #' # okay #' lint( #' text = " #' library(dplyr) #' print('test') #' ", #' linters = library_call_linter() #' ) #' #' lint( #' text = " #' # comment #' library(dplyr) #' ", #' linters = library_call_linter() #' ) #' #' @evalRd rd_tags("library_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export library_call_linter <- function(allow_preamble = TRUE) { attach_call <- "text() = 'library' or text() = 'require'" unsuppressed_call <- glue("not( {attach_call} or starts-with(text(), 'suppress'))") if (allow_preamble) { unsuppressed_call <- xp_and( unsuppressed_call, glue("@line1 > //SYMBOL_FUNCTION_CALL[{ attach_call }][1]/@line1") ) } xpath <- glue(" //SYMBOL_FUNCTION_CALL[{ attach_call }][last()] /preceding::expr /SYMBOL_FUNCTION_CALL[{ unsuppressed_call }][last()] /following::expr[SYMBOL_FUNCTION_CALL[{ attach_call }]] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content bad_expr <- xml_find_all(xml, xpath) call_name <- xp_call_name(bad_expr) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = sprintf("Move all %s calls to the top of the script.", call_name), type = "warning" ) }) } lintr/R/function_return_linter.R0000644000176200001440000000314714577052532016546 0ustar liggesusers#' Lint common mistakes/style issues cropping up from return statements #' #' `return(x <- ...)` is either distracting (because `x` is ignored), or #' confusing (because assigning to `x` has some side effect that is muddled #' by the dual-purpose expression). #' #' @examples #' # will produce lints #' lint( #' text = "foo <- function(x) return(y <- x + 1)", #' linters = function_return_linter() #' ) #' #' lint( #' text = "foo <- function(x) return(x <<- x + 1)", #' linters = function_return_linter() #' ) #' #' writeLines("e <- new.env() \nfoo <- function(x) return(e$val <- x + 1)") #' lint( #' text = "e <- new.env() \nfoo <- function(x) return(e$val <- x + 1)", #' linters = function_return_linter() #' ) #' #' # okay #' lint( #' text = "foo <- function(x) return(x + 1)", #' linters = function_return_linter() #' ) #' #' code_lines <- " #' foo <- function(x) { #' x <<- x + 1 #' return(x) #' } #' " #' lint( #' text = code_lines, #' linters = function_return_linter() #' ) #' #' code_lines <- " #' e <- new.env() #' foo <- function(x) { #' e$val <- x + 1 #' return(e$val) #' } #' " #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = function_return_linter() #' ) #' #' @evalRd rd_tags("function_return_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export function_return_linter <- make_linter_from_xpath( xpath = " //SYMBOL_FUNCTION_CALL[text() = 'return'] /parent::expr/parent::expr/expr[LEFT_ASSIGN or RIGHT_ASSIGN] ", lint_message = "Move the assignment outside of the return() clause, or skip assignment altogether." ) lintr/R/nested_ifelse_linter.R0000644000176200001440000000557514577052532016142 0ustar liggesusers#' Block usage of nested `ifelse()` calls #' #' Calling [ifelse()] in nested calls is problematic for two main reasons: #' 1. It can be hard to read -- mapping the code to the expected output #' for such code can be a messy task/require a lot of mental bandwidth, #' especially for code that nests more than once #' 2. It is inefficient -- `ifelse()` can evaluate _all_ of its arguments at #' both yes and no (see ); this issue #' is exacerbated for nested calls #' #' Users can instead rely on a more readable alternative modeled after SQL #' CASE WHEN statements. #' #' Let's say this is our original code: #' #' ```r #' ifelse( #' x == "a", #' 2L, #' ifelse(x == "b", 3L, 1L) #' ) #' ``` #' #' Here are a few ways to avoid nesting and make the code more readable: #' #' - Use `data.table::fcase()` #' #' ```r #' data.table::fcase( #' x == "a", 2L, #' x == "b", 3L, #' default = 1L #' ) #' ``` #' #' - Use `dplyr::case_match()` #' #' ```r #' dplyr::case_match( #' x, #' "a" ~ 2L, #' "b" ~ 3L, #' .default = 1L #' ) #' ``` #' #' - Use a look-up-and-merge approach (build a mapping table between values #' and outputs and merge this to the input) #' #' ```r #' default <- 1L #' values <- data.frame( #' a = 2L, #' b = 3L #' ) #' found_value <- values[[x]] #' ifelse(is.null(found_value), default, found_value) #' ``` #' #' @examples #' # will produce lints #' lint( #' text = 'ifelse(x == "a", 1L, ifelse(x == "b", 2L, 3L))', #' linters = nested_ifelse_linter() #' ) #' #' # okay #' lint( #' text = 'dplyr::case_when(x == "a" ~ 1L, x == "b" ~ 2L, TRUE ~ 3L)', #' linters = nested_ifelse_linter() #' ) #' #' lint( #' text = 'data.table::fcase(x == "a", 1L, x == "b", 2L, default = 3L)', #' linters = nested_ifelse_linter() #' ) #' #' @evalRd rd_tags("nested_ifelse_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export nested_ifelse_linter <- function() { # NB: land on the nested (inner) call, not the outer call, and throw a lint with the inner call's name xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)}] /parent::expr /following-sibling::expr[expr[1][SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)} ]]] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_call <- xp_call_name(bad_expr) lint_message <- paste( sprintf("Don't use nested %s() calls;", matched_call), "instead, try (1) data.table::fcase; (2) dplyr::case_when; or (3) using a lookup table." ) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/implicit_integer_linter.R0000644000176200001440000000475614577052532016660 0ustar liggesusers#' Implicit integer linter #' #' Check that integers are explicitly typed using the form `1L` instead of `1`. #' #' @param allow_colon Logical, default `FALSE`. If `TRUE`, expressions involving `:` #' won't throw a lint regardless of whether the inputs are implicitly integers. #' @examples #' # will produce lints #' lint( #' text = "x <- 1", #' linters = implicit_integer_linter() #' ) #' #' lint( #' text = "x[2]", #' linters = implicit_integer_linter() #' ) #' #' lint( #' text = "1:10", #' linters = implicit_integer_linter() #' ) #' #' # okay #' lint( #' text = "x <- 1.0", #' linters = implicit_integer_linter() #' ) #' #' lint( #' text = "x <- 1L", #' linters = implicit_integer_linter() #' ) #' #' lint( #' text = "x[2L]", #' linters = implicit_integer_linter() #' ) #' #' lint( #' text = "1:10", #' linters = implicit_integer_linter(allow_colon = TRUE) #' ) #' #' @evalRd rd_tags("implicit_integer_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export implicit_integer_linter <- function(allow_colon = FALSE) { if (allow_colon) { xpath <- "//NUM_CONST[not(parent::expr/parent::expr/OP-COLON)]" } else { xpath <- "//NUM_CONST" } Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content numbers <- xml_find_all(xml, xpath) xml_nodes_to_lints( numbers[is_implicit_integer(xml_text(numbers))], source_expression = source_expression, lint_message = "Integers should not be implicit. Use the form 1L for integers or 1.0 for doubles.", type = "style", column_number_xpath = "number(./@col2 + 1)", # mark at end range_end_xpath = "number(./@col2 + 1)" # end after number for easy fixing (enter "L" or ".0") ) }) } is_implicit_integer <- function(s) { # styler: off is_implicit <- !re_matches(s, rex(or( group(start, upper), # Inf, NaN and logicals (TRUE, FALSE, NA, NA_*) group(start, "0x"), # hexadecimals, e.g. 0x0f, 0x1p+0 or 0x1.ec6666666p+6 dot, # doubles with a decimal point, e.g. 1.0 or 0.945 group("i", end), # complex type, e.g. 1+3i group("L", end) # integer type, e.g. 62L or 1e3L ))) # styler: on # only keep numbers that are within the range representable by R, and that are whole n <- as.double(s[is_implicit]) is_implicit[is_implicit] <- abs(n) <= .Machine[["integer.max"]] & floor(n) == n is_implicit } lintr/R/length_test_linter.R0000644000176200001440000000301414577052532015633 0ustar liggesusers#' Check for a common mistake where length is applied in the wrong place #' #' Usage like `length(x == 0)` is a mistake. If you intended to check `x` is empty, #' use `length(x) == 0`. Other mistakes are possible, but running `length()` on the #' outcome of a logical comparison is never the best choice. #' #' @examples #' # will produce lints #' lint( #' text = "length(x == 0)", #' linters = length_test_linter() #' ) #' #' # okay #' lint( #' text = "length(x) > 0", #' linters = length_test_linter() #' ) #' @evalRd rd_tags("class_equals_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export length_test_linter <- function() { xpath <- glue::glue(" //SYMBOL_FUNCTION_CALL[text() = 'length'] /parent::expr /following-sibling::expr[{ xp_or(infix_metadata$xml_tag[infix_metadata$comparator]) }] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) expr_parts <- vapply(lapply(bad_expr, xml_find_all, "expr[2]/*"), xml_text, character(3L)) lint_message <- sprintf( "Checking the length of a logical vector is likely a mistake. Did you mean `length(%s) %s %s`?", expr_parts[1L, ], expr_parts[2L, ], expr_parts[3L, ] ) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) }) } lintr/R/scalar_in_linter.R0000644000176200001440000000257714577052532015263 0ustar liggesusers#' Block usage like x %in% "a" #' #' `vector %in% set` is appropriate for matching a vector to a set, but if #' that set has size 1, `==` is more appropriate. `%chin%` from `{data.table}` #' is matched as well. #' #' `scalar %in% vector` is OK, because the alternative (`any(vector == scalar)`) #' is more circuitous & potentially less clear. #' #' @evalRd rd_tags("scalar_in_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export scalar_in_linter <- function() { # TODO(#2085): Extend to include other cases where the RHS is clearly a scalar # NB: all of logical, integer, double, hex, complex are parsed as NUM_CONST xpath <- " //SPECIAL[text() = '%in%' or text() = '%chin%'] /following-sibling::expr[NUM_CONST[not(starts-with(text(), 'NA'))] or STR_CONST] /parent::expr " return(Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) in_op <- xml_find_chr(bad_expr, "string(SPECIAL)") lint_msg <- paste0("Use == to match length-1 scalars, not ", in_op, ". Note that == preserves NA where ", in_op, " does not.") xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_msg, type = "warning" ) })) } lintr/R/ids_with_token.R0000644000176200001440000000314614577052532014756 0ustar liggesusers#' Get parsed IDs by token #' #' Gets the source IDs (row indices) corresponding to given token. #' #' @param source_expression A list of source expressions, the result of a call to [get_source_expressions()], #' for the desired filename. #' @param value Character. String corresponding to the token to search for. #' For example: #' #' * "SYMBOL" #' * "FUNCTION" #' * "EQ_FORMALS" #' * "$" #' * "(" #' #' @param fun For additional flexibility, a function to search for in #' the `token` column of `parsed_content`. Typically `==` or `%in%`. #' @param source_file (DEPRECATED) Same as `source_expression`. Will be removed. #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) #' source_exprs <- get_source_expressions(tmp) #' ids_with_token(source_exprs$expressions[[1L]], value = "SYMBOL") #' with_id(source_exprs$expressions[[1L]], 2L) #' #' @return `ids_with_token`: The indices of the `parsed_content` data frame #' entry of the list of source expressions. Indices correspond to the #' *rows* where `fun` evaluates to `TRUE` for the `value` in the *token* column. #' @export ids_with_token <- function(source_expression, value, fun = `==`, source_file = NULL) { if (!missing(source_file)) { lintr_deprecated(old = "source_file", new = "source_expression", version = "3.0.0", type = "Argument") source_expression <- source_file } if (!is_lint_level(source_expression, "expression")) { return(integer()) } loc <- which(fun(source_expression$parsed_content$token, value)) if (loc %==% integer()) { return(integer()) } loc } lintr/R/pipe_consistency_linter.R0000644000176200001440000000463114577052532016677 0ustar liggesusers#' Pipe consistency linter #' #' Check that pipe operators are used consistently by file, or optionally #' specify one valid pipe operator. #' #' @param pipe Which pipe operator is valid (either `"%>%"` or `"|>"`). By default #' (`"auto"`), the linter has no preference but will check that each file uses #' only one type of pipe operator. #' #' @examples #' # will produce lints #' lint( #' text = "1:3 |> mean() %>% as.character()", #' linters = pipe_consistency_linter() #' ) #' #' lint( #' text = "1:3 %>% mean() %>% as.character()", #' linters = pipe_consistency_linter("|>") #' ) #' #' # okay #' lint( #' text = "1:3 %>% mean() %>% as.character()", #' linters = pipe_consistency_linter() #' ) #' #' lint( #' text = "1:3 |> mean() |> as.character()", #' linters = pipe_consistency_linter() #' ) #' @evalRd rd_tags("pipe_consistency_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export pipe_consistency_linter <- function(pipe = c("auto", "%>%", "|>")) { pipe <- match.arg(pipe) xpath_magrittr <- glue("//SPECIAL[{ xp_text_in_table(magrittr_pipes) }]") xpath_native <- "//PIPE" Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content match_magrittr <- xml_find_all(xml, xpath_magrittr) match_native <- xml_find_all(xml, xpath_native) n_magrittr <- length(match_magrittr) n_native <- length(match_native) if (pipe == "auto" && n_magrittr > 0L && n_native > 0L) { xml_nodes_to_lints( xml = c(match_magrittr, match_native), source_expression = source_expression, lint_message = glue( "Found {n_magrittr} instances of %>% and {n_native} instances of |>. ", "Stick to one pipe operator." ), type = "style" ) } else if (pipe == "%>%" && n_native > 0L) { xml_nodes_to_lints( xml = match_native, source_expression = source_expression, lint_message = "Use the %>% pipe operator instead of the |> pipe operator.", type = "style" ) } else if (pipe == "|>" && n_magrittr > 0L) { xml_nodes_to_lints( xml = match_magrittr, source_expression = source_expression, lint_message = "Use the |> pipe operator instead of the %>% pipe operator.", type = "style" ) } else { list() } }) } lintr/R/system_file_linter.R0000644000176200001440000000347514577052532015651 0ustar liggesusers#' Block usage of `file.path()` with `system.file()` #' #' [system.file()] has a `...` argument which, internally, is passed to #' [file.path()], so including it in user code is repetitive. #' #' @examples #' # will produce lints #' lint( #' text = 'system.file(file.path("path", "to", "data"), package = "foo")', #' linters = system_file_linter() #' ) #' #' lint( #' text = 'file.path(system.file(package = "foo"), "path", "to", "data")', #' linters = system_file_linter() #' ) #' #' # okay #' lint( #' text = 'system.file("path", "to", "data", package = "foo")', #' linters = system_file_linter() #' ) #' #' @evalRd rd_tags("system_file_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export system_file_linter <- function() { funs <- c("system.file", "file.path") # either system.file(file.path(...)) or file.path(system.file(...)) xpath_parts <- glue(" //SYMBOL_FUNCTION_CALL[text() = '{funs}'] /parent::expr[following-sibling::expr/expr/SYMBOL_FUNCTION_CALL[text() = '{rev(funs)}']] /parent::expr ") xpath <- paste(xpath_parts, collapse = " | ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) outer_call <- xp_call_name(bad_expr) lint_message <- paste( "Use the `...` argument of system.file() to expand paths,", 'e.g. system.file("data", "model.csv", package = "myrf") instead of', ifelse( outer_call == "system.file", 'system.file(file.path("data", "model.csv"), package = "myrf")', 'file.path(system.file(package = "myrf"), "data", "model.csv")' ) ) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/sprintf_linter.R0000644000176200001440000000761614577052532015014 0ustar liggesusers#' Require correct `sprintf()` calls #' #' Check for an inconsistent number of arguments or arguments with incompatible types (for literal arguments) in #' [sprintf()] calls. #' #' [gettextf()] calls are also included, since `gettextf()` is a thin wrapper around `sprintf()`. #' #' @examples #' # will produce lints #' lint( #' text = 'sprintf("hello %s %s %d", x, y)', #' linters = sprintf_linter() #' ) #' #' # okay #' lint( #' text = 'sprintf("hello %s %s %d", x, y, z)', #' linters = sprintf_linter() #' ) #' #' lint( #' text = 'sprintf("hello %s %s %d", x, y, ...)', #' linters = sprintf_linter() #' ) #' #' @evalRd rd_tags("sprintf_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export sprintf_linter <- function() { call_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'sprintf' or text() = 'gettextf'] /parent::expr /parent::expr[ ( OP-LEFT-PAREN/following-sibling::expr[1]/STR_CONST or SYMBOL_SUB[text() = 'fmt']/following-sibling::expr[1]/STR_CONST ) and not(expr/SYMBOL[text() = '...']) ] " pipes <- setdiff(magrittr_pipes, "%$%") in_pipe_xpath <- glue("self::expr[ preceding-sibling::*[1][self::PIPE or self::SPECIAL[{ xp_text_in_table(pipes) }]] and ( preceding-sibling::*[2]/STR_CONST or SYMBOL_SUB[text() = 'fmt']/following-sibling::expr[1]/STR_CONST ) ]") is_missing <- function(x) is.symbol(x) && !nzchar(x) # Zap sprintf() call to contain only constants # # Set all extra arguments to 0L if they aren't a constant # # @param parsed_expr A parsed `sprintf()` call # # @return A `sprintf()` call with all non-constants replaced by `0L` # (which is compatible with all sprintf format specifiers) zap_extra_args <- function(parsed_expr) { if ("fmt" %in% names(parsed_expr)) { fmt_loc <- which(names(parsed_expr) == "fmt") } else { fmt_loc <- 2L } if (length(parsed_expr) >= 3L) { for (i in setdiff(seq_along(parsed_expr), c(1L, fmt_loc))) { if (!is_missing(parsed_expr[[i]]) && !is.atomic(parsed_expr[[i]])) { parsed_expr[[i]] <- 0L } } } parsed_expr } # Anticipate warnings of a sprintf() call # # Try running a static sprintf() call to determine whether it will produce warnings or errors due to format # misspecification # # @param xml An XML node representing a `sprintf()` call (i.e. the `` node containing the call) # # @return A string, either `NA_character_` or the text of generated errors and warnings from the `sprintf()` call when # replacing all dynamic components by 0, which is compatible with all format specifiers. capture_sprintf_warning <- function(xml) { parsed_expr <- xml2lang(xml) # convert x %>% sprintf(...) to sprintf(x, ...) if (length(xml_find_first(xml, in_pipe_xpath)) > 0L) { arg_names <- names(parsed_expr) arg_idx <- 2L:length(parsed_expr) parsed_expr[arg_idx + 1L] <- parsed_expr[arg_idx] names(parsed_expr)[arg_idx + 1L] <- arg_names[arg_idx] parsed_expr[[2L]] <- xml2lang(xml_find_first(xml, "preceding-sibling::*[2]")) names(parsed_expr)[2L] <- "" } parsed_expr <- zap_extra_args(parsed_expr) res <- tryCatch(eval(parsed_expr, envir = baseenv()), warning = identity, error = identity) if (inherits(res, "condition")) { conditionMessage(res) } else { NA_character_ } } Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content sprintf_calls <- xml_find_all(xml, call_xpath) message <- vapply(sprintf_calls, capture_sprintf_warning, character(1L)) has_message <- !is.na(message) xml_nodes_to_lints( sprintf_calls[has_message], source_expression = source_expression, lint_message = message[has_message], type = "warning" ) }) } lintr/R/redundant_ifelse_linter.R0000644000176200001440000000735414577052532016641 0ustar liggesusers#' Prevent `ifelse()` from being used to produce `TRUE`/`FALSE` or `1`/`0` #' #' Expressions like `ifelse(x, TRUE, FALSE)` and `ifelse(x, FALSE, TRUE)` are #' redundant; just `x` or `!x` suffice in R code where logical vectors are a #' core data structure. `ifelse(x, 1, 0)` is also `as.numeric(x)`, but even #' this should be needed only rarely. #' #' @evalRd rd_tags("redundant_ifelse_linter") #' @param allow10 Logical, default `FALSE`. If `TRUE`, usage like #' `ifelse(x, 1, 0)` is allowed, i.e., only usage like #' `ifelse(x, TRUE, FALSE)` is linted. #' #' @examples #' # will produce lints #' lint( #' text = "ifelse(x >= 2.5, TRUE, FALSE)", #' linters = redundant_ifelse_linter() #' ) #' #' lint( #' text = "ifelse(x < 2.5, 1L, 0L)", #' linters = redundant_ifelse_linter() #' ) #' #' # okay #' lint( #' text = "x >= 2.5", #' linters = redundant_ifelse_linter() #' ) #' #' # Note that this is just to show the strict equivalent of the example above; #' # converting to integer is often unnecessary and the logical vector itself #' # should suffice. #' lint( #' text = "as.integer(x < 2.5)", #' linters = redundant_ifelse_linter() #' ) #' #' lint( #' text = "ifelse(x < 2.5, 1L, 0L)", #' linters = redundant_ifelse_linter(allow10 = TRUE) #' ) #' #' @seealso [linters] for a complete list of linters available in lintr. #' @export redundant_ifelse_linter <- function(allow10 = FALSE) { tf_xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)} ] /parent::expr /parent::expr[ expr[position() <= 4 and NUM_CONST[text() = 'TRUE']] and expr[position() <= 4 and NUM_CONST[text() = 'FALSE']] and ( count(expr) = 4 or expr[5]/NUM_CONST[text() = 'NA'] ) ] ") num_xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(ifelse_funs)} ] /parent::expr /parent::expr[ expr[position() <= 4 and NUM_CONST[text() = '1' or text() = '1L']] and expr[position() <= 4 and NUM_CONST[text() = '0' or text() = '0L']] and ( count(expr) = 4 or expr[5]/NUM_CONST[text() = 'NA' or text() = 'NA_integer_' or text() = 'NA_real_'] ) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content lints <- list() tf_expr <- xml_find_all(xml, tf_xpath) matched_call <- xp_call_name(tf_expr) # [1] call; [2] logical condition first_arg <- xml_find_chr(tf_expr, "string(expr[3]/NUM_CONST)") second_arg <- xml_find_chr(tf_expr, "string(expr[4]/NUM_CONST)") tf_message <- sprintf( "Just use the logical condition (or its negation) directly instead of calling %s(x, %s, %s)", matched_call, first_arg, second_arg ) lints <- c(lints, xml_nodes_to_lints(tf_expr, source_expression, tf_message, type = "warning")) if (!allow10) { num_expr <- xml_find_all(xml, num_xpath) matched_call <- xp_call_name(num_expr) # [1] call; [2] logical condition first_arg <- xml_find_chr(num_expr, "string(expr[3]/NUM_CONST)") second_arg <- xml_find_chr(num_expr, "string(expr[4]/NUM_CONST)") is_numeric_01 <- first_arg %in% c("0", "1") | second_arg %in% c("0", "1") coercion_function <- ifelse(is_numeric_01, "as.numeric", "as.integer") is_negated <- first_arg %in% c("0", "0L") replacement_argument <- ifelse(is_negated, "!x", "x") lint_message <- paste( sprintf( "Prefer %s(%s) to %s(x, %s, %s) if really needed.", coercion_function, replacement_argument, matched_call, first_arg, second_arg ) ) lints <- c(lints, xml_nodes_to_lints(num_expr, source_expression, lint_message, type = "warning")) } return(lints) }) } lintr/R/class_equals_linter.R0000644000176200001440000000343514577052532016001 0ustar liggesusers#' Block comparison of class with `==` #' #' Usage like `class(x) == "character"` is prone to error since class in R #' is in general a vector. The correct version for S3 classes is [inherits()]: #' `inherits(x, "character")`. Often, class `k` will have an `is.` equivalent, #' for example [is.character()] or [is.data.frame()]. #' #' Similar reasoning applies for `class(x) %in% "character"`. #' #' @examples #' # will produce lints #' lint( #' text = 'is_lm <- class(x) == "lm"', #' linters = class_equals_linter() #' ) #' #' lint( #' text = 'if ("lm" %in% class(x)) is_lm <- TRUE', #' linters = class_equals_linter() #' ) #' #' # okay #' lint( #' text = 'is_lm <- inherits(x, "lm")', #' linters = class_equals_linter() #' ) #' #' lint( #' text = 'if (inherits(x, "lm")) is_lm <- TRUE', #' linters = class_equals_linter() #' ) #' #' @evalRd rd_tags("class_equals_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export class_equals_linter <- function() { xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'class'] /parent::expr /parent::expr /parent::expr[ not(preceding-sibling::OP-LEFT-BRACKET) and (EQ or NE or SPECIAL[text() = '%in%']) ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) operator <- xml_find_chr(bad_expr, "string(*[2])") lint_message <- sprintf( "Instead of comparing class(x) with %s, use inherits(x, 'class-name') or is. or is(x, 'class')", operator ) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) }) } lintr/R/any_duplicated_linter.R0000644000176200001440000000745714577052532016317 0ustar liggesusers#' Require usage of `anyDuplicated(x) > 0` over `any(duplicated(x))` #' #' [anyDuplicated()] exists as a replacement for `any(duplicated(.))`, which is #' more efficient for simple objects, and is at worst equally efficient. #' Therefore, it should be used in all situations instead of the latter. #' #' Also match usage like `length(unique(x$col)) == nrow(x)`, which can #' be replaced by `anyDuplicated(x$col) == 0L`. #' #' @examples #' # will produce lints #' lint( #' text = "any(duplicated(x), na.rm = TRUE)", #' linters = any_duplicated_linter() #' ) #' #' lint( #' text = "length(unique(x)) == length(x)", #' linters = any_duplicated_linter() #' ) #' #' # okay #' lint( #' text = "anyDuplicated(x)", #' linters = any_duplicated_linter() #' ) #' #' lint( #' text = "anyDuplicated(x) == 0L", #' linters = any_duplicated_linter() #' ) #' #' @evalRd rd_tags("any_duplicated_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export any_duplicated_linter <- function() { any_duplicated_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'any'] /parent::expr /following-sibling::expr[1][expr[1][SYMBOL_FUNCTION_CALL[text() = 'duplicated']]] /parent::expr[ count(expr) = 2 or (count(expr) = 3 and SYMBOL_SUB[text() = 'na.rm']) ] " # outline: # EQ/NE/GT/LT: ensure we're in a comparison clause # check for length(unique()) matching: # - length(unique( _ )) == length( _ ) # - length(unique( << _$col or _[["col"]] >> )) == nrow( _ ) # NB: parent::expr/.../following-sibling::expr is the path from # the expr of the unique() call to the call that needs to match. # the final parent::expr/expr gets us to the expr on the other side of EQ; # this lets us match on either side of EQ, where following-sibling # assumes we are before EQ, preceding-sibling assumes we are after EQ. length_unique_xpath_parts <- glue(" //{ c('EQ', 'NE', 'GT', 'LT') } /parent::expr /expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'length']] and expr[expr[1][ SYMBOL_FUNCTION_CALL[text() = 'unique'] and ( following-sibling::expr = parent::expr /parent::expr /parent::expr /expr /expr[1][SYMBOL_FUNCTION_CALL[text()= 'length']] /following-sibling::expr or following-sibling::expr[OP-DOLLAR or LBB]/expr[1] = parent::expr /parent::expr /parent::expr /expr /expr[1][SYMBOL_FUNCTION_CALL[text()= 'nrow']] /following-sibling::expr ) ]] ] ") length_unique_xpath <- paste(length_unique_xpath_parts, collapse = " | ") uses_nrow_xpath <- "./parent::expr/expr/expr[1]/SYMBOL_FUNCTION_CALL[text() = 'nrow']" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content any_duplicated_expr <- xml_find_all(xml, any_duplicated_xpath) any_duplicated_lints <- xml_nodes_to_lints( any_duplicated_expr, source_expression = source_expression, lint_message = "anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...).", type = "warning" ) length_unique_expr <- xml_find_all(xml, length_unique_xpath) lint_message <- ifelse( is.na(xml_find_first(length_unique_expr, uses_nrow_xpath)), "anyDuplicated(x) == 0L is better than length(unique(x)) == length(x).", "anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF)" ) length_unique_lints <- xml_nodes_to_lints( length_unique_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) return(c(any_duplicated_lints, length_unique_lints)) }) } lintr/R/infix_spaces_linter.R0000644000176200001440000000765014577052532016000 0ustar liggesusers#' Infix spaces linter #' #' Check that infix operators are surrounded by spaces. Enforces the corresponding Tidyverse style guide rule; #' see . #' #' @param exclude_operators Character vector of operators to exclude from consideration for linting. #' Default is to include the following "low-precedence" operators: #' `+`, `-`, `~`, `>`, `>=`, `<`, `<=`, `==`, `!=`, `&`, `&&`, `|`, `||`, `<-`, `:=`, `<<-`, `->`, `->>`, #' `=`, `/`, `*`, and any infix operator (exclude infixes by passing `"%%"`). Note that `"="` here includes #' three different operators, from the parser's point of view. To lint only some of these, pass the #' corresponding parse tags (i.e., some of `"EQ_ASSIGN"`, `"EQ_SUB"`, and `"EQ_FORMALS"`; see #' [utils::getParseData()]). #' @param allow_multiple_spaces Logical, default `TRUE`. If `FALSE`, usage like `x = 2` will also be linted; #' excluded by default because such usage can sometimes be used for better code alignment, as is allowed #' by the style guide. #' #' @examples #' # will produce lints #' lint( #' text = "x<-1L", #' linters = infix_spaces_linter() #' ) #' #' lint( #' text = "1:4 %>%sum()", #' linters = infix_spaces_linter() #' ) #' #' # okay #' lint( #' text = "x <- 1L", #' linters = infix_spaces_linter() #' ) #' #' lint( #' text = "1:4 %>% sum()", #' linters = infix_spaces_linter() #' ) #' #' code_lines <- " #' ab <- 1L #' abcdef <- 2L #' " #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = infix_spaces_linter(allow_multiple_spaces = TRUE) #' ) #' #' lint( #' text = "a||b", #' linters = infix_spaces_linter(exclude_operators = "||") #' ) #' #' lint( #' text = "sum(1:10, na.rm=TRUE)", #' linters = infix_spaces_linter(exclude_operators = "EQ_SUB") #' ) #' #' @evalRd rd_tags("infix_spaces_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export infix_spaces_linter <- function(exclude_operators = NULL, allow_multiple_spaces = TRUE) { if (allow_multiple_spaces) { op <- "<" lint_message <- "Put spaces around all infix operators." } else { op <- "!=" lint_message <- "Put exactly one space on each side of infix operators." } infix_tokens <- infix_metadata$xml_tag_exact[ infix_metadata$low_precedence & !infix_metadata$string_value %in% exclude_operators & # parse_tag, not xml_tag, since the former is easier for the user to discover with getParseData() !infix_metadata$parse_tag %in% exclude_operators ] # NB: preceding-sibling::* and not preceding-sibling::expr because # of the foo(a=1) case, where the tree is # NB: parent::*[count(expr | SYMBOL_SUB)) > 1] for the unary case, e.g. x[-1] # SYMBOL_SUB for case with missing argument like alist(a =) # NB: the last not() disables lints inside box::use() declarations global_xpath <- paste0("//", infix_tokens, collapse = "|") xpath <- glue("({global_xpath})[ parent::*[count(expr | SYMBOL_SUB) > 1] and ( ( @line1 = preceding-sibling::*[1]/@line2 and @start {op} preceding-sibling::*[1]/@end + 2 ) or ( @line1 = following-sibling::*[1]/@line1 and following-sibling::*[1]/@start {op} @end + 2 ) ) and not( self::OP-SLASH[ ancestor::expr/preceding-sibling::OP-LEFT-PAREN/preceding-sibling::expr[ ./SYMBOL_PACKAGE[text() = 'box'] and ./SYMBOL_FUNCTION_CALL[text() = 'use'] ] ] ) ]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_message, type = "style" ) }) } lintr/R/routine_registration_linter.R0000644000176200001440000000234314577052532017576 0ustar liggesusers#' Identify unregistered native routines #' #' It is preferable to register routines for efficiency and safety. #' #' @examples #' # will produce lints #' lint( #' text = '.Call("cpp_routine", PACKAGE = "mypkg")', #' linters = routine_registration_linter() #' ) #' #' lint( #' text = '.Fortran("f_routine", PACKAGE = "mypkg")', #' linters = routine_registration_linter() #' ) #' #' # okay #' lint( #' text = ".Call(cpp_routine)", #' linters = routine_registration_linter() #' ) #' #' lint( #' text = ".Fortran(f_routine)", #' linters = routine_registration_linter() #' ) #' #' @evalRd rd_tags("routine_registration_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' #' @export routine_registration_linter <- local({ native_routine_callers <- c(".C", ".Call", ".Fortran", ".External") make_linter_from_xpath( xpath = glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(native_routine_callers)} ] /parent::expr /following-sibling::expr[1]/STR_CONST /parent::expr "), lint_message = "Register your native code routines with useDynLib and R_registerRoutines()." ) }) lintr/R/conjunct_test_linter.R0000644000176200001440000001304214577052532016177 0ustar liggesusers#' Force `&&` conditions to be written separately where appropriate #' #' For readability of test outputs, testing only one thing per call to #' [testthat::expect_true()] is preferable, i.e., #' `expect_true(A); expect_true(B)` is better than `expect_true(A && B)`, and #' `expect_false(A); expect_false(B)` is better than `expect_false(A || B)`. #' #' Similar reasoning applies to `&&` usage inside [stopifnot()] and `assertthat::assert_that()` calls. #' #' Relatedly, `dplyr::filter(DF, A & B)` is the same as `dplyr::filter(DF, A, B)`, but the latter will be more readable #' / easier to format for long conditions. Note that this linter assumes usages of `filter()` are `dplyr::filter()`; #' if you're using another function named `filter()`, e.g. [stats::filter()], please namespace-qualify it to avoid #' false positives. You can omit linting `filter()` expressions altogether via `allow_filter = TRUE`. #' #' @param allow_named_stopifnot Logical, `TRUE` by default. If `FALSE`, "named" calls to `stopifnot()`, #' available since R 4.0.0 to provide helpful messages for test failures, are also linted. #' @param allow_filter Character naming the method for linting calls to `filter()`. The default, `"never"`, means #' `filter()` and `dplyr::filter()` calls are linted; `"not_dplyr"` means only `dplyr::filter()` calls are linted; #' and `"always"` means no calls to `filter()` are linted. Calls like `stats::filter()` are never linted. #' #' @examples #' # will produce lints #' lint( #' text = "expect_true(x && y)", #' linters = conjunct_test_linter() #' ) #' #' lint( #' text = "expect_false(x || (y && z))", #' linters = conjunct_test_linter() #' ) #' #' lint( #' text = "stopifnot('x must be a logical scalar' = length(x) == 1 && is.logical(x) && !is.na(x))", #' linters = conjunct_test_linter(allow_named_stopifnot = FALSE) #' ) #' #' lint( #' text = "dplyr::filter(mtcars, mpg > 20 & vs == 0)", #' linters = conjunct_test_linter() #' ) #' #' lint( #' text = "filter(mtcars, mpg > 20 & vs == 0)", #' linters = conjunct_test_linter() #' ) #' #' # okay #' lint( #' text = "expect_true(x || (y && z))", #' linters = conjunct_test_linter() #' ) #' #' lint( #' text = 'stopifnot("x must be a logical scalar" = length(x) == 1 && is.logical(x) && !is.na(x))', #' linters = conjunct_test_linter(allow_named_stopifnot = TRUE) #' ) #' #' lint( #' text = "dplyr::filter(mtcars, mpg > 20 & vs == 0)", #' linters = conjunct_test_linter(allow_filter = "always") #' ) #' #' lint( #' text = "filter(mtcars, mpg > 20 & vs == 0)", #' linters = conjunct_test_linter(allow_filter = "not_dplyr") #' ) #' #' lint( #' text = "stats::filter(mtcars$cyl, mtcars$mpg > 20 & mtcars$vs == 0)", #' linters = conjunct_test_linter() #' ) #' #' @evalRd rd_tags("conjunct_test_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export conjunct_test_linter <- function(allow_named_stopifnot = TRUE, allow_filter = c("never", "not_dplyr", "always")) { allow_filter <- match.arg(allow_filter) expect_true_assert_that_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_true' or text() = 'assert_that'] /parent::expr /following-sibling::expr[1][AND2] /parent::expr " named_stopifnot_condition <- if (allow_named_stopifnot) "and not(preceding-sibling::*[1][self::EQ_SUB])" else "" stopifnot_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'stopifnot'] /parent::expr /following-sibling::expr[1][AND2 {named_stopifnot_condition}] /parent::expr ") expect_false_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_false'] /parent::expr /following-sibling::expr[1][OR2] /parent::expr " test_xpath <- paste( expect_true_assert_that_xpath, stopifnot_xpath, expect_false_xpath, sep = " | " ) filter_ns_cond <- switch(allow_filter, never = "not(SYMBOL_PACKAGE[text() != 'dplyr'])", not_dplyr = "SYMBOL_PACKAGE[text() = 'dplyr']", always = "true" ) filter_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'filter'] /parent::expr[{ filter_ns_cond }] /parent::expr /expr[AND] ") Linter(function(source_expression) { # need the full file to also catch usages at the top level if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content test_expr <- xml_find_all(xml, test_xpath) matched_fun <- xp_call_name(test_expr) operator <- xml_find_chr(test_expr, "string(expr/*[self::AND2 or self::OR2])") replacement_fmt <- ifelse( matched_fun %in% c("expect_true", "expect_false"), "write multiple expectations like %1$s(A) and %1$s(B)", "write multiple conditions like %s(A, B)." ) lint_message <- paste( sprintf("Instead of %s(A %s B),", matched_fun, operator), # as.character() needed for 0-lint case where ifelse(logical(0)) returns logical(0) sprintf(as.character(replacement_fmt), matched_fun), "The latter will produce better error messages in the case of failure." ) lints <- xml_nodes_to_lints( test_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) if (allow_filter != "always") { filter_expr <- xml_find_all(xml, filter_xpath) filter_lints <- xml_nodes_to_lints( filter_expr, source_expression = source_expression, lint_message = "Use dplyr::filter(DF, A, B) instead of dplyr::filter(DF, A & B).", type = "warning" ) lints <- c(lints, filter_lints) } lints }) } lintr/R/use_lintr.R0000644000176200001440000000324414577052532013747 0ustar liggesusers#' Use lintr in your project #' #' Create a minimal lintr config file as a starting point for customization #' #' @param path Path to project root, where a `.lintr` file should be created. #' If the `.lintr` file already exists, an error will be thrown. #' @param type What kind of configuration to create? #' #' * `tidyverse` creates a minimal lintr config, based on the default linters ([linters_with_defaults()]). #' These are suitable for following [the tidyverse style guide](https://style.tidyverse.org/). #' * `full` creates a lintr config using all available linters via [linters_with_tags()]. #' #' @return Path to the generated configuration, invisibly. #' #' @export #' @seealso `vignette("lintr")` for detailed introduction to using and configuring lintr. #' @examples #' if (FALSE) { #' # use the default set of linters #' lintr::use_lintr() #' # or try all linters #' lintr::use_lintr(type = "full") #' #' # then #' lintr::lint_dir() #' } use_lintr <- function(path = ".", type = c("tidyverse", "full")) { config_file <- normalizePath(file.path(path, lintr_option("linter_file")), mustWork = FALSE) if (file.exists(config_file)) { stop("Found an existing configuration file at '", config_file, "'.") } type <- match.arg(type) the_config <- switch( type, tidyverse = list( linters = 'linters_with_defaults() # see vignette("lintr")', encoding = '"UTF-8"' ), full = list( linters = 'all_linters(packages = "lintr") # see vignette("lintr")', encoding = '"UTF-8"', exclusions = 'list("renv", "packrat") # see ?lintr::exclude' ) ) write.dcf(the_config, config_file, width = Inf) invisible(config_file) } lintr/R/expect_length_linter.R0000644000176200001440000000305614577052532016152 0ustar liggesusers#' Require usage of `expect_length(x, n)` over `expect_equal(length(x), n)` #' #' [testthat::expect_length()] exists specifically for testing the [length()] of #' an object. [testthat::expect_equal()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = "expect_equal(length(x), 2L)", #' linters = expect_length_linter() #' ) #' #' # okay #' lint( #' text = "expect_length(x, 2L)", #' linters = expect_length_linter() #' ) #' #' @evalRd rd_tags("expect_length_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_length_linter <- function() { # TODO(michaelchirico): also catch expect_true(length(x) == 1) xpath <- sprintf(" //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'length']] and (position() = 1 or preceding-sibling::expr[NUM_CONST]) ] /parent::expr[not(SYMBOL_SUB[text() = 'info' or contains(text(), 'label')])] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_function <- xp_call_name(bad_expr) lint_message <- sprintf("expect_length(x, n) is better than %s(length(x), n)", matched_function) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/missing_package_linter.R0000644000176200001440000000372014577052532016443 0ustar liggesusers#' Missing package linter #' #' Check for missing packages in `library()`, `require()`, `loadNamespace()`, and `requireNamespace()` calls. #' #' @examples #' # will produce lints #' lint( #' text = "library(xyzxyz)", #' linters = missing_package_linter() #' ) #' #' # okay #' lint( #' text = "library(stats)", #' linters = missing_package_linter() #' ) #' #' @evalRd rd_tags("missing_package_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export missing_package_linter <- function() { library_require_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'library' or text() = 'require'] /parent::expr /parent::expr[ expr[2][STR_CONST] or ( expr[2][SYMBOL] and not( SYMBOL_SUB[text() = 'character.only'] /following-sibling::expr[1] /NUM_CONST[text() = 'TRUE' or text() = 'T'] ) ) ] " load_require_namespace_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'loadNamespace' or text() = 'requireNamespace'] /parent::expr /following-sibling::expr[1][STR_CONST] /parent::expr " call_xpath <- paste(library_require_xpath, "|", load_require_namespace_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content pkg_calls <- xml_find_all(xml, call_xpath) pkg_names <- get_r_string(xml_find_all( pkg_calls, "OP-LEFT-PAREN[1]/following-sibling::expr[1][SYMBOL | STR_CONST]" )) # run here, not in the factory, to allow for run- vs. "compile"-time differences in available packages installed_packges <- .packages(all.available = TRUE) missing_pkgs <- !(pkg_names %in% installed_packges) xml_nodes_to_lints( pkg_calls[missing_pkgs], source_expression = source_expression, lint_message = sprintf("Package '%s' is not installed.", pkg_names[missing_pkgs]), type = "warning" ) }) } lintr/R/make_linter_from_xpath.R0000644000176200001440000000242514577052532016464 0ustar liggesusers#' Create a linter from an XPath #' #' @inheritParams xml_nodes_to_lints #' @inheritParams is_lint_level #' @param xpath Character string, an XPath identifying R code to lint. #' See [xmlparsedata::xml_parse_data()] and [get_source_expressions()]. #' #' @examples #' number_linter <- make_linter_from_xpath("//NUM_CONST", "This is a number.") #' lint(text = "1 + 2", linters = number_linter()) #' @export make_linter_from_xpath <- function(xpath, lint_message, type = c("warning", "style", "error"), level = c("expression", "file")) { type <- match.arg(type) level <- match.arg(level) stopifnot( "xpath should be a character string" = is.character(xpath) && length(xpath) == 1L && !is.na(xpath) ) xml_key <- if (level == "expression") "xml_parsed_content" else "full_xml_parsed_content" function() { Linter(function(source_expression) { if (!is_lint_level(source_expression, level)) { return(list()) } xml <- source_expression[[xml_key]] expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( expr, source_expression = source_expression, lint_message = lint_message, type = type ) }) } } lintr/R/object_length_linter.R0000644000176200001440000000500714577052532016126 0ustar liggesusers#' Object length linter #' #' Check that object names are not too long. #' The length of an object name is defined as the length in characters, after removing extraneous parts: #' #' * generic prefixes for implementations of S3 generics, e.g. `as.data.frame.my_class` has length 8. #' * leading `.`, e.g. `.my_hidden_function` has length 18. #' * "%%" for infix operators, e.g. `%my_op%` has length 5. #' * trailing `<-` for assignment functions, e.g. `my_attr<-` has length 7. #' #' Note that this behavior relies in part on having packages in your Imports available; #' see the detailed note in [object_name_linter()] for more details. #' #' @param length maximum variable name length allowed. #' #' @examples #' # will produce lints #' lint( #' text = "very_very_long_variable_name <- 1L", #' linters = object_length_linter(length = 10L) #' ) #' #' # okay #' lint( #' text = "very_very_long_variable_name <- 1L", #' linters = object_length_linter(length = 30L) #' ) #' #' lint( #' text = "var <- 1L", #' linters = object_length_linter(length = 10L) #' ) #' #' @evalRd rd_tags("object_length_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export object_length_linter <- function(length = 30L) { lint_message <- paste("Variable and function names should not be longer than", length, "characters.") Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content assignments <- xml_find_all(xml, object_name_xpath) # Retrieve assigned name nms <- strip_names( xml_text(assignments) ) # run namespace_imports at run-time, not "compile" time to allow package structure to change pkg <- find_package(source_expression$filename) ns_imports <- namespace_imports(pkg) generics <- strip_names(c( declared_s3_generics(xml), imported_s3_generics(ns_imports)$fun, exported_s3_generics(pkg)$fun, .base_s3_generics )) generics <- unique(generics[nzchar(generics)]) # Remove generic function names from generic implementations # This only lints S3 implementations if the class names are too long, still lints generics if they are too long. nms_stripped <- re_substitutes(nms, rex(start, or(generics), "."), "") too_long <- nchar(nms_stripped) > length xml_nodes_to_lints( assignments[too_long], source_expression = source_expression, lint_message = lint_message, type = "style" ) }) } lintr/R/tree_utils.R0000644000176200001440000000134614577052532014123 0ustar liggesusersgenerate_top_level_map <- function(pc) { if (is.null(pc)) { return(NULL) } tl_ids <- pc$id[pc$parent <= 0L] tl_parent <- pc$parent tl_parent[pc$parent <= 0L] <- tl_ids i_not_assigned <- which(!tl_parent %in% tl_ids) while ((prev_length <- length(i_not_assigned)) > 0L) { # nolint: implicit_assignment_linter. TODO(#2015): remove this. tl_parent[i_not_assigned] <- pc$parent[match(tl_parent[i_not_assigned], pc$id)] i_not_assigned <- which(!tl_parent %in% tl_ids) if (length(i_not_assigned) >= prev_length) { stop("Logical error: unassigned set did not shrink. Check file syntax and please report as a lintr bug.") # nocov } } tl_parent } lag <- function(x) { c(NA, x[seq_len(length(x) - 1L)]) } lintr/R/sort_linter.R0000644000176200001440000001066414577052532014313 0ustar liggesusers#' Check for common mistakes around sorting vectors #' #' This linter checks for some common mistakes when using [order()] or [sort()]. #' #' First, it requires usage of `sort()` over `.[order(.)]`. #' #' [sort()] is the dedicated option to sort a list or vector. It is more legible #' and around twice as fast as `.[order(.)]`, with the gap in performance #' growing with the vector size. #' #' Second, it requires usage of [is.unsorted()] over equivalents using `sort()`. #' #' The base function `is.unsorted()` exists to test the sortedness of a vector. #' Prefer it to inefficient and less-readable equivalents like #' `x != sort(x)`. The same goes for checking `x == sort(x)` -- use #' `!is.unsorted(x)` instead. #' #' Moreover, use of `x == sort(x)` can be risky because [sort()] drops missing #' elements by default, meaning `==` might end up trying to compare vectors #' of differing lengths. #' #' @examples #' # will produce lints #' lint( #' text = "x[order(x)]", #' linters = sort_linter() #' ) #' #' lint( #' text = "x[order(x, decreasing = TRUE)]", #' linters = sort_linter() #' ) #' #' lint( #' text = "sort(x) == x", #' linters = sort_linter() #' ) #' #' # okay #' lint( #' text = "x[sample(order(x))]", #' linters = sort_linter() #' ) #' #' lint( #' text = "y[order(x)]", #' linters = sort_linter() #' ) #' #' lint( #' text = "sort(x, decreasing = TRUE) == x", #' linters = sort_linter() #' ) #' #' # If you are sorting several objects based on the order of one of them, such #' # as: #' x <- sample(1:26) #' y <- letters #' newx <- x[order(x)] #' newy <- y[order(x)] #' # This will be flagged by the linter. However, in this very specific case, #' # it would be clearer and more efficient to run order() once and assign it #' # to an object, rather than mix and match order() and sort() #' index <- order(x) #' newx <- x[index] #' newy <- y[index] #' #' @evalRd rd_tags("sort_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export sort_linter <- function() { non_keyword_arg <- "expr[not(preceding-sibling::*[1][self::EQ_SUB])]" order_xpath <- glue(" //OP-LEFT-BRACKET /following-sibling::expr[1][ expr[1][ SYMBOL_FUNCTION_CALL[text() = 'order'] and count(following-sibling::{non_keyword_arg}) = 1 and following-sibling::{non_keyword_arg} = parent::expr[1]/parent::expr[1]/expr[1] ] ] ") sorted_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'sort'] /parent::expr /parent::expr[not(SYMBOL_SUB)] /parent::expr[ (EQ or NE) and expr/expr = expr ] " args_xpath <- ".//SYMBOL_SUB[text() = 'method' or text() = 'decreasing' or text() = 'na.last']" arg_values_xpath <- glue("{args_xpath}/following-sibling::expr[1]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content order_expr <- xml_find_all(xml, order_xpath) var <- xml_text(xml_find_first( order_expr, ".//SYMBOL_FUNCTION_CALL[text() = 'order']/parent::expr[1]/following-sibling::expr[1]" )) orig_call <- sprintf("%s[%s]", var, get_r_string(order_expr)) # Reconstruct new argument call for each expression separately args <- vapply(order_expr, function(e) { arg_names <- xml_text(xml_find_all(e, args_xpath)) arg_values <- xml_text(xml_find_all(e, arg_values_xpath)) if (!"na.last" %in% arg_names) { arg_names <- c(arg_names, "na.last") arg_values <- c(arg_values, "TRUE") } paste(arg_names, "=", arg_values, collapse = ", ") }, character(1L)) new_call <- sprintf("sort(%s, %s)", var, args) order_lints <- xml_nodes_to_lints( order_expr, source_expression = source_expression, lint_message = paste0(new_call, " is better than ", orig_call, "."), type = "warning" ) sorted_expr <- xml_find_all(xml, sorted_xpath) sorted_op <- xml_text(xml_find_first(sorted_expr, "*[2]")) lint_message <- ifelse( sorted_op == "==", "Use !is.unsorted(x) to test the sortedness of a vector.", "Use is.unsorted(x) to test the unsortedness of a vector." ) sorted_lints <- xml_nodes_to_lints( sorted_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) c(order_lints, sorted_lints) }) } lintr/R/settings.R0000644000176200001440000001602314577052532013602 0ustar liggesusers#' Read lintr settings #' #' Lintr searches for settings for a given source file in the following order: #' 1. options defined as `linter.setting`. #' 2. `linter_file` in the same directory #' 3. `linter_file` in the project directory #' 4. `linter_file` in the user home directory #' 5. [default_settings()] #' #' The default linter_file name is `.lintr` but it can be changed with option `lintr.linter_file` #' or the environment variable `R_LINTR_LINTER_FILE` #' This file is a DCF file, see [base::read.dcf()] for details. #' Experimentally, we also support keeping the config in a plain R file. By default we look for #' a file named '.lintr.R' (in the same directories where we search for '.lintr'). #' We are still deciding the future of config support in lintr, so user feedback is welcome. #' The advantage of R is that it maps more closely to how the configs are actually stored, #' whereas the DCF approach requires somewhat awkward formatting of parseable R code within #' valid DCF key-value pairs. The main disadvantage of the R file is it might be _too_ flexible, #' with users tempted to write configs with side effects causing hard-to-detect bugs or #" otherwise "abusing" the ability to evaluate generic R code. Other recursive key-value stores #' like YAML could work, but require new dependencies and are harder to parse #' both programmatically and visually. #' @param filename source file to be linted read_settings <- function(filename) { reset_settings() config_file <- find_config(filename) default_encoding <- find_default_encoding(filename) if (!is.null(default_encoding)) { # Locally override the default for encoding if we found a smart default default_settings[["encoding"]] <- default_encoding } config <- read_config_file(config_file) validate_config_file(config, config_file, default_settings) for (setting in names(default_settings)) { value <- get_setting(setting, config, default_settings) if (setting == "exclusions") { if (!is.null(config_file)) { root <- dirname(config_file) } else { root <- getwd() } value <- normalize_exclusions(value, root = root) } settings[[setting]] <- value } } read_config_file <- function(config_file) { if (is.null(config_file)) { return(NULL) } config <- new.env() if (endsWith(config_file, ".R")) { load_config <- function(file) sys_source(file, config) malformed <- function(e) { stop("Malformed config file, ensure it is valid R syntax\n ", conditionMessage(e), call. = FALSE) } } else { load_config <- function(file) { dcf_values <- read.dcf(file, all = TRUE) for (setting in names(dcf_values)) { tryCatch( assign(setting, eval(str2lang(dcf_values[[setting]])), envir = config), error = function(e) stop("Malformed config setting '", setting, "'\n ", conditionMessage(e), call. = FALSE) ) } } malformed <- function(e) { stop("Malformed config file, ensure it ends in a newline\n ", conditionMessage(e), call. = FALSE) } } tryCatch(load_config(config_file), warning = malformed, error = malformed) config } validate_config_file <- function(config, config_file, defaults) { matched <- names(config) %in% names(defaults) if (!all(matched)) { warning("Found unused settings in config '", config_file, "': ", toString(names(config)[!matched])) } validate_regex(config, c("exclude", "exclude_next", "exclude_start", "exclude_end", "exclude_linter", "exclude_linter_sep") ) validate_character_string(config, c("encoding", "cache_directory", "comment_token")) validate_true_false(config, c("comment_bot", "error_on_lint")) validate_linters(config$linters) validate_exclusions(config$exclusions) } is_character_string <- function(x) is.character(x) && length(x) == 1L && !is.na(x) is_valid_regex <- function(str) !inherits(tryCatch(grepl(str, ""), condition = identity), "condition") is_single_regex <- function(x) is_character_string(x) && is_valid_regex(x) is_true_false <- function(x) is.logical(x) && length(x) == 1L && !is.na(x) validate_keys <- function(config, keys, test, what) { for (key in keys) { val <- config[[key]] if (is.null(val)) { next } if (!test(val)) { stop("Setting '", key, "' should be ", what, ", not '", toString(val), "'.") } } } validate_regex <- function(config, keys) { validate_keys(config, keys, is_single_regex, "a single regular expression") } validate_character_string <- function(config, keys) { validate_keys(config, keys, is_character_string, "a character string") } validate_true_false <- function(config, keys) { validate_keys(config, keys, is_true_false, "TRUE or FALSE") } validate_linters <- function(linters) { if (is.null(linters)) { return(invisible()) } is_linters <- vapply(linters, is_linter, logical(1L)) if (!all(is_linters)) { stop( "Setting 'linters' should be a list of linters, but found non-linters at elements ", toString(which(!is_linters)), "." ) } } validate_exclusions <- function(exclusions) { if (is.null(exclusions)) { return(invisible()) } exclusion_names <- names2(exclusions) has_names <- exclusion_names != "" unnamed_is_string <- vapply(exclusions[!has_names], function(x) is.character(x) && length(x) == 1L && !is.na(x), logical(1L)) if (!all(unnamed_is_string)) { stop( "Unnamed entries of setting 'exclusions' should be strings naming files or directories, check entries: ", toString(which(!has_names)[!unnamed_is_string]), "." ) } for (ii in which(has_names)) validate_named_exclusion(exclusions, ii) } validate_named_exclusion <- function(exclusions, idx) { entry <- exclusions[[idx]] if (is.list(entry)) { valid_entry <- vapply(entry, function(x) is.numeric(x) && !anyNA(x), logical(1L)) } else { valid_entry <- is.numeric(entry) && !anyNA(entry) } if (!all(valid_entry)) { stop( "Named entries of setting 'exclusions' should designate line numbers for exclusion, ", "check exclusion: ", idx, "." ) } } lintr_option <- function(setting, default = NULL) getOption(paste0("lintr.", setting), default) get_setting <- function(setting, config, defaults) { lintr_option(setting) %||% config[[setting]] %||% defaults[[setting]] } reset_settings <- function() list2env(default_settings, envir = settings) find_default_encoding <- function(filename) { if (is.null(filename)) { return(NULL) } root_path <- find_package(filename, allow_rproj = TRUE) rproj_enc <- get_encoding_from_dcf(find_rproj_at(root_path)) if (!is.null(rproj_enc)) { return(rproj_enc) } rproj_enc } get_encoding_from_dcf <- function(file) { if (is.null(file)) { return(NULL) } encodings <- tryCatch( unname(drop(read.dcf(file, "Encoding"))), error = function(e) NULL, warning = function(e) NULL ) if (!is.null(encodings)) { # Produces a warning in R <= 3.5 if encoding is NULL encodings <- encodings[!is.na(encodings)] } if (length(encodings) > 0L) { return(encodings[1L]) } NULL } lintr/R/namespace.R0000644000176200001440000000611614577052532013700 0ustar liggesusers# Parse namespace files and return imports exports, methods namespace_imports <- function(path = find_package(".")) { namespace_data <- tryCatch( parseNamespaceFile(basename(path), package.lib = file.path(path, "..")), error = function(e) NULL ) if (length(namespace_data$imports) == 0L) { return(empty_namespace_data()) } do.call(rbind, lapply(namespace_data$imports, safe_get_exports)) } # this loads the namespaces, but is the easiest way to do it # test package availability to avoid failing out as in #1360 # typically, users are running this on their own package directories and thus # will have the namespace dependencies installed, but we can't guarantee this. safe_get_exports <- function(ns) { # check package exists for both import(x) and importFrom(x, y) usages if (!requireNamespace(ns[[1L]], quietly = TRUE)) { return(empty_namespace_data()) } # importFrom directives appear as list(ns, imported_funs) if (length(ns) > 1L) { return(data.frame(pkg = ns[[1L]], fun = ns[[2L]], stringsAsFactors = FALSE)) } # relevant only if there are any exported objects fun <- getNamespaceExports(ns) if (length(fun) > 0L) { data.frame(pkg = ns, fun = fun, stringsAsFactors = FALSE) } } empty_namespace_data <- function() { data.frame(pkg = character(), fun = character(), stringsAsFactors = FALSE) } # filter namespace_imports() for S3 generics # this loads all imported namespaces imported_s3_generics <- function(ns_imports) { # `NROW()` for the `NULL` case of 0-export dependencies (cf. #1503) is_generic <- vapply( seq_len(NROW(ns_imports)), function(i) { fun_obj <- get(ns_imports$fun[i], envir = asNamespace(ns_imports$pkg[i])) is.function(fun_obj) && is_s3_generic(fun_obj) }, logical(1L) ) ns_imports[is_generic, ] } exported_s3_generics <- function(path = find_package(".")) { namespace_data <- tryCatch( parseNamespaceFile(basename(path), package.lib = file.path(path, "..")), error = function(e) NULL ) if (length(namespace_data$S3methods) == 0L || nrow(namespace_data$S3methods) == 0L) { return(empty_namespace_data()) } data.frame( pkg = basename(path), fun = unique(namespace_data$S3methods[, 1L]), stringsAsFactors = FALSE ) } is_s3_generic <- function(fun) { # Inspired by `utils::isS3stdGeneric`, though it will detect functions that # have `useMethod()` in places other than the first expression. bdexpr <- body(fun) while (is.call(bdexpr) && bdexpr[[1L]] == "{") bdexpr <- bdexpr[[length(bdexpr)]] ret <- is.call(bdexpr) && identical(bdexpr[[1L]], as.name("UseMethod")) if (ret) { names(ret) <- bdexpr[[2L]] } ret } .base_s3_generics <- unique(c( names(.knownS3Generics), if (getRversion() >= "3.5.0") { .S3_methods_table[, 1L] } else { # R < 3.5.0 doesn't provide .S3_methods_table # fallback: search baseenv() for generic methods imported_s3_generics(data.frame(pkg = "base", fun = ls(baseenv()), stringsAsFactors = FALSE))$fun }, # Contains S3 generic groups, see ?base::groupGeneric and src/library/base/R/zzz.R ls(.GenericArgsEnv) )) lintr/R/spaces_inside_linter.R0000644000176200001440000000531314577052532016130 0ustar liggesusers#' Spaces inside linter #' #' Check that parentheses and square brackets do not have spaces directly #' inside them, i.e., directly following an opening delimiter or directly #' preceding a closing delimiter. #' #' @examples #' # will produce lints #' lint( #' text = "c( TRUE, FALSE )", #' linters = spaces_inside_linter() #' ) #' #' lint( #' text = "x[ 1L ]", #' linters = spaces_inside_linter() #' ) #' #' # okay #' lint( #' text = "c(TRUE, FALSE)", #' linters = spaces_inside_linter() #' ) #' #' lint( #' text = "x[1L]", #' linters = spaces_inside_linter() #' ) #' #' @evalRd rd_tags("spaces_inside_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export spaces_inside_linter <- function() { left_xpath_condition <- " not(following-sibling::*[1][self::COMMENT]) and @end != following-sibling::*[1]/@start - 1 and @line1 = following-sibling::*[1]/@line1 " left_xpath <- glue(" //OP-LEFT-BRACKET[{left_xpath_condition}] | //LBB[{left_xpath_condition}] | //OP-LEFT-PAREN[{left_xpath_condition}]") right_xpath_condition <- " not(preceding-sibling::*[1][self::OP-COMMA]) and @start != preceding-sibling::*[1]/@end + 1 and @line1 = preceding-sibling::*[1]/@line2 " right_xpath <- glue(" //OP-RIGHT-BRACKET[{right_xpath_condition}] | //OP-RIGHT-PAREN[{right_xpath_condition} and not(preceding-sibling::*[1][self::EQ_SUB])]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content left_expr <- xml_find_all(xml, left_xpath) left_msg <- ifelse( xml_text(left_expr) %in% c("[", "[["), "Do not place spaces after square brackets.", "Do not place spaces after parentheses." ) left_lints <- xml_nodes_to_lints( left_expr, source_expression = source_expression, lint_message = left_msg, range_start_xpath = "number(./@col2 + 1)", # start after ( or [ range_end_xpath = "number(./following-sibling::*[1]/@col1 - 1)" # end before following expr ) right_expr <- xml_find_all(xml, right_xpath) right_msg <- ifelse( xml_text(right_expr) == "]", "Do not place spaces before square brackets.", "Do not place spaces before parentheses." ) right_lints <- xml_nodes_to_lints( right_expr, source_expression = source_expression, lint_message = right_msg, range_start_xpath = "number(./preceding-sibling::*[1]/@col2 + 1)", # start after preceding expression range_end_xpath = "number(./@col1 - 1)" # end before ) or ] ) c(left_lints, right_lints) }) } lintr/R/linter_tag_docs.R0000644000176200001440000000767514577052532015117 0ustar liggesusers# This file contains all documentation for linter tags in lintr except for default_linters. #' Style linters #' @name style_linters #' @description #' Linters highlighting code style issues. #' @evalRd rd_linters("style") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Robustness linters #' @name robustness_linters #' @description #' Linters highlighting code robustness issues, such as possibly wrong edge case behavior. #' @evalRd rd_linters("robustness") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Best practices linters #' @name best_practices_linters #' @description #' Linters checking the use of coding best practices, such as explicit typing of numeric constants. #' @evalRd rd_linters("best_practices") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Consistency linters #' @name consistency_linters #' @description #' Linters checking enforcing a consistent alternative if there are multiple syntactically valid ways to write #' something. #' @evalRd rd_linters("consistency") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Readability linters #' @name readability_linters #' @description #' Linters highlighting readability issues, such as missing whitespace. #' @evalRd rd_linters("readability") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Correctness linters #' @name correctness_linters #' @description #' Linters highlighting possible programming mistakes, such as unused variables. #' @evalRd rd_linters("correctness") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Common mistake linters #' @name common_mistakes_linters #' @description #' Linters highlighting common mistakes, such as duplicate arguments. #' @evalRd rd_linters("common_mistakes") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Efficiency linters #' @name efficiency_linters #' @description #' Linters highlighting code efficiency problems, such as unnecessary function calls. #' @evalRd rd_linters("efficiency") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Configurable linters #' @name configurable_linters #' @description #' Generic linters which support custom configuration to your needs. #' @evalRd rd_linters("configurable") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Package development linters #' @name package_development_linters #' @description #' Linters useful to package developers, for example for writing consistent tests. #' @evalRd rd_linters("package_development") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Deprecated linters #' @name deprecated_linters #' @description #' Linters that are deprecated and provided for backwards compatibility only. #' These linters will be excluded from `linters_with_tags()` by default. #' @evalRd rd_linters("deprecated") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Code executing linters #' @name executing_linters #' @description #' Linters that evaluate parts of the linted code, such as loading referenced packages. #' These linters should not be used with untrusted code, and may need dependencies of the linted package or project to #' be available in order to function correctly. For package authors, note that this includes loading the package itself, #' e.g. with `pkgload::load_all()` or installing and attaching the package. #' @evalRd rd_linters("executing") #' @seealso [linters] for a complete list of linters available in lintr. NULL #' Testthat linters #' @name pkg_testthat_linters #' @description #' Linters encouraging best practices within testthat suites. #' @evalRd rd_linters("pkg_testthat") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - NULL lintr/R/inner_combine_linter.R0000644000176200001440000001120214577052532016120 0ustar liggesusers#' Require `c()` to be applied before relatively expensive vectorized functions #' #' `as.Date(c(a, b))` is logically equivalent to `c(as.Date(a), as.Date(b))`. #' The same equivalence holds for several other vectorized functions like #' [as.POSIXct()] and math functions like [sin()]. The former is to be #' preferred so that the most expensive part of the operation ([as.Date()]) #' is applied only once. #' #' @examples #' # will produce lints #' lint( #' text = "c(log10(x), log10(y), log10(z))", #' linters = inner_combine_linter() #' ) #' #' # okay #' lint( #' text = "log10(c(x, y, z))", #' linters = inner_combine_linter() #' ) #' #' lint( #' text = "c(log(x, base = 10), log10(x, base = 2))", #' linters = inner_combine_linter() #' ) #' #' @evalRd rd_tags("inner_combine_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export inner_combine_linter <- function() { # these don't take any other arguments (except maybe by non-default # methods), so don't need to check equality of other arguments no_arg_vectorized_funs <- c( "sin", "cos", "tan", "sinpi", "cospi", "tanpi", "asin", "acos", "atan", "log2", "log10", "log1p", "exp", "expm1", "sqrt", "abs" ) # TODO(michaelchirico): the need to spell out specific arguments is pretty brittle, # but writing the xpath for the alternative case was proving too tricky. # It's messy enough as is -- it may make sense to take another pass at # writing the xpath from scratch to see if it can't be simplified. # See ?as.Date, ?as.POSIXct. tryFormats is not explicitly in any default # POSIXct method, but it is in as.Date.character and as.POSIXlt.character -- # the latter is what actually gets invoked when running as.POSIXct # on a character. So it is indeed an argument by pass-through. date_args <- c("format", "origin", "tz", "tryFormats") date_funs <- c("as.Date", "as.POSIXct", "as.POSIXlt") # See ?log. Only these two take a 'base' argument. log_funs <- c("log", "logb") log_args <- "base" # See ?lubridate::ymd and ?lubridate::ymd_hms lubridate_args <- c("quiet", "tz", "locale", "truncated") lubridate_funs <- c( "ymd", "ydm", "mdy", "myd", "dmy", "dym", "yq", "ym", "my", "ymd_hms", "ymd_hm", "ymd_h", "dmy_hms", "dmy_hm", "dmy_h", "mdy_hms", "mdy_hm", "mdy_h", "ydm_hms", "ydm_hm", "ydm_h" ) date_args_cond <- build_arg_condition(date_funs, date_args) log_args_cond <- build_arg_condition(log_funs, log_args) lubridate_args_cond <- build_arg_condition(lubridate_funs, lubridate_args) c_expr_cond <- xp_and( sprintf( "expr[1][SYMBOL_FUNCTION_CALL[%s]]", xp_text_in_table(c(no_arg_vectorized_funs, date_funs, log_funs, lubridate_funs)) ), "not(following-sibling::expr[not(expr[1][SYMBOL_FUNCTION_CALL])])", "not(expr[1]/SYMBOL_FUNCTION_CALL != following-sibling::expr/expr[1]/SYMBOL_FUNCTION_CALL)", date_args_cond, log_args_cond, lubridate_args_cond ) xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'c'] /parent::expr[count(following-sibling::expr) > 1] /following-sibling::expr[1][ {c_expr_cond} ] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_call <- xp_call_name(bad_expr, depth = 2L) lint_message <- paste( "Combine inputs to vectorized functions first to take full advantage of vectorization, e.g.,", sprintf( "%1$s(c(x, y)) only runs the more expensive %1$s() once as compared to c(%1$s(x), %1$s(y)).", matched_call ) ) xml_nodes_to_lints(bad_expr, source_expression = source_expression, lint_message, type = "warning") }) } #' Make the XPath condition ensuring an argument matches across calls #' #' @param arg Character scalar naming an argument #' @noRd arg_match_condition <- function(arg) { this_symbol <- sprintf("SYMBOL_SUB[text() = '%s']", arg) following_symbol <- sprintf("following-sibling::expr/%s", this_symbol) next_expr <- "following-sibling::expr[1]" return(xp_or( sprintf("not(%s) and not(%s)", this_symbol, following_symbol), xp_and( this_symbol, following_symbol, sprintf( "not(%1$s/%3$s != %2$s/%3$s)", this_symbol, following_symbol, next_expr ) ) )) } build_arg_condition <- function(calls, arguments) { xp_or( sprintf("not(expr[1][SYMBOL_FUNCTION_CALL[%s]])", xp_text_in_table(calls)), "not(EQ_SUB) and not(following-sibling::expr/EQ_SUB)", xp_and(vapply(arguments, arg_match_condition, character(1L))) ) } lintr/R/expect_type_linter.R0000644000176200001440000000611614577052532015652 0ustar liggesusers#' Require usage of `expect_type(x, type)` over `expect_equal(typeof(x), type)` #' #' [testthat::expect_type()] exists specifically for testing the storage type #' of objects. [testthat::expect_equal()], [testthat::expect_identical()], and #' [testthat::expect_true()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = 'expect_equal(typeof(x), "double")', #' linters = expect_type_linter() #' ) #' #' lint( #' text = 'expect_identical(typeof(x), "double")', #' linters = expect_type_linter() #' ) #' #' # okay #' lint( #' text = 'expect_type(x, "double")', #' linters = expect_type_linter() #' ) #' #' @evalRd rd_tags("expect_type_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_type_linter <- function() { # NB: the full list of values that can arise from `typeof(x)` is available # in ?typeof (or, slightly more robustly, in the R source: src/main/util.c. # Not all of them are available in is. form, e.g. 'any' or # 'special'. 'builtin' and 'closure' are special cases, corresponding to # is.primitive and is.function (essentially). base_types <- c( "raw", "logical", "integer", "double", "complex", "character", "list", "numeric", "function", "primitive", "environment", "pairlist", "promise", # Per ?is.language, it's the same as is.call || is.name || is.expression. # so by blocking it, we're forcing more precise tests of one of # those directly ("language", "symbol", and "expression", resp.) # NB: is.name and is.symbol are identical. "language", "call", "name", "symbol", "expression" ) base_type_tests <- xp_text_in_table(paste0("is.", base_types)) expect_equal_identical_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'typeof']] and (position() = 1 or preceding-sibling::expr[STR_CONST]) ] /parent::expr[not(SYMBOL_SUB[text() = 'info' or text() = 'label' or text() = 'expected.label'])] " expect_true_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][expr[1][SYMBOL_FUNCTION_CALL[ {base_type_tests} ]]] /parent::expr[not(SYMBOL_SUB[text() = 'info' or text() = 'label'])] ") xpath <- paste(expect_equal_identical_xpath, "|", expect_true_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_function <- xp_call_name(bad_expr) msg <- ifelse( matched_function %in% c("expect_equal", "expect_identical"), sprintf("expect_type(x, t) is better than %s(typeof(x), t)", matched_function), "expect_type(x, t) is better than expect_true(is.(x))" ) xml_nodes_to_lints( bad_expr, source_expression, lint_message = msg, type = "warning" ) }) } lintr/R/unnecessary_nested_if_linter.R0000644000176200001440000000277314577052532017705 0ustar liggesusers#' Avoid unnecessary nested `if` conditional statements #' #' @examples #' # will produce lints #' writeLines("if (x) { \n if (y) { \n return(1L) \n } \n}") #' lint( #' text = "if (x) { \n if (y) { \n return(1L) \n } \n}", #' linters = unnecessary_nested_if_linter() #' ) #' #' # okay #' writeLines("if (x && y) { \n return(1L) \n}") #' lint( #' text = "if (x && y) { \n return(1L) \n}", #' linters = unnecessary_nested_if_linter() #' ) #' #' writeLines("if (x) { \n y <- x + 1L\n if (y) { \n return(1L) \n } \n}") #' lint( #' text = "if (x) { \n y <- x + 1L\n if (y) { \n return(1L) \n } \n}", #' linters = unnecessary_nested_if_linter() #' ) #' #' @evalRd rd_tags("unnecessary_nested_if_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unnecessary_nested_if_linter <- make_linter_from_xpath( xpath = paste0( "//IF/parent::expr[not(ELSE)]/OP-RIGHT-PAREN/", c( "following-sibling::expr[IF and not(ELSE)]", # catch if (cond) if (other_cond) { ... } "following-sibling::expr[OP-LEFT-BRACE and count(expr) = 1] /expr[IF and not(ELSE)]" # catch if (cond) { if (other_cond) { ... } } ), collapse = " | " ), lint_message = paste( "Don't use nested `if` statements,", "where a single `if` with the combined conditional expression will do.", "For example, instead of `if (x) { if (y) { ... }}`, use `if (x && y) { ... }`." ), # need the full file to also catch usages at the top level level = "file" ) lintr/R/any_is_na_linter.R0000644000176200001440000000304414577052532015256 0ustar liggesusers#' Require usage of `anyNA(x)` over `any(is.na(x))` #' #' [anyNA()] exists as a replacement for `any(is.na(x))` which is more efficient #' for simple objects, and is at worst equally efficient. #' Therefore, it should be used in all situations instead of the latter. #' #' @examples #' # will produce lints #' lint( #' text = "any(is.na(x), na.rm = TRUE)", #' linters = any_is_na_linter() #' ) #' #' lint( #' text = "any(is.na(foo(x)))", #' linters = any_is_na_linter() #' ) #' #' # okay #' lint( #' text = "anyNA(x)", #' linters = any_is_na_linter() #' ) #' #' lint( #' text = "anyNA(foo(x))", #' linters = any_is_na_linter() #' ) #' #' lint( #' text = "any(!is.na(x), na.rm = TRUE)", #' linters = any_is_na_linter() #' ) #' #' @evalRd rd_tags("any_is_na_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export any_is_na_linter <- function() { xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'any'] /parent::expr /following-sibling::expr[1][expr[1][SYMBOL_FUNCTION_CALL[text() = 'is.na']]] /parent::expr[ count(expr) = 2 or (count(expr) = 3 and SYMBOL_SUB[text() = 'na.rm']) ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "anyNA(x) is better than any(is.na(x)).", type = "warning" ) }) } lintr/R/lintr-deprecated.R0000644000176200001440000001605514577052532015175 0ustar liggesusers#' @name lintr-deprecated #' @title Deprecated functions in lintr #' #' @description #' #' These functions have been deprecated from lintr. #' #' - `open_curly_linter()` and `closed_curly_linter()` check that open and closed curly braces #' are on their own line unless they follow an else, a comma, or a closing bracket. #' Deprecated in favor of `brace_linter()`. #' #' - `paren_brace_linter()` checks that there is a space between right parentheses and an opening #' curly brace. E.g., `function(){}` doesn't have a space, while `function() {}` does. #' Deprecated in favor of `brace_linter()`. #' #' - `semicolon_terminator_linter()` checks that no semicolons terminate expressions. #' Deprecated in favor of `semicolon_linter()`. #' #' @param allow_single_line if `TRUE`, allow an open and closed curly pair on the same line. #' @param semicolon A character vector defining which semicolons to report: #' \describe{ #' \item{compound}{Semicolons that separate two statements on the same line.} #' \item{trailing}{Semicolons following the last statement on the line.} #' } #' #' @seealso [linters] for a complete list of linters available in lintr. #' @evalRd rd_tags("closed_curly_linter") #' @keywords internal NULL #' Closed curly linter #' @rdname lintr-deprecated #' @export closed_curly_linter <- function(allow_single_line = FALSE) { lintr_deprecated("closed_curly_linter", new = "brace_linter", version = "3.0.0", type = "Linter") xp_cond_closed <- xp_and(c( # matching { is on same line if (isTRUE(allow_single_line)) { "(@line1 != preceding-sibling::OP-LEFT-BRACE/@line1)" }, # immediately followed by ",", "]" or ")" "not( @line1 = ancestor::expr/following-sibling::*[1][ self::OP-COMMA or self::OP-RIGHT-BRACKET or self::OP-RIGHT-PAREN ]/@line1 )", # double curly "not( (@line1 = parent::expr/following-sibling::OP-RIGHT-BRACE/@line1) or (@line1 = preceding-sibling::expr/OP-RIGHT-BRACE/@line1) )" )) xpath <- glue("//OP-RIGHT-BRACE[ { xp_cond_closed } and ( (@line1 = preceding-sibling::*[1]/@line2) or (@line1 = parent::expr/following-sibling::*[1][not(self::ELSE)]/@line1) ) ]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml_nodes_to_lints( xml_find_all(source_expression$xml_parsed_content, xpath), source_expression = source_expression, lint_message = "Closing curly-braces should always be on their own line, unless they are followed by an else." ) }) } #' Open curly linter #' @rdname lintr-deprecated #' @export open_curly_linter <- function(allow_single_line = FALSE) { lintr_deprecated("open_curly_linter", new = "brace_linter", version = "3.0.0", type = "Linter") xpath_before <- "//OP-LEFT-BRACE[ not(following-sibling::expr[1][OP-LEFT-BRACE]) and not(parent::expr/preceding-sibling::*[1][OP-LEFT-BRACE]) and @line1 != parent::expr/preceding-sibling::*[1][not(self::ELSE)]/@line2 ]" if (allow_single_line) { xpath_after <- "//OP-LEFT-BRACE[ not(following-sibling::expr[1][OP-LEFT-BRACE]) and not(parent::expr/preceding-sibling::OP-LEFT-BRACE) and not(@line2 = following-sibling::OP-RIGHT-BRACE/@line1) and @line2 = following-sibling::expr[position() = 1 and not(OP-LEFT-BRACE)]/@line1 ]" message_after <- paste( "Opening curly braces should always be followed by a new line", "unless the paired closing brace is on the same line." ) } else { xpath_after <- "//OP-LEFT-BRACE[ not(following-sibling::expr[1][OP-LEFT-BRACE]) and not(parent::expr/preceding-sibling::OP-LEFT-BRACE) and @line2 = following-sibling::expr[1]/@line1 ]" message_after <- "Opening curly braces should always be followed by a new line." } Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content expr_before <- xml_find_all(xml, xpath_before) lints_before <- xml_nodes_to_lints( expr_before, source_expression = source_expression, lint_message = "Opening curly braces should never go on their own line.", type = "style" ) expr_after <- xml_find_all(xml, xpath_after) lints_after <- xml_nodes_to_lints( expr_after, source_expression = source_expression, lint_message = message_after, type = "style" ) return(c(lints_before, lints_after)) }) } #' Parentheses before brace linter #' @rdname lintr-deprecated #' @export paren_brace_linter <- function() { lintr_deprecated("paren_brace_linter", new = "brace_linter", version = "3.0.0", type = "Linter") xpath <- paste( "//OP-LEFT-BRACE[", "@line1 = parent::expr/preceding-sibling::OP-RIGHT-PAREN/@line1", "and", "@col1 = parent::expr/preceding-sibling::OP-RIGHT-PAREN/@col1 + 1", "]" ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content match_exprs <- xml_find_all(xml, xpath) xml_nodes_to_lints( match_exprs, source_expression = source_expression, lint_message = "There should be a space between right parenthesis and an opening curly brace.", type = "style" ) }) } #' Semicolon linter #' @rdname lintr-deprecated #' @export semicolon_terminator_linter <- function(semicolon = c("compound", "trailing")) { lintr_deprecated( old = "semicolon_terminator_linter", new = "semicolon_linter", version = "3.0.0", type = "Linter" ) semicolon <- match.arg(semicolon, several.ok = TRUE) allow_compound <- !"compound" %in% semicolon allow_trailing <- !"trailing" %in% semicolon semicolon_linter(allow_compound, allow_trailing) } #' Unnecessary concatenation linter #' @rdname lintr-deprecated #' @export unneeded_concatenation_linter <- function(allow_single_expression = TRUE) { lintr_deprecated( old = "unneeded_concatenation_linter", new = "unnecessary_concatenation_linter", version = "3.1.0", type = "Linter" ) stopifnot( is.logical(allow_single_expression), length(allow_single_expression) == 1L ) unnecessary_concatenation_linter(allow_single_expression = allow_single_expression) } #' Single quotes linter #' @rdname lintr-deprecated #' @export single_quotes_linter <- function() { lintr_deprecated( old = "single_quotes_linter", new = "quotes_linter", version = "3.1.0", type = "Linter" ) quotes_linter() } #' Consecutive stopifnot linter #' @rdname lintr-deprecated #' @export consecutive_stopifnot_linter <- function() { lintr_deprecated( old = "consecutive_stopifnot_linter", new = "consecutive_assertion_linter", version = "3.1.0", type = "Linter" ) consecutive_assertion_linter() } #' No tabs linter #' @rdname lintr-deprecated #' @export no_tab_linter <- function() { lintr_deprecated( old = "no_tab_linter", new = "whitespace_linter", version = "3.1.0", type = "Linter" ) whitespace_linter() } lintr/R/missing_argument_linter.R0000644000176200001440000000407114577055717016702 0ustar liggesusers#' Missing argument linter #' #' Check for missing arguments in function calls (e.g. `stats::median(1:10, )`). #' #' @param except a character vector of function names as exceptions. #' @param allow_trailing always allow trailing empty arguments? #' #' @examples #' # will produce lints #' lint( #' text = 'tibble(x = "a", )', #' linters = missing_argument_linter() #' ) #' #' # okay #' lint( #' text = 'tibble(x = "a")', #' linters = missing_argument_linter() #' ) #' #' lint( #' text = 'tibble(x = "a", )', #' linters = missing_argument_linter(except = "tibble") #' ) #' #' lint( #' text = 'tibble(x = "a", )', #' linters = missing_argument_linter(allow_trailing = TRUE) #' ) #' #' @evalRd rd_tags("missing_argument_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export missing_argument_linter <- function(except = c("alist", "quote", "switch"), allow_trailing = FALSE) { conds <- c( "self::OP-COMMA[preceding-sibling::*[not(self::COMMENT)][1][self::OP-LEFT-PAREN or self::OP-COMMA]]", "self::EQ_SUB[following-sibling::*[not(self::COMMENT)][1][self::OP-RIGHT-PAREN or self::OP-COMMA]]" ) if (!allow_trailing) { conds <- c(conds, "self::OP-RIGHT-PAREN[preceding-sibling::*[not(self::COMMENT)][1][self::OP-LEFT-PAREN or self::OP-COMMA]]" ) } # require >3 children to exclude foo(), which is xpath <- glue("//SYMBOL_FUNCTION_CALL/parent::expr/parent::expr[count(*) > 3]/*[{xp_or(conds)}]") to_function_xpath <- "string(./preceding-sibling::expr[last()]/SYMBOL_FUNCTION_CALL)" Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content missing_args <- xml_find_all(xml, xpath) function_call_name <- get_r_string(xml_find_chr(missing_args, to_function_xpath)) xml_nodes_to_lints( missing_args[!function_call_name %in% except], source_expression = source_expression, lint_message = "Missing argument in function call." ) }) } lintr/R/string_boundary_linter.R0000644000176200001440000001372514577052532016536 0ustar liggesusers#' Require usage of `startsWith()` and `endsWith()` over `grepl()`/`substr()` versions #' #' [startsWith()] is used to detect fixed initial substrings; it is more #' readable and more efficient than equivalents using [grepl()] or [substr()]. #' c.f. `startsWith(x, "abc")`, `grepl("^abc", x)`, #' `substr(x, 1L, 3L) == "abc"`. #' #' Ditto for using [endsWith()] to detect fixed terminal substrings. #' #' Note that there is a difference in behavior between how `grepl()` and `startsWith()` #' (and `endsWith()`) handle missing values. In particular, for `grepl()`, `NA` inputs #' are considered `FALSE`, while for `startsWith()`, `NA` inputs have `NA` outputs. #' That means the strict equivalent of `grepl("^abc", x)` is #' `!is.na(x) & startsWith(x, "abc")`. #' #' We lint `grepl()` usages by default because the `!is.na()` version is more explicit #' with respect to `NA` handling -- though documented, the way `grepl()` handles #' missing inputs may be surprising to some users. #' #' @param allow_grepl Logical, default `FALSE`. If `TRUE`, usages with `grepl()` #' are ignored. Some authors may prefer the conciseness offered by `grepl()` whereby #' `NA` input maps to `FALSE` output, which doesn't have a direct equivalent #' with `startsWith()` or `endsWith()`. #' #' @examples #' # will produce lints #' lint( #' text = 'grepl("^a", x)', #' linters = string_boundary_linter() #' ) #' #' lint( #' text = 'grepl("z$", x)', #' linters = string_boundary_linter() #' ) #' #' # okay #' lint( #' text = 'startsWith(x, "a")', #' linters = string_boundary_linter() #' ) #' #' lint( #' text = 'endsWith(x, "z")', #' linters = string_boundary_linter() #' ) #' #' # If missing values are present, the suggested alternative wouldn't be strictly #' # equivalent, so this linter can also be turned off in such cases. #' lint( #' text = 'grepl("z$", x)', #' linters = string_boundary_linter(allow_grepl = TRUE) #' ) #' #' @evalRd rd_tags("string_boundary_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export string_boundary_linter <- function(allow_grepl = FALSE) { str_cond <- xp_and( "string-length(text()) > 3", "contains(text(), '^') or contains(text(), '$')" ) str_detect_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'str_detect'] /parent::expr /following-sibling::expr[2] /STR_CONST[ {str_cond} ] ") if (!allow_grepl) { grepl_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'grepl'] /parent::expr /parent::expr[ not(SYMBOL_SUB[ text() = 'ignore.case' and not(following-sibling::expr[1][NUM_CONST[text() = 'FALSE'] or SYMBOL[text() = 'F']]) ]) and not(SYMBOL_SUB[ text() = 'fixed' and not(following-sibling::expr[1][NUM_CONST[text() = 'FALSE'] or SYMBOL[text() = 'F']]) ]) ] /expr[2] /STR_CONST[ {str_cond} ] ") } get_regex_lint_data <- function(xml, xpath) { expr <- xml_find_all(xml, xpath) patterns <- get_r_string(expr) initial_anchor <- startsWith(patterns, "^") search_start <- 1L + initial_anchor search_end <- nchar(patterns) - 1L + initial_anchor can_replace <- is_not_regex(substr(patterns, search_start, search_end)) list(lint_expr = expr[can_replace], initial_anchor = initial_anchor[can_replace]) } substr_xpath_parts <- glue(" //{ c('EQ', 'NE') } /parent::expr[ expr[STR_CONST] and expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'substr' or text() = 'substring']] and expr[ ( position() = 3 and NUM_CONST[text() = '1' or text() = '1L'] ) or ( position() = 4 and expr[1][SYMBOL_FUNCTION_CALL[text() = 'nchar']] and expr[position() = 2] = preceding-sibling::expr[2] ) ] ] ] ") substr_xpath <- paste(substr_xpath_parts, collapse = " | ") substr_arg2_xpath <- "string(./expr[expr[1][SYMBOL_FUNCTION_CALL]]/expr[3])" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content lints <- list() str_detect_lint_data <- get_regex_lint_data(xml, str_detect_xpath) str_detect_lint_message <- paste( ifelse( str_detect_lint_data$initial_anchor, "Use startsWith() to detect a fixed initial substring.", "Use endsWith() to detect a fixed terminal substring." ), "Doing so is more readable and more efficient." ) lints <- c(lints, xml_nodes_to_lints( str_detect_lint_data$lint_expr, source_expression = source_expression, lint_message = str_detect_lint_message, type = "warning" )) if (!allow_grepl) { grepl_lint_data <- get_regex_lint_data(xml, grepl_xpath) grepl_replacement <- ifelse(grepl_lint_data$initial_anchor, "startsWith", "endsWith") grepl_type <- ifelse(grepl_lint_data$initial_anchor, "initial", "terminal") grepl_lint_message <- paste( sprintf( "Use !is.na(x) & %s(x, string) to detect a fixed %s substring, or, if missingness is not a concern, just %s.", grepl_replacement, grepl_type, grepl_replacement ), "Doing so is more readable and more efficient." ) lints <- c(lints, xml_nodes_to_lints( grepl_lint_data$lint_expr, source_expression = source_expression, lint_message = grepl_lint_message, type = "warning" )) } substr_expr <- xml_find_all(xml, substr_xpath) substr_one <- xml_find_chr(substr_expr, substr_arg2_xpath) %in% c("1", "1L") substr_lint_message <- paste( ifelse( substr_one, "Use startsWith() to detect an initial substring.", "Use endsWith() to detect a terminal substring." ), "Doing so is more readable and more efficient." ) lints <- c(lints, xml_nodes_to_lints(substr_expr, source_expression, substr_lint_message, type = "warning")) lints }) } lintr/R/fixed_regex_linter.R0000644000176200001440000001241214577052532015606 0ustar liggesusers#' Require usage of `fixed=TRUE` in regular expressions where appropriate #' #' Invoking a regular expression engine is overkill for cases when the search #' pattern only involves static patterns. #' #' NB: for `stringr` functions, that means wrapping the pattern in `stringr::fixed()`. #' #' NB: this linter is likely not able to distinguish every possible case when #' a fixed regular expression is preferable, rather it seeks to identify #' likely cases. It should _never_ report false positives, however; please #' report false positives as an error. #' #' @param allow_unescaped Logical, default `FALSE`. If `TRUE`, only patterns that #' require regex escapes (e.g. `"\\$"` or `"[$]"`) will be linted. See examples. #' @examples #' # will produce lints #' code_lines <- 'gsub("\\\\.", "", x)' #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("a[*]b", x)', #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("a[*]b", x)', #' linters = fixed_regex_linter(allow_unescaped = TRUE) #' ) #' #' code_lines <- 'stringr::str_subset(x, "\\\\$")' #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("Munich", address)', #' linters = fixed_regex_linter() #' ) #' #' # okay #' code_lines <- 'gsub("\\\\.", "", x, fixed = TRUE)' #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("a*b", x, fixed = TRUE)', #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'stringr::str_subset(x, stringr::fixed("$"))', #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("Munich", address, fixed = TRUE)', #' linters = fixed_regex_linter() #' ) #' #' lint( #' text = 'grepl("Munich", address)', #' linters = fixed_regex_linter(allow_unescaped = TRUE) #' ) #' #' @evalRd rd_tags("fixed_regex_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export fixed_regex_linter <- function(allow_unescaped = FALSE) { # regular expression pattern is the first argument pos_1_regex_funs <- xp_text_in_table(c( "grep", "gsub", "sub", "regexec", "grepl", "regexpr", "gregexpr" )) # regular expression pattern is the second argument pos_2_regex_funs <- xp_text_in_table(c( # base functions. "strsplit", # data.table functions. "tstrsplit", # stringr functions. # even though the user action is different # (setting fixed=TRUE vs. wrapping stringr::fixed()), # detection of the lint is the same "str_count", "str_detect", "str_ends", "str_extract", "str_extract_all", "str_locate", "str_locate_all", "str_match", "str_match_all", "str_remove", "str_remove_all", "str_replace", "str_replace_all", "str_split", "str_starts", "str_subset", "str_view", "str_view_all", "str_which" )) pipes <- setdiff(magrittr_pipes, c("%$%", "%T>%")) in_pipe_cond <- glue(" parent::expr/preceding-sibling::SPECIAL[{ xp_text_in_table(pipes) }] | parent::expr/preceding-sibling::PIPE ") # NB: strsplit doesn't have an ignore.case argument # NB: we intentionally exclude cases like gsub(x, c("a" = "b")), where "b" is fixed xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {pos_1_regex_funs} ] /parent::expr[ not(following-sibling::SYMBOL_SUB[ (text() = 'fixed' or text() = 'ignore.case') and following-sibling::expr[1][NUM_CONST[text() = 'TRUE'] or SYMBOL[text() = 'T']] ]) ] /following-sibling::expr[ ( position() = 1 and STR_CONST and not(EQ_SUB) and not({ in_pipe_cond }) ) or ( STR_CONST and preceding-sibling::*[2][self::SYMBOL_SUB/text() = 'pattern'] ) ] | //SYMBOL_FUNCTION_CALL[ {pos_2_regex_funs} ] /parent::expr[ not(following-sibling::SYMBOL_SUB[ text() = 'fixed' and following-sibling::expr[1][NUM_CONST[text() = 'TRUE'] or SYMBOL[text() = 'T']] ]) ] /following-sibling::expr[ position() = 2 - count({ in_pipe_cond }) and STR_CONST and not(EQ_SUB) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content patterns <- xml_find_all(xml, xpath) pattern_strings <- get_r_string(patterns) is_static <- is_not_regex(pattern_strings, allow_unescaped) fixed_equivalent <- encodeString(get_fixed_string(pattern_strings[is_static]), quote = '"', justify = "none") call_name <- xml_find_chr(patterns[is_static], "string(preceding-sibling::expr[last()]/SYMBOL_FUNCTION_CALL)") is_stringr <- startsWith(call_name, "str_") replacement <- ifelse( is_stringr, sprintf("stringr::fixed(%s)", fixed_equivalent), fixed_equivalent ) msg <- paste( "This regular expression is static, i.e., its matches can be expressed as a fixed substring expression, which", "is faster to compute. Here, you can use", replacement, ifelse(is_stringr, "as the pattern.", "with fixed = TRUE.") ) xml_nodes_to_lints( patterns[is_static], source_expression = source_expression, lint_message = msg, type = "warning" ) }) } lintr/R/path_utils.R0000644000176200001440000001224314577052532014116 0ustar liggesuserscontrol_char_regex <- rex(one_of(intToUtf8(seq.int(0L, 31L), multiple = TRUE))) # control chars (non-printing) win32_bad_punct_regex <- rex(one_of( "*", "?", "\"", "<", ">", "|", ":", "/", "\\" )) win32_good_punct_regex <- rex(one_of( "!", "#", "$", "%", "&", "'", "(", ")", "+", ",", "-", ".", ";", "=", "@", "[", "]", "^", "_", "`", "{", "}", "~" )) # win32BadPunct + win32AllowedPunct = [:punct:] unsafe_char_regex <- rex(or(control_char_regex, win32_bad_punct_regex)) safe_char_regex <- rex(or(alnum, " ", win32_good_punct_regex)) # alphanumeric chars (including UTF-8), spaces, and win32-allowed punctuation portable_char_regex <- rex(character_class("A-Za-z0-9_\\-.")) # ASCII letters, digits, dot, hyphen and underscore protocol_regex <- rex(one_or_more(letter), "://") root_regex <- list( unix = rex(start, "/"), tilde = rex(start, "~", zero_or_more(portable_char_regex), zero_or_more("/")), win32 = rex(start, letter, ":", maybe(one_of("/", "\\"))), unc = rex(start, "\\\\", maybe(one_or_more(portable_char_regex), maybe("\\"))) ) root_path_regex <- rex(start, or(root_regex), end) # styler: off absolute_path_regex <- rex(or( root_regex[["unix"]] %if_next_isnt% one_of(space, "/"), root_regex[["tilde"]] %if_next_isnt% one_of(space, quote), root_regex[["win32"]] %if_next_isnt% one_of(space, "/", "\\"), root_regex[["unc"]] %if_next_isnt% one_of(space, "\\", quote) )) relative_path_regex <- rex( start %if_next_isnt% or(quote, absolute_path_regex, protocol_regex), or( # not an absolute path or protocol, and then group(one_or_more(safe_char_regex), or("/", "\\")), # (back)slash-separated paths group(dot, maybe(dot), end) # or single or double dot ) # (paths without slash or dots left out) %if_next_isnt% quote ) # styler: on path_regex <- rex(or(absolute_path_regex, relative_path_regex)) is_absolute_path <- function(path) { re_matches(path, absolute_path_regex) } is_root_path <- function(path) { re_matches(path, root_path_regex) } is_relative_path <- function(path) { re_matches(path, relative_path_regex) } is_path <- function(path) { re_matches(path, path_regex) } is_valid_path <- function(paths, lax = FALSE) { # Given a character vector of paths, return FALSE for directory or file having valid characters. # On Windows, invalid chars are all control chars and: * ? " < > | : # On Unix, all characters are valid, except when lax=TRUE (use same invalid chars as Windows). as.logical( Map( function(dirs, is_win32) { if (!is_win32 && !lax) { return(TRUE) } if (length(dirs) > 0L && is_root_path(dirs[[1L]])) { dirs <- tail(dirs, -1L) # remove root element ("/", "C:", or "\\") } !any(re_matches(dirs, unsafe_char_regex)) }, split_paths(paths), re_matches(paths, rex(or(list(root_regex[["win32"]], root_regex[["unc"]], "\\")))) ) ) } is_long_path <- function(path) { # Take a character vector of paths and determine if they are "long enough" # TRUE , e.g.: "./foo", "C:\\foo", "foo/bar" # FALSE, e.g.: "/", "\\", "n/a", "/foo", "foo/" re_matches( re_substitutes(path, ":", ""), rex(at_least(safe_char_regex, 1L), one_of("/", "\\"), at_least(safe_char_regex, 2L)) ) } is_valid_long_path <- function(path, lax = FALSE) { # Convenience function to avoid linting short paths and those unlikely to be valid paths ret <- is_valid_path(path, lax) if (lax) { ret <- ret & is_long_path(path) } ret } split_paths <- function(path, sep = "/|\\\\") { if (!is.character(path)) { stop("argument 'path' should be a character vector") } if (!is.character(sep) || length(sep) != 1L || !nzchar(sep)) { stop("argument 'sep' should be a non-empty regular expression character string") } Map(split_path, strsplit(path, sep), substr(path, 1L, 1L)) } split_path <- function(dirs, prefix) { # add root dir if needed nonempty_dirs <- nzchar(dirs) i <- match(TRUE, nonempty_dirs, nomatch = length(dirs) + 1L) - 1L if (i > 0L) { dirs <- c(strrep(prefix, i), tail(dirs, -i)) } # add // to protocols (like http, smb, ...) if (length(dirs) > 0L && grepl("..:$", dirs[[1L]])) { dirs[[1L]] <- paste0(dirs[[1L]], "//") } # remove empty dirs dirs[nzchar(dirs)] } #' @include utils.R path_linter_factory <- function(path_function, message, linter, name = linter_auto_name()) { force(name) Linter(function(source_expression) { lapply( ids_with_token(source_expression, "STR_CONST"), function(id) { token <- with_id(source_expression, id) path <- get_r_string(token$text) if (path_function(path)) { start <- token[["col1"]] + 1L end <- token[["col2"]] - 1L Lint( filename = source_expression[["filename"]], line_number = token[["line1"]], column_number = start, type = "warning", message = message, line = source_expression[["lines"]][[as.character(token[["line1"]])]], ranges = list(c(start, end)) ) } } ) }, name = name) } lintr/R/paste_linter.R0000644000176200001440000002522114577052532014433 0ustar liggesusers#' Raise lints for several common poor usages of `paste()` #' #' The following issues are linted by default by this linter #' (see arguments for which can be de-activated optionally): #' #' 1. Block usage of [paste()] with `sep = ""`. [paste0()] is a faster, more concise alternative. #' 2. Block usage of `paste()` or `paste0()` with `collapse = ", "`. [toString()] is a direct #' wrapper for this, and alternatives like [glue::glue_collapse()] might give better messages for humans. #' 3. Block usage of `paste0()` that supplies `sep=` -- this is not a formal argument to `paste0`, and #' is likely to be a mistake. #' 4. Block usage of `paste()` / `paste0()` combined with [rep()] that could be replaced by #' [strrep()]. `strrep()` can handle the task of building a block of repeated strings #' (e.g. often used to build "horizontal lines" for messages). This is both more readable and #' skips the (likely small) overhead of putting two strings into the global string cache when only one is needed. #' #' Only target scalar usages -- `strrep` can handle more complicated cases (e.g. `strrep(letters, 26:1)`, #' but those aren't as easily translated from a `paste(collapse=)` call. #' #' @evalRd rd_tags("paste_linter") #' @param allow_empty_sep Logical, default `FALSE`. If `TRUE`, usage of #' `paste()` with `sep = ""` is not linted. #' @param allow_to_string Logical, default `FALSE`. If `TRUE`, usage of #' `paste()` and `paste0()` with `collapse = ", "` is not linted. #' @param allow_file_path String, one of `"never"`, `"double_slash"`, or `"always"`; `"double_slash"` by default. #' If `"never"`, usage of `paste()` and `paste0()` to construct file paths is not linted. If `"double_slash"`, #' strings containing consecutive forward slashes will not lint. The main use case here is for URLs -- "paths" like #' `"https://"` will not induce lints, since constructing them with `file.path()` might be deemed unnatural. #' Lastly, if `"always"`, strings with consecutive forward slashes will also lint. Note that `"//"` is never linted #' when it comes at the beginning or end of the input, to avoid requiring empty inputs like #' `file.path("", ...)` or `file.path(..., "")`. #' #' @examples #' # will produce lints #' lint( #' text = 'paste("a", "b", sep = "")', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste(c("a", "b"), collapse = ", ")', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste0(c("a", "b"), sep = " ")', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste0(rep("*", 10L), collapse = "")', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste0("http://site.com/", path)', #' linters = paste_linter(allow_file_path = "never") #' ) #' #' # okay #' lint( #' text = 'paste0("a", "b")', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste("a", "b", sep = "")', #' linters = paste_linter(allow_empty_sep = TRUE) #' ) #' #' lint( #' text = 'toString(c("a", "b"))', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste(c("a", "b"), collapse = ", ")', #' linters = paste_linter(allow_to_string = TRUE) #' ) #' #' lint( #' text = 'paste(c("a", "b"))', #' linters = paste_linter() #' ) #' #' lint( #' text = 'strrep("*", 10L)', #' linters = paste_linter() #' ) #' #' lint( #' text = 'paste0(year, "/", month, "/", day)', #' linters = paste_linter(allow_file_path = "always") #' ) #' #' lint( #' text = 'paste0("http://site.com/", path)', #' linters = paste_linter() #' ) #' #' @seealso [linters] for a complete list of linters available in lintr. #' @export paste_linter <- function(allow_empty_sep = FALSE, allow_to_string = FALSE, allow_file_path = c("double_slash", "always", "never")) { allow_file_path <- match.arg(allow_file_path) check_file_paths <- allow_file_path %in% c("double_slash", "never") paste_sep_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'paste'] /parent::expr /following-sibling::SYMBOL_SUB[text() = 'sep' and following-sibling::expr[1][STR_CONST]] /parent::expr " to_string_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'paste' or text() = 'paste0'] /parent::expr /parent::expr[ count(expr) = 3 and SYMBOL_SUB[text() = 'collapse']/following-sibling::expr[1][STR_CONST] ] " paste0_sep_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'paste0'] /parent::expr /following-sibling::SYMBOL_SUB[text() = 'sep'] /parent::expr " paste_strrep_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'paste' or text() = 'paste0'] /parent::expr[ count(following-sibling::expr) = 2 and following-sibling::expr[1][expr[1][SYMBOL_FUNCTION_CALL[text() = 'rep']] and expr[2][STR_CONST]] and following-sibling::SYMBOL_SUB[text() = 'collapse'] ] /parent::expr " # Type II: paste0(x, "/", y, "/", z) # NB: some conditions require evaluating the R string, only a few can be done in pure XPath. See below. paste0_file_path_xpath <- xp_strip_comments(" //SYMBOL_FUNCTION_CALL[text() = 'paste0'] /parent::expr /parent::expr[ (: exclude paste0(x) :) count(expr) > 2 (: An expression matching _any_ of these conditions is _not_ a file path :) and not( (: Any numeric input :) expr/NUM_CONST (: A call using collapse= :) or SYMBOL_SUB[text() = 'collapse'] (: Consecutive non-strings like paste0(x, y) :) or expr[(SYMBOL or expr) and following-sibling::expr[1][SYMBOL or expr]] ) ] ") empty_paste_note <- 'Note that paste() converts empty inputs to "", whereas file.path() leaves it empty.' Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content optional_lints <- list() # Both of these look for paste(..., sep = "..."), differing in which 'sep' is linted, # so run the expensive XPath search/R parse only once if (!allow_empty_sep || check_file_paths) { paste_sep_expr <- xml_find_all(xml, paste_sep_xpath) paste_sep_value <- get_r_string(paste_sep_expr, xpath = "./SYMBOL_SUB[text() = 'sep']/following-sibling::expr[1]") } if (!allow_empty_sep) { optional_lints <- c(optional_lints, xml_nodes_to_lints( paste_sep_expr[!nzchar(paste_sep_value)], source_expression = source_expression, lint_message = 'paste0(...) is better than paste(..., sep = "").', type = "warning" )) } if (!allow_to_string) { # 3 expr: the function call, the argument, and collapse= to_string_expr <- xml_find_all(xml, to_string_xpath) collapse_value <- get_r_string( to_string_expr, xpath = "./SYMBOL_SUB[text() = 'collapse']/following-sibling::expr[1]" ) optional_lints <- c(optional_lints, xml_nodes_to_lints( to_string_expr[collapse_value == ", "], source_expression = source_expression, lint_message = paste( 'toString(.) is more expressive than paste(., collapse = ", ").', "Note also glue::glue_collapse() and and::and()", "for constructing human-readable / translation-friendly lists" ), type = "warning" )) } paste0_sep_expr <- xml_find_all(xml, paste0_sep_xpath) paste0_sep_lints <- xml_nodes_to_lints( paste0_sep_expr, source_expression = source_expression, lint_message = "sep= is not a formal argument to paste0(); did you mean to use paste(), or collapse=?", type = "warning" ) paste_strrep_expr <- xml_find_all(xml, paste_strrep_xpath) collapse_arg <- get_r_string(paste_strrep_expr, "SYMBOL_SUB/following-sibling::expr[1]/STR_CONST") paste_strrep_expr <- paste_strrep_expr[!nzchar(collapse_arg)] paste_call <- xp_call_name(paste_strrep_expr) paste_strrep_lints <- xml_nodes_to_lints( paste_strrep_expr, source_expression = source_expression, lint_message = sprintf('strrep(x, times) is better than %s(rep(x, times), collapse = "").', paste_call), type = "warning" ) if (check_file_paths) { paste_sep_slash_expr <- paste_sep_expr[paste_sep_value == "/"] optional_lints <- c(optional_lints, xml_nodes_to_lints( # in addition to paste(..., sep = "/") we ensure collapse= is not present paste_sep_slash_expr[is.na(xml_find_first(paste_sep_slash_expr, "./SYMBOL_SUB[text() = 'collapse']"))], source_expression = source_expression, lint_message = paste( 'Construct file paths with file.path(...) instead of paste(..., sep = "/").', 'If you are using paste(sep = "/") to construct a date,', "consider using format() or lubridate helpers instead.", empty_paste_note ), type = "warning" )) paste0_file_path_expr <- xml_find_all(xml, paste0_file_path_xpath) is_file_path <- !vapply(paste0_file_path_expr, check_is_not_file_path, logical(1L), allow_file_path = allow_file_path) optional_lints <- c(optional_lints, xml_nodes_to_lints( paste0_file_path_expr[is_file_path], source_expression = source_expression, lint_message = paste( 'Construct file paths with file.path(...) instead of paste0(x, "/", y, "/", z).', empty_paste_note ), type = "warning" )) } c(optional_lints, paste0_sep_lints, paste_strrep_lints) }) } check_is_not_file_path <- function(expr, allow_file_path) { args <- xml_find_all(expr, "expr[position() > 1]") is_string <- !is.na(xml_find_first(args, "STR_CONST")) string_values <- character(length(args)) string_values[is_string] <- get_r_string(args[is_string]) not_start_slash <- which(!startsWith(string_values, "/")) not_end_slash <- which(!endsWith(string_values, "/")) if (allow_file_path == "double_slash") { check_double_slash <- function(str) any(grepl("//", str, fixed = TRUE)) } else { # always skip on strings starting/ending with //, since switching to # file.path() would require file.path("", ...) or file.path(..., "") check_double_slash <- function(str) any(grepl("^//|//$", str)) } # First input is '/', meaning file.path() would need to start with '' string_values[1L] == "/" || # Last input is '/', meaning file.path() would need to end with '' string_values[length(string_values)] == "/" || check_double_slash(string_values) || # A string not ending with /, followed by non-string, # or a string not starting with /, preceded by a non-string !all(is_string[c(not_end_slash + 1L, not_start_slash - 1L)], na.rm = TRUE) || # A string not starting with / preceded by a string not ending with / any(not_start_slash %in% (not_end_slash + 1L)) } lintr/R/unreachable_code_linter.R0000644000176200001440000001154314577054223016563 0ustar liggesusers#' Block unreachable code and comments following return statements #' #' Code after e.g. a [return()] or [stop()] #' or in deterministically false conditional loops like `if (FALSE)` can't be reached; #' typically this is vestigial code left after refactoring or sandboxing code, which #' is fine for exploration, but shouldn't ultimately be checked in. Comments #' meant for posterity should be placed *before* the final `return()`. #' #' @examples #' # will produce lints #' code_lines <- "f <- function() {\n return(1 + 1)\n 2 + 2\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' code_lines <- "f <- if (FALSE) {\n 2 + 2\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' code_lines <- "f <- while (FALSE) {\n 2 + 2\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' # okay #' code_lines <- "f <- function() {\n return(1 + 1)\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' code_lines <- "f <- if (foo) {\n 2 + 2\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' code_lines <- "f <- while (foo) {\n 2 + 2\n}" #' writeLines(code_lines) #' lint( #' text = code_lines, #' linters = unreachable_code_linter() #' ) #' #' @evalRd rd_tags("unreachable_code_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unreachable_code_linter <- function() { expr_after_control <- " (//REPEAT | //ELSE | //FOR)/following-sibling::expr[1] | (//IF | //WHILE)/following-sibling::expr[2] " # NB: use not(OP-DOLLAR) to prevent matching process$stop(), #1051 xpath_return_stop <- glue(" ( {expr_after_control} | (//FUNCTION | //OP-LAMBDA)[following-sibling::expr[1]/*[1][self::OP-LEFT-BRACE]]/following-sibling::expr[1] ) /expr[expr[1][ not(OP-DOLLAR or OP-AT) and SYMBOL_FUNCTION_CALL[text() = 'return' or text() = 'stop'] ]] /following-sibling::*[ not(self::OP-RIGHT-BRACE or self::OP-SEMICOLON) and (not(self::COMMENT) or @line2 > preceding-sibling::*[1]/@line2) ][1] ") xpath_next_break <- glue(" ({expr_after_control}) /expr[NEXT or BREAK] /following-sibling::*[ not(self::OP-RIGHT-BRACE or self::OP-SEMICOLON) and (not(self::COMMENT) or @line2 > preceding-sibling::*[1]/@line2) ][1] ") xpath_if_while <- " (//WHILE | //IF) /following-sibling::expr[1][NUM_CONST[text() = 'FALSE']] /following-sibling::expr[1] " xpath_else <- " //IF[following-sibling::expr[1]/NUM_CONST[text() = 'TRUE']] /following-sibling::ELSE/following-sibling::expr[1] " handle_inline_conditions <- function(expr) { expr <- lapply( expr, function(x) { if (xml_name(xml2::xml_child(x)) == "OP-LEFT-BRACE") { xml_find_first(x, "expr") } else { x } } ) expr[vapply(expr, xml2::xml_length, integer(1L)) != 0L] } # exclude comments that start with a nolint directive drop_nolint_end_comment <- function(expr) { is_nolint_end_comment <- xml2::xml_name(expr) == "COMMENT" & re_matches(xml_text(expr), settings$exclude_end) expr[!is_nolint_end_comment] } Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content expr_return_stop <- xml_find_all(xml, xpath_return_stop) lints_return_stop <- xml_nodes_to_lints( drop_nolint_end_comment(expr_return_stop), source_expression = source_expression, lint_message = "Code and comments coming after a return() or stop() should be removed.", type = "warning" ) expr_next_break <- xml_find_all(xml, xpath_next_break) lints_next_break <- xml_nodes_to_lints( drop_nolint_end_comment(expr_next_break), source_expression = source_expression, lint_message = "Code and comments coming after a `next` or `break` should be removed.", type = "warning" ) expr_if_while <- handle_inline_conditions(xml_find_all(xml, xpath_if_while)) lints_if_while <- xml_nodes_to_lints( expr_if_while, source_expression = source_expression, lint_message = "Code inside a conditional loop with a deterministically false condition should be removed.", type = "warning" ) expr_else <- handle_inline_conditions(xml_find_all(xml, xpath_else)) lints_else <- xml_nodes_to_lints( expr_else, source_expression = source_expression, lint_message = "Code inside an else block after a deterministically true if condition should be removed.", type = "warning" ) c(lints_return_stop, lints_next_break, lints_if_while, lints_else) }) } lintr/R/vector_logic_linter.R0000644000176200001440000000643714577055422016007 0ustar liggesusers#' Enforce usage of scalar logical operators in conditional statements #' #' Usage of `&` in conditional statements is error-prone and inefficient. #' `condition` in `if (condition) expr` must always be of length 1, in which #' case `&&` is to be preferred. Ditto for `|` vs. `||`. #' #' This linter covers inputs to `if()` and `while()` conditions and to #' [testthat::expect_true()] and [testthat::expect_false()]. #' #' Note that because `&` and `|` are generics, it is possible that #' `&&` / `||` are not perfect substitutes because `&` is doing #' method dispatch in an incompatible way. #' #' Moreover, be wary of code that may have side effects, most commonly #' assignments. Consider `if ((a <- foo(x)) | (b <- bar(y))) { ... }` #' vs. `if ((a <- foo(x)) || (b <- bar(y))) { ... }`. Because `||` exits #' early, if `a` is `TRUE`, the second condition will never be evaluated #' and `b` will not be assigned. Such usage is not allowed by the Tidyverse #' style guide, and the code can easily be refactored by pulling the #' assignment outside the condition, so using `||` is still preferable. #' #' @examples #' # will produce lints #' lint( #' text = "if (TRUE & FALSE) 1", #' linters = vector_logic_linter() #' ) #' #' lint( #' text = "if (TRUE && (TRUE | FALSE)) 4", #' linters = vector_logic_linter() #' ) #' #' # okay #' lint( #' text = "if (TRUE && FALSE) 1", #' linters = vector_logic_linter() #' ) #' #' lint( #' text = "if (TRUE && (TRUE || FALSE)) 4", #' linters = vector_logic_linter() #' ) #' #' @evalRd rd_tags("vector_logic_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export vector_logic_linter <- function() { # ensures the expr is in the cond part of `if/while (cond) expr` -- # if on the XML parse tree is structured like # # | # # ... # <- loop condition # # ... # <- evaluation; includes BRACEs if present # # (here & below is optional) # ... # # we _don't_ want to match anything on the second expr, hence this xpath <- " (//AND | //OR)[ ancestor::expr[ not(preceding-sibling::OP-RIGHT-PAREN) and preceding-sibling::*[ self::IF or self::WHILE or self::expr[SYMBOL_FUNCTION_CALL[text() = 'expect_true' or text() = 'expect_false']] ] ] and not(ancestor::expr[ preceding-sibling::expr[last()][SYMBOL_FUNCTION_CALL[not(text() = 'expect_true' or text() = 'expect_false')]] or preceding-sibling::OP-LEFT-BRACKET ]) and not(parent::expr/expr[ STR_CONST or expr/SYMBOL_FUNCTION_CALL[text() = 'as.raw' or text() = 'as.octmode' or text() = 'as.hexmode'] ]) ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "Conditional expressions require scalar logical operators (&& and ||)", type = "warning" ) }) } lintr/R/lintr-package.R0000644000176200001440000000162514577052532014465 0ustar liggesusers#' Lintr #' #' Checks adherence to a given style, syntax errors, and possible semantic issues. #' Supports on the fly checking of R code edited with Emacs, Vim, and Sublime Text. #' #' @seealso [lint()], [lint_package()], [lint_dir()], [linters] #' @keywords internal "_PACKAGE" ## lintr namespace: start #' @importFrom cyclocomp cyclocomp #' @importFrom glue glue glue_collapse #' @importFrom rex rex regex re_matches re_substitutes character_class #' @importFrom stats na.omit #' @importFrom utils capture.output getParseData getTxtProgressBar globalVariables head relist #' setTxtProgressBar tail txtProgressBar #' @importFrom xml2 as_list #' xml_attr xml_find_all xml_find_chr xml_find_lgl xml_find_num xml_find_first xml_name xml_text #' @rawNamespace #' if (getRversion() >= "4.0.0") { #' importFrom(tools, R_user_dir) #' } else { #' importFrom(backports, R_user_dir) #' } ## lintr namespace: end NULL lintr/R/commas_linter.R0000644000176200001440000000571614577052532014605 0ustar liggesusers#' Commas linter #' #' Check that all commas are followed by spaces, but do not have spaces before them. #' #' @param allow_trailing If `TRUE`, the linter allows a comma to be followed #' directly by a closing bracket without a space. #' #' @examples #' # will produce lints #' lint( #' text = "switch(op , x = foo, y = bar)", #' linters = commas_linter() #' ) #' #' lint( #' text = "mean(x,trim = 0.2,na.rm = TRUE)", #' linters = commas_linter() #' ) #' #' lint( #' text = "x[ ,, drop=TRUE]", #' linters = commas_linter() #' ) #' #' lint( #' text = "x[1,]", #' linters = commas_linter() #' ) #' #' # okay #' lint( #' text = "switch(op, x = foo, y = bar)", #' linters = commas_linter() #' ) #' #' lint( #' text = "switch(op, x = , y = bar)", #' linters = commas_linter() #' ) #' #' lint( #' text = "mean(x, trim = 0.2, na.rm = TRUE)", #' linters = commas_linter() #' ) #' #' lint( #' text = "a[1, , 2, , 3]", #' linters = commas_linter() #' ) #' #' lint( #' text = "x[1,]", #' linters = commas_linter(allow_trailing = TRUE) #' ) #' #' @evalRd rd_tags("commas_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export commas_linter <- function(allow_trailing = FALSE) { # conditions are in carefully-chosen order for performance -- # an expression like c(a,b,c,....) with many elements can have # a huge number of preceding-siblings and the performance of # preceding-sibling::*[1][not(self::OP-COMMA)] is terrible. # This approach exits early on most nodes ('and' condition) # to avoid this. See #1340. xpath_before <- " //OP-COMMA[ @col1 != preceding-sibling::*[1]/@col2 + 1 and @line1 = preceding-sibling::*[1]/@line1 and not(preceding-sibling::*[1][self::OP-COMMA or self::EQ_SUB]) ]" xpath_after <- paste0( "//OP-COMMA[@line1 = following-sibling::*[1]/@line1 and @col1 = following-sibling::*[1]/@col1 - 1 ", if (allow_trailing) "and not(following-sibling::*[1][self::OP-RIGHT-BRACKET or self::RBB or self::OP-RIGHT-PAREN])", "]" ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content before_lints <- xml_nodes_to_lints( xml_find_all(xml, xpath_before), source_expression = source_expression, lint_message = "Commas should never have a space before.", range_start_xpath = "number(./preceding-sibling::*[1]/@col2 + 1)", # start after preceding expression range_end_xpath = "number(./@col1 - 1)" # end before comma ) after_lints <- xml_nodes_to_lints( xml_find_all(xml, xpath_after), source_expression = source_expression, lint_message = "Commas should always have a space after.", range_start_xpath = "number(./@col2 + 1)", # start and end after comma range_end_xpath = "number(./@col2 + 1)" ) c(before_lints, after_lints) }) } lintr/R/namespace_linter.R0000644000176200001440000001270714577052532015260 0ustar liggesusers#' Namespace linter #' #' Check for missing packages and symbols in namespace calls. #' Note that using `check_exports=TRUE` or `check_nonexports=TRUE` will load packages used in user code so it could #' potentially change the global state. #' #' @param check_exports Check if `symbol` is exported from `namespace` in `namespace::symbol` calls. #' @param check_nonexports Check if `symbol` exists in `namespace` in `namespace:::symbol` calls. #' #' @examples #' # will produce lints #' lint( #' text = "xyzxyz::sd(c(1, 2, 3))", #' linters = namespace_linter() #' ) #' #' lint( #' text = "stats::ssd(c(1, 2, 3))", #' linters = namespace_linter() #' ) #' #' # okay #' lint( #' text = "stats::sd(c(1, 2, 3))", #' linters = namespace_linter() #' ) #' #' lint( #' text = "stats::ssd(c(1, 2, 3))", #' linters = namespace_linter(check_exports = FALSE) #' ) #' #' lint( #' text = "stats:::ssd(c(1, 2, 3))", #' linters = namespace_linter(check_nonexports = FALSE) #' ) #' #' @evalRd rd_tags("namespace_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export namespace_linter <- function(check_exports = TRUE, check_nonexports = TRUE) { Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content ns_nodes <- xml_find_all(xml, "//NS_GET | //NS_GET_INT") if (length(ns_nodes) == 0L) { return(list()) } ## Case 1: pkg is uninstalled in pkg::foo package_nodes <- xml_find_all(ns_nodes, "preceding-sibling::*[1]") packages <- get_r_string(package_nodes) lints <- list() # run here, not in the factory, to allow for run- vs. "compile"-time differences in available packages installed_packages <- .packages(all.available = TRUE) installed <- packages %in% installed_packages if (!all(installed)) { lints <- c(lints, xml_nodes_to_lints( package_nodes[!installed], source_expression = source_expression, lint_message = sprintf("Package '%s' is not installed.", packages[!installed]), type = "warning" )) ns_nodes <- ns_nodes[installed] packages <- packages[installed] package_nodes <- package_nodes[installed] } if (!check_exports && !check_nonexports) { return(lints) } ## Case 2/3/4: problems with foo in pkg::foo / pkg:::foo # run here, not in the factory, to allow for run- vs. "compile"-time differences in package structure namespaces <- lapply(packages, function(package) tryCatch(getNamespace(package), error = identity)) failed_namespace <- vapply(namespaces, inherits, "condition", FUN.VALUE = logical(1L)) # nocov start if (any(failed_namespace)) { stop( "Failed to retrieve namespaces for one or more of the packages used with `::` or `:::`. ", "Please report the issue at https://github.com/r-lib/lintr/issues." ) } # nocov end ns_get <- xml_text(ns_nodes) == "::" symbol_nodes <- xml_find_all(ns_nodes, "following-sibling::*[1]") symbols <- get_r_string(symbol_nodes) if (check_nonexports) { lints <- c(lints, build_ns_get_int_lints( packages[!ns_get], symbols[!ns_get], symbol_nodes[!ns_get], namespaces[!ns_get], source_expression )) } if (check_exports) { lints <- c(lints, build_ns_get_lints( packages[ns_get], symbols[ns_get], symbol_nodes[ns_get], namespaces[ns_get], source_expression )) } lints }) } namespace_symbols <- function(ns, exported = TRUE) { if (exported) { c(getNamespaceExports(ns), names(.getNamespaceInfo(ns, "lazydata"))) } else { ls(ns, all.names = TRUE) } } is_in_pkg <- function(symbols, namespaces, exported = TRUE) { vapply( seq_along(symbols), function(ii) symbols[[ii]] %in% namespace_symbols(namespaces[[ii]], exported = exported), logical(1L) ) } build_ns_get_int_lints <- function(packages, symbols, symbol_nodes, namespaces, source_expression) { ## Case 2: foo does not exist in pkg:::foo non_symbols <- !is_in_pkg(symbols, namespaces, exported = FALSE) non_symbols_lints <- xml_nodes_to_lints( symbol_nodes[non_symbols], source_expression = source_expression, lint_message = sprintf("'%s' does not exist in {%s}.", symbols[non_symbols], packages[non_symbols]), type = "warning" ) packages <- packages[!non_symbols] symbols <- symbols[!non_symbols] symbol_nodes <- symbol_nodes[!non_symbols] namespaces <- namespaces[!non_symbols] ## Case 3: foo is already exported pkg:::foo exported <- is_in_pkg(symbols, namespaces) exported_lints <- xml_nodes_to_lints( symbol_nodes[exported], source_expression = source_expression, lint_message = sprintf("'%1$s' is exported from {%2$s}. Use %2$s::%1$s instead.", symbols[exported], packages[exported]), type = "warning" ) c(non_symbols_lints, exported_lints) } build_ns_get_lints <- function(packages, symbols, symbol_nodes, namespaces, source_expression) { # strip backticked symbols; `%>%` is the same as %>% (#1752). symbols <- gsub("^`(.*)`$", "\\1", symbols) ## Case 4: foo is not an export in pkg::foo unexported <- !is_in_pkg(symbols, namespaces) xml_nodes_to_lints( symbol_nodes[unexported], source_expression = source_expression, lint_message = sprintf("'%s' is not exported from {%s}.", symbols[unexported], packages[unexported]), type = "warning" ) } lintr/R/object_name_linter.R0000644000176200001440000001644614577052532015576 0ustar liggesusers#' Object name linter #' #' Check that object names conform to a naming style. #' The default naming styles are "snake_case" and "symbols". #' #' Quotes (`` `"' ``) and specials (`%` and trailing `<-`) are not considered part of the object name. #' #' Note when used in a package, in order to ignore objects imported #' from other namespaces, this linter will attempt [getNamespaceExports()] #' whenever an `import(PKG)` or `importFrom(PKG, ...)` statement is found #' in your NAMESPACE file. If [requireNamespace()] fails (e.g., the package #' is not yet installed), the linter won't be able to ignore some usages #' that would otherwise be allowed. #' #' Suppose, for example, you have `import(upstream)` in your NAMESPACE, #' which makes available its exported S3 generic function #' `a_really_quite_long_function_name` that you then extend in your package #' by defining a corresponding method for your class `my_class`. #' Then, if `upstream` is not installed when this linter runs, a lint #' will be thrown on this object (even though you don't "own" its full name). #' #' The best way to get lintr to work correctly is to install the package so #' that it's available in the session where this linter is running. #' #' @param styles A subset of #' \Sexpr[stage=render, results=rd]{lintr:::regexes_rd}. A name should #' match at least one of these styles. The `"symbols"` style refers to #' names containing *only* non-alphanumeric characters; e.g., defining `%+%` #' from ggplot2 or `%>%` from magrittr would not generate lint markers, #' whereas `%m+%` from lubridate (containing both alphanumeric *and* #' non-alphanumeric characters) would. #' #' @param regexes A (possibly named) character vector specifying a custom naming convention. #' If named, the names will be used in the lint message. Otherwise, the regexes enclosed by `/` will be used in the #' lint message. #' Note that specifying `regexes` overrides the default `styles`. So if you want to combine `regexes` and `styles`, #' both need to be explicitly specified. #' #' @examples #' # will produce lints #' lint( #' text = "my_var <- 1L", #' linters = object_name_linter(styles = "CamelCase") #' ) #' #' lint( #' text = "xYz <- 1L", #' linters = object_name_linter(styles = c("UPPERCASE", "lowercase")) #' ) #' #' lint( #' text = "MyVar <- 1L", #' linters = object_name_linter(styles = "dotted.case") #' ) #' #' lint( #' text = "asd <- 1L", #' linters = object_name_linter(regexes = c(my_style = "F$", "f$")) #' ) #' #' # okay #' lint( #' text = "my_var <- 1L", #' linters = object_name_linter(styles = "snake_case") #' ) #' #' lint( #' text = "xyz <- 1L", #' linters = object_name_linter(styles = "lowercase") #' ) #' #' lint( #' text = "my.var <- 1L; myvar <- 2L", #' linters = object_name_linter(styles = c("dotted.case", "lowercase")) #' ) #' #' lint( #' text = "asdf <- 1L; asdF <- 1L", #' linters = object_name_linter(regexes = c(my_style = "F$", "f$")) #' ) #' #' @evalRd rd_tags("object_name_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export object_name_linter <- function(styles = c("snake_case", "symbols"), regexes = character()) { if ((!missing(styles) || missing(regexes)) && length(styles) > 0L) { # Allow `object_name_linter(NULL, "my_regex")` styles <- match.arg(styles, names(style_regexes), several.ok = TRUE) style_list <- style_regexes[styles] } else { style_list <- list() } if (length(regexes) > 0L) { if (!is.character(regexes)) { stop("`regexes` must be a character vector.") } rx_names <- names2(regexes) missing_name <- !nzchar(rx_names) rx_names[missing_name] <- paste0("/", regexes[missing_name], "/") # auto-name regex "asd" -> /asd/ names(regexes) <- rx_names style_list <- c(style_list, as.list(regexes)) } if (length(style_list) == 0L) { stop("At least one style must be specified using `styles` or `regexes`.") } lint_message <- paste0( "Variable and function name style should match ", glue_collapse(unique(names(style_list)), sep = ", ", last = " or "), "." ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content assignments <- xml_find_all(xml, object_name_xpath) # Retrieve assigned name nms <- strip_names( xml_text(assignments) ) # run namespace_imports at run-time, not "compile" time to allow package structure to change pkg <- find_package(source_expression$filename) generics <- c( declared_s3_generics(xml), imported_s3_generics(namespace_imports(pkg))$fun, exported_s3_generics(pkg)$fun, .base_s3_generics ) generics <- unique(generics[nzchar(generics)]) style_matches <- lapply(style_list, function(style) { check_style(nms, style, generics) }) matches_a_style <- Reduce(`|`, style_matches) xml_nodes_to_lints( assignments[!matches_a_style], source_expression, lint_message = lint_message, type = "style" ) }) } check_style <- function(nms, style, generics = character()) { conforming <- re_matches(nms, style) # mark empty names and NA names as conforming conforming[!nzchar(nms) | is.na(conforming)] <- TRUE if (!all(conforming)) { possible_s3 <- re_matches( nms[!conforming], rex(start, capture(name = "generic", or(generics)), ".", capture(name = "method", something), end) ) if (!all(is.na(possible_s3))) { has_generic <- possible_s3$generic %in% generics # If they are not conforming, but are S3 methods then ignore them conforming[!conforming][has_generic] <- TRUE } # exclude namespace hooks like .onLoad, .Last.lib, etc (#500) and ... is_special <- is_special_function(nms[!conforming]) | nms[!conforming] == "..." conforming[!conforming][is_special] <- TRUE } conforming } # see ?".onLoad", ?Startup, and ?quit. Remove leading dot to match behavior of strip_names(). # All of .onLoad, .onAttach, and .onUnload are used in base packages, # and should be caught in is_base_function; they're included here for completeness / stability # (they don't strictly _have_ to be defined in base, so could in principle be removed). # .Last.sys and .First.sys are part of base itself, so aren't included here. special_funs <- c( ".onLoad", ".onAttach", ".onUnload", ".onDetach", ".Last.lib", ".First", ".Last" ) is_special_function <- function(x) { x %in% special_funs } loweralnum <- rex(one_of(lower, digit)) upperalnum <- rex(one_of(upper, digit)) style_regexes <- list( symbols = rex(start, zero_or_more(none_of(alnum)), end), CamelCase = rex(start, maybe("."), upper, zero_or_more(alnum), end), camelCase = rex(start, maybe("."), lower, zero_or_more(alnum), end), snake_case = rex(start, maybe("."), some_of(lower, digit), any_of("_", lower, digit), end), SNAKE_CASE = rex(start, maybe("."), some_of(upper, digit), any_of("_", upper, digit), end), dotted.case = rex(start, maybe("."), one_or_more(loweralnum), zero_or_more(dot, one_or_more(loweralnum)), end), lowercase = rex(start, maybe("."), one_or_more(loweralnum), end), UPPERCASE = rex(start, maybe("."), one_or_more(upperalnum), end) ) regexes_rd <- toString(paste0("\\sQuote{", names(style_regexes), "}")) lintr/R/lengths_linter.R0000644000176200001440000000213614577052532014763 0ustar liggesusers#' Require usage of `lengths()` where possible #' #' [lengths()] is a function that was added to base R in version 3.2.0 to #' get the length of each element of a list. It is equivalent to #' `sapply(x, length)`, but faster and more readable. #' #' @examples #' # will produce lints #' lint( #' text = "sapply(x, length)", #' linters = lengths_linter() #' ) #' #' lint( #' text = "vapply(x, length, integer(1L))", #' linters = lengths_linter() #' ) #' #' lint( #' text = "purrr::map_int(x, length)", #' linters = lengths_linter() #' ) #' #' # okay #' lint( #' text = "lengths(x)", #' linters = lengths_linter() #' ) #' #' @evalRd rd_tags("lengths_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export lengths_linter <- local({ loop_funs <- c("sapply", "vapply", "map_int", "map_dbl") make_linter_from_xpath( xpath = glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(loop_funs)} ] /parent::expr /parent::expr[expr/SYMBOL[text() = 'length']] "), lint_message = "Use lengths() to find the length of each element in a list." ) }) lintr/R/expect_null_linter.R0000644000176200001440000000424414577052532015643 0ustar liggesusers#' Require usage of `expect_null` for checking `NULL` #' #' Require usage of `expect_null(x)` over `expect_equal(x, NULL)` and similar #' usages. #' #' [testthat::expect_null()] exists specifically for testing for `NULL` objects. #' [testthat::expect_equal()], [testthat::expect_identical()], and #' [testthat::expect_true()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = "expect_equal(x, NULL)", #' linters = expect_null_linter() #' ) #' #' lint( #' text = "expect_identical(x, NULL)", #' linters = expect_null_linter() #' ) #' #' lint( #' text = "expect_true(is.null(x))", #' linters = expect_null_linter() #' ) #' #' #' # okay #' lint( #' text = "expect_null(x)", #' linters = expect_null_linter() #' ) #' #' @evalRd rd_tags("expect_null_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_null_linter <- function() { # two cases two match: # (1) expect_{equal,identical}(x, NULL) (or NULL, x) # (2) expect_true(is.null(x)) expect_equal_identical_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[position() <= 2 and NULL_CONST] /parent::expr " expect_true_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][expr[1]/SYMBOL_FUNCTION_CALL[text() = 'is.null']] /parent::expr " xpath <- paste(expect_equal_identical_xpath, "|", expect_true_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_function <- xp_call_name(bad_expr) msg <- ifelse( matched_function %in% c("expect_equal", "expect_identical"), sprintf("expect_null(x) is better than %s(x, NULL)", matched_function), "expect_null(x) is better than expect_true(is.null(x))" ) xml_nodes_to_lints( bad_expr, source_expression, lint_message = msg, type = "warning" ) }) } lintr/R/undesirable_function_linter.R0000644000176200001440000000652514577052532017527 0ustar liggesusers#' Undesirable function linter #' #' Report the use of undesirable functions (e.g. [base::return()], [base::options()], or #' [base::sapply()]) and suggest an alternative. #' #' @param fun Named character vector. `names(fun)` correspond to undesirable functions, #' while the values give a description of why the function is undesirable. #' If `NA`, no additional information is given in the lint message. Defaults to #' [default_undesirable_functions]. To make small customizations to this list, #' use [modify_defaults()]. #' @param symbol_is_undesirable Whether to consider the use of an undesirable function #' name as a symbol undesirable or not. #' #' @examples #' # defaults for which functions are considered undesirable #' names(default_undesirable_functions) #' #' # will produce lints #' lint( #' text = "sapply(x, mean)", #' linters = undesirable_function_linter() #' ) #' #' lint( #' text = "log10(x)", #' linters = undesirable_function_linter(fun = c("log10" = NA)) #' ) #' #' lint( #' text = "log10(x)", #' linters = undesirable_function_linter(fun = c("log10" = "use log()")) #' ) #' #' lint( #' text = 'dir <- "path/to/a/directory"', #' linters = undesirable_function_linter(fun = c("dir" = NA)) #' ) #' #' # okay #' lint( #' text = "vapply(x, mean, FUN.VALUE = numeric(1))", #' linters = undesirable_function_linter() #' ) #' #' lint( #' text = "log(x, base = 10)", #' linters = undesirable_function_linter(fun = c("log10" = "use log()")) #' ) #' #' lint( #' text = 'dir <- "path/to/a/directory"', #' linters = undesirable_function_linter(fun = c("dir" = NA), symbol_is_undesirable = FALSE) #' ) #' #' @evalRd rd_tags("undesirable_function_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export undesirable_function_linter <- function(fun = default_undesirable_functions, symbol_is_undesirable = TRUE) { stopifnot(is.logical(symbol_is_undesirable)) if (is.null(names(fun)) || !all(nzchar(names(fun))) || length(fun) == 0L) { stop("'fun' should be a non-empty named character vector; use missing elements to indicate default messages.") } xp_condition <- xp_and( xp_text_in_table(names(fun)), paste0( "not(parent::expr/preceding-sibling::expr[last()][SYMBOL_FUNCTION_CALL[", xp_text_in_table(c("library", "require")), "]])" ), "not(parent::expr[OP-DOLLAR or OP-AT])" ) if (symbol_is_undesirable) { xpath <- glue("//SYMBOL_FUNCTION_CALL[{xp_condition}] | //SYMBOL[{xp_condition}]") } else { xpath <- glue("//SYMBOL_FUNCTION_CALL[{xp_condition}]") } Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } matched_nodes <- xml_find_all(source_expression$xml_parsed_content, xpath) fun_names <- get_r_string(matched_nodes) msgs <- vapply( stats::setNames(nm = unique(fun_names)), function(fun_name) { msg <- sprintf('Function "%s" is undesirable.', fun_name) alternative <- fun[[fun_name]] if (!is.na(alternative)) { msg <- paste(msg, sprintf("As an alternative, %s.", alternative)) } msg }, character(1L) ) xml_nodes_to_lints( matched_nodes, source_expression = source_expression, lint_message = unname(msgs[fun_names]) ) }) } lintr/R/literal_coercion_linter.R0000644000176200001440000001042414577052532016633 0ustar liggesusers#' Require usage of correctly-typed literals over literal coercions #' #' `as.integer(1)` (or `rlang::int(1)`) is the same as `1L` but the latter is #' more concise and gets typed correctly at compilation. #' #' The same applies to missing sentinels like `NA` -- typically, it is not #' necessary to specify the storage type of `NA`, but when it is, prefer #' using the typed version (e.g. `NA_real_`) instead of a coercion #' (like `as.numeric(NA)`). #' #' @examples #' # will produce lints #' lint( #' text = "int(1)", #' linters = literal_coercion_linter() #' ) #' #' lint( #' text = "as.character(NA)", #' linters = literal_coercion_linter() #' ) #' #' lint( #' text = "rlang::lgl(1L)", #' linters = literal_coercion_linter() #' ) #' #' # okay #' lint( #' text = "1L", #' linters = literal_coercion_linter() #' ) #' #' lint( #' text = "NA_character_", #' linters = literal_coercion_linter() #' ) #' #' lint( #' text = "TRUE", #' linters = literal_coercion_linter() #' ) #' #' @evalRd rd_tags("literal_coercion_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export literal_coercion_linter <- function() { rlang_coercers <- c("lgl", "int", "dbl", "chr") coercers <- xp_text_in_table(c( # base coercers paste0("as.", c("logical", "integer", "numeric", "double", "character")), rlang_coercers )) # notes for clarification: # - as.integer(1e6) is arguably easier to read than 1000000L # - in x$"abc", the "abc" STR_CONST is at the top level, so exclude OP-DOLLAR (ditto OP-AT) # - need condition against STR_CONST w/ EQ_SUB to skip quoted keyword arguments (see tests) # - for {rlang} coercers, both `int(1)` and `int(1, )` need to be linted not_extraction_or_scientific <- " not(OP-DOLLAR or OP-AT) and ( NUM_CONST[not(contains(translate(text(), 'E', 'e'), 'e'))] or STR_CONST[not(following-sibling::*[1][self::EQ_SUB])] ) " xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {coercers} ] /parent::expr /parent::expr[ count(expr) = 2 and expr[2][ {not_extraction_or_scientific} ] ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) coercer <- xp_call_name(bad_expr) # tiptoe around the fact that we don't require {rlang} is_rlang_coercer <- coercer %in% rlang_coercers if (any(is_rlang_coercer) && !requireNamespace("rlang", quietly = TRUE)) { # nocov start: test suite will have 'rlang' available # NB: we _could_ do some extreme customization where each lint # gets a message according to whether the coercer is from rlang, # but this seems like overkill. Just use a generic message and move on. lint_message <- paste( "Use literals directly where possible, instead of coercion.", "c.f. 1L instead of as.integer(1) or rlang::int(1), or NA_real_ instead of as.numeric(NA).", "NB: this message can be improved to show a specific replacement if 'rlang' is installed." ) # nocov end } else { # duplicate, unless we add 'rlang::' and it wasn't there originally coercion_str <- report_str <- xml_text(bad_expr) if (any(is_rlang_coercer) && !("package:rlang" %in% search())) { needs_prefix <- is_rlang_coercer & !startsWith(coercion_str, "rlang::") coercion_str[needs_prefix] <- paste0("rlang::", coercion_str[needs_prefix]) } # the linter logic & rlang requirement should ensure that it's safe to run eval() here # TODO(michaelchirico): this recommends '1' to replace as.numeric(1), where our # own implicit_integer_linter(), if active, would require this to be 1.0. Should # we recommend this instead, or offer it as an alternative? literal_equivalent_str <- vapply(str2expression(coercion_str), function(expr) deparse1(eval(expr)), character(1L)) lint_message <- sprintf( "Use %s instead of %s, i.e., use literals directly where possible, instead of coercion.", literal_equivalent_str, report_str ) } xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = lint_message, type = "warning" ) }) } lintr/R/boolean_arithmetic_linter.R0000644000176200001440000000434714577052532017155 0ustar liggesusers#' Require usage of boolean operators over equivalent arithmetic #' #' `length(which(x == y)) == 0` is the same as `!any(x == y)`, but the latter #' is more readable and more efficient. #' #' @examples #' # will produce lints #' lint( #' text = "length(which(x == y)) == 0L", #' linters = boolean_arithmetic_linter() #' ) #' #' lint( #' text = "sum(grepl(pattern, x)) == 0", #' linters = boolean_arithmetic_linter() #' ) #' #' # okay #' lint( #' text = "!any(x == y)", #' linters = boolean_arithmetic_linter() #' ) #' #' lint( #' text = "!any(grepl(pattern, x))", #' linters = boolean_arithmetic_linter() #' ) #' #' @evalRd rd_tags("boolean_arithmetic_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export boolean_arithmetic_linter <- function() { # TODO(#1580): sum() cases x %in% y, A [&|] B, !A, is.na/is.nan/is.finite/is.infinite/is.element # TODO(#1581): extend to include all()-alike expressions zero_expr <- "(EQ or NE or GT or LE) and expr[NUM_CONST[text() = '0' or text() = '0L']]" one_expr <- "(LT or GE) and expr[NUM_CONST[text() = '1' or text() = '1L']]" length_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'which' or text() = 'grep'] /parent::expr /parent::expr /parent::expr[ expr[SYMBOL_FUNCTION_CALL[text() = 'length']] and parent::expr[ ({zero_expr}) or ({one_expr})] ] ") sum_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'sum'] /parent::expr /parent::expr[ expr[ expr[SYMBOL_FUNCTION_CALL[text() = 'grepl']] or (EQ or NE or GT or LT or GE or LE) ] and parent::expr[ ({zero_expr}) or ({one_expr})] ] ") any_xpath <- paste(length_xpath, "|", sum_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content any_expr <- xml_find_all(xml, any_xpath) xml_nodes_to_lints( any_expr, source_expression = source_expression, # TODO(michaelchirico): customize this? lint_message = paste( "Use any() to express logical aggregations.", "For example, replace length(which(x == y)) == 0 with !any(x == y)." ), type = "warning" ) }) } lintr/R/settings_utils.R0000644000176200001440000000565014577052532015026 0ustar liggesusershas_description <- function(path) { desc_info <- file.info(file.path(path, "DESCRIPTION")) !is.na(desc_info$size) && desc_info$size > 0.0 && !desc_info$isdir } has_rproj <- function(path) { length(Sys.glob(file.path(path, "*.Rproj"))) > 0L } find_package <- function(path, allow_rproj = FALSE, max_depth = 2L) { path <- normalizePath(path, mustWork = !allow_rproj) if (allow_rproj) { found <- function(path) has_description(path) || has_rproj(path) } else { found <- function(path) has_description(path) } depth <- max_depth while (!found(path)) { path <- dirname(path) if (is_root(path) || depth <= 0L) { return(NULL) } depth <- depth - 1L } path } find_rproj_at <- function(path) { head(Sys.glob(file.path(path, "*.Rproj")), n = 1L) } is_root <- function(path) { identical(path, dirname(path)) } is_directory <- function(filename) isTRUE(file.info(filename)$isdir) #' Return the first of a vector of files that exists. #' #' Avoid running 'expensive' [file.exists()] for the full vector, #' since typically the first entries will lead to early exit. #' TODO(#2204): check if the implementation should be simpler #' @noRd first_exists <- function(files) { for (file in files) { if (file.exists(file)) { return(file) } } NULL } find_config <- function(filename) { if (is.null(filename)) { return(NULL) } linter_file <- lintr_option("linter_file") ## if users changed lintr.linter_file, return immediately. if (is_absolute_path(linter_file) && file.exists(linter_file)) { return(linter_file) } path <- if (is_directory(filename)) { filename } else { dirname(filename) } path <- normalizePath(path, mustWork = FALSE) # NB: This vector specifies a priority order for where to find the configs, # i.e. the first location where a config exists is chosen and configs which # may exist in subsequent directories are ignored file_locations <- c( # Local (incl. parent) directories find_local_config(path, basename(linter_file)), # User directory # cf: rstudio@bc9b6a5 SessionRSConnect.R#L32 file.path(Sys.getenv("HOME", unset = "~"), linter_file), # Next check for a global config file file.path(R_user_dir("lintr", which = "config"), "config") ) first_exists(file_locations) } find_local_config <- function(path, config_file) { # R config gets precedence configs_to_check <- c(paste0(config_file, ".R"), config_file) repeat { guesses_in_dir <- c( file.path(path, configs_to_check), file.path(path, ".github", "linters", configs_to_check) ) found <- first_exists(guesses_in_dir) if (!is.null(found)) { return(found) } path <- dirname(path) if (is_root(path)) { return(character()) } } } pkg_name <- function(path = find_package()) { if (is.null(path)) { return(NULL) } else { read.dcf(file.path(path, "DESCRIPTION"), fields = "Package")[1L] } } lintr/R/make_linter_from_regex.R0000644000176200001440000000474414577052532016460 0ustar liggesusersmake_linter_from_regex <- function(regex, lint_type, lint_msg) { function() { Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } all_matches <- re_matches( source_expression[["file_lines"]], regex, locations = TRUE, global = FALSE ) all_matches <- all_matches[!is.na(all_matches$start), ] all_matches$line_number <- as.integer(rownames(all_matches)) matches_by_row <- split(all_matches, seq_len(nrow(all_matches))) lints <- lapply(matches_by_row, function(.match) { if (is_match_covered(.match, source_expression)) { return() } Lint( filename = source_expression[["filename"]], line_number = .match$line_number, type = lint_type, message = lint_msg, line = source_expression[["file_lines"]][[rownames(.match)]], ranges = list(c(.match$start, .match$end)) ) }) lints[lengths(lints) > 0L] }) } } #' Determine if a regex match is covered by an expression in a source_expression #' #' @param match The position where a regex match was observed. #' match must have entries "start", "end", and "line_number". #' @param source_expression A source_expression #' @param token_type Restrict analysis to tokens of this type, for example, #' with token_type = "STR_CONST" you can check that a regex match occurs #' within a string #' @noRd is_match_covered <- function(match, source_expression, token_type = "STR_CONST") { line_number <- match$line_number pc <- source_expression[["full_parsed_content"]] if (!is.null(token_type)) { pc <- pc[pc[["token"]] == token_type, ] } covering_rows <- pc[["line1"]] <= line_number & pc[["line2"]] >= line_number pc_cover <- pc[covering_rows, ] any_single_line_covers <- function() { x <- pc_cover[pc_cover[["line1"]] == pc_cover[["line2"]], ] any( x[["col1"]] <= match[["start"]] & x[["col2"]] >= match[["end"]] ) } any_multi_line_covers <- function() { x <- pc_cover[pc_cover[["line1"]] < pc_cover[["line2"]], ] any( (x[["line1"]] < line_number & x[["line2"]] > line_number) | (x[["line1"]] == line_number & x[["col1"]] <= match[["start"]]) | (x[["line2"]] == line_number & x[["col2"]] >= match[["end"]]) ) } any_single_line_covers() || any_multi_line_covers() } lintr/R/seq_linter.R0000644000176200001440000000676014577052532014116 0ustar liggesusers#' Sequence linter #' #' This linter checks for `1:length(...)`, `1:nrow(...)`, `1:ncol(...)`, #' `1:NROW(...)` and `1:NCOL(...)` expressions in base-R, or their usage in #' conjunction with `seq()` (e.g., `seq(length(...))`, `seq(nrow(...))`, etc.). #' #' Additionally, it checks for `1:n()` (from `{dplyr}`) and `1:.N` (from `{data.table}`). #' #' These often cause bugs when the right-hand side is zero. #' It is safer to use [base::seq_len()] or [base::seq_along()] instead. #' #' @examples #' # will produce lints #' lint( #' text = "seq(length(x))", #' linters = seq_linter() #' ) #' #' lint( #' text = "1:nrow(x)", #' linters = seq_linter() #' ) #' #' lint( #' text = "dplyr::mutate(x, .id = 1:n())", #' linters = seq_linter() #' ) #' #' # okay #' lint( #' text = "seq_along(x)", #' linters = seq_linter() #' ) #' #' lint( #' text = "seq_len(nrow(x))", #' linters = seq_linter() #' ) #' #' lint( #' text = "dplyr::mutate(x, .id = seq_len(n()))", #' linters = seq_linter() #' ) #' #' @evalRd rd_tags("seq_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export seq_linter <- function() { bad_funcs <- xp_text_in_table(c("length", "n", "nrow", "ncol", "NROW", "NCOL", "dim")) # Exact `xpath` depends on whether bad function was used in conjunction with `seq()` seq_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'seq'] /parent::expr /following-sibling::expr[1][expr/SYMBOL_FUNCTION_CALL[ {bad_funcs} ]] /parent::expr[count(expr) = 2] ") # `.N` from {data.table} is special since it's not a function but a symbol colon_xpath <- glue(" //OP-COLON /parent::expr[ expr[NUM_CONST[text() = '1' or text() = '1L']] and ( expr[expr[(expr|self::*)[SYMBOL_FUNCTION_CALL[ {bad_funcs} ]]]] or expr[SYMBOL = '.N'] ) ] ") xpath <- paste(seq_xpath, "|", colon_xpath) ## The actual order of the nodes is document order ## In practice we need to handle length(x):1 get_fun <- function(expr, n) { funcall <- xml_find_chr(expr, sprintf("string(./expr[%d])", n)) # `dplyr::n()` is special because it has no arguments, so the lint message # should mention `n()`, and not `n(...)` if (identical(funcall, "n()")) { return(funcall) } fun <- gsub("\\(.*\\)", "(...)", trimws(funcall)) bad_fun <- fun %in% bad_funcs fun[bad_fun] <- paste0(fun[bad_fun], "(...)") fun } Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content badx <- xml_find_all(xml, xpath) dot_expr1 <- get_fun(badx, 1L) dot_expr2 <- get_fun(badx, 2L) seq_along_idx <- grepl("length(", dot_expr1, fixed = TRUE) | grepl("length(", dot_expr2, fixed = TRUE) rev_idx <- startsWith(dot_expr2, "1") replacement <- rep("seq_along(...)", length(badx)) replacement[!seq_along_idx] <- paste0("seq_len(", ifelse(rev_idx, dot_expr1, dot_expr2)[!seq_along_idx], ")") replacement[rev_idx] <- paste0("rev(", replacement[rev_idx], ")") lint_message <- ifelse( grepl("seq", dot_expr1, fixed = TRUE), sprintf( "%s(%s) is likely to be wrong in the empty edge case. Use %s instead.", dot_expr1, dot_expr2, replacement ), sprintf( "%s:%s is likely to be wrong in the empty edge case. Use %s instead.", dot_expr1, dot_expr2, replacement ) ) xml_nodes_to_lints(badx, source_expression, lint_message, type = "warning") }) } lintr/R/spaces_left_parentheses_linter.R0000644000176200001440000000575714506330025020210 0ustar liggesusers#' Spaces before parentheses linter #' #' Check that all left parentheses have a space before them unless they are in a function call. #' #' @examples #' # will produce lints #' lint( #' text = "if(TRUE) x else y", #' linters = spaces_left_parentheses_linter() #' ) #' #' # okay #' lint( #' text = "if (TRUE) x else y", #' linters = spaces_left_parentheses_linter() #' ) #' #' @evalRd rd_tags("spaces_left_parentheses_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - [function_left_parentheses_linter()] #' @export spaces_left_parentheses_linter <- function() { file_level_xpath <- "//OP-LEFT-PAREN[@start - 1 = ancestor::expr/preceding-sibling::OP-SEMICOLON/@end]" # apply the lint by requiring a gap in three cases: # (1) if/while loop conditions, e.g. 'if(x>2) { }', including 'else(' # (2) for loop conditions, e.g. 'for(i in 1:5) { }' [very similar to (1) in code but different in XML] # (3) non-unary infix operators, e.g. 'x&(y | z)', and including commas, braces, e.g. 'c(a,(a+b))' # -1 on LHS because, when RHS matches nothing, +1 tricks the condition into returning true... # while @start will always be there, the @end may not be. if_while_cond <- "@start - 1 = preceding-sibling::*[self::IF or self::WHILE]/@end" for_cond <- "@start - 1 = parent::forcond/preceding-sibling::FOR/@end" # see infix_spaces_linter.R; preceding-sibling::* is needed for unary operators where '-(a)' is ok unary_nodes <- infix_metadata[infix_metadata$unary, "xml_tag"] unary_selves <- paste0("self::", unary_nodes, "[preceding-sibling::*]", collapse = " or ") binary_nodes <- c( infix_metadata[infix_metadata$low_precedence & !infix_metadata$unary, "xml_tag"], "OP-COMMA", "OP-LEFT-BRACE", "ELSE", "IN" ) binary_selves <- paste0("self::", binary_nodes, collapse = " or ") # preceding-symbol::* catches (1) function definitions and (2) function calls # ancestor::expr needed for nested RHS expressions, e.g. 'y1<-(abs(yn)>90)*1' infix_cond <- sprintf( "not(preceding-sibling::*) and (@start - 1 = ancestor::expr/preceding-sibling::*[%s]/@end)", paste(unary_selves, "or", binary_selves) ) expression_level_xpath <- sprintf( "//OP-LEFT-PAREN[(%s) or (%s) or (%s)]", if_while_cond, for_cond, infix_cond ) Linter(function(source_expression) { # else/if structure for trees that are missing XML content (both global & local) if (is_lint_level(source_expression, "file")) { # 'x = 1;(x + 2)' can't be detected from the expression-level tree xml <- source_expression$full_xml_parsed_content xpath <- file_level_xpath } else { xml <- source_expression$xml_parsed_content xpath <- expression_level_xpath } bad_paren <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_paren, source_expression, lint_message = "Place a space before left parenthesis, except in a function call.", type = "style" ) }) } lintr/R/expect_lint.R0000644000176200001440000001210414577052532014254 0ustar liggesusers#' Lint expectation #' #' This is an expectation function to test that the lints produced by `lint` satisfy a number of checks. #' #' @param content a character vector for the file content to be linted, each vector element representing a line of #' text. #' @param checks checks to be performed: #' \describe{ #' \item{NULL}{check that no lints are returned.} #' \item{single string or regex object}{check that the single lint returned has a matching message.} #' \item{named list}{check that the single lint returned has fields that match. Accepted fields are the same as those #' taken by [Lint()].} #' \item{list of named lists}{for each of the multiple lints returned, check that it matches the checks in the #' corresponding named list (as described in the point above).} #' } #' Named vectors are also accepted instead of named lists, but this is a compatibility feature that #' is not recommended for new code. #' @param ... arguments passed to [lint()], e.g. the linters or cache to use. #' @param file if not `NULL`, read content from the specified file rather than from `content`. #' @param language temporarily override Rs `LANGUAGE` envvar, controlling localization of base R error messages. #' This makes testing them reproducible on all systems irrespective of their native R language setting. #' @return `NULL`, invisibly. #' @examples #' # no expected lint #' expect_lint("a", NULL, trailing_blank_lines_linter()) #' #' # one expected lint #' expect_lint("a\n", "superfluous", trailing_blank_lines_linter()) #' expect_lint("a\n", list(message = "superfluous", line_number = 2), trailing_blank_lines_linter()) #' #' # several expected lints #' expect_lint("a\n\n", list("superfluous", "superfluous"), trailing_blank_lines_linter()) #' expect_lint( #' "a\n\n", #' list( #' list(message = "superfluous", line_number = 2), #' list(message = "superfluous", line_number = 3) #' ), #' trailing_blank_lines_linter() #' ) #' @export expect_lint <- function(content, checks, ..., file = NULL, language = "en") { if (!requireNamespace("testthat", quietly = TRUE)) { stop( # nocov start "'expect_lint' is designed to work within the 'testthat' testing framework, but 'testthat' is not installed." ) # nocov end } old_lang <- set_lang(language) on.exit(reset_lang(old_lang)) if (is.null(file)) { file <- tempfile() on.exit(unlink(file), add = TRUE) local({ con <- base::file(file, encoding = "UTF-8") on.exit(close(con)) writeLines(content, con = con, sep = "\n") }) } lints <- lint(file, ...) n_lints <- length(lints) lint_str <- if (n_lints) paste0(c("", lints), collapse = "\n") else "" wrong_number_fmt <- "got %d lints instead of %d%s" if (is.null(checks)) { msg <- sprintf(wrong_number_fmt, n_lints, length(checks), lint_str) return(testthat::expect(n_lints %==% 0L, msg)) } if (!is.list(checks) || !is.null(names(checks))) { # vector or named list checks <- list(checks) } checks[] <- lapply(checks, fix_names, "message") if (n_lints != length(checks)) { msg <- sprintf(wrong_number_fmt, n_lints, length(checks), lint_str) return(testthat::expect(FALSE, msg)) } local({ itr <- 0L # keep 'linter' as a field even if we remove the deprecated argument from Lint() in the future lint_fields <- unique(c(names(formals(Lint)), "linter")) Map( function(lint, check) { itr <<- itr + 1L lapply(names(check), function(field) { if (!field %in% lint_fields) { stop(sprintf( "check #%d had an invalid field: \"%s\"\nValid fields are: %s\n", itr, field, toString(lint_fields) )) } check <- check[[field]] value <- lint[[field]] msg <- sprintf( "check #%d: %s %s did not match %s", itr, field, deparse(value), deparse(check) ) # deparse ensures that NULL, list(), etc are handled gracefully exp <- if (field == "message") { re_matches(value, check) } else { isTRUE(all.equal(value, check)) } if (!is.logical(exp)) { stop( "Invalid regex result, did you mistakenly have a capture group in the regex? ", "Be sure to escape parenthesis with `[]`", call. = FALSE ) } testthat::expect(exp, msg) }) }, lints, checks ) }) invisible(NULL) } #' Test that the package is lint free #' #' This function is a thin wrapper around lint_package that simply tests there are no #' lints in the package. It can be used to ensure that your tests fail if the package #' contains lints. #' #' @param ... arguments passed to [lint_package()] #' @export expect_lint_free <- function(...) { testthat::skip_on_cran() testthat::skip_on_covr() lints <- lint_package(...) has_lints <- length(lints) > 0L lint_output <- NULL if (has_lints) { lint_output <- format(lints) } result <- testthat::expect( !has_lints, paste0("Not lint free\n", lint_output) ) invisible(result) } lintr/R/expect_s4_class_linter.R0000644000176200001440000000345414577052532016406 0ustar liggesusers#' Require usage of `expect_s4_class(x, k)` over `expect_true(is(x, k))` #' #' [testthat::expect_s4_class()] exists specifically for testing the class #' of S4 objects. [testthat::expect_true()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = 'expect_true(is(x, "Matrix"))', #' linters = expect_s4_class_linter() #' ) #' #' # okay #' lint( #' text = 'expect_s4_class(x, "Matrix")', #' linters = expect_s4_class_linter() #' ) #' #' @evalRd rd_tags("expect_s4_class_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [expect_s3_class_linter()] #' @export expect_s4_class_linter <- function() { # require 2 expressions because methods::is(x) alone is a valid call, even # though the character output wouldn't make any sense for expect_true(). xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][count(expr) = 3 and expr[1][SYMBOL_FUNCTION_CALL[text() = 'is']]] /parent::expr[not(SYMBOL_SUB[text() = 'info' or text() = 'label'])] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content # TODO(michaelchirico): also catch expect_{equal,identical}(methods::is(x), k). # this seems empirically rare, but didn't check many S4-heavy packages. bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( "expect_s4_class(x, k) is better than expect_true(is(x, k)).", "Note also expect_s3_class() available for testing S3 objects." ), type = "warning" ) }) } lintr/R/function_left_parentheses_linter.R0000644000176200001440000000754314577052532020566 0ustar liggesusers#' Function left parentheses linter #' #' Check that all left parentheses in a function call do not have spaces before them #' (e.g. `mean (1:3)`). Although this is syntactically valid, it makes the code #' difficult to read. #' #' Exceptions are made for control flow functions (`if`, `for`, etc.). #' #' @examples #' # will produce lints #' lint( #' text = "mean (x)", #' linters = function_left_parentheses_linter() #' ) #' #' lint( #' text = "stats::sd(c (x, y, z))", #' linters = function_left_parentheses_linter() #' ) #' #' # okay #' lint( #' text = "mean(x)", #' linters = function_left_parentheses_linter() #' ) #' #' lint( #' text = "stats::sd(c(x, y, z))", #' linters = function_left_parentheses_linter() #' ) #' #' lint( #' text = "foo <- function(x) (x + 1)", #' linters = function_left_parentheses_linter() #' ) #' #' @evalRd rd_tags("function_left_parentheses_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - [spaces_left_parentheses_linter()] #' @export function_left_parentheses_linter <- function() { # nolint: object_length. # NB: attach to SYMBOL_FUNCTION_CALL instead of SYMBOL_FUNCTION_CALL/parent::expr because # the latter might be associated with a different line, e.g. in the case of a # complicated call to an "extracted" function (see #1963). This mistake was made earlier # because it allows the xpath to be the same for both FUNCTION and SYMBOL_FUNCTION_CALL. # Further, write 4 separate XPaths because the 'range_end_xpath' differs for these two nodes. bad_line_fun_xpath <- "(//FUNCTION | //OP-LAMBDA)[@line1 != following-sibling::OP-LEFT-PAREN/@line1]" bad_line_call_xpath <- "//SYMBOL_FUNCTION_CALL[@line1 != parent::expr/following-sibling::OP-LEFT-PAREN/@line1]" bad_col_fun_xpath <- "(//FUNCTION | //OP-LAMBDA)[ @line1 = following-sibling::OP-LEFT-PAREN/@line1 and @col2 != following-sibling::OP-LEFT-PAREN/@col1 - 1 ]" bad_col_call_xpath <- "//SYMBOL_FUNCTION_CALL[ @line1 = parent::expr/following-sibling::OP-LEFT-PAREN/@line1 and @col2 != parent::expr/following-sibling::OP-LEFT-PAREN/@col1 - 1 ]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_line_fun_exprs <- xml_find_all(xml, bad_line_fun_xpath) bad_line_fun_lints <- xml_nodes_to_lints( bad_line_fun_exprs, source_expression = source_expression, lint_message = "Left parenthesis should be on the same line as the 'function' symbol." ) bad_line_call_exprs <- xml_find_all(xml, bad_line_call_xpath) bad_line_call_lints <- xml_nodes_to_lints( bad_line_call_exprs, source_expression = source_expression, lint_message = "Left parenthesis should be on the same line as the function's symbol." ) bad_col_fun_exprs <- xml_find_all(xml, bad_col_fun_xpath) bad_col_fun_lints <- xml_nodes_to_lints( bad_col_fun_exprs, source_expression = source_expression, lint_message = "Remove spaces before the left parenthesis in a function definition.", range_start_xpath = "number(./@col2 + 1)", # start after function range_end_xpath = "number(./following-sibling::OP-LEFT-PAREN/@col1 - 1)" # end before ( ) bad_col_call_exprs <- xml_find_all(xml, bad_col_call_xpath) bad_col_call_lints <- xml_nodes_to_lints( bad_col_call_exprs, source_expression = source_expression, lint_message = "Remove spaces before the left parenthesis in a function call.", range_start_xpath = "number(./@col2 + 1)", # start after call name range_end_xpath = "number(./parent::expr/following-sibling::OP-LEFT-PAREN/@col1 - 1)" # end before ( ) c(bad_line_fun_lints, bad_line_call_lints, bad_col_fun_lints, bad_col_call_lints) }) } lintr/R/expect_s3_class_linter.R0000644000176200001440000000662414577052532016407 0ustar liggesusers#' Require usage of `expect_s3_class()` #' #' [testthat::expect_s3_class()] exists specifically for testing the class #' of S3 objects. [testthat::expect_equal()], [testthat::expect_identical()], #' and [testthat::expect_true()] can also be used for such tests, #' but it is better to use the tailored function instead. #' #' @examples #' # will produce lints #' lint( #' text = 'expect_equal(class(x), "data.frame")', #' linters = expect_s3_class_linter() #' ) #' #' lint( #' text = 'expect_equal(class(x), "numeric")', #' linters = expect_s3_class_linter() #' ) #' #' # okay #' lint( #' text = 'expect_s3_class(x, "data.frame")', #' linters = expect_s3_class_linter() #' ) #' #' lint( #' text = 'expect_type(x, "double")', #' linters = expect_s3_class_linter() #' ) #' #' @evalRd rd_tags("expect_s3_class_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [expect_s4_class_linter()] #' @export expect_s3_class_linter <- function() { # (1) expect_{equal,identical}(class(x), C) # (2) expect_true(is.(x)) and expect_true(inherits(x, C)) expect_equal_identical_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical'] /parent::expr /following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'class']] and (position() = 1 or preceding-sibling::expr[STR_CONST]) ] /parent::expr[not(SYMBOL_SUB[text() = 'info' or text() = 'label' or text() = 'expected.label'])] " # NB: there is no easy way to make an exhaustive list of places where an # is. call can be replaced by expect_s3_class(); this list was manually # populated from the default R packages by inspection. For example, # is.matrix(x) cannot be replaced by expect_s3_class(x, "matrix") because # it is not actually an S3 class (is.object(x) is not TRUE). # Further, there are functions named is. that have nothing to do with # object type, e.g. is.finite(), is.nan(), or is.R(). is_s3_class_calls <- paste0("is.", c( # base "data.frame", "factor", "numeric_version", "ordered", "package_version", "qr", "table", # utils grDevices tcltk tcltk grid grid "relistable", "raster", "tclObj", "tkwin", "grob", "unit", # stats "mts", "stepfun", "ts", "tskernel" )) is_class_call <- xp_text_in_table(c(is_s3_class_calls, "inherits")) expect_true_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][expr[1][SYMBOL_FUNCTION_CALL[ {is_class_call} ]]] /parent::expr[not(SYMBOL_SUB[text() = 'info' or text() = 'label'])] ") xpath <- paste(expect_equal_identical_xpath, "|", expect_true_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_function <- xp_call_name(bad_expr) msg <- ifelse( matched_function %in% c("expect_equal", "expect_identical"), sprintf("expect_s3_class(x, k) is better than %s(class(x), k).", matched_function), "expect_s3_class(x, k) is better than expect_true(is.(x)) or expect_true(inherits(x, k))." ) xml_nodes_to_lints( bad_expr, source_expression, lint_message = paste(msg, "Note also expect_s4_class() available for testing S4 objects."), type = "warning" ) }) } lintr/R/duplicate_argument_linter.R0000644000176200001440000000436314577203244017175 0ustar liggesusers#' Duplicate argument linter #' #' Check for duplicate arguments in function calls. Some cases are run-time errors #' (e.g. `mean(x = 1:5, x = 2:3)`), otherwise this linter is used to discourage #' explicitly providing duplicate names to objects (e.g. `c(a = 1, a = 2)`). #' Duplicate-named objects are hard to work with programmatically and #' should typically be avoided. #' #' @param except A character vector of function names as exceptions. Defaults to #' functions that allow sequential updates to variables, currently `dplyr::mutate()` #' and `dplyr::transmute()`. #' #' @examples #' # will produce lints #' lint( #' text = "list(x = 1, x = 2)", #' linters = duplicate_argument_linter() #' ) #' #' lint( #' text = "fun(arg = 1, arg = 2)", #' linters = duplicate_argument_linter() #' ) #' #' # okay #' lint( #' text = "list(x = 1, x = 2)", #' linters = duplicate_argument_linter(except = "list") #' ) #' #' lint( #' text = "df %>% dplyr::mutate(x = a + b, x = x + d)", #' linters = duplicate_argument_linter() #' ) #' #' @evalRd rd_tags("duplicate_argument_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export duplicate_argument_linter <- function(except = c("mutate", "transmute")) { # NB: approach checking for duplicates in XPath is hard because of # quoted names, e.g. foo(a = 1, `a` = 2), so compute duplicates in R xpath_call_with_args <- glue(" //EQ_SUB[not( preceding-sibling::expr/SYMBOL_FUNCTION_CALL[{ xp_text_in_table(except) }] )] /parent::expr[count(EQ_SUB) > 1] ") xpath_arg_name <- "./EQ_SUB/preceding-sibling::*[not(self::COMMENT)][1]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content call_expr <- xml_find_all(xml, xpath_call_with_args) bad_expr <- lapply( call_expr, function(expr) { arg_expr <- xml_find_all(expr, xpath_arg_name) arg_expr[duplicated(get_r_string(arg_expr))] } ) xml_nodes_to_lints( unlist(bad_expr, recursive = FALSE), source_expression = source_expression, lint_message = "Avoid duplicate arguments in function calls.", type = "warning" ) }) } lintr/R/strings_as_factors_linter.R0000644000176200001440000000700014577052532017207 0ustar liggesusers#' Identify cases where `stringsAsFactors` should be supplied explicitly #' #' Designed for code bases written for versions of R before 4.0 seeking to upgrade to R >= 4.0, where #' one of the biggest pain points will surely be the flipping of the #' default value of `stringsAsFactors` from `TRUE` to `FALSE`. #' #' It's not always possible to tell statically whether the change will break #' existing code because R is dynamically typed -- e.g. in `data.frame(x)` #' if `x` is a string, this code will be affected, but if `x` is a number, #' this code will be unaffected. However, in `data.frame(x = "a")`, the #' output will unambiguously be affected. We can instead supply #' `stringsAsFactors = TRUE`, which will make this code backwards-compatible. #' #' See . #' #' @examples #' # will produce lints #' lint( #' text = 'data.frame(x = "a")', #' linters = strings_as_factors_linter() #' ) #' #' # okay #' lint( #' text = 'data.frame(x = "a", stringsAsFactors = TRUE)', #' linters = strings_as_factors_linter() #' ) #' #' lint( #' text = 'data.frame(x = "a", stringsAsFactors = FALSE)', #' linters = strings_as_factors_linter() #' ) #' #' lint( #' text = "data.frame(x = 1.2)", #' linters = strings_as_factors_linter() #' ) #' #' @evalRd rd_tags("strings_as_factors_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export strings_as_factors_linter <- function() { # a call to c() with only literal string inputs, # e.g. c("a") or c("a", "b"), but not c("a", b) c_combine_strings <- " expr[1][SYMBOL_FUNCTION_CALL[text() = 'c']] and expr[STR_CONST] and not(expr[SYMBOL or expr]) " # like data.frame(character()) or data.frame(as.character(1)) known_character_funs <- c( "character", "as.character", "paste", "sprintf", "format", "formatC", "prettyNum", "toString", "encodeString" ) # four inclusions of arguments to data.frame(): # (1) "a" (but exclude argument names, e.g. in c("a b" = 1), #1036) # (2) c("a", "b") # (3) rep("a", 2) # (4) rep(c("a", "b"), 2) # two exclusions # (1) above argument is to row.names= # (2) stringsAsFactors is manually supplied (with any value) xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'data.frame'] /parent::expr /parent::expr[ expr[ ( STR_CONST[not(following-sibling::*[1][self::EQ_SUB])] or ( {c_combine_strings} ) or expr[1][ SYMBOL_FUNCTION_CALL[text() = 'rep'] and following-sibling::expr[1][STR_CONST or ({c_combine_strings})] ] or expr[1][SYMBOL_FUNCTION_CALL[ {xp_text_in_table(known_character_funs)} ]] ) and not(preceding-sibling::*[2][self::SYMBOL_SUB and text() = 'row.names']) ] and not(SYMBOL_SUB[text() = 'stringsAsFactors']) ] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( "This code relies on the default value of stringsAsFactors,", "which changed in version R 4.0. Please supply an explicit value for", "stringsAsFactors for this code to work with versions of R both before", "and after this switch." ), type = "warning" ) }) } lintr/R/with_id.R0000644000176200001440000000144114577052532013367 0ustar liggesusers #' Extract row by ID #' #' @describeIn ids_with_token #' Return the row of the `parsed_content` entry of the `[get_source_expressions]()` object. Typically used in #' conjunction with `ids_with_token` to iterate over rows containing desired tokens. #' #' @param id Integer. The index corresponding to the desired row #' of `parsed_content`. #' @return `with_id`: A data frame corresponding to the row(s) specified in `id`. #' @export with_id <- function(source_expression, id, source_file) { if (!missing(source_file)) { lintr_deprecated(old = "source_file", new = "source_expression", version = "3.0.0", type = "Argument") source_expression <- source_file } if (!is_lint_level(source_expression, "expression")) { return(data.frame()) } source_expression$parsed_content[id, ] } lintr/R/cache.R0000644000176200001440000001001114577052532012774 0ustar liggesusers#' Clear the lintr cache #' #' @param file filename whose cache to clear. If you pass `NULL`, it will delete all of the caches. #' @param path directory to store caches. Reads option 'lintr.cache_directory' as the default. #' @return 0 for success, 1 for failure, invisibly. #' @export clear_cache <- function(file = NULL, path = NULL) { if (is.null(path)) { # Only retrieve settings if `path` isn't specified. # Otherwise, other settings may inadvertently be loaded, such as exclusions. read_settings(file) path <- settings$cache_directory } if (!is.null(file)) { path <- get_cache_file_path(file, path) } unlink(path, recursive = TRUE) } define_cache_path <- function(cache) { if (isTRUE(cache)) { settings$cache_directory } else if (is.character(cache)) { cache } else { character() } } define_cache_key <- function(filename, inline_data, lines) { if (inline_data) list(content = get_content(lines), TRUE) else filename } get_cache_file_path <- function(file, path) { # this assumes that a normalized absolute file path was given file.path(path, digest::digest(file, algo = "sha1")) } load_cache <- function(file, path = NULL) { if (is.null(path) || length(path) == 0L) { return() } env <- new.env(parent = emptyenv()) file <- get_cache_file_path(file, path) if (file.exists(file)) { tryCatch( load(file = file, envir = env), warning = function(w) { invokeRestart("muffleWarning") }, error = function(e) { warning( "Could not load cache file '", file, "':\n", conditionMessage(e) ) } ) } # else nothing to do for source file that has no cache env } save_cache <- function(cache, file, path = NULL) { if (is.null(cache)) { return() } if (!file.exists(path)) { dir.create(path, recursive = TRUE) } save(file = get_cache_file_path(file, path), envir = cache, list = ls(envir = cache)) } cache_file <- function(cache, filename, linters, lints) { if (is.null(cache)) { return() } assign( envir = cache, x = digest_content(linters, filename), value = lints, inherits = FALSE ) } retrieve_file <- function(cache, filename, linters) { if (is.null(cache)) { return(NULL) } mget( envir = cache, x = digest_content(linters, filename), mode = "list", inherits = FALSE, ifnotfound = list(NULL) )[[1L]] } cache_lint <- function(cache, expr, linter, lints) { if (is.null(cache)) { return() } assign( envir = cache, x = digest_content(linter, expr), value = lints, inherits = FALSE ) } retrieve_lint <- function(cache, expr, linter, lines) { lints <- get( envir = cache, x = digest_content(linter, expr), mode = "list", inherits = FALSE ) for (i in seq_along(lints)) { new_line_number <- find_new_line( lints[[i]]$line_number, unname(lints[[i]]$line), lines ) if (is.na(new_line_number)) { return(NULL) } else { lints[[i]]$line_number <- new_line_number } } cache_lint(cache, expr, linter, lints) lints } has_lint <- function(cache, expr, linter) { if (is.null(cache)) { return(FALSE) } exists( envir = cache, x = digest_content(linter, expr), mode = "list", inherits = FALSE ) } digest_content <- function(linters, obj) { content <- if (is.list(obj)) { # assume an expression (global expression if obj$parsed_content is lacking) list(linters, obj$content, is.null(obj$parsed_content)) } else { # assume a filename list(linters, readLines(obj)) } digest::digest(content, algo = "sha1") } find_new_line <- function(line_number, line, lines) { if (lines[line_number] %==% line) { return(line_number) } width <- 1L while (width <= length(lines)) { low <- line_number - width if (low > 0L && lines[low] %==% line) { return(low) } high <- line_number + width if (high <= length(lines) && lines[high] %==% line) { return(high) } width <- width + 1L } NA } lintr/R/line_length_linter.R0000644000176200001440000000270714577052532015613 0ustar liggesusers#' Line length linter #' #' Check that the line length of both comments and code is less than `length`. #' #' @param length maximum line length allowed. Default is 80L (Hollerith limit). #' #' @examples #' # will produce lints #' lint( #' text = strrep("x", 23L), #' linters = line_length_linter(length = 20L) #' ) #' #' # okay #' lint( #' text = strrep("x", 21L), #' linters = line_length_linter(length = 40L) #' ) #' #' @evalRd rd_tags("line_length_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export line_length_linter <- function(length = 80L) { general_msg <- paste("Lines should not be more than", length, "characters.") Linter(function(source_expression) { # Only go over complete file if (!is_lint_level(source_expression, "file")) { return(list()) } line_lengths <- nchar(source_expression$file_lines) long_lines <- which(line_lengths > length) Map( function(long_line, line_length) { Lint( filename = source_expression$filename, line_number = long_line, column_number = length + 1L, type = "style", message = paste(general_msg, "This line is", line_length, "characters."), line = source_expression$file_lines[long_line], ranges = list(c(1L, line_length)) ) }, long_lines, line_lengths[long_lines] ) }) } lintr/R/comments.R0000644000176200001440000000556614577052532013601 0ustar liggesusersin_ci <- function() { in_travis() || in_wercker() || in_jenkins() } ci_type <- function() { if (in_travis()) { return("travis") } if (in_wercker()) { return("wercker") } if (in_jenkins()) { return("jenkins") } "" } in_jenkins <- function() { nzchar(Sys.getenv("JENKINS_URL")) && !is.null(jenkins_build_info()) } jenkins_build_info <- function() { git_url <- Sys.getenv("GIT_URL", Sys.getenv("GIT_URL_1", NA)) if (is.na(git_url)) { return(NULL) } pattern <- "(https?:\\/\\/|git@)github\\.com[:\\/](.+\\/.+)\\.git" if (!length(grep(pattern, git_url))) { return(NULL) } slug <- gsub(pattern, "\\2", git_url) slug_info <- strsplit(slug, "/", fixed = TRUE)[[1L]] list( user = slug_info[1L], repo = slug_info[2L], pull = Sys.getenv("CHANGE_ID", NA) %||% NULL, commit = Sys.getenv("GIT_COMMIT", NA) %||% NULL ) } in_travis <- function() { return(nzchar(Sys.getenv("TRAVIS_REPO_SLUG"))) } travis_build_info <- function() { slug <- Sys.getenv("TRAVIS_REPO_SLUG") slug_info <- strsplit(slug, "/", fixed = TRUE)[[1L]] list( user = slug_info[1L] %||% "", repo = slug_info[2L] %||% "", pull = Sys.getenv("TRAVIS_PULL_REQUEST"), branch = Sys.getenv("TRAVIS_BRANCH"), commit = Sys.getenv("TRAVIS_COMMIT") ) } in_wercker <- function() { return(nzchar(Sys.getenv("WERCKER_GIT_BRANCH"))) } ci_build_info <- function() { type <- ci_type() switch( type, travis = travis_build_info(), wercker = wercker_build_info(), jenkins = jenkins_build_info() ) } wercker_build_info <- function() { list( user = Sys.getenv("WERCKER_GIT_OWNER"), repo = Sys.getenv("WERCKER_GIT_REPOSITORY"), branch = Sys.getenv("WERCKER_GIT_BRANCH"), commit = Sys.getenv("WERCKER_GIT_COMMIT") ) } # nocov start github_comment <- function(text, info = NULL, token = settings$comment_token) { if (!requireNamespace("httr", quietly = TRUE)) { stop("Package 'httr' is required to post comments with github_comment().") } if (!requireNamespace("jsonlite", quietly = TRUE)) { stop("Package 'jsonlite' is required to post comments with github_comment().") } if (is.null(info)) { info <- ci_build_info() } if (!is.null(info$pull) && info$pull != "false") { api_subdir <- file.path("issues", info$pull) } else if (!is.null(info$commit)) { api_subdir <- file.path("commits", info$commit) } else { stop("Expected a pull or a commit, but received ci_build_info() = ", format(info)) } response <- httr::POST( "https://api.github.com", path = file.path("repos", info$user, info$repo, api_subdir, "comments"), body = list(body = jsonlite::unbox(text)), query = list(access_token = token), encode = "json" ) if (httr::status_code(response) >= 300L) { message(httr::http_condition(response, "error", task = httr::content(response, as = "text"))) } } # nocov end lintr/R/expect_comparison_linter.R0000644000176200001440000000446414577055450017051 0ustar liggesusers#' Require usage of `expect_gt(x, y)` over `expect_true(x > y)` (and similar) #' #' [testthat::expect_gt()], [testthat::expect_gte()], [testthat::expect_lt()], #' [testthat::expect_lte()], and [testthat::expect_equal()] exist specifically #' for testing comparisons between two objects. [testthat::expect_true()] can #' also be used for such tests, but it is better to use the tailored function #' instead. #' #' @examples #' # will produce lints #' lint( #' text = "expect_true(x > y)", #' linters = expect_comparison_linter() #' ) #' #' lint( #' text = "expect_true(x <= y)", #' linters = expect_comparison_linter() #' ) #' #' lint( #' text = "expect_true(x == (y == 2))", #' linters = expect_comparison_linter() #' ) #' #' # okay #' lint( #' text = "expect_gt(x, y)", #' linters = expect_comparison_linter() #' ) #' #' lint( #' text = "expect_lte(x, y)", #' linters = expect_comparison_linter() #' ) #' #' lint( #' text = "expect_identical(x, y == 2)", #' linters = expect_comparison_linter() #' ) #' #' lint( #' text = "expect_true(x < y | x > y^2)", #' linters = expect_comparison_linter() #' ) #' #' @evalRd rd_tags("expect_comparison_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_comparison_linter <- function() { # != doesn't have a clean replacement comparator_nodes <- setdiff(as.list(infix_metadata$xml_tag[infix_metadata$comparator]), "NE") xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][ {xp_or(comparator_nodes)} ] /parent::expr[not(SYMBOL_SUB[text() = 'info'])] ") comparator_expectation_map <- c( `>` = "expect_gt", `>=` = "expect_gte", `<` = "expect_lt", `<=` = "expect_lte", `==` = "expect_identical" ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) comparator <- xml_find_chr(bad_expr, "string(expr[2]/*[2])") expectation <- comparator_expectation_map[comparator] lint_message <- sprintf("%s(x, y) is better than expect_true(x %s y).", expectation, comparator) xml_nodes_to_lints(bad_expr, source_expression, lint_message = lint_message, type = "warning") }) } lintr/R/extract.R0000644000176200001440000001407314577052532013417 0ustar liggesusers# content is the file content from readLines extract_r_source <- function(filename, lines, error = identity) { pattern <- get_knitr_pattern(filename, lines) if (is.null(pattern$chunk.begin) || is.null(pattern$chunk.end)) { return(lines) } # mask non-source lines by NA, but keep total line count identical so the line number for EOF is correct, see #1400 output <- rep.int(NA_character_, length(lines)) chunks <- tryCatch(get_chunk_positions(pattern = pattern, lines = lines), error = error) if (inherits(chunks, "error") || inherits(chunks, "lint")) { assign("e", chunks, envir = parent.frame()) # error, so return empty code return(output) } # no chunks found, so just return the lines if (length(chunks[["starts"]]) == 0L || length(chunks[["ends"]]) == 0L) { return(output) } output_env <- environment() # nolint: object_usage_linter. False positive-ish -- used below. Map( function(start, end, indent) { line_seq <- seq(start + 1L, end - 1L) chunk_code <- lines[line_seq] output_env$output[line_seq] <- if (indent > 0L) substr(chunk_code, indent + 1L, nchar(chunk_code)) else chunk_code }, chunks[["starts"]], chunks[["ends"]], chunks[["indents"]] ) # drop <> references, too is.na(output) <- grep(pattern$ref.chunk, output) replace_prefix(output, pattern$chunk.code) } get_knitr_pattern <- function(filename, lines) { # Early return if the source code is parseable as plain R code. # Otherwise, R code containing a line which matches any knitr pattern will be treated as a knitr file. # See #1406 for details. if (parsable(lines)) { return(NULL) } # suppressWarnings for #1920. TODO(michaelchirico): this is a bit sloppy -- we ignore # warnings here because encoding issues are caught later and that code path handles them # correctly by converting to a lint. It would require some refactoring to get that # right here as well, but it would avoid the duplication. pattern <- withCallingHandlers( ("knitr" %:::% "detect_pattern")(lines, tolower(("knitr" %:::% "file_ext")(filename))), warning = function(cond) { if (!grepl("invalid UTF-8", conditionMessage(cond), fixed = TRUE)) { warning(cond) } invokeRestart("muffleWarning") } ) if (!is.null(pattern)) { knitr::all_patterns[[pattern]] } else { NULL } } get_chunk_positions <- function(pattern, lines) { starts <- filter_chunk_start_positions( starts = grep(pattern$chunk.begin, lines, perl = TRUE), lines = lines ) ends <- filter_chunk_end_positions( starts = starts, ends = grep(pattern$chunk.end, lines, perl = TRUE) ) # only keep those blocks that contain at least one line of code keep <- which(ends - starts > 1L) starts <- starts[keep] ends <- ends[keep] # Check indent on all lines in the chunk to allow for staggered indentation within a chunk; # set the initial column to the leftmost one within each chunk (including the start+end gates). See tests. # use 'ws_re' to make clear that we're matching knitr's definition of initial whitespace. ws_re <- sub("```.*", "", pattern$chunk.begin) extract_min_chunk_indent <- function(start, end) { indents <- attr(regexpr(ws_re, lines[start:end], perl = TRUE), "match.length") min(indents) } # NB: min() guarantees length(indents) == length(starts) indents <- unlist(Map(extract_min_chunk_indent, starts, ends)) list(starts = starts, ends = ends, indents = indents) } filter_chunk_start_positions <- function(starts, lines) { # keep blocks that don't set a knitr engine (and so contain evaluated R code) drop <- defines_knitr_engine(lines[starts]) starts[!drop] } filter_chunk_end_positions <- function(starts, ends) { # In a valid file, possibly with plain-code-blocks, # - there should be at least as many ends as starts # In Rmarkdown and Quarto, unevaluated blocks may open & close with the same ``` pattern # that defines the end-pattern for an evaluated block # This returns the first end-position that succeeds each start-position # starts (1, 3, 5, 7, 11) --> (1, 3, 5, 7, 11) # ends (2, 4, 6, 8, 9, 10, 12) --> (2, 4, 6, 8, 12) # return this length_difference <- length(ends) - length(starts) if (length_difference == 0L && all(ends > starts)) { return(ends) } positions <- sort(c(starts = starts, ends = ends)) code_start_indexes <- grep("starts", names(positions), fixed = TRUE) code_ends <- positions[pmin(1L + code_start_indexes, length(positions))] bad_end_indexes <- grep("starts", names(code_ends), fixed = TRUE) if (length(bad_end_indexes) > 0L) { bad_start_positions <- positions[code_start_indexes[bad_end_indexes]] # This error message is formatted like a parse error stop(sprintf( ":%1$d:1: Missing chunk end for chunk (maybe starting at line %1$d).\n", bad_start_positions[1L] ), call. = FALSE) } code_ends } defines_knitr_engine <- function(start_lines) { # Other packages defining custom engines should have them loaded and thus visible # via knitr_engines$get() below. It seems the simplest way to accomplish this is # for those packages to set some code in their .onLoad() hook, but that's not # always done (nor quite recommended as a "best practice" by knitr). # See the discussion on #1552. # TODO(#1617): explore running loadNamespace() automatically. engines <- names(knitr::knit_engines$get()) # {some_engine}, {some_engine label, ...} or {some_engine, ...} bare_engine_pattern <- rex( "{", or(engines), one_of("}", " ", ",") ) # {... engine = "some_engine" ...} explicit_engine_pattern <- rex( boundary, "engine", any_spaces, "=" ) re_matches(start_lines, explicit_engine_pattern) | re_matches(start_lines, bare_engine_pattern) } replace_prefix <- function(lines, prefix_pattern) { if (is.null(prefix_pattern)) { return(lines) } m <- gregexpr(prefix_pattern, lines) non_na <- !is.na(m) prefix_lengths <- lapply(regmatches(lines[non_na], m[non_na]), nchar) regmatches(lines[non_na], m[non_na]) <- lapply(prefix_lengths, strrep, x = " ") lines } lintr/R/numeric_leading_zero_linter.R0000644000176200001440000000207614510650642017477 0ustar liggesusers#' Require usage of a leading zero in all fractional numerics #' #' While .1 and 0.1 mean the same thing, the latter is easier to read due #' to the small size of the '.' glyph. #' #' @examples #' # will produce lints #' lint( #' text = "x <- .1", #' linters = numeric_leading_zero_linter() #' ) #' #' lint( #' text = "x <- -.1", #' linters = numeric_leading_zero_linter() #' ) #' #' # okay #' lint( #' text = "x <- 0.1", #' linters = numeric_leading_zero_linter() #' ) #' #' lint( #' text = "x <- -0.1", #' linters = numeric_leading_zero_linter() #' ) #' #' @evalRd rd_tags("numeric_leading_zero_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export numeric_leading_zero_linter <- make_linter_from_xpath( # NB: # 1. negative constants are split to two components: # OP-MINUS, NUM_CONST # 2. complex constants are split to three components: # NUM_CONST, OP-PLUS/OP-MINUS, NUM_CONST xpath = "//NUM_CONST[starts-with(text(), '.')]", lint_message = "Include the leading zero for fractional numeric constants." ) lintr/R/xp_utils.R0000644000176200001440000001117714577070535013621 0ustar liggesusers# utils for working with xpaths # like `text() %in% table`, translated to XPath 1.0 xp_text_in_table <- function(table) { if (length(table) == 0L) { return("true") } # xpath doesn't seem to have a standard way of escaping quotes, so attempt # to use "" whenever the string has ' (not a perfect solution). info on # escaping from https://stackoverflow.com/questions/14822153 single_quoted <- grepl("'", table, fixed = TRUE) table[single_quoted] <- quote_wrap(table[single_quoted], '"') table[!single_quoted] <- quote_wrap(table[!single_quoted], "'") return(paste0("text() = ", table, collapse = " or ")) } paren_wrap <- function(..., sep) { sep <- paste(")", sep, "(") dots <- list(...) if (length(dots) == 1L && length(dots[[1L]]) > 1L) { inner <- paste(dots[[1L]], collapse = sep) } else { inner <- paste(..., sep = sep) } paste0("(", inner, ")") } #' Safer wrapper for paste(..., sep = " and ") #' #' The intent is to use this for readability when combining XPath conditions so #' the `...` elements should be in that language, but this is not enforced. #' #' Inputs are also wrapped in `()` so as not to risk mixing operator precedence. #' #' @param ... Series of conditions #' @noRd xp_and <- function(...) paren_wrap(..., sep = "and") #' Safer wrapper for paste(..., sep = " or ") #' #' The intent is to use this for readability when combining XPath conditions so #' the `...` elements should be in that language, but this is not enforced. #' #' Inputs are also wrapped in `()` so as not to risk mixing operator precedence. #' #' @param ... Series of conditions #' @noRd xp_or <- function(...) paren_wrap(..., sep = "or") #' Get the name of the function matched by an XPath #' #' Often, it is more helpful to tailor the `message` of a lint to record #' which function was matched by the lint logic. This function encapsulates #' the logic to pull out the matched call in common situations. #' #' @param expr An `xml_node` or `xml_nodeset`, e.g. from [xml2::xml_find_all()]. #' @param depth Integer, default `1L`. How deep in the AST represented by `expr` #' should we look to find the call? By default, we assume `expr` is matched #' to an `` node under which the corresponding `` #' node is found directly. `depth = 0L` means `expr` is matched directly #' to the `SYMBOL_FUNCTION_CALL`; `depth > 1L` means `depth` total `` #' nodes must be traversed before finding the call. #' @param condition An additional (XPath condition on the `SYMBOL_FUNCTION_CALL` #' required for a match. The default (`NULL`) is no condition. See examples. #' #' @examples #' xml_from_code <- function(str) { #' xml2::read_xml(xmlparsedata::xml_parse_data(parse(text = str, keep.source = TRUE))) #' } #' xml <- xml_from_code("sum(1:10)") #' xp_call_name(xml, depth = 2L) #' #' xp_call_name(xml2::xml_find_first(xml, "expr")) #' #' xml <- xml_from_code(c("sum(1:10)", "sd(1:10)")) #' xp_call_name(xml, depth = 2L, condition = "text() = 'sum'") #' #' @export xp_call_name <- function(expr, depth = 1L, condition = NULL) { stopifnot( is.numeric(depth), depth >= 0L, is.null(condition) || is.character(condition) ) is_valid_expr <- is_node(expr) || is_nodeset(expr) if (!is_valid_expr) { stop( "Expected an xml_nodeset or an xml_node, instead got an object of class(es): ", toString(class(expr)) ) } if (is.null(condition)) { node <- "SYMBOL_FUNCTION_CALL" } else { node <- sprintf("SYMBOL_FUNCTION_CALL[%s]", condition) } xpath <- paste0("string(", strrep("expr/", depth), node, ")") xml_find_chr(expr, xpath) } xp_find_location <- function(xml, xpath) { if (identical(xpath, "number(./@col1)")) { as.integer(xml_attr(xml, "col1")) } else if (identical(xpath, "number(./@col2)")) { as.integer(xml_attr(xml, "col2")) } else { as.integer(xml_find_num(xml, xpath)) } } #' Strip XPath 2.0-style comments from an XPath #' #' `{xml2}` uses XPath 1.0, which has no support for comments. But comments are #' useful in a codebase with as many XPaths as we maintain, so we fudge our #' way to XPath 2.0-ish support by writing this simple function to remove comments. #' #' @noRd xpath_comment_re <- rex::rex( "(:", zero_or_more(not(":)")), ":)" ) xp_strip_comments <- function(xpath) rex::re_substitutes(xpath, xpath_comment_re, "", global = TRUE) #' Combine two or more nodesets to a single nodeset #' #' Useful for calling `{xml2}` functions on a combined set of nodes obtained using different XPath searches. #' #' @noRd # TODO(r-lib/xml2#433): remove this and just use c() combine_nodesets <- function(...) { res <- c(...) class(res) <- "xml_nodeset" res } lintr/R/expect_identical_linter.R0000644000176200001440000000705514577052532016630 0ustar liggesusers#' Require usage of `expect_identical(x, y)` where appropriate #' #' This linter enforces the usage of [testthat::expect_identical()] as the #' default expectation for comparisons in a testthat suite. `expect_true(identical(x, y))` #' is an equivalent but unadvised method of the same test. Further, #' [testthat::expect_equal()] should only be used when `expect_identical()` #' is inappropriate, i.e., when `x` and `y` need only be *numerically #' equivalent* instead of fully identical (in which case, provide the #' `tolerance=` argument to `expect_equal()` explicitly). This also applies #' when it's inconvenient to check full equality (e.g., names can be ignored, #' in which case `ignore_attr = "names"` should be supplied to #' `expect_equal()` (or, for 2nd edition, `check.attributes = FALSE`). #' #' @section Exceptions: #' #' The linter allows `expect_equal()` in three circumstances: #' 1. A named argument is set (e.g. `ignore_attr` or `tolerance`) #' 2. Comparison is made to an explicit decimal, e.g. #' `expect_equal(x, 1.0)` (implicitly setting `tolerance`) #' 3. `...` is passed (wrapper functions which might set #' arguments such as `ignore_attr` or `tolerance`) #' #' @examples #' # will produce lints #' lint( #' text = "expect_equal(x, y)", #' linters = expect_identical_linter() #' ) #' #' lint( #' text = "expect_true(identical(x, y))", #' linters = expect_identical_linter() #' ) #' #' # okay #' lint( #' text = "expect_identical(x, y)", #' linters = expect_identical_linter() #' ) #' #' lint( #' text = "expect_equal(x, y, check.attributes = FALSE)", #' linters = expect_identical_linter() #' ) #' #' lint( #' text = "expect_equal(x, y, tolerance = 1e-6)", #' linters = expect_identical_linter() #' ) #' #' @evalRd rd_tags("expect_identical_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_identical_linter <- function() { # outline: # - skip when any named argument is set. most commonly this # is check.attributes (for 2e tests) or one of the ignore_* # arguments (for 3e tests). This will generate some false # negatives, but will be much easier to maintain. # - skip cases like expect_equal(x, 1.02) or the constant vector version # where a numeric constant indicates inexact testing is preferable # - skip calls using dots (`...`); see tests expect_equal_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_equal'] /parent::expr[not( following-sibling::EQ_SUB or following-sibling::expr[ expr[1][SYMBOL_FUNCTION_CALL[text() = 'c']] and expr[NUM_CONST[contains(text(), '.')]] ] or following-sibling::expr[NUM_CONST[contains(text(), '.')]] or following-sibling::expr[SYMBOL[text() = '...']] )] /parent::expr " expect_true_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'expect_true'] /parent::expr /following-sibling::expr[1][expr[1]/SYMBOL_FUNCTION_CALL[text() = 'identical']] /parent::expr " xpath <- paste(expect_equal_xpath, "|", expect_true_xpath) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = paste( "Use expect_identical(x, y) by default; resort to expect_equal() only when needed,", "e.g. when setting ignore_attr= or tolerance=." ), type = "warning" ) }) } lintr/R/pipe_call_linter.R0000644000176200001440000000275014577052532015251 0ustar liggesusers#' Pipe call linter #' #' Force explicit calls in magrittr pipes, e.g., `1:3 %>% sum()` instead of `1:3 %>% sum`. #' Note that native pipe always requires a function call, i.e. `1:3 |> sum` will produce an error. #' #' @examples #' # will produce lints #' lint( #' text = "1:3 %>% mean %>% as.character", #' linters = pipe_call_linter() #' ) #' #' # okay #' lint( #' text = "1:3 %>% mean() %>% as.character()", #' linters = pipe_call_linter() #' ) #' #' @evalRd rd_tags("pipe_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export pipe_call_linter <- function() { # NB: the text() here shows up as %>% but that's not matched, %>% is # NB: use *[1][self::SYMBOL] to ensure the first element is SYMBOL, otherwise # we include expressions like x %>% .$col pipes <- setdiff(magrittr_pipes, "%$%") xpath <- glue("//SPECIAL[{ xp_text_in_table(pipes) }]/following-sibling::expr[*[1][self::SYMBOL]]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) pipe <- xml_text(xml_find_first(bad_expr, "preceding-sibling::SPECIAL[1]")) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = sprintf("Use explicit calls in magrittr pipes, i.e., `a %1$s foo` should be `a %1$s foo()`.", pipe), type = "warning" ) }) } lintr/R/is_lint_level.R0000644000176200001440000000233614577052532014574 0ustar liggesusers#' Is this an expression- or a file-level source object? #' #' Helper for determining whether the current `source_expression` contains #' all expressions in the current file, or just a single expression. #' #' @param source_expression A parsed expression object, i.e., an element #' of the object returned by [get_source_expressions()]. #' @param level Which level of expression is being tested? `"expression"` #' means an individual expression, while `"file"` means all expressions #' in the current file are available. #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' tmp <- withr::local_tempfile(lines = c("x <- 1", "y <- x + 1")) #' source_exprs <- get_source_expressions(tmp) #' is_lint_level(source_exprs$expressions[[1L]], level = "expression") #' is_lint_level(source_exprs$expressions[[1L]], level = "file") #' is_lint_level(source_exprs$expressions[[3L]], level = "expression") #' is_lint_level(source_exprs$expressions[[3L]], level = "file") #' #' @export is_lint_level <- function(source_expression, level = c("expression", "file")) { stopifnot(!missing(level)) level <- match.arg(level) required_key <- paste0(if (level == "file") "full_", "parsed_content") required_key %in% names(source_expression) } lintr/R/exclude.R0000644000176200001440000003360014577052532013373 0ustar liggesusers#' Exclude lines or files from linting #' #' @param lints that need to be filtered. #' @param exclusions manually specified exclusions #' @param linter_names character vector of names of the active linters, used for parsing inline exclusions. #' @param ... additional arguments passed to [parse_exclusions()] #' @eval c( #' # we use @eval for the details section to avoid a literal nolint exclusion tag with non-existing linter names #' # those produce a warning from [parse_exclusions()] otherwise. See #1219 for details. #' "@details", #' "Exclusions can be specified in three different ways.", #' "", #' "1. Single line in the source file. default: `# nolint`, possibly followed by a listing of linters to exclude.", #' " If the listing is missing, all linters are excluded on that line. The default listing format is", #' paste( #' " `#", #' "nolint: linter_name, linter2_name.`. There may not be anything between the colon and the line exclusion tag" #' ), #' " and the listing must be terminated with a full stop (`.`) for the linter list to be respected.", #' "2. Line range in the source file. default: `# nolint start`, `# nolint end`. `# nolint start` accepts linter", #' " lists in the same form as `# nolint`.", #' "3. Exclusions parameter, a list with named and/or unnamed entries. ", #' " Outer elements have the following characteristics:", #' " 1. Unnamed elements specify filenames or directories.", #' " 2. Named elements are a vector or list of line numbers, with `Inf` indicating 'all lines'.", #' " The name gives a path relative to the config.", #' " 1. Unnamed elements denote exclusion of all linters in the given path or directory.", #' " 2. Named elements, where the name specifies a linter, denote exclusion for that linter.", #' " For convenience, a vector can be used in place of a list whenever it would not introduce ambiguity, e.g.", #' " a character vector of files to exclude or a vector of lines to exclude.", #' NULL #' ) exclude <- function(lints, exclusions = settings$exclusions, linter_names = NULL, ...) { if (length(lints) <= 0L) { return(lints) } df <- as.data.frame(lints) filenames <- unique(df$filename) source_exclusions <- lapply(filenames, parse_exclusions, linter_names = linter_names, ...) names(source_exclusions) <- filenames exclusions <- normalize_exclusions(c(source_exclusions, exclusions)) to_exclude <- vapply( seq_len(nrow(df)), function(i) { file <- df$filename[i] file %in% names(exclusions) && is_excluded(df$line_number[i], df$linter[i], exclusions[[file]]) }, logical(1L) ) if (any(to_exclude)) { lints <- lints[!to_exclude] } lints } is_excluded <- function(line_number, linter, file_exclusion) { excluded_lines <- unlist(file_exclusion[names2(file_exclusion) %in% c("", linter)]) Inf %in% excluded_lines || line_number %in% excluded_lines } is_excluded_file <- function(file_exclusion) { any(vapply( file_exclusion[!nzchar(names2(file_exclusion))], function(full_exclusion) Inf %in% full_exclusion, logical(1L) )) } line_info <- function(line_numbers, type = c("start", "end")) { type <- match.arg(type) range_word <- paste0("range ", type, if (length(line_numbers) != 1L) "s") n <- length(line_numbers) if (n == 0L) { paste("0", range_word) } else if (n == 1L) { paste0("1 ", range_word, " (line ", line_numbers, ")") } else { paste0(n, " ", range_word, " (lines ", toString(line_numbers), ")") } } #' read a source file and parse all the excluded lines from it #' #' @param file R source file #' @param exclude Regular expression used to mark lines to exclude. #' @param exclude_next Regular expression used to mark lines immediately preceding excluded lines. #' @param exclude_start Regular expression used to mark the start of an excluded range. #' @param exclude_end Regular expression used to mark the end of an excluded range. #' @param exclude_linter Regular expression used to capture a list of to-be-excluded linters immediately following a #' `exclude` or `exclude_start` marker. #' @param exclude_linter_sep Regular expression used to split a linter list into individual linter names for exclusion. #' @param lines A character vector of the content lines of `file`. #' @param linter_names Names of active linters. #' #' @return A possibly named list of excluded lines, possibly for specific linters. parse_exclusions <- function(file, exclude = settings$exclude, exclude_next = settings$exclude_next, exclude_start = settings$exclude_start, exclude_end = settings$exclude_end, exclude_linter = settings$exclude_linter, exclude_linter_sep = settings$exclude_linter_sep, lines = NULL, linter_names = NULL) { if (is.null(lines)) { lines <- read_lines(file) } exclusions <- list() if (is_tainted(lines)) { # Invalid encoding. Don't parse exclusions. return(list()) } start_locations <- re_matches(lines, exclude_start, locations = TRUE)[, "end"] + 1L end_locations <- re_matches(lines, exclude_end, locations = TRUE)[, "start"] starts <- which(!is.na(start_locations)) ends <- which(!is.na(end_locations)) if (length(starts) > 0L) { if (length(starts) != length(ends)) { starts_msg <- line_info(starts, type = "start") ends_msg <- line_info(ends, type = "end") stop(file, " has ", starts_msg, " but only ", ends_msg, " for exclusion from linting!") } for (i in seq_along(starts)) { excluded_lines <- seq(starts[i], ends[i]) linters_string <- substring(lines[starts[i]], start_locations[starts[i]]) linters_string <- re_matches(linters_string, exclude_linter)[, 1L] exclusions <- add_exclusions(exclusions, excluded_lines, linters_string, exclude_linter_sep, linter_names) } } next_locations <- re_matches(lines, exclude_next, locations = TRUE)[, "end"] + 1L nexts <- which(!is.na(next_locations)) nolint_locations <- re_matches(lines, exclude, locations = TRUE)[, "end"] + 1L nolints <- which(!is.na(nolint_locations)) # Disregard nolint tags if they also match nolint next / start / end nolints <- setdiff(nolints, c(nexts, starts, ends)) for (nolint in nolints) { linters_string <- get_linters_string(lines[nolint], nolint_locations[nolint], exclude_linter) exclusions <- add_exclusions(exclusions, nolint, linters_string, exclude_linter_sep, linter_names) } for (nextt in nexts) { linters_string <- get_linters_string(lines[nextt], next_locations[nextt], exclude_linter) exclusions <- add_exclusions(exclusions, nextt + 1L, linters_string, exclude_linter_sep, linter_names) } exclusions[] <- lapply(exclusions, function(lines) sort(unique(lines))) exclusions } get_linters_string <- function(line, loc, exclude_linter) { linters_string <- substring(line, loc) re_matches(linters_string, exclude_linter)[, 1L] } add_excluded_lines <- function(exclusions, excluded_lines, excluded_linters) { for (linter in excluded_linters) { if (linter %in% names2(exclusions)) { i <- which(names2(exclusions) %in% linter) exclusions[[i]] <- c(exclusions[[i]], excluded_lines) } else { exclusions <- c(exclusions, list(excluded_lines)) if (nzchar(linter)) { if (is.null(names(exclusions))) { # Repair names if linter == "" is the first exclusion added. names(exclusions) <- "" } names(exclusions)[length(exclusions)] <- linter } } } exclusions } add_exclusions <- function(exclusions, lines, linters_string, exclude_linter_sep, linter_names) { # No match for linter list: Add to global excludes if (is.na(linters_string)) { exclusions <- add_excluded_lines(exclusions, lines, "") } else { # Matched a linter list: only add excluded lines for the listed linters. excluded_linters <- strsplit(linters_string, exclude_linter_sep)[[1L]] if (!is.null(linter_names)) { idxs <- pmatch(excluded_linters, linter_names, duplicates.ok = TRUE) matched <- !is.na(idxs) if (!all(matched)) { bad <- excluded_linters[!matched] warning( "Could not find linter", if (length(bad) > 1L) "s" else "", " named ", glue_collapse(sQuote(bad), sep = ", ", last = " and "), " in the list of active linters. Make sure the linter is uniquely identified by the given name or prefix." ) } excluded_linters[matched] <- linter_names[idxs[matched]] } exclusions <- add_excluded_lines(exclusions, lines, excluded_linters) } exclusions } #' Normalize lint exclusions #' #' @param x Exclusion specification #' - A character vector of filenames or directories relative to `root` #' - A named list of integers specifying lines to be excluded per file #' - A named list of named lists specifying linters and lines to be excluded for the linters per file. #' @param normalize_path Should the names of the returned exclusion list be normalized paths? #' If no, they will be relative to `root`. #' @param root Base directory for relative filename resolution. #' @param pattern If non-NULL, only exclude files in excluded directories if they match #' `pattern`. Passed to [list.files][base::list.files] if a directory is excluded. #' #' @return A named list of file exclusions. #' The names of the list specify the filenames to be excluded. #' #' Each file exclusion is a possibly named list containing line numbers to exclude, or the sentinel `Inf` for #' completely excluded files. If the an entry is named, the exclusions only take effect for the linter with the same #' name. #' #' If `normalize_path` is `TRUE`, file names will be normalized relative to `root`. #' Otherwise the paths are left as provided (relative to `root` or absolute). #' #' @keywords internal normalize_exclusions <- function(x, normalize_path = TRUE, root = getwd(), pattern = NULL) { if (is.null(x) || length(x) <= 0L) { return(list()) } x <- as.list(x) unnamed <- !nzchar(names2(x)) if (any(unnamed)) { # must be character vectors of length 1 bad <- vapply( seq_along(x), function(i) { unnamed[i] && (!is.character(x[[i]]) || length(x[[i]]) != 1L) }, logical(1L) ) if (any(bad)) { stop( "Full file exclusions must be character vectors of length 1. items: ", toString(which(bad)), " are not!", call. = FALSE ) } # Normalize unnamed entries to list( = list(Inf), ...) names(x)[unnamed] <- x[unnamed] x[unnamed] <- rep_len(list(list(Inf)), sum(unnamed)) } full_line_exclusions <- !vapply(x, is.list, logical(1L)) if (any(full_line_exclusions)) { # must be integer or numeric vectors are_numeric <- vapply(x, is.numeric, logical(1L)) bad <- full_line_exclusions & !are_numeric if (any(bad)) { stop( "Full line exclusions must be numeric or integer vectors. items: ", toString(which(bad)), " are not!", call. = FALSE ) } # Normalize list( = c()) to # list( = list(c())) x[full_line_exclusions] <- lapply(x[full_line_exclusions], list) } paths <- names(x) rel_path <- !is_absolute_path(paths) paths[rel_path] <- file.path(root, paths[rel_path]) is_dir <- dir.exists(paths) if (any(is_dir)) { dirs <- names(x)[is_dir] x <- x[!is_dir] all_file_names <- unlist(lapply( dirs, function(dir) { dir_path <- if (is_absolute_path(dir)) dir else file.path(root, dir) files <- list.files( path = dir_path, pattern = pattern, recursive = TRUE ) file.path(dir, files) # non-normalized relative paths } )) # Only exclude file if there is no more specific exclusion already all_file_names <- setdiff(all_file_names, names(x)) dir_exclusions <- rep_len(list(Inf), length(all_file_names)) names(dir_exclusions) <- all_file_names x <- c(x, dir_exclusions) } if (normalize_path) { paths <- names(x) # specify relative paths w.r.t. root rel_path <- !is_absolute_path(paths) paths[rel_path] <- file.path(root, paths[rel_path]) names(x) <- paths x <- x[file.exists(paths)] # remove exclusions for non-existing files names(x) <- normalizePath(names(x)) # get full path for remaining files } remove_line_duplicates( remove_linter_duplicates( remove_file_duplicates( remove_empty(x) ) ) ) } # Combines file exclusions for identical files. remove_file_duplicates <- function(x) { unique_names <- unique(names(x)) ## check for duplicate files if (length(unique_names) < length(names(x))) { x <- lapply( unique_names, function(name) { vals <- unname(x[names(x) == name]) do.call(c, vals) } ) names(x) <- unique_names } x } # Removes duplicate line information for each linter within each file. remove_line_duplicates <- function(x) { x[] <- lapply(x, function(ex) { ex[] <- lapply(ex, unique) ex }) x } # Combines line exclusions for identical linters within each file. remove_linter_duplicates <- function(x) { x[] <- lapply(x, function(ex) { unique_linters <- unique(names2(ex)) if (length(unique_linters) < length(ex)) { ex <- lapply(unique_linters, function(linter) { lines <- unlist(ex[names2(ex) == linter]) if (Inf %in% lines) { Inf } else { lines } }) if (!identical(unique_linters, "")) { names(ex) <- unique_linters } } ex }) x } # Removes linter exclusions without lines and files without any linter exclusions. remove_empty <- function(x) { x[] <- lapply(x, function(ex) ex[lengths(ex) > 0L]) x[lengths(x) > 0L] } lintr/R/brace_linter.R0000644000176200001440000001541314577052532014375 0ustar liggesusers#' Brace linter #' #' Perform various style checks related to placement and spacing of curly braces: #' #' - Opening curly braces are never on their own line and are always followed by a newline. #' - Opening curly braces have a space before them. #' - Closing curly braces are on their own line unless they are followed by an `else`. #' - Closing curly braces in `if` conditions are on the same line as the corresponding `else`. #' - Either both or neither branch in `if`/`else` use curly braces, i.e., either both branches use `{...}` or neither #' does. #' - Functions spanning multiple lines use curly braces. #' #' @param allow_single_line if `TRUE`, allow an open and closed curly pair on the same line. #' #' @examples #' # will produce lints #' lint( #' text = "f <- function() { 1 }", #' linters = brace_linter() #' ) #' #' writeLines("if (TRUE) {\n return(1) }") #' lint( #' text = "if (TRUE) {\n return(1) }", #' linters = brace_linter() #' ) #' #' # okay #' writeLines("f <- function() {\n 1\n}") #' lint( #' text = "f <- function() {\n 1\n}", #' linters = brace_linter() #' ) #' #' writeLines("if (TRUE) { \n return(1) \n}") #' lint( #' text = "if (TRUE) { \n return(1) \n}", #' linters = brace_linter() #' ) #' #' # customizing using arguments #' writeLines("if (TRUE) { return(1) }") #' lint( #' text = "if (TRUE) { return(1) }", #' linters = brace_linter(allow_single_line = TRUE) #' ) #' @evalRd rd_tags("brace_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' - #' @export brace_linter <- function(allow_single_line = FALSE) { xp_cond_open <- xp_and(c( # matching } is on same line if (isTRUE(allow_single_line)) { "(@line1 != following-sibling::OP-LEFT-BRACE/@line1)" }, # double curly "not( (@line1 = parent::expr/preceding-sibling::OP-LEFT-BRACE/@line1) or (@line1 = following-sibling::expr/OP-LEFT-BRACE/@line1) )", # allow `(`, `,` and `%>%` on preceding line # # note that '{' is not supported in RHS call of base-R's native pipe (`|>`), # so no exception needs to be made for this operator glue("not( @line1 > parent::expr/preceding-sibling::*[not(self::COMMENT)][1][ self::OP-LEFT-PAREN or self::OP-COMMA or (self::SPECIAL and ({xp_text_in_table(magrittr_pipes)}) ) ]/@line2 )") )) # TODO (AshesITR): if c_style_braces is TRUE, invert the preceding-sibling condition xp_open_curly <- glue("//OP-LEFT-BRACE[ { xp_cond_open } and ( not(@line1 = parent::expr/preceding-sibling::*/@line2) or @line1 = following-sibling::*[1][not(self::COMMENT or self::OP-RIGHT-BRACE)]/@line1 ) ]") xp_open_preceding <- "parent::expr/preceding-sibling::*[1][self::OP-RIGHT-PAREN or self::ELSE or self::REPEAT]" xp_paren_brace <- glue("//OP-LEFT-BRACE[ @line1 = { xp_open_preceding }/@line1 and @col1 = { xp_open_preceding }/@col2 + 1 ]") xp_cond_closed <- xp_and(c( # matching { is on same line if (isTRUE(allow_single_line)) { "(@line1 != preceding-sibling::OP-LEFT-BRACE/@line1)" }, # immediately followed by ",", "]" or ")" "not( @line1 = ancestor::expr/following-sibling::*[1][ self::OP-COMMA or self::OP-RIGHT-BRACKET or self::OP-RIGHT-PAREN ] /@line1 )", # double curly "not( (@line1 = parent::expr/following-sibling::OP-RIGHT-BRACE/@line1) or (@line1 = preceding-sibling::expr/OP-RIGHT-BRACE/@line1) )" )) # TODO (AshesITR): if c_style_braces is TRUE, skip the not(ELSE) condition xp_closed_curly <- glue("//OP-RIGHT-BRACE[ { xp_cond_closed } and ( (@line1 = preceding-sibling::*[1][not(self::OP-LEFT-BRACE)]/@line2) or (@line1 = parent::expr/following-sibling::*[1][not(self::ELSE)]/@line1) ) ]") xp_else_closed_curly <- "preceding-sibling::IF/following-sibling::expr[2]/OP-RIGHT-BRACE" # need to (?) repeat previous_curly_path since != will return true if there is # no such node. ditto for approach with not(@line1 = ...). # TODO (AshesITR): if c_style_braces is TRUE, this needs to be @line2 + 1 xp_else_same_line <- glue("//ELSE[{xp_else_closed_curly} and @line1 != {xp_else_closed_curly}/@line2]") xp_function_brace <- "(//FUNCTION | //OP-LAMBDA)/parent::expr[@line1 != @line2 and not(expr[OP-LEFT-BRACE])]" # if (x) { ... } else if (y) { ... } else { ... } is OK; fully exact pairing # of if/else would require this to be # if (x) { ... } else { if (y) { ... } else { ... } } since there's no # elif operator/token in R, which is pretty unseemly xp_if_else_match_brace <- " //IF[ following-sibling::expr[2][OP-LEFT-BRACE] and following-sibling::ELSE /following-sibling::expr[1][not(OP-LEFT-BRACE or IF/following-sibling::expr[2][OP-LEFT-BRACE])] ] | //ELSE[ following-sibling::expr[1][OP-LEFT-BRACE] and preceding-sibling::IF/following-sibling::expr[2][not(OP-LEFT-BRACE)] ] " Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content lints <- list() lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_open_curly), source_expression = source_expression, lint_message = "Opening curly braces should never go on their own line and should always be followed by a new line." ) ) lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_paren_brace), source_expression = source_expression, lint_message = "There should be a space before an opening curly brace." ) ) lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_closed_curly), source_expression = source_expression, lint_message = "Closing curly-braces should always be on their own line, unless they are followed by an else." ) ) lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_else_same_line), source_expression = source_expression, lint_message = "`else` should come on the same line as the previous `}`." ) ) lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_function_brace), source_expression = source_expression, lint_message = "Any function spanning multiple lines should use curly braces." ) ) lints <- c( lints, xml_nodes_to_lints( xml_find_all(xml, xp_if_else_match_brace), source_expression = source_expression, lint_message = "Either both or neither branch in `if`/`else` should use curly braces." ) ) lints }) } lintr/R/nonportable_path_linter.R0000644000176200001440000000115514577052532016656 0ustar liggesusers#' Non-portable path linter #' #' Check that [file.path()] is used to construct safe and portable paths. #' #' @inheritParams absolute_path_linter #' @evalRd rd_tags("nonportable_path_linter") #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - [absolute_path_linter()] #' @export nonportable_path_linter <- function(lax = TRUE) { path_linter_factory( path_function = function(path) { is_path(path) && is_valid_long_path(path, lax) && path != "/" && re_matches(path, rex(one_of("/", "\\"))) }, message = "Use file.path() to construct portable file paths." ) } lintr/R/empty_assignment_linter.R0000644000176200001440000000231414510650642016674 0ustar liggesusers#' Block assignment of `{}` #' #' Assignment of `{}` is the same as assignment of `NULL`; use the latter #' for clarity. Closely related: [unnecessary_concatenation_linter()]. #' #' @examples #' # will produce lints #' lint( #' text = "x <- {}", #' linters = empty_assignment_linter() #' ) #' #' writeLines("x = {\n}") #' lint( #' text = "x = {\n}", #' linters = empty_assignment_linter() #' ) #' #' # okay #' lint( #' text = "x <- { 3 + 4 }", #' linters = empty_assignment_linter() #' ) #' #' lint( #' text = "x <- NULL", #' linters = empty_assignment_linter() #' ) #' #' @evalRd rd_tags("empty_assignment_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export empty_assignment_linter <- make_linter_from_xpath( # for some reason, the parent in the `=` case is , not , hence parent::expr xpath = " //OP-LEFT-BRACE[following-sibling::*[1][self::OP-RIGHT-BRACE]] /parent::expr[ preceding-sibling::LEFT_ASSIGN or preceding-sibling::EQ_ASSIGN or following-sibling::RIGHT_ASSIGN ] /parent::* ", lint_message = "Assign NULL explicitly or, whenever possible, allocate the empty object with the right type and size." ) lintr/R/unnecessary_lambda_linter.R0000644000176200001440000001432614577055355017170 0ustar liggesusers#' Block usage of anonymous functions in iteration functions when unnecessary #' #' Using an anonymous function in, e.g., [lapply()] is not always necessary, #' e.g. `lapply(DF, sum)` is the same as `lapply(DF, function(x) sum(x))` and #' the former is more readable. #' #' Cases like `lapply(x, \(xi) grep("ptn", xi))` are excluded because, though #' the anonymous function _can_ be avoided, doing so is not always more #' readable. #' #' @examples #' # will produce lints #' lint( #' text = "lapply(list(1:3, 2:4), function(xi) sum(xi))", #' linters = unnecessary_lambda_linter() #' ) #' #' # okay #' lint( #' text = "lapply(list(1:3, 2:4), sum)", #' linters = unnecessary_lambda_linter() #' ) #' #' lint( #' text = 'lapply(x, function(xi) grep("ptn", xi))', #' linters = unnecessary_lambda_linter() #' ) #' #' lint( #' text = "lapply(x, function(xi) data.frame(col = xi))", #' linters = unnecessary_lambda_linter() #' ) #' #' @evalRd rd_tags("unnecessary_lambda_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export unnecessary_lambda_linter <- function() { # include any base function like those where FUN is an argument # and ... follows positionally directly afterwards (with ... # being passed on to FUN). That excludes functions like # Filter/Reduce (which don't accept ...), as well as functions # like sweep() (where check.margin comes between FUN and ..., # and thus would need to be supplied in order to replicate # positional arguments). Technically, these latter could # be part of the linter logic (e.g., detect if the anonymous # call is using positional or keyword arguments -- we can # throw a lint for sweep() lambdas where the following arguments # are all named) but for now it seems like overkill. apply_funs <- xp_text_in_table(c( # nolint: object_usage_linter. Used in glue call below. "lapply", "sapply", "vapply", "apply", "tapply", "rapply", "eapply", "dendrapply", "mapply", "by", "outer", "mclapply", "mcmapply", "parApply", "parCapply", "parLapply", "parLapplyLB", "parRapply", "parSapply", "parSapplyLB", "pvec", purrr_mappers )) # outline: # 1. match one of the identified mappers # 2. match an anonymous function that can be "symbol-ized" # a. it's a one-variable function [TODO(michaelchirico): is this necessary?] # b. the function is a single call # c. that call's _first_ argument is just the function argument (a SYMBOL) # - and it has to be passed positionally (not as a keyword) # d. the function argument doesn't appear elsewhere in the call # TODO(#1703): handle explicit returns too: function(x) return(x) default_fun_xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {apply_funs} ] /parent::expr /following-sibling::expr[(FUNCTION or OP-LAMBDA) and count(SYMBOL_FORMALS) = 1] /expr[last()][ count(.//SYMBOL[self::* = preceding::SYMBOL_FORMALS[1]]) = 1 and count(.//SYMBOL_FUNCTION_CALL[text() != 'return']) = 1 and preceding-sibling::SYMBOL_FORMALS = .//expr[ position() = 2 and preceding-sibling::expr/SYMBOL_FUNCTION_CALL and not(preceding-sibling::*[1][self::EQ_SUB]) and not(parent::expr[ preceding-sibling::expr[not(SYMBOL_FUNCTION_CALL)] or following-sibling::*[not(self::OP-RIGHT-PAREN or self::OP-RIGHT-BRACE)] ]) ]/SYMBOL and count(OP-LEFT-PAREN) + count(OP-LEFT-BRACE/following-sibling::expr/OP-LEFT-PAREN) = 1 ] /parent::expr ") # purrr-style inline formulas-as-functions, e.g. ~foo(.x) # logic is basically the same as that above, except we need # 1. a formula (OP-TILDE) # 2. the lone argument marker `.x` or `.` purrr_symbol <- "SYMBOL[text() = '.x' or text() = '.']" purrr_fun_xpath <- glue(" //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(purrr_mappers)} ] /parent::expr /following-sibling::expr[ OP-TILDE and expr[OP-LEFT-PAREN/following-sibling::expr[1][not(preceding-sibling::*[2][self::SYMBOL_SUB])]/{purrr_symbol}] and not(expr/OP-LEFT-PAREN/following-sibling::expr[position() > 1]//{purrr_symbol}) ] ") # path to calling function symbol from the matched expressions fun_xpath <- "./parent::expr/expr/SYMBOL_FUNCTION_CALL" # path to the symbol of the simpler function that avoids a lambda symbol_xpath <- "expr[last()]//expr[SYMBOL_FUNCTION_CALL[text() != 'return']]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content default_fun_expr <- xml_find_all(xml, default_fun_xpath) # TODO(michaelchirico): further message customization is possible here, # e.g. don't always refer to 'lapply()' in the example, and customize to # whether arguments need to be subsumed in '...' or not. The trouble is in # keeping track of which argument the anonymous function is supplied (2nd # argument for many calls, but 3rd e.g. for apply()) default_call_fun <- xml_text(xml_find_first(default_fun_expr, fun_xpath)) default_symbol <- xml_text(xml_find_first(default_fun_expr, symbol_xpath)) default_fun_lints <- xml_nodes_to_lints( default_fun_expr, source_expression = source_expression, lint_message = paste0( "Pass ", default_symbol, " directly as a symbol to ", default_call_fun, "() ", "instead of wrapping it in an unnecessary anonymous function. ", "For example, prefer lapply(DF, sum) to lapply(DF, function(x) sum(x))." ), type = "warning" ) purrr_fun_expr <- xml_find_all(xml, purrr_fun_xpath) purrr_call_fun <- xml_text(xml_find_first(purrr_fun_expr, fun_xpath)) purrr_symbol <- xml_text(xml_find_first(purrr_fun_expr, symbol_xpath)) purrr_fun_lints <- xml_nodes_to_lints( purrr_fun_expr, source_expression = source_expression, lint_message = paste0( "Pass ", purrr_symbol, " directly as a symbol to ", purrr_call_fun, "() ", "instead of wrapping it in an unnecessary anonymous function. ", "For example, prefer purrr::map(DF, sum) to purrr::map(DF, ~sum(.x))." ), type = "warning" ) c(default_fun_lints, purrr_fun_lints) }) } lintr/R/consecutive_assertion_linter.R0000644000176200001440000000421614577071036017736 0ustar liggesusers#' Force consecutive calls to assertions into just one when possible #' #' [stopifnot()] accepts any number of tests, so sequences like #' `stopifnot(x); stopifnot(y)` are redundant. Ditto for tests using #' `assertthat::assert_that()` without specifying `msg=`. #' #' @examples #' # will produce lints #' lint( #' text = "stopifnot(x); stopifnot(y)", #' linters = consecutive_assertion_linter() #' ) #' #' lint( #' text = "assert_that(x); assert_that(y)", #' linters = consecutive_assertion_linter() #' ) #' #' # okay #' lint( #' text = "stopifnot(x, y)", #' linters = consecutive_assertion_linter() #' ) #' #' lint( #' text = 'assert_that(x, msg = "Bad x!"); assert_that(y)', #' linters = consecutive_assertion_linter() #' ) #' #' @evalRd rd_tags("consecutive_assertion_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export consecutive_assertion_linter <- function() { # annoying expr-but-not-really nodes next_expr <- "following-sibling::*[self::expr or self::expr_or_assign_or_help or self::equal_assign][1]" stopifnot_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'stopifnot'] /parent::expr /parent::expr[ expr[1]/SYMBOL_FUNCTION_CALL = {next_expr}/expr[1]/SYMBOL_FUNCTION_CALL ] ") assert_that_xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'assert_that'] /parent::expr /parent::expr[ not(SYMBOL_SUB[text() = 'msg']) and not(following-sibling::expr[1]/SYMBOL_SUB[text() = 'msg']) and expr[1]/SYMBOL_FUNCTION_CALL = {next_expr}/expr[1]/SYMBOL_FUNCTION_CALL ] ") Linter(function(source_expression) { # need the full file to also catch usages at the top level if (!is_lint_level(source_expression, "file")) { return(list()) } xml <- source_expression$full_xml_parsed_content bad_expr <- combine_nodesets( xml_find_all(xml, stopifnot_xpath), xml_find_all(xml, assert_that_xpath) ) matched_function <- xp_call_name(bad_expr) xml_nodes_to_lints( bad_expr, source_expression, lint_message = sprintf("Unify consecutive calls to %s().", matched_function), type = "warning" ) }) } lintr/R/length_levels_linter.R0000644000176200001440000000140014577052532016143 0ustar liggesusers#' Require usage of nlevels over length(levels(.)) #' #' `length(levels(x))` is the same as `nlevels(x)`, but harder to read. #' #' @examples #' # will produce lints #' lint( #' text = "length(levels(x))", #' linters = length_levels_linter() #' ) #' #' # okay #' lint( #' text = "length(c(levels(x), levels(y)))", #' linters = length_levels_linter() #' ) #' #' @evalRd rd_tags("length_levels_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export length_levels_linter <- make_linter_from_xpath( xpath = " //SYMBOL_FUNCTION_CALL[text() = 'levels'] /parent::expr /parent::expr /parent::expr[expr/SYMBOL_FUNCTION_CALL[text() = 'length']] ", lint_message = "nlevels(x) is better than length(levels(x))." ) lintr/R/AAA.R0000644000176200001440000000172314510650642012316 0ustar liggesusers#' @include utils.R #' @include xp_utils.R #' @include make_linter_from_xpath.R NULL #' Available linters #' @name linters #' #' @description A variety of linters are available in \pkg{lintr}. The most popular ones are readily #' accessible through [default_linters()]. #' #' Within a [lint()] function call, the linters in use are initialized with the provided #' arguments and fed with the source file (provided by [get_source_expressions()]). #' #' A data frame of all available linters can be retrieved using [available_linters()]. #' Documentation for linters is structured into tags to allow for easier discovery; #' see also [available_tags()]. #' #' @evalRd rd_taglist() #' @evalRd rd_linterlist() NULL # need to register rex shortcuts as globals to avoid CRAN check errors rex::register_shortcuts("lintr") globalVariables( c( "line1", "col1", "line2", "col2", # columns of parsed_content "id", "parent", "token", "terminal", "text" # ditto ), "lintr" ) lintr/R/yoda_test_linter.R0000644000176200001440000000513114577052532015310 0ustar liggesusers#' Block obvious "yoda tests" #' #' Yoda tests use `(expected, actual)` instead of the more common `(actual, expected)`. #' This is not always possible to detect statically; this linter focuses on #' the simple case of testing an expression against a literal value, e.g. #' `(1L, foo(x))` should be `(foo(x), 1L)`. #' #' @examples #' # will produce lints #' lint( #' text = "expect_equal(2, x)", #' linters = yoda_test_linter() #' ) #' #' lint( #' text = 'expect_identical("a", x)', #' linters = yoda_test_linter() #' ) #' #' # okay #' lint( #' text = "expect_equal(x, 2)", #' linters = yoda_test_linter() #' ) #' #' lint( #' text = 'expect_identical(x, "a")', #' linters = yoda_test_linter() #' ) #' #' @evalRd rd_tags("yoda_test_linter") #' @seealso #' [linters] for a complete list of linters available in lintr. #' #' @export yoda_test_linter <- function() { # catch the following types of literal in the first argument: # (1) numeric literal (e.g. TRUE, 1L, 1.0, NA) [NUM_CONST] # (2) string literal (e.g. 'str' or "str") [STR_CONST] # (but _not_ x$"key", #1067) # (3) arithmetic literal (e.g. 1+1 or 0+1i) [OP-PLUS or OP-MINUS...] # TODO(#963): fully generalize this & re-use elsewhere const_condition <- " NUM_CONST or (STR_CONST and not(OP-DOLLAR or OP-AT)) or ((OP-PLUS or OP-MINUS) and count(expr[NUM_CONST]) = 2) " pipes <- setdiff(magrittr_pipes, c("%$%", "%<>%")) xpath <- glue(" //SYMBOL_FUNCTION_CALL[text() = 'expect_equal' or text() = 'expect_identical' or text() = 'expect_setequal'] /parent::expr /following-sibling::expr[1][ {const_condition} ] /parent::expr[not(preceding-sibling::*[self::PIPE or self::SPECIAL[{ xp_text_in_table(pipes) }]])] ") second_const_xpath <- glue("expr[position() = 3 and ({const_condition})]") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) matched_call <- xp_call_name(bad_expr) second_const <- xml_find_first(bad_expr, second_const_xpath) lint_message <- ifelse( is.na(second_const), paste( "Tests should compare objects in the order 'actual', 'expected', not the reverse.", sprintf("For example, do %1$s(foo(x), 2L) instead of %1$s(2L, foo(x)).", matched_call) ), sprintf("Avoid storing placeholder tests like %s(1, 1)", matched_call) ) xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "warning") }) } lintr/R/lint.R0000644000176200001440000005362114577052532012715 0ustar liggesusers#' Lint a file, directory, or package #' #' * `lint()` lints a single file. #' * `lint_dir()` lints all files in a directory. #' * `lint_package()` lints all likely locations for R files in a package, i.e. #' `R/`, `tests/`, `inst/`, `vignettes/`, `data-raw/`, `demo/`, and `exec/`. #' #' Read `vignette("lintr")` to learn how to configure which linters are run #' by default. #' Note that if files contain unparseable encoding problems, only the encoding problem will be linted to avoid #' unintelligible error messages from other linters. #' #' @param filename Either the filename for a file to lint, or a character string of inline R code for linting. #' The latter (inline data) applies whenever `filename` has a newline character (\\n). #' @param linters A named list of linter functions to apply. See [linters] for a full list of default and available #' linters. #' @param ... Provide additional arguments to be passed to: #' - [exclude()] (in case of `lint()`; e.g. `lints` or `exclusions`) #' - [lint()] (in case of `lint_dir()` and `lint_package()`; e.g. `linters` or `cache`) #' @param cache When logical, toggle caching of lint results. I1f passed a character string, store the cache in this #' directory. #' @param parse_settings Logical, default `TRUE`. Whether to try and parse the settings; #' otherwise, the [default_settings()] are used. #' @param text Optional argument for supplying a string or lines directly, e.g. if the file is already in memory or #' linting is being done ad hoc. #' #' @aliases lint_file # TODO(next release after 3.0.0): remove the alias #' @return An object of class `c("lints", "list")`, each element of which is a `"list"` object. #' #' @examplesIf requireNamespace("withr", quietly = TRUE) #' f <- withr::local_tempfile(lines = "a=1", fileext = "R") #' lint(f) # linting a file #' lint("a = 123\n") # linting inline-code #' lint(text = "a = 123") # linting inline-code #' #' @export lint <- function(filename, linters = NULL, ..., cache = FALSE, parse_settings = TRUE, text = NULL) { if (has_positional_logical(list(...))) { stop("'cache' is no longer available as a positional argument; please supply 'cache' as a named argument instead.") } check_dots(...names(), c("exclude", "parse_exclusions")) needs_tempfile <- missing(filename) || re_matches(filename, rex(newline)) inline_data <- !is.null(text) || needs_tempfile lines <- get_lines(filename, text) if (needs_tempfile) { filename <- tempfile() con <- file(filename, open = "w", encoding = settings$encoding) on.exit(unlink(filename), add = TRUE) writeLines(text = lines, con = con, sep = "\n") close(con) } filename <- normalizePath(filename, mustWork = !inline_data) # to ensure a unique file in cache source_expressions <- get_source_expressions(filename, lines) if (isTRUE(parse_settings)) { read_settings(filename) on.exit(reset_settings(), add = TRUE) } linters <- define_linters(linters) linters <- Map(validate_linter_object, linters, names(linters)) cache_path <- define_cache_path(cache) lint_cache <- load_cache(filename, cache_path) lint_obj <- define_cache_key(filename, inline_data, lines) lints <- retrieve_file(lint_cache, lint_obj, linters) if (!is.null(lints)) { return(exclude(lints, lines = lines, linter_names = names(linters), ...)) } lints <- list() if (!is_tainted(source_expressions$lines)) { for (expr in source_expressions$expressions) { for (linter in names(linters)) { # use withCallingHandlers for friendlier failures on unexpected linter errors lints[[length(lints) + 1L]] <- withCallingHandlers( get_lints(expr, linter, linters[[linter]], lint_cache, source_expressions$lines), error = function(cond) { stop("Linter '", linter, "' failed in ", filename, ": ", conditionMessage(cond), call. = FALSE) } ) } } } lints <- maybe_append_error_lint(lints, source_expressions$error, lint_cache, filename) lints <- reorder_lints(flatten_lints(lints)) class(lints) <- c("lints", "list") cache_file(lint_cache, filename, linters, lints) save_cache(lint_cache, filename, cache_path) res <- exclude(lints, lines = lines, linter_names = names(linters), ...) # simplify filename if inline zap_temp_filename(res, needs_tempfile) } #' @param path For the base directory of the project (for `lint_dir()`) or #' package (for `lint_package()`). #' @param relative_path if `TRUE`, file paths are printed using their path relative to the base directory. #' If `FALSE`, use the full absolute path. #' @param exclusions exclusions for [exclude()], relative to the package path. #' @param pattern pattern for files, by default it will take files with any of the extensions #' .R, .Rmd, .qmd, .Rnw, .Rhtml, .Rrst, .Rtex, .Rtxt allowing for lowercase r (.r, ...). #' @param show_progress Logical controlling whether to show linting progress with a simple text #' progress bar _via_ [utils::txtProgressBar()]. The default behavior is to show progress in #' [interactive()] sessions not running a testthat suite. #' #' @examples #' if (FALSE) { #' lint_dir() #' #' lint_dir( #' linters = list(semicolon_linter()), #' exclusions = list( #' "inst/doc/creating_linters.R" = 1, #' "inst/example/bad.R", #' "renv" #' ) #' ) #' } #' @export #' @rdname lint lint_dir <- function(path = ".", ..., relative_path = TRUE, exclusions = list("renv", "packrat"), # TODO(r-lib/rex#85): Re-write in case-sensitive rex() pattern = "(?i)[.](r|rmd|qmd|rnw|rhtml|rrst|rtex|rtxt)$", parse_settings = TRUE, show_progress = NULL) { if (has_positional_logical(list(...))) { stop( "'relative_path' is no longer available as a positional argument; ", "please supply 'relative_path' as a named argument instead. " ) } check_dots(...names(), c("lint", "exclude", "parse_exclusions")) if (isTRUE(parse_settings)) { read_settings(path) on.exit(reset_settings(), add = TRUE) exclusions <- c(exclusions, settings$exclusions) } if (is.null(show_progress)) show_progress <- interactive() && !identical(Sys.getenv("TESTTHAT"), "true") exclusions <- normalize_exclusions( exclusions, root = path, pattern = pattern ) # normalizePath ensures names(exclusions) and files have the same names for the same files. # Otherwise on windows, files might incorrectly not be excluded in to_exclude files <- normalizePath(dir( path, pattern = pattern, recursive = TRUE, full.names = TRUE )) # Remove fully ignored files to avoid reading & parsing files <- drop_excluded(files, exclusions) if (length(files) == 0L) { lints <- list() class(lints) <- "lints" return(lints) } pb <- if (isTRUE(show_progress)) { txtProgressBar(max = length(files), style = 3L) } lints <- flatten_lints(lapply( files, function(file) { maybe_report_progress(pb) lint(file, ..., parse_settings = FALSE, exclusions = exclusions) } )) if (!is.null(pb)) close(pb) lints <- reorder_lints(lints) if (relative_path) { path <- normalizePath(path, mustWork = FALSE) lints[] <- lapply( lints, function(x) { x$filename <- re_substitutes(x$filename, rex(path, one_of("/", "\\")), "") x } ) attr(lints, "path") <- path } class(lints) <- "lints" lints } drop_excluded <- function(files, exclusions) { to_exclude <- vapply( files, function(file) file %in% names(exclusions) && is_excluded_file(exclusions[[file]]), logical(1L) ) files[!to_exclude] } #' @examples #' if (FALSE) { #' lint_package() #' #' lint_package( #' linters = linters_with_defaults(semicolon_linter = semicolon_linter()), #' exclusions = list("inst/doc/creating_linters.R" = 1, "inst/example/bad.R") #' ) #' } #' @export #' @rdname lint lint_package <- function(path = ".", ..., relative_path = TRUE, exclusions = list("R/RcppExports.R"), parse_settings = TRUE, show_progress = NULL) { if (has_positional_logical(list(...))) { # nocov start: dead code path stop( "'relative_path' is no longer available as a positional argument; ", "please supply 'relative_path' as a named argument instead. " ) # nocov end } if (length(path) > 1L) { stop("Only linting one package at a time is supported.") } pkg_path <- find_package(path) if (is.null(pkg_path)) { warning(sprintf("Didn't find any R package searching upwards from '%s'.", normalizePath(path))) return(NULL) } if (parse_settings) { read_settings(pkg_path) on.exit(reset_settings(), add = TRUE) } exclusions <- normalize_exclusions( c(exclusions, settings$exclusions), root = pkg_path ) r_directories <- file.path(pkg_path, c("R", "tests", "inst", "vignettes", "data-raw", "demo", "exec")) lints <- lint_dir(r_directories, relative_path = FALSE, exclusions = exclusions, parse_settings = FALSE, show_progress = show_progress, ... ) if (isTRUE(relative_path)) { path <- normalizePath(pkg_path, mustWork = FALSE) lints[] <- lapply( lints, function(x) { x$filename <- re_substitutes(x$filename, rex(path, one_of("/", "\\")), "") x } ) attr(lints, "path") <- path } lints } #' Run a linter on a source expression, optionally using a cache #' #' @param expr A source expression. #' @param linter Name of the linter. #' @param linter_fun Closure of the linter. #' @param lint_cache Cache environment, or `NULL` if caching is disabled. #' #' @return A list of lints generated by the linter on `expr`. #' #' @noRd get_lints <- function(expr, linter, linter_fun, lint_cache, lines) { expr_lints <- NULL if (has_lint(lint_cache, expr, linter)) { # retrieve_lint() might return NULL if missing line number is encountered. # It could be caused by nolint comments. expr_lints <- retrieve_lint(lint_cache, expr, linter, lines) } if (is.null(expr_lints)) { expr_lints <- flatten_lints(linter_fun(expr)) for (i in seq_along(expr_lints)) { expr_lints[[i]]$linter <- linter } cache_lint(lint_cache, expr, linter, expr_lints) } expr_lints } define_linters <- function(linters = NULL) { if (is.null(linters)) { linters <- settings$linters names(linters) <- auto_names(linters) } else if (is_linter(linters)) { linters <- list(linters) names(linters) <- attr(linters[[1L]], "name", exact = TRUE) } else if (is.list(linters)) { names(linters) <- auto_names(linters) } else { name <- deparse(substitute(linters)) linters <- list(linters) names(linters) <- name } linters } validate_linter_object <- function(linter, name) { if (!is_linter(linter) && is.function(linter)) { if (is_linter_factory(linter)) { old <- "Passing linters as variables" new <- "a call to the linters (see ?linters)" lintr_deprecated( old = old, new = new, version = "3.0.0", type = "" ) linter <- linter() } else { old <- "The use of linters of class 'function'" new <- "linters classed as 'linter' (see ?Linter)" lintr_deprecated( old = old, new = new, version = "3.0.0", type = "" ) linter <- Linter(linter, name = name) } } else if (!is.function(linter)) { stop(gettextf( "Expected '%s' to be a function of class 'linter', not a %s of class '%s'", name, typeof(linter), class(linter)[[1L]] )) } linter } is_linter_factory <- function(fun) { # A linter factory is a function whose last call is to Linter() bdexpr <- body(fun) # covr internally transforms each call into if (TRUE) { covr::count(...); call } while (is.call(bdexpr) && (bdexpr[[1L]] == "{" || (bdexpr[[1L]] == "if" && bdexpr[[2L]] == "TRUE"))) { bdexpr <- bdexpr[[length(bdexpr)]] } is.call(bdexpr) && identical(bdexpr[[1L]], as.name("Linter")) } reorder_lints <- function(lints) { files <- vapply(lints, `[[`, character(1L), "filename") lines <- vapply(lints, `[[`, integer(1L), "line_number") columns <- vapply(lints, `[[`, integer(1L), "column_number") lints[order( files, lines, columns )] } #' Create a `lint` object #' @param filename path to the source file that was linted. #' @param line_number line number where the lint occurred. #' @param column_number column number where the lint occurred. #' @param type type of lint. #' @param message message used to describe the lint error #' @param line code source where the lint occurred #' @param ranges a list of ranges on the line that should be emphasized. #' @param linter deprecated. No longer used. #' @return an object of class `c("lint", "list")`. #' @name lint-s3 #' @export Lint <- function(filename, line_number = 1L, column_number = 1L, # nolint: object_name. type = c("style", "warning", "error"), message = "", line = "", ranges = NULL, linter = "") { if (!missing(linter)) { lintr_deprecated( old = "Using the `linter` argument of `Lint()`", version = "3.0.0", type = "" ) } if (length(line) != 1L || !is.character(line)) { stop("`line` must be a string.") } max_col <- max(nchar(line) + 1L, 1L, na.rm = TRUE) if (!is_number(column_number) || column_number < 0L || column_number > max_col) { stop(sprintf( "`column_number` must be an integer between 0 and nchar(line) + 1 (%d). It was %s.", max_col, column_number )) } if (!is_number(line_number) || line_number < 1L) { stop(sprintf("`line_number` must be a positive integer. It was %s.", line_number)) } check_ranges(ranges, max_col) type <- match.arg(type) obj <- list( filename = filename, line_number = as.integer(line_number), column_number = as.integer(column_number), type = type, message = message, line = line, ranges = ranges, linter = NA_character_ ) class(obj) <- c("lint", "list") obj } is_number <- function(number, n = 1L) { length(number) == n && is.numeric(number) && !anyNA(number) } is_valid_range <- function(range, max_col) { 0L <= range[[1L]] && range[[1L]] <= range[[2L]] && range[[2L]] <= max_col } check_ranges <- function(ranges, max_col) { if (is.null(ranges)) { return() } if (!is.list(ranges)) { stop("`ranges` must be NULL or a list.") } for (range in ranges) { if (!is_number(range, 2L)) { stop("`ranges` must only contain length 2 integer vectors without NAs.") } else if (!is_valid_range(range, max_col)) { stop(sprintf( "All entries in `ranges` must satisfy 0 <= range[1L] <= range[2L] <= nchar(line) + 1 (%d).", max_col )) } } } rstudio_source_markers <- function(lints) { if (!requireNamespace("rstudioapi", quietly = TRUE)) { stop("'rstudioapi' is required for rstudio_source_markers().") # nocov } # package path will be NULL unless it is a relative path package_path <- attr(lints, "path") # generate the markers markers <- lapply(lints, function(x) { filename <- if (!is.null(package_path)) { file.path(package_path, x$filename) } else { x$filename } marker <- list() marker$type <- x$type marker$file <- filename marker$line <- x$line_number marker$column <- x$column_number marker$message <- paste0("[", x$linter, "] ", x$message) marker }) # request source markers out <- rstudioapi::callFun( "sourceMarkers", name = "lintr", markers = markers, basePath = package_path, autoSelect = "first" ) # workaround to avoid focusing an empty Markers pane # when possible, better solution is to delete the "lintr" source marker list # https://github.com/rstudio/rstudioapi/issues/209 if (length(lints) == 0L) { Sys.sleep(0.1) rstudioapi::executeCommand("activateConsole") } out } #' Checkstyle Report for lint results #' #' Generate a report of the linting results using the [Checkstyle](https://checkstyle.sourceforge.io) XML format. #' #' @param lints the linting results. #' @param filename the name of the output report #' @export checkstyle_output <- function(lints, filename = "lintr_results.xml") { # package path will be NULL unless it is a relative path package_path <- attr(lints, "path") # setup file d <- xml2::xml_new_document() n <- xml2::xml_add_child(d, "checkstyle", version = paste0("lintr-", utils::packageVersion("lintr"))) # output the style markers to the file lapply(split(lints, names(lints)), function(lints_per_file) { filename <- if (!is.null(package_path)) { file.path(package_path, lints_per_file[[1L]]$filename) } else { lints_per_file[[1L]]$filename } f <- xml2::xml_add_child(n, "file", name = filename) lapply(lints_per_file, function(x) { xml2::xml_add_child( f, "error", line = as.character(x$line_number), column = as.character(x$column_number), severity = switch(x$type, style = "info", x$type ), message = x$message ) }) }) xml2::write_xml(d, filename) } #' SARIF Report for lint results #' #' Generate a report of the linting results using the [SARIF](https://sarifweb.azurewebsites.net/) format. #' #' @param lints the linting results. #' @param filename the name of the output report #' @export sarif_output <- function(lints, filename = "lintr_results.sarif") { if (!requireNamespace("jsonlite", quietly = TRUE)) { stop("'jsonlite' is required to produce SARIF reports, please install to continue.") # nocov } # package path will be `NULL` unless it is a relative path package_path <- attr(lints, "path") if (is.null(package_path)) { stop("Package path needs to be a relative path.", call. = FALSE) } # setup template sarif <- jsonlite::fromJSON( system.file("extdata", "sarif-template.json", package = "lintr"), simplifyVector = TRUE, simplifyDataFrame = FALSE, simplifyMatrix = FALSE ) # assign values sarif$runs[[1L]]$results <- NULL sarif$runs[[1L]]$tool$driver$rules <- NULL sarif$runs[[1L]]$tool$driver$version <- as.character(utils::packageVersion("lintr")) sarif$runs[[1L]]$originalUriBaseIds$ROOTPATH$uri <- "" rule_index_exists <- FALSE root_path_uri <- gsub("\\", "/", package_path, fixed = TRUE) if (startsWith(root_path_uri, "/")) { root_path_uri <- paste0("file://", root_path_uri) } else { root_path_uri <- paste0("file:///", root_path_uri) # nocov } if (!endsWith(root_path_uri, "/")) { root_path_uri <- paste0(root_path_uri, "/") } sarif$runs[[1L]]$originalUriBaseIds$ROOTPATH$uri <- root_path_uri # loop and assign result values for (lint in lints) { one_result <- list() if (is.null(sarif$runs[[1L]]$tool$driver$rules)) { rule_index_exists <- 0L } else { rule_index_exists <- which(vapply( sarif$runs[[1L]]$tool$driver$rules, function(x) x$id == lint$linter, logical(1L) )) if (length(rule_index_exists) == 0L || is.na(rule_index_exists[1L])) { rule_index_exists <- 0L } } if (rule_index_exists == 0L) { new_rule <- list( id = lint$linter, fullDescription = list(text = lint$message), defaultConfiguration = list( level = switch(lint$type, style = "note", lint$type) ) ) sarif$runs[[1L]]$tool$driver$rules <- append(sarif$runs[[1L]]$tool$driver$rules, list(new_rule)) rule_index <- length(sarif$runs[[1L]]$tool$driver$rules) - 1L } else { rule_index <- rule_index_exists - 1L } one_result <- append(one_result, c(ruleId = lint$linter)) one_result <- append(one_result, c(ruleIndex = rule_index)) one_result <- append(one_result, list(message = list(text = lint$message))) one_location <- list(physicalLocation = list( artifactLocation = list( uri = gsub("\\", "/", lint$filename, fixed = TRUE), uriBaseId = "ROOTPATH" ), region = list( startLine = lint$line_number, startColumn = lint$column_number, snippet = list(text = lint$line) ) )) one_result <- append(one_result, c(locations = list(list(one_location)))) sarif$runs[[1L]]$results <- append(sarif$runs[[1L]]$results, list(one_result)) } # if lints is empty, add empty results list if (length(lints) == 0L) { sarif$runs[[1L]]$results <- list() } write(jsonlite::toJSON(sarif, pretty = TRUE, auto_unbox = TRUE), filename) } highlight_string <- function(message, column_number = NULL, ranges = NULL) { maximum <- max(column_number, unlist(ranges)) line <- fill_with(" ", maximum) for (range in ranges) { substr(line, range[1L], range[2L]) <- fill_with("~", range[2L] - range[1L] + 1L) } substr(line, column_number, column_number + 1L) <- "^" line } fill_with <- function(character = " ", length = 1L) { paste0(collapse = "", rep.int(character, length)) } has_positional_logical <- function(dots) { length(dots) > 0L && is.logical(dots[[1L]]) && !nzchar(names2(dots)[1L]) } maybe_report_progress <- function(pb) { if (is.null(pb)) { return(invisible()) } setTxtProgressBar(pb, getTxtProgressBar(pb) + 1L) } maybe_append_error_lint <- function(lints, error, lint_cache, filename) { if (inherits(error, "lint")) { error$linter <- "error" lints[[length(lints) + 1L]] <- error if (!is.null(lint_cache)) { cache_lint(lint_cache, list(filename = filename, content = ""), "error", error) } } lints } get_lines <- function(filename, text) { if (!is.null(text)) { strsplit(paste(text, collapse = "\n"), "\n", fixed = TRUE)[[1L]] } else if (re_matches(filename, rex(newline))) { strsplit(gsub("\n$", "", filename), "\n", fixed = TRUE)[[1L]] } else { read_lines(filename) } } zap_temp_filename <- function(res, needs_tempfile) { if (needs_tempfile) { for (i in seq_along(res)) { res[[i]][["filename"]] <- "" } } res } lintr/R/for_loop_index_linter.R0000644000176200001440000000212014510650642016307 0ustar liggesusers#' Block usage of for loops directly overwriting the indexing variable #' #' `for (x in x)` is a poor choice of indexing variable. This overwrites #' `x` in the calling scope and is confusing to read. #' #' @examples #' # will produce lints #' lint( #' text = "for (x in x) { TRUE }", #' linters = for_loop_index_linter() #' ) #' #' lint( #' text = "for (x in foo(x, y)) { TRUE }", #' linters = for_loop_index_linter() #' ) #' #' # okay #' lint( #' text = "for (xi in x) { TRUE }", #' linters = for_loop_index_linter() #' ) #' #' lint( #' text = "for (col in DF$col) { TRUE }", #' linters = for_loop_index_linter() #' ) #' #' @evalRd rd_tags("for_loop_index_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export for_loop_index_linter <- make_linter_from_xpath( xpath = " //forcond /SYMBOL[text() = following-sibling::expr //SYMBOL[not(parent::expr[OP-DOLLAR or OP-AT or preceding-sibling::OP-LEFT-BRACKET])] /text() ] ", lint_message = "Don't re-use any sequence symbols as the index symbol in a for loop." ) lintr/R/comment_linters.R0000644000176200001440000001061614577052532015146 0ustar liggesusersops <- list( "+", # "-", "=", "==", "!=", "<=", ">=", "<-", "<<-", "<", ">", "->", "->>", "%%", "/", "^", "*", "**", "|", "||", "&", "&&", rex("%", except_any_of("%"), "%") ) #' Commented code linter #' #' Check that there is no commented code outside roxygen blocks. #' #' @examples #' # will produce lints #' lint( #' text = "# x <- 1", #' linters = commented_code_linter() #' ) #' #' lint( #' text = "x <- f() # g()", #' linters = commented_code_linter() #' ) #' #' lint( #' text = "x + y # + z[1, 2]", #' linters = commented_code_linter() #' ) #' #' # okay #' lint( #' text = "x <- 1; x <- f(); x + y", #' linters = commented_code_linter() #' ) #' #' lint( #' text = "#' x <- 1", #' linters = commented_code_linter() #' ) #' #' @evalRd rd_tags("commented_code_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export commented_code_linter <- function() { code_candidate_regex <- rex( some_of("#"), any_spaces, capture( name = "code", anything, or( some_of("{}[]"), # code-like parentheses or(ops), # any operator group(graphs, "(", anything, ")"), # a function call group("!", alphas) # a negation ), anything ) ) Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } all_comment_nodes <- xml_find_all(source_expression$full_xml_parsed_content, "//COMMENT") all_comments <- xml_text(all_comment_nodes) code_candidates <- re_matches(all_comments, code_candidate_regex, global = FALSE, locations = TRUE) extracted_code <- code_candidates[, "code"] # ignore trailing ',' when testing for parsability extracted_code <- re_substitutes(extracted_code, rex(",", any_spaces, end), "") extracted_code <- re_substitutes(extracted_code, rex(start, any_spaces, ","), "") is_parsable <- which(vapply(extracted_code, parsable, logical(1L))) lint_list <- xml_nodes_to_lints( all_comment_nodes[is_parsable], source_expression = source_expression, lint_message = "Commented code should be removed." ) # Location info needs updating for (i in seq_along(lint_list)) { rng <- lint_list[[i]]$ranges[[1L]] rng[2L] <- rng[1L] + code_candidates[is_parsable[i], "code.end"] - 1L rng[1L] <- rng[1L] + code_candidates[is_parsable[i], "code.start"] - 1L lint_list[[i]]$column_number <- rng[1L] lint_list[[i]]$ranges <- list(rng) } lint_list }) } # is given text parsable parsable <- function(x) { if (anyNA(x)) { return(FALSE) } res <- try_silently(parse(text = x)) !inherits(res, "try-error") } #' TODO comment linter #' #' Check that the source contains no TODO comments (case-insensitive). #' #' @param todo Vector of strings that identify TODO comments. #' #' @examples #' # will produce lints #' lint( #' text = "x + y # TODO", #' linters = todo_comment_linter() #' ) #' #' lint( #' text = "pi <- 1.0 # FIXME", #' linters = todo_comment_linter() #' ) #' #' lint( #' text = "x <- TRUE # hack", #' linters = todo_comment_linter(todo = c("todo", "fixme", "hack")) #' ) #' #' # okay #' lint( #' text = "x + y # my informative comment", #' linters = todo_comment_linter() #' ) #' #' lint( #' text = "pi <- 3.14", #' linters = todo_comment_linter() #' ) #' #' lint( #' text = "x <- TRUE", #' linters = todo_comment_linter() #' ) #' #' @evalRd rd_tags("todo_comment_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export todo_comment_linter <- function(todo = c("todo", "fixme")) { todo_comment_regex <- rex(one_or_more("#"), any_spaces, or(todo)) Linter(function(source_expression) { tokens <- with_id(source_expression, ids_with_token(source_expression, "COMMENT")) are_todo <- re_matches(tokens[["text"]], todo_comment_regex, ignore.case = TRUE) tokens <- tokens[are_todo, ] lapply( split(tokens, seq_len(nrow(tokens))), function(token) { Lint( filename = source_expression[["filename"]], line_number = token[["line1"]], column_number = token[["col1"]], type = "style", message = "TODO comments should be removed.", line = source_expression[["lines"]][[as.character(token[["line1"]])]], ranges = list(c(token[["col1"]], token[["col2"]])) ) } ) }) } lintr/R/object_usage_linter.R0000644000176200001440000002077714577052532015764 0ustar liggesusers#' Object usage linter #' #' Check that closures have the proper usage using [codetools::checkUsage()]. #' Note that this runs [base::eval()] on the code, so **do not use with untrusted code**. #' #' @param interpret_glue If `TRUE`, interpret [glue::glue()] calls to avoid false positives caused by local variables #' which are only used in a glue expression. #' @param skip_with A logical. If `TRUE` (default), code in `with()` expressions #' will be skipped. This argument will be passed to `skipWith` argument of #' `codetools::checkUsage()`. #' #' @examples #' # will produce lints #' lint( #' text = "foo <- function() { x <- 1 }", #' linters = object_usage_linter() #' ) #' #' # okay #' lint( #' text = "foo <- function(x) { x <- 1 }", #' linters = object_usage_linter() #' ) #' #' lint( #' text = "foo <- function() { x <- 1; return(x) }", #' linters = object_usage_linter() #' ) #' @evalRd rd_linters("package_development") #' @seealso [linters] for a complete list of linters available in lintr. #' @export object_usage_linter <- function(interpret_glue = TRUE, skip_with = TRUE) { # NB: difference across R versions in how EQ_ASSIGN is represented in the AST # (under or ) # NB: the repeated expr[2][FUNCTION] XPath has no performance impact, so the different direct assignment XPaths are # split for better readability, see PR#1197 # TODO(#1106): use //[...] to capture assignments in more scopes xpath_function_assignment <- " expr[LEFT_ASSIGN or EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA] | expr_or_assign_or_help[EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA] | equal_assign[EQ_ASSIGN]/expr[2][FUNCTION or OP-LAMBDA] | //SYMBOL_FUNCTION_CALL[text() = 'assign']/parent::expr/following-sibling::expr[2][FUNCTION or OP-LAMBDA] | //SYMBOL_FUNCTION_CALL[text() = 'setMethod']/parent::expr/following-sibling::expr[3][FUNCTION or OP-LAMBDA] " # not all instances of linted symbols are potential sources for the observed violations -- see #1914 symbol_exclude_cond <- "preceding-sibling::OP-DOLLAR or preceding-sibling::OP-AT or ancestor::expr[OP-TILDE]" xpath_culprit_symbol <- glue(" descendant::SYMBOL[not( {symbol_exclude_cond} )] | descendant::SYMBOL_FUNCTION_CALL[not( {symbol_exclude_cond} )] | descendant::SPECIAL | descendant::LEFT_ASSIGN[text() = ':='] ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "file")) { return(list()) } pkg_name <- pkg_name(find_package(dirname(source_expression$filename))) declared_globals <- try_silently(globalVariables(package = pkg_name %||% globalenv())) xml <- source_expression$full_xml_parsed_content # run the following at run-time, not "compile" time to allow package structure to change env <- make_check_env(pkg_name, xml) fun_assignments <- xml_find_all(xml, xpath_function_assignment) lapply(fun_assignments, function(fun_assignment) { code <- get_content(lines = source_expression$content, fun_assignment) fun <- try_silently(eval( envir = env, parse( text = code, keep.source = TRUE ) )) if (inherits(fun, "try-error")) { return() } known_used_symbols <- extract_glued_symbols(fun_assignment, interpret_glue = interpret_glue) res <- parse_check_usage( fun, known_used_symbols = known_used_symbols, declared_globals = declared_globals, start_line = as.integer(xml_attr(fun_assignment, "line1")), end_line = as.integer(xml_attr(fun_assignment, "line2")), skip_with = skip_with ) # TODO handle assignment functions properly # e.g. `not_existing<-`(a, b) res$name <- re_substitutes(res$name, rex("<-"), "") lintable_symbols <- xml_find_all(fun_assignment, xpath_culprit_symbol) lintable_symbol_names <- gsub("^`|`$", "", xml_text(lintable_symbols)) lintable_symbol_lines <- as.integer(xml_attr(lintable_symbols, "line1")) matched_symbol <- vapply( seq_len(nrow(res)), function(i) { match( TRUE, lintable_symbol_names == res$name[i] & lintable_symbol_lines >= res$line1[i] & lintable_symbol_lines <= res$line2[i] ) }, integer(1L) ) nodes <- unclass(lintable_symbols)[matched_symbol] # fallback to line based matching if no symbol is found missing_symbol <- is.na(matched_symbol) nodes[missing_symbol] <- lapply(which(missing_symbol), function(i) { line_based_match <- xml_find_first( fun_assignment, glue::glue_data(res[i, ], "descendant::expr[@line1 = {line1} and @line2 = {line2}]") ) if (is.na(line_based_match)) fun_assignment else line_based_match }) xml_nodes_to_lints(nodes, source_expression = source_expression, lint_message = res$message, type = "warning") }) }) } make_check_env <- function(pkg_name, xml) { if (!is.null(pkg_name)) { parent_env <- try_silently(getNamespace(pkg_name)) } if (is.null(pkg_name) || inherits(parent_env, "try-error")) { parent_env <- globalenv() } env <- new.env(parent = parent_env) symbols <- c( get_assignment_symbols(xml), get_imported_symbols(xml) ) # Just assign them an empty function for (symbol in symbols) { assign(symbol, function(...) invisible(), envir = env) } env } get_assignment_symbols <- function(xml) { get_r_string(xml_find_all( xml, " expr[LEFT_ASSIGN]/expr[1]/SYMBOL[1] | equal_assign/expr[1]/SYMBOL[1] | expr[expr[1][SYMBOL_FUNCTION_CALL/text()='assign']]/expr[2]/* | expr[expr[1][SYMBOL_FUNCTION_CALL/text()='setMethod']]/expr[2]/* " )) } get_check_usage_results <- function(expression, known_used_symbols, declared_globals, skip_with) { report_env <- new.env(parent = emptyenv()) report_env$vals <- character() report <- function(x) report_env$vals <- c(report_env$vals, x) old <- options(useFancyQuotes = FALSE) on.exit(options(old)) try( codetools::checkUsage( expression, report = report, suppressLocalUnused = known_used_symbols, suppressUndefined = declared_globals, skipWith = skip_with ) ) report_env$vals } parse_check_usage <- function(expression, known_used_symbols = character(), declared_globals = character(), start_line = 1L, end_line = 1L, skip_with = TRUE) { vals <- get_check_usage_results(expression, known_used_symbols, declared_globals, skip_with) function_name <- rex(anything, ": ") line_info <- rex( " ", "(", capture(name = "path", non_spaces), ":", capture(name = "line1", digits), maybe("-", capture(name = "line2", digits)), ")" ) res <- re_matches( vals, rex( function_name, capture( name = "message", zero_or_more(any, type = "lazy"), maybe( "'", capture(name = "name", anything), "'", zero_or_more(any, type = "lazy") ) ), or(line_info, end) ) ) # nocov start missing <- is.na(res$message) if (any(missing)) { # TODO (AshesITR): Remove this in the future, if no bugs arise from this safeguard warning( "Possible bug in lintr: Couldn't parse usage message ", sQuote(vals[missing][[1L]]), ". ", "Ignoring ", sum(missing), " usage warnings. Please report an issue at https://github.com/r-lib/lintr/issues." ) } # nocov end res <- res[!missing, ] res$line1 <- ifelse( nzchar(res$line1), as.integer(res$line1) + start_line - 1L, NA_integer_ ) res$line2 <- ifelse( nzchar(res$line2), as.integer(res$line2) + start_line - 1L, res$line1 ) res$line1[is.na(res$line1)] <- start_line res$line2[is.na(res$line2)] <- end_line res } get_imported_symbols <- function(xml) { import_exprs_xpath <- " //SYMBOL_FUNCTION_CALL[text() = 'library' or text() = 'require'] /parent::expr /parent::expr[ not(SYMBOL_SUB[ text() = 'character.only' and following-sibling::expr[1][NUM_CONST[text() = 'TRUE'] or SYMBOL[text() = 'T']] ]) or expr[2][STR_CONST] ] /expr[STR_CONST or SYMBOL][1] " import_exprs <- xml_find_all(xml, import_exprs_xpath) imported_pkgs <- get_r_string(import_exprs) unlist(lapply(imported_pkgs, function(pkg) { tryCatch( getNamespaceExports(pkg), error = function(e) character() ) })) } lintr/R/cyclocomp_linter.R0000644000176200001440000000276414577052532015316 0ustar liggesusers#' Cyclomatic complexity linter #' #' Check for overly complicated expressions. See [cyclocomp::cyclocomp()]. #' #' @param complexity_limit Maximum cyclomatic complexity, default 15. Expressions more complex #' than this are linted. See [cyclocomp::cyclocomp()]. #' #' @examples #' # will produce lints #' lint( #' text = "if (TRUE) 1 else 2", #' linters = cyclocomp_linter(complexity_limit = 1L) #' ) #' #' # okay #' lint( #' text = "if (TRUE) 1 else 2", #' linters = cyclocomp_linter(complexity_limit = 2L) #' ) #' #' @evalRd rd_tags("cyclocomp_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export cyclocomp_linter <- function(complexity_limit = 15L) { Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } complexity <- try_silently( cyclocomp::cyclocomp(parse(text = source_expression$content)) ) if (inherits(complexity, "try-error") || complexity <= complexity_limit) { return(list()) } col1 <- source_expression[["column"]][1L] Lint( filename = source_expression[["filename"]], line_number = source_expression[["line"]][1L], column_number = source_expression[["column"]][1L], type = "style", message = sprintf( "Functions should have cyclomatic complexity of less than %d, this has %d.", complexity_limit, complexity ), ranges = list(rep(col1, 2L)), line = source_expression$lines[1L] ) }) } lintr/R/regex_subset_linter.R0000644000176200001440000000656114577052532016024 0ustar liggesusers#' Require usage of direct methods for subsetting strings via regex #' #' Using `value = TRUE` in [grep()] returns the subset of the input that matches #' the pattern, e.g. `grep("[a-m]", letters, value = TRUE)` will return the #' first 13 elements (`a` through `m`). #' #' `letters[grep("[a-m]", letters)]` and `letters[grepl("[a-m]", letters)]` #' both return the same thing, but more circuitously and more verbosely. #' #' The `stringr` package also provides an even more readable alternative, #' namely `str_subset()`, which should be preferred to versions using #' `str_detect()` and `str_which()`. #' #' @section Exceptions: #' Note that `x[grep(pattern, x)]` and `grep(pattern, x, value = TRUE)` #' are not _completely_ interchangeable when `x` is not character #' (most commonly, when `x` is a factor), because the output of the #' latter will be a character vector while the former remains a factor. #' It still may be preferable to refactor such code, as it may be faster #' to match the pattern on `levels(x)` and use that to subset instead. #' #' @evalRd rd_tags("regex_subset_linter") #' #' @examples #' # will produce lints #' lint( #' text = "x[grep(pattern, x)]", #' linters = regex_subset_linter() #' ) #' #' lint( #' text = "x[stringr::str_which(x, pattern)]", #' linters = regex_subset_linter() #' ) #' #' # okay #' lint( #' text = "grep(pattern, x, value = TRUE)", #' linters = regex_subset_linter() #' ) #' #' lint( #' text = "stringr::str_subset(x, pattern)", #' linters = regex_subset_linter() #' ) #' #' @seealso [linters] for a complete list of linters available in lintr. #' @export regex_subset_linter <- function() { # parent::expr for LEFT_ASSIGN and RIGHT_ASSIGN, but, strangely, # parent::equal_assign for EQ_ASSIGN. So just use * as a catchall. # See https://www.w3.org/TR/1999/REC-xpath-19991116/#booleans; # equality of nodes is based on the string value of the nodes, which # is basically what we need, i.e., whatever expression comes in # [grepl(pattern, )] matches exactly, e.g. names(x)[grepl(ptn, names(x))]. xpath_fmt <- " //SYMBOL_FUNCTION_CALL[ {xp_text_in_table(calls)} ] /parent::expr /parent::expr[ parent::expr[ OP-LEFT-BRACKET and not(parent::*[LEFT_ASSIGN or EQ_ASSIGN or RIGHT_ASSIGN]) ] and expr[position() = {arg_pos} ] = parent::expr/expr[1] ] " grep_xpath <- glue(xpath_fmt, calls = c("grepl", "grep"), arg_pos = 3L) stringr_xpath <- glue(xpath_fmt, calls = c("str_detect", "str_which"), arg_pos = 2L) Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content grep_expr <- xml_find_all(xml, grep_xpath) grep_lints <- xml_nodes_to_lints( grep_expr, source_expression = source_expression, lint_message = "Prefer grep(pattern, x, ..., value = TRUE) over x[grep(pattern, x, ...)] and x[grepl(pattern, x, ...)].", type = "warning" ) stringr_expr <- xml_find_all(xml, stringr_xpath) stringr_lints <- xml_nodes_to_lints( stringr_expr, source_expression = source_expression, lint_message = "Prefer stringr::str_subset(x, pattern) over x[str_detect(x, pattern)] and x[str_which(x, pattern)].", type = "warning" ) return(c(grep_lints, stringr_lints)) }) } lintr/R/equals_na_linter.R0000644000176200001440000000267314577052532015275 0ustar liggesusers#' Equality check with NA linter #' #' Check for `x == NA`, `x != NA` and `x %in% NA`. Such usage is almost surely incorrect -- #' checks for missing values should be done with [is.na()]. #' #' @examples #' # will produce lints #' lint( #' text = "x == NA", #' linters = equals_na_linter() #' ) #' #' lint( #' text = "x != NA", #' linters = equals_na_linter() #' ) #' #' lint( #' text = "x %in% NA", #' linters = equals_na_linter() #' ) #' #' # okay #' lint( #' text = "is.na(x)", #' linters = equals_na_linter() #' ) #' #' lint( #' text = "!is.na(x)", #' linters = equals_na_linter() #' ) #' #' @evalRd rd_tags("equals_na_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export equals_na_linter <- function() { na_table <- xp_text_in_table(c("NA", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_")) xpath <- glue(" //NUM_CONST[ {na_table} ] /parent::expr /parent::expr[EQ or NE] | //SPECIAL[text() = '%in%' and following-sibling::expr/NUM_CONST[ {na_table} ]] /parent::expr ") Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression, lint_message = "Use is.na for comparisons to NA (not == or != or %in%)", type = "warning" ) }) } lintr/R/paren_body_linter.R0000644000176200001440000000301514577052532015436 0ustar liggesusers#' Parenthesis before body linter #' #' Check that there is a space between right parenthesis and a body expression. #' #' @evalRd rd_tags("paren_body_linter") #' #' @examples #' # will produce lints #' lint( #' text = "function(x)x + 1", #' linters = paren_body_linter() #' ) #' #' # okay #' lint( #' text = "function(x) x + 1", #' linters = paren_body_linter() #' ) #' #' @seealso #' - [linters] for a complete list of linters available in lintr. #' - #' @export paren_body_linter <- make_linter_from_xpath( # careful to do recursive search to the less common OP-RIGHT-PAREN # and forcond nodes (vs. //expr) for performance -- there can # be O(100K) nodes but in all but pathological examples, # these other nodes will only be a small fraction of this amount. # note also that only has one following-sibling::expr. xpath = " //OP-RIGHT-PAREN[ @end = following-sibling::expr[1]/@start - 1 and @line1 = following-sibling::expr[1]/@line1 and ( preceding-sibling::FUNCTION or preceding-sibling::OP-LAMBDA or preceding-sibling::IF or preceding-sibling::WHILE or preceding-sibling::OP-LAMBDA ) ] /following-sibling::expr[1] | //forcond[ @line1 = following-sibling::expr/@line2 and OP-RIGHT-PAREN/@col1 = following-sibling::expr/@col1 - 1 ] /following-sibling::expr ", lint_message = "There should be a space between a right parenthesis and a body expression.", type = "style" ) lintr/R/whitespace_linter.R0000644000176200001440000000165514457657444015472 0ustar liggesusers#' Whitespace linter #' #' Check that the correct character is used for indentation. #' #' Currently, only supports linting in the presence of tabs. #' #' Much ink has been spilled on this topic, and we encourage you to check #' out references for more information. #' #' @include make_linter_from_regex.R #' #' @examples #' # will produce lints #' lint( #' text = "\tx", #' linters = whitespace_linter() #' ) #' #' # okay #' lint( #' text = " x", #' linters = whitespace_linter() #' ) #' #' @evalRd rd_tags("whitespace_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' #' @references #' - https://www.jwz.org/doc/tabs-vs-spaces.html #' - https://blog.codinghorror.com/death-to-the-space-infidels/ #' @export whitespace_linter <- make_linter_from_regex( regex = rex(start, zero_or_more(regex("\\s")), one_or_more("\t")), lint_type = "style", lint_msg = "Use spaces to indent, not tabs." ) lintr/R/expect_not_linter.R0000644000176200001440000000201214577052532015460 0ustar liggesusers#' Require usage of `expect_false(x)` over `expect_true(!x)` #' #' [testthat::expect_false()] exists specifically for testing that an output is #' `FALSE`. [testthat::expect_true()] can also be used for such tests by #' negating the output, but it is better to use the tailored function instead. #' The reverse is also true -- use `expect_false(A)` instead of #' `expect_true(!A)`. #' #' @examples #' # will produce lints #' lint( #' text = "expect_true(!x)", #' linters = expect_not_linter() #' ) #' #' # okay #' lint( #' text = "expect_false(x)", #' linters = expect_not_linter() #' ) #' #' @evalRd rd_tags("expect_not_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export expect_not_linter <- make_linter_from_xpath( xpath = " //SYMBOL_FUNCTION_CALL[text() = 'expect_true' or text() = 'expect_false'] /parent::expr /following-sibling::expr[OP-EXCLAMATION] /parent::expr ", lint_message = "expect_false(x) is better than expect_true(!x), and vice versa." ) lintr/NEWS.md0000644000176200001440000020626414577221120012513 0ustar liggesusers# lintr 3.1.2 ## New and improved features ### Lint accuracy fixes: removing false positives * `unreachable_code_linter()` ignores reachable code in inline functions like `function(x) if (x > 2) stop() else x` (#2259, @MEO265). * `unnecessary_lambda_linter()` + ignores extractions with explicit returns like `lapply(l, function(x) foo(x)$bar)` (#2258, @MichaelChirico). + ignores calls on the RHS of operators like `lapply(l, function(x) "a" %in% names(x))` (#2310, @MichaelChirico). * `vector_logic_linter()` recognizes some cases where bitwise `&`/`|` are used correctly (#1453, @MichaelChirico). * `expect_comparison_linter()` ignores faulty usage like `expect_true(x, y > z)` (#2083, @MichaelChirico). Note that `y > z` is being passed to the `info=` argument, so this is likely a mistake. * `consecutive_assertion_linter()` ignores cases where a second assertion follows an intervening assignment with `=` (#2444, @MichaelChirico). ### Lint accuracy fixes: removing false negatives * `missing_argument_linter()` catches all missing arguments in calls with several, e.g. `foo(,,)` gives 3 lints instead of 2 (#2399, @MichaelChirico). * `duplicate_argument_linter()` no longer misses cases with duplicate arguments where a comment comes between the argument name and `=` (#2402, @MichaelChirico). ## Notes * Fixed a test assuming a specific parser error message that recently changed in r-devel (#2527, @IndrajeetPatil). * @MichaelChirico has taken over CRAN maintainer duties for the package. Many thanks to @jimhester for more than 10 years and 15 releases wearing that hat!! # lintr 3.1.1 ## Breaking changes * `infix_spaces_linter()` distinguishes `<-`, `:=`, `<<-` and `->`, `->>`, i.e. `infix_spaces_linter(exclude_operators = "->")` will no longer exclude `->>` (#2115, @MichaelChirico). This change is breaking for users relying on manually-supplied `exclude_operators` containing `"<-"` to also exclude `:=` and `<<-`. The fix is to manually supply `":="` and `"<<-"` as well. We don't expect this change to affect many users, the fix is simple, and the new behavior is much more transparent, so we are including this breakage in a minor release. * Removed `find_line()` and `find_column()` entries from `get_source_expressions()` expression-level objects. These have been marked deprecated since version 3.0.0. No users were found on GitHub. * There is experimental support for writing config in plain R scripts (as opposed to DCF files; #1210, @MichaelChirico). The script is run in a new environment and variables matching settings (`?default_settings`) are copied over. In particular, this removes the need to write R code in a DCF-friendly way, and allows normal R syntax highlighting in the saved file. We may eventually deprecate the DCF approach in favor of this one; user feedback is welcome on strong preferences for either approach, or for a different approach like YAML. Generally you should be able to convert your existing `.lintr` file to an equivalent R config by replacing the `:` key-value separators with assignments (`<-`). By default, such a config is searched for in a file named '.lintr.R'. This is a mildly breaking change if you happened to be keeping a file '.lintr.R' around since that file is given precedence over '.lintr'. + We also validate config files up-front make it clearer when invalid configs are present (#2195, @MichaelChirico). There is a warning for "invalid" settings, i.e., settings not part of `?default_settings`. We think this is more likely to affect users declaring settings in R, since any variable defined in the config that's not a setting must be removed to make it clearer which variables are settings vs. ancillary. ## Bug fixes * `sprintf_linter()` doesn't error in cases where whitespace in `...` arguments is significant, e.g. `sprintf("%s", if (A) "" else y)`, which won't parse if whitespace is removed (#2131, @MichaelChirico). ## Changes to default linters * `assignment_linter()` lints the {magrittr} assignment pipe `%<>%` (#2008, @MichaelChirico). This can be deactivated by setting the new argument `allow_pipe_assign` to `TRUE`. * `object_usage_linter()`: + assumes `glue()` is `glue::glue()` when `interpret_glue=TRUE` (#2032, @MichaelChirico). + finds function usages, including infix usage, inside `glue()` calls to avoid false positives for "unused objects" (#2029 and #2069, @MichaelChirico). * `object_name_linter()` no longer attempts to lint strings in function calls on the LHS of assignments (#1466, @MichaelChirico). * `infix_spaces_linter()` allows finer control for linting `=` in different scenarios using parse tags `EQ_ASSIGN`, `EQ_SUB`, and `EQ_FORMALS` (#1977, @MichaelChirico). * `equals_na_linter()` checks for `x %in% NA`, which is a more convoluted form of `is.na(x)` (#2088, @MichaelChirico). ## New and improved features * New exclusion sentinel `# nolint next` to signify the next line should skip linting (#1791, @MichaelChirico). The usual rules apply for excluding specific linters, e.g. `# nolint next: assignment_linter.`. The exact string used to match a subsequent-line exclusion is controlled by the `exclude_next` config entry or R option `"lintr.exclude_next"`. * New `xp_call_name()` helper to facilitate writing custom linters (#2023, @MichaelChirico). This helper converts a matched XPath to the R function to which it corresponds. This is useful for including the "offending" function in the lint's message. * New `make_linter_from_xpath()` to facilitate making simple linters directly from a single XPath (#2064, @MichaelChirico). This is especially helpful for making on-the-fly/exploratory linters, but also extends to any case where the linter can be fully defined from a static lint message and single XPath. * Toggle lint progress indicators with argument `show_progress` to `lint_dir()` and `lint_package()` (#972, @MichaelChirico). The default is still to show progress in `interactive()` sessions. Progress is also now shown with a "proper" progress bar (`utils::txtProgressBar()`), which in particular solves the issue of progress `.` spilling well past the width of the screen in large directories. * `lint()`, `lint_dir()`, and `lint_package()` fail more gracefully when the user mis-spells an argument name (#2134, @MichaelChirico). * Quarto files (.qmd) are included by `lint_dir()` by default (#2150, @dave-lovell). ### New linters * `library_call_linter()` can detect if all library/require calls are not at the top of your script (#2027, #2043, #2163, and #2170, @nicholas-masel and @MichaelChirico). * `keyword_quote_linter()` for finding unnecessary or discouraged quoting of symbols in assignment, function arguments, or extraction (part of #884, @MichaelChirico). Quoting is unnecessary when the target is a valid R name, e.g. `c("a" = 1)` can be `c(a = 1)`. The same goes to assignment (`"a" <- 1`) and extraction (`x$"a"`). Where quoting is necessary, the linter encourages doing so with backticks (e.g. `` x$`a b` `` instead of `x$"a b"`). * `length_levels_linter()` for using the specific function `nlevels()` instead of checking `length(levels(x))` (part of #884, @MichaelChirico). * `scalar_in_linter()` for discouraging `%in%` when the right-hand side is a scalar, e.g. `x %in% 1` (part of #884, @MichaelChirico). * `if_not_else_linter()` for encouraging `if` statements to be structured as `if (A) x else y` instead of `if (!A) y else x` (part of #884, @MichaelChirico). * `repeat_linter()` for encouraging `repeat` for infinite loops instead of `while (TRUE)` (#2106, @MEO265). * `length_test_linter()` detects the common mistake `length(x == 0)` which is meant to be `length(x) == 0` (#1991, @MichaelChirico). ### Extensions to existing linters * `fixed_regex_linter()` gains an option `allow_unescaped` (default `FALSE`) to toggle linting regexes not requiring any escapes or character classes (#1689, @MichaelChirico). Thus `fixed_regex_linter(allow_unescaped = TRUE)` would lint on `grepl("[$]", x)` but not on `grepl("a", x)` since the latter does not use any regex special characters. * `line_length_linter()` helpfully includes the line length in the lint message (#2057, @MichaelChirico). * `conjunct_test_linter()` also lints usage like `dplyr::filter(x, A & B)` in favor of using `dplyr::filter(x, A, B)` (part of #884; #2110 and #2078, @salim-b and @MichaelChirico). Option `allow_filter` toggles when this applies. `allow_filter = "always"` drops such lints entirely, while `"not_dplyr"` only lints calls explicitly qualified as `dplyr::filter()`. The default, `"never"`, assumes all unqualified calls to `filter()` are `dplyr::filter()`. * `sort_linter()` checks for code like `x == sort(x)` which is better served by using the function `is.unsorted()` (part of #884, @MichaelChirico). * `paste_linter()` gains detection for file paths that are better constructed with `file.path()`, e.g. `paste0(dir, "/", file)` would be better as `file.path(dir, file)` (part of #884, #2082, @MichaelChirico). What exactly gets linted here can be fine-tuned with the `allow_file_path` option (`"double_slash"` by default, with alternatives `"never"` and `"always"`). When `"always"`, these rules are ignored. When `"double_slash"`, paths appearing to construct a URL that have consecutive forward slashes (`/`) are skipped. When `"never"`, even URLs should be constructed with `file.path()`. * `seq_linter()` recommends `rev()` in the lint message for lints like `nrow(x):1` (#1542, @MichaelChirico). * `function_argument_linter()` detects usage of `missing()` for the linted argument (#1546, @MichaelChirico). The simplest fix for `function_argument_linter()` lints is typically to set that argument to `NULL` by default, in which case it's usually preferable to update function logic checking `missing()` to check `is.null()` instead. * `commas_linter()` gains an option `allow_trailing` (default `FALSE`) to allow trailing commas while indexing. (#2104, @MEO265) * `unreachable_code_linter()` + checks for code inside `if (FALSE)` and other conditional loops with deterministically false conditions (#1428, @ME0265). + checks for unreachable code inside `if`, `else`, `for`, `while`, and `repeat` blocks, including combinations with `break` and `next` statements. (#2105, @ME0265). * `implicit_assignment_linter()` gains an argument `allow_lazy` (default `FALSE`) that allows optionally skipping lazy assignments like `A && (B <- foo(A))` (#2016, @MichaelChirico). * `unused_import_linter()` gains an argument `interpret_glue` (default `TRUE`) paralleling that in `object_usage_linter()` to toggle whether `glue::glue()` expressions should be inspected for exported object usage (#2042, @MichaelChirico). * `default_undesirable_functions` is updated to also include `Sys.unsetenv()` and `structure()` (#2192 and #2228, @IndrajeetPatil and @MichaelChirico). * Linters with logic around the magrittr pipe `%>%` consistently apply it to the other pipes `%!>%`, `%T>%`, `%<>%` (and possibly `%$%`) where appropriate (#2008, @MichaelChirico). + `brace_linter()` + `pipe_call_linter()` + `pipe_continuation_linter()` + `unnecessary_concatenation_linter()` + `unnecessary_placeholder_linter()` * Linters with logic around function declarations consistently include the R 4.0.0 shorthand `\()` (#2190, @MichaelChirico). + `brace_linter()` + `function_left_parentheses_linter()` + `indentation_linter()` + `object_length_linter()` + `object_name_linter()` + `package_hooks_linter()` + `paren_body_linter()` + `unnecessary_lambda_linter()` + `unreachable_code_linter()` ### Lint accuracy fixes: removing false positives * `fixed_regex_linter()` + Is pipe-aware, in particular removing false positives around piping into {stringr} functions like `x |> str_replace(fixed("a"), "b")` (#1811, @MichaelChirico). + Ignores non-string inputs to `pattern=` as a keyword argument (#2159, @MichaelChirico). * Several linters avoiding false positives in `$` extractions get the same exceptions for `@` extractions, e.g. `S4@T` will no longer throw a `T_and_F_symbol_linter()` hit (#2039, @MichaelChirico). + `T_and_F_symbol_linter()` + `for_loop_index_linter()` + `literal_coercion_linter()` + `object_name_linter()` + `undesirable_function_linter()` + `unreachable_code_linter()` + `yoda_test_linter()` * `sprintf_linter()` is pipe-aware, so that `x %>% sprintf(fmt = "%s")` no longer lints (#1943, @MichaelChirico). * `condition_message_linter()` ignores usages of extracted calls like `env$stop(paste(a, b))` (#1455, @MichaelChirico). * `inner_combine_linter()` no longer throws on length-1 calls to `c()` like `c(exp(2))` or `c(log(3))` (#2017, @MichaelChirico). Such usage is discouraged by `unnecessary_concatenation_linter()`, but `inner_combine_linter()` _per se_ does not apply. * `sort_linter()` only lints on `order()` of a single vector, excluding e.g. `x[order(x, y)]` and `x[order(y, x)]` (#2156, @MichaelChirico). * `redundant_ifelse_linter()` is aware of `dplyr::if_else()`'s `missing=` argument, so that `if_else(A, TRUE, FALSE, missing = FALSE)` doesn't lint, but `if_else(A, TRUE, FALSE, NA)` does (#1941, @MichaelChirico). Note that `dplyr::coalesce()` or `tidyr::replace_na()` may still be preferable. ### Lint accuracy fixes: removing false negatives * `unreachable_code_linter()` finds unreachable code even in the presence of a comment or semicolon after `return()` or `stop()` (#2127, @MEO265). * `implicit_assignment_linter()` + finds assignments in call arguments besides the first one (#2136, @MichaelChirico). + finds assignments in parenthetical expressions like `if (A && (B <- foo(A))) { }` (#2138, @MichaelChirico). * `unnecessary_lambda_linter()` checks for cases using explicit returns, e.g. `lapply(x, \(xi) return(sum(xi)))` (#1567, @MichaelChirico). + thanks to @Bisaloo and @strengejacke for detecting a regression in the original fix (#2231, #2247). # lintr 3.1.0 ## Deprecations & Breaking Changes * `.lintr` files can now be kept in the directory `.github/linters` for better compatibility with Super-Linter. Note that this may be a breaking change if you already have a config in `.github/linters` inside a subdirectory as well as in your R project's root, since the former will now be discovered first where it was ignored before. Please see `vignette("lintr")` for details on how configs are discovered (#1746, @tonyk7440 and @klmr). * `single_quotes_linter()` is deprecated in favor of the more generalizable `quotes_linter()` (#1729, @MichaelChirico). * `unneeded_concatentation_linter()` is deprecated in favor of `unnecessary_concatenation_linter()` for naming consistency (#1707, @IndrajeetPatil). * `consecutive_stopifnot_linter()` is deprecated in favor of the more general (see below) `consecutive_assertion_linter()` (#1604, @MichaelChirico). * `no_tab_linter()` is deprecated in favor of `whitespace_linter()` for naming consistency and future generalization (#1954, @MichaelChirico). * `available_linters()` prioritizes `tags` over `exclude_tags` in the case of overlap, i.e., tags listed in both arguments are included, not excluded. We don't expect many people to be affected by this, and the old behavior was not made explicit in the documentation, but make note of it here since it required changing a test in lintr's own suite where `linters_with_tags()` implicitly assumed this behavior. * `lint()`, `lint_dir()`, and `lint_package()` no longer accept certain arguments (`cache=` for `lint()`, `relative_path=` for the latter two) positionally. The `warning()` since 3.0.0 has been upgraded to an error. ## Bug fixes * `linters_with_tags()` now includes the previously missing spaces around "and" when listing missing linters advertised by `available_linters()`. This error message may appear e.g. when you update lintr to a version with new linters but don't restart your R session (#1946, @Bisaloo) * `fixed_regex_linter()` is more robust to errors stemming from unrecognized escapes (#1545, #1845, @IndrajeetPatil). * `get_source_expressions()` can handle Sweave/Rmarkdown documents with reference chunks like `<>` (#779, @MichaelChirico). Note that these are simply skipped, rather than attempting to retrieve the reference and also lint it. * `assignment_linter()` no longer lints assignments in braces that include comments when `allow_trailing = FALSE` (#1701, @ashbaldry) * `object_usage_linter()` + No longer silently ignores usage warnings that don't contain a quoted name (#1714, @AshesITR) + No longer fails on code with comments inside a multi-line call to `glue::glue()` (#1919, @MichaelChirico) * `namespace_linter()` correctly recognizes backticked operators to be exported from respective namespaces (like `` rlang::`%||%` ``) (#1752, @IndrajeetPatil) * `lint_package()` correctly finds a package from within a subdir if the `path` points to anywhere within the package (#1759, @AshesITR) * Improved error behavior in `Lint()`, `lint()` and `xml_nodes_to_lints()` (#1427, #763, @AshesITR) + `Lint()` validates its inputs more thoroughly, preventing errors during `print.Lints` like "Error in rep.int(character, length) : invalid 'times' value:". + `lint()` no longer tries to create an expression tree with unexpected end of input errors, because they can be broken. + `xml_nodes_to_lints()` warns if it can't find lint locations and uses dummy locations as a fallback. * `linters_with_defaults()` no longer erroneously marks linter factories as linters (#1725, @AshesITR). * Row names for `available_linters()` data frame are now contiguous (#1781, @IndrajeetPatil). * `object_name_linter()` allows all S3 group Generics (see `?base::groupGeneric`) and S3 generics defined in a different file in the same package (#1808, #1841, @AshesITR) * `object_usage_linter()` improves identification of the exact source of a lint + for undefined variables in expressions with where the variable is used as a symbol in a usual way, for example in a formula or in an extraction with `$` (#1914, @MichaelChirico). + for general usage warnings without location info (#1986 and #1917, @AshesITR) * `function_left_parentheses_linter()` produces a more specific lint (and no longer fails) when the opening parenthesis is on a different line than `function` or the call name (#1953, @MichaelChirico). Thanks also to @IndrajeetPatil and @lorenzwalthert for identifying a regression in the initial fix, #1963. ## Changes to defaults * Set the default for the `except` argument in `duplicate_argument_linter()` to `c("mutate", "transmute")`. This allows sequential updates like `x |> mutate(a = b + 1, a = log(a))` (#1345, @IndrajeetPatil). * `object_usage_linter()` + gains `skip_with` argument to skip code in `with()` expressions. To be consistent with `R CMD check`, it defaults to `TRUE` (#941, #1458, @IndrajeetPatil). + Handles backticked symbols inside {glue} expressions correctly, e.g. ``glue("{`x`}")`` correctly determines `x` was used (#1619, @MichaelChirico) + Detects problems inside R4.1.0+ lambda functions (`\(...)`) (#1933, @MichaelChirico) * `spaces_inside_linter()` allows terminal missing keyword arguments (e.g. `alist(arg = )`; #540, @MichaelChirico) * `brace_linter()` allows empty braced expression on the same line (e.g. `while (updating_condition()) { }`) regardless of `allow_single_line` to match the corresponding behavior in {styler}. This is an expedient while the style guide on handling this case awaits clarification: https://github.com/tidyverse/style/issues/191. (#1346, @MichaelChirico) * `undesirable_function_linter()` and `undesirable_operator_linter()` now produce an error if empty vector of undesirable functions or operators is provided (#1867, @IndrajeetPatil). * New linters which are also included as defaults (see "New linters" for more details): + `indentation_linter()` + `quotes_linter()` + `unnecessary_concatenation_linter()` + `whitespace_linter()` * `lint_package()` also looks for files in `exec/` (#1950, @jmaspons). ## New and improved features * New `get_r_string()` helper to get the R-equivalent value of a string, especially useful for R-4-style raw strings. Previously an internal `lintr` helper, now exported to facilitate writing custom linters (#1493, @MichaelChirico). * `object_usage_linter()` improves lint metadata when detecting undefined infix operators, e.g. `%>%` or `:=` (#1497, @MichaelChirico) * `unused_import_linter()` can detect datasets from imported packages and no longer warns when a package is imported only for its datasets (#1545, @IndrajeetPatil). * When a linter triggers an error, `lint()` will provide a more actionable summary of where the error occurred, particularly useful for cases like `lint_package()` where both the responsible file and the responsible linter would be unknown (@MichaelChirico). Typically, linters should not themselves cause R to stop -- syntax errors lead to error lints, for example. Please report such failures as they are likely bugs. * `pipe_continuation_linter()` recognizes violations involving the native R pipe `|>` (#1609, @MichaelChirico) * `paste_linter()` also catches usages like `paste(rep("*", 10L), collapse = "")` that can be written more concisely as `strrep("*", 10L)` (#1108, @MichaelChirico) * `spaces_inside_linter()` produces lints for spaces inside `[[` (#1673, @IndrajeetPatil). * `sprintf_linter()` also applies to `gettextf()` (#1677, @MichaelChirico) * Documentation for all linters contains examples of code that does and does not produce lints (#1492, @IndrajeetPatil). * `implicit_integer_linter()` gains parameter `allow_colon` to skip lints on expressions like `1:10` (#1155, @MichaelChirico) * `infix_spaces_linter()` supports the native R pipe `|>` (#1793, @AshesITR) * `unnecessary_concatenation_linter()` (f.k.a. `unneeded_concatenation_linter()`) no longer lints on `c(...)` (i.e., passing `...` in a function call) when `allow_single_expression = FALSE` (#1696, @MichaelChirico) * `object_name_linter()` gains parameter `regexes` to allow custom naming conventions (#822, #1421, @AshesITR) * `literal_coercion_linter()` reports a replacement in the lint message, e.g. code like `as.integer(1)` will suggest using `1L` instead, and code like `as.numeric(NA)` will suggest using `NA_real_` instead (#1439, @MichaelChirico) * Added `format()` functions for `lint` and `lints` (#1784, @AshesITR) * `all_linters()` function provides an easy way to access all available linters (#1843, @IndrajeetPatil) * `missing_argument_linter()` allows missing arguments in `quote()` calls (#1889, @IndrajeetPatil). * `get_source_expressions()` correctly extracts indented code chunks from R Markdown documents, which helps avoid spurious lints related to whitespace (#1945, @MichaelChirico). The convention taken is that, within each chunk, all code is anchored relative to the leftmost non-whitespace column. * `available_linters()` gives priority to `tags` over `exclude_tags` in the case of overlap. In particular, this means that `available_linters(tags = "deprecated")` will work to return deprecated linters without needing to specify `exclude_tags` (#1959, @MichaelChirico). * The {lintr} configuration file is now searched in the system's user configuration path; the lintr config filename can also be configured explicitly by setting the environment variable `R_LINTR_LINTER_FILE` (#460, @klmr) * Errors in the {lintr} configuration file now produce more informative error messages (#886, @AshesITR) ### New linters * `matrix_apply_linter()` recommends use of dedicated `rowSums()`, `colSums()`, `colMeans()`, `rowMeans()` over `apply(., MARGIN, sum)` or `apply(., MARGIN, mean)`. The recommended alternative is much more efficient and more readable (#1869, @Bisaloo). * `unnecessary_lambda_linter()`: detect unnecessary lambdas (anonymous functions), e.g. `lapply(x, function(xi) sum(xi))` can be `lapply(x, sum)` and `purrr::map(x, ~quantile(.x, 0.75, na.rm = TRUE))` can be `purrr::map(x, quantile, 0.75, na.rm = TRUE)`. Naming `probs = 0.75` can further improve readability (#1531, #1866, @MichaelChirico, @Bisaloo). * `redundant_equals_linter()` for redundant comparisons to `TRUE` or `FALSE` like `is_treatment == TRUE` (#1500, @MichaelChirico) * `lengths_linter()` for encouraging usage of `lengths(x)` instead of `sapply(x, length)` (and similar) * `function_return_linter()` for handling issues in function `return()` statements. Currently handles assignments within the `return()` clause, e.g. `return(x <- foo())` (@MichaelChirico) * `boolean_arithmetic_linter()` for identifying places where logical aggregations are more appropriate, e.g. `length(which(x == y)) == 0` is the same as `!any(x == y)` or even `all(x != y)` (@MichaelChirico) * `for_loop_index_linter()` to prevent overwriting local variables in a `for` loop declared like `for (x in x) { ... }` (@MichaelChirico) * `is_numeric_linter()` for redundant checks equivalent to `is.numeric(x)` such as `is.numeric(x) || is.integer(x)` or `class(x) %in% c("numeric", "integer")` (@MichaelChirico) * `empty_assignment_linter()` for identifying empty assignments like `x = {}` that are more clearly written as `x = NULL` (@MichaelChirico) * `unnecessary_placeholder_linter()` for identifying where usage of the {magrittr} placeholder `.` could be omitted (@MichaelChirico) * `routine_registration_linter()` for identifying native routines that don't use registration (`useDynLib` in the `NAMESPACE`; @MichaelChirico) * `indentation_linter()` for checking that the indentation conforms to 2-space Tidyverse-style (@AshesITR and @dgkf, #1411, #1792, #1898). * `unnecessary_nested_if_linter()` for checking unnecessary nested `if` statements where a single `if` statement with appropriate conditional expression would suffice (@IndrajeetPatil and @AshesITR, #1778). * `implicit_assignment_linter()` for checking implicit assignments in function calls (@IndrajeetPatil and @AshesITR, #1777). * `quotes_linter()` is a generalized version of (now deprecated) `single_quotes_linter()`. It accepts an argument `delimiter` to specify whether `"` or `'` should be the accepted method for delimiting character literals. The default, `"`, reflects the Tidyverse style guide recommendation and matches the behavior of `single_quotes_linter()`. * `unnecessary_concatenation_linter()` is simply `unneeded_concatenation_linter()`, renamed. * `consecutive_assertion_linter()` (f.k.a. `consecutive_stopifnot_linter()`) now lints for consecutive calls to `assertthat::assert_that()` (as long as the `msg=` argument is not used; #1604, @MichaelChirico). * `whitespace_linter()` is simply `no_tab_linter()`, renamed. In the future, we plan to extend it to work for different whitespace preferences. ## Notes * {lintr} now depends on R version 3.5.0, in line with the tidyverse policy for R version compatibility. * `lint()` continues to support Rmarkdown documents. For users of custom .Rmd engines, e.g. `marginformat` from {tufte} or `theorem` from {bookdown}, note that those engines must be registered in {knitr} prior to running `lint()` in order for {lintr} to behave as expected, i.e., they should be shown as part of `knitr::knit_engines$get()`. For {tufte} and {bookdown} in particular, one only needs to load the package namespace to accomplish this (i.e., minimally `loadNamespace("tufte")` or `loadNamespace("bookdown")`, respectively, will register those packages' custom engines; since `library()` also runs `loadNamespace()`, running `library()` will also work). Note further that {tufte} only added this code to their `.onLoad()` recently after our request to do so (see https://github.com/rstudio/tufte/issues/117). Therefore, ensure you're using a more recent version to get the behavior described here for {tufte}. More generally, there is no requirement that `loadNamespace()` will register a package's custom {knitr} engines, so you may need to work with other package authors to figure out a solution for other engines. Thanks to Yihui and other developers for their helpful discussions around this issue (#797, @IndrajeetPatil). * The output of `lint()` and `Lint()` gain S3 class `"list"` to assist with S3 dispatch (#1494, @MichaelChirico) + As a corollary, we now register an `as_tibble` method for class `lints`, conditional on {tibble} availability, to avoid dispatching to the `list` method which does not work with `lint()` output (#1997, @MichaelChirico) * `object_usage_linter()` gives a more helpful warning when a `glue()` expression fails to evaluate (#1985, @MichaelChirico) * The documentation of `object_name_linter()` now describes how `"symbols"` works when passed to the `styles` parameter (#1924, @hedsnz). # lintr 3.0.2 * Fix test to avoid leaving behind cache files in the global cache directory. # lintr 3.0.1 * Skip multi-byte tests in non UTF-8 locales (#1504) * `modify_defaults()` no longer uses the mistaken `"lintr_function"` S3 class, instead applying the `"linter"` class also common to `Linter()`. `Linter()` also includes `"function"` in the S3 class of its output to facilitate S3 dispatch to `function` methods where appropriate (#1392, @MichaelChirico). ## Changes to defaults * `brace_linter()` allows opening curly braces on a new line when there is a comment ending the preceding line (#1433 and #1434, @IndrajeetPatil). * `seq_linter()` produces lint for `seq(...)`, since it also cannot properly handle empty edge cases (#1468, @IndrajeetPatil). * `seq_linter()` additionally lints on `1:n()` (from {dplyr}) and `1:.N` (from {data.table}) (#1396, @IndrajeetPatil). * `literal_coercion_linter()` lints {rlang}'s atomic vector constructors (i.e., `int()`, `chr()`, `lgl()`, and `dbl()`) if the argument is a scalar (#1437, @IndrajeetPatil). * `redundant_ifelse_linter()`'s lint message correctly suggests negation when the `yes` condition is `0` (#1432, @IndrajeetPatil). * `seq_linter()` provides more specific replacement code in lint message (#1475, @IndrajeetPatil). ## New and improved features * New `sort_linter()` to detect `x[order(x)]` and recommend the faster and clearer alternative: `sort(x)` (#1528, @Bisaloo) * `unreachable_code_linter()` ignores trailing comments if they match a closing nolint block (#1347, @AshesITR). * New `function_argument_linter()` to enforce that arguments with defaults appear last in function declarations, see the [Tidyverse design guide](https://design.tidyverse.org/required-no-defaults.html) (#450, @AshesITR). * New `allow_trailing` argument added to `assignment_linter()` to check when assignment operators are at the end of a line, and the value is on the following line (#1491, @ashbaldry) * New `sarif_output()` function to output lints to SARIF output (#1424, @shaopeng-gh) * `commented_code_linter()` now lints commented argument code, containing a trailing comma, as well (#386, @AshesITR). For example a comment containing `# na.rm = TRUE,` now triggers a lint. ## Bug fixes * `object_length_linter()` does not fail in case there are dependencies with no exports (e.g. data-only packages) (#1424, #1509, @IndrajeetPatil). * `get_source_expressions()` no longer fails on R files that match a knitr pattern (#743, #879, #1406, @AshesITR). * Parse error lints now appear with the linter name `"error"` instead of `NA` (#1405, @AshesITR). Also, linting no longer runs if the `source_expressions` contain invalid string data that would cause error messages in other linters. in other linters. * Prevent `lint()` from hanging on Rmd files with some syntax errors (#1443, @MichaelChirico). * `get_source_expressions()` no longer omits trailing non-code lines from knitr files (#1400, #1415, @AshesITR). This fixes the location information for `trailing_blank_lines_linter()` in RMarkdown documents without terminal newlines. * The `vignette("lintr")` incorrectly cited `exclude` as the key for setting file exclusions in `.lintr` when it is actually `exclusions`. (#1401, @AshesITR) * Fixed file exclusion detection in `lint_dir()` so it no longer errors if there are multiple exclusions or no global exclusions configured for a single file (#1413, #1442, @AshesITR). ## Other changes * The minimum needed version for soft dependency `{withr}` has been bumped to `2.5.0` (#1404, @IndrajeetPatil). * Changed the deprecation warning for `with_defaults()` to also mention `modify_defaults()` (#1438, @AshesITR). * Quarto files (`.qmd`) were supported out of the box. The documentation and the testing infrastructure are updated to reflect this (#1486, @IndrajeetPatil). # lintr 3.0.0 ## Breaking changes * All linters are now function factories (i.e., functions that return functions) for consistency. Previously, only linters with customizable parameters were factories (#245, @fangly, @AshesITR, and @MichaelChirico). This means that usage such as `lint("file.R", seq_linter)` should be updated to `lint("file.R", seq_linter())`, and the following update for custom linters: ```r my_custom_linter <- function(source_expression) { ... } # becomes my_custom_linter <- function() Linter(function(source_expression) { ... }) ``` * Exclusions specified in the `.lintr` file are now relative to the location of that file and support excluding entire directories (#158, #438, @AshesITR). * Removed long-deprecated linters (they've been marked as deprecated since v1.0.1 in 2017): + `absolute_paths_linter()` + `camel_case_linter()` + `multiple_dots_linter()` + `snake_case_linter()` + `trailing_semicolons_linter()` * Removed `return()` from `all_undesirable_functions` because early returns (which often improve readability and reduce code complexity) require explicit use of `return()`. Follow #1100 for an upcoming `return_linter()` to lint unnecessary `return()` statements (#1146, @AshesITR). Note that you can replicate old behavior by supplying `return` as a custom undesirable function: `undesirable_function_linter(c(all_undesirable_functions, list(return = NA)))` ## Deprecations * Lints are now marked with the name of the `linter` that caused them instead of the name of their implementation function. Deprecated the obsolete `linter` argument of `Lint()` (#664, #673, #746, @AshesITR). Downstream custom linters should follow suit. * Renamed `semicolon_terminator_linter()` to `semicolon_linter()` for better consistency. `semicolon_terminator_linter()` survives but is marked for deprecation. The new linter also has a new signature, taking arguments `allow_compound` and `allow_trailing` to replace the old single argument `semicolon`, again for signature consistency with other linters. * The following linters were subsumed into `brace_linter()` and are now deprecated; see the item on `brace_linter()` below: + `closed_curly_linter()` + `open_curly_linter()` + `paren_brace_linter()` * The `...` argument for `lint()`, `lint_dir()`, and `lint_package()` has been promoted to an earlier position to better match the [Tidyverse design principle](https://design.tidyverse.org/required-no-defaults.html) of data->descriptor->details. This change enables passing objects to `...` without needing to specify non-required arguments, e.g. `lint_dir("/path/to/dir", linter())` now works without the need to specify `relative_path`. This affects some code that uses positional arguments (#935, @MichaelChirico). + For `lint()`, `...` is now the 3rd argument, where earlier this was `cache`. + For `lint_dir()` and `lint_package()`, `...` is now the 2nd argument, where earlier this was `relative_path`. * Deprecated argument `source_file` to exported functions `with_id()` and `ids_with_token()`. It has been renamed to `source_expression` to better reflect that this argument is typically the output of `get_source_expressions()`. For now, the old argument `source_file` can still be used (with warning). The now-private functional versions of many linters also underwent the same renaming (`source_file` -> `source_expression`). This has no direct effect on packages importing lintr, but is mentioned in case custom linters imitating `lintr` style had also adopted the `source_file` naming and want to adapt to keep in sync. * Deprecated `with_defaults()` in favor of `linters_with_defaults()`, and add `modify_defaults()` which is intended to be used more generally to modify (i.e., extend, trim, and/or update) a list of defaults. Note that the argument corresponding to `with_defaults()`'s `default=` is called `defaults=` (i.e., pluralized) in both of these, and that usage like `with_defaults(default = NULL, ...)` should be converted to `linters_with_defaults(defaults = list(), ...)` (#1029, #1336, #1361, @AshesITR and @michaelchirico). * Deprecated the `find_line()` and `find_column()` helpers from the item-level `expressions` returned with `get_source_expressions()`. These helpers were typically associated with regex-based logic for building linters, which is rarely needed and prone to false positives; now that lintr almost exclusively uses XPath-based logic for linters, these are no longer necessary (#1373, @MichaelChirico). ## Other changes to defaults ### Updates to `default_linters` * New `brace_linter()` which combines several curly brace related linters, deprecating the following predecessors (#1041, @AshesITR): + `closed_curly_linter()`; both now also allow `}]` in addition to `})` and `},` as exceptions, i.e., `}` doesn't need to be on its own line if paired with a closing square bracket, a closing parenthesis, or a comma. Also improved lint metadata so that source markers land at the closing brace instead of the closing parenthesis to improve the experience of fixing the lint (#583, @AshesITR). + `open_curly_linter()`; both also no longer lint unnecessary trailing whitespace (use `trailing_whitespace_linter()` for this) and also allow `(`, `,`, and `%>%` on preceding lines as exceptions, i.e., `{` can be alone on a line if the previous line is terminated with an opening parenthesis, a comma, or a pipe (`%>%`) (#487, #1028, @AshesITR). + `paren_brace_linter()`; `brace_linter()` also lints `if`/`else` and `repeat` with missing whitespace. + `brace_linter()` also newly enforces the following rules surrounding curly braces (originally Google linters, see below): - Require `else` to come on the same line as the preceding `}`, if present (#884, @MichaelChirico). - Require functions spanning multiple lines to use curly braces (#987, @MichaelChirico). - Require balanced usage of `{}` in `if`/`else` conditions, i.e., if the `if` branch uses braces, then so must the `else` branch, and _vice versa_ (#983, @MichaelChirico). * New `paren_body_linter()` checks that there is a space between a right parenthesis and a body expression (#809, @kpagacz). * Added `semicolon_linter()` as a default because it enforces a tidyverse style guide rule (#683, @AshesITR). * `assignment_linter()` (#915, @MichaelChirico): + Right assignments are now linted by default (`->` and `->>`). + New argument `allow_cascading_assign` (`TRUE` by default) toggles whether to lint `<<-` and `->>`. + New argument `allow_right_assign` (`FALSE` by default) toggles whether to lint `->` and `->>`. * `commented_code_linter()`: use the parse tree to find comments, eliminating some false positives (#451, @AshesITR). * `equals_na_linter()` (#545, @MichaelChirico): + Extended to lint `x != NA` (before, only `==` was caught) and `NA == x` (before, only `NA` on RHS was caught). + Extended to skip usages in comments like `is.na(x) # use is.na(x), not x == NA`. * `function_left_parentheses_linter()`: improved location information (#1266, #1267, @AshesITR). * `infix_spaces_linter()`: + Added argument `allow_multiple_spaces` (`TRUE` by default) which toggles whether to generate a lint for operators used with multiple spaces, e.g. `x + 2`. The default setting allows extra spacing to be used to increase line-to-line alignment (#940, @f-ritter and @MichaelChirico). + Extended so that usages like `a~b` and `function(a=1) { ... }` are linted (#930, #michaelchirico). + Added argument `exclude_operators` to disable lints on selected infix operators. By default, all "low-precedence" operators throw lints; see `?infix_spaces_linter` for an enumeration of these. (#914, @MichaelChirico). + Add an exception for `/` usage in `box::use()` declarations (#1087, @klmr). * `line_length_linter()`: place the source marker at the margin of the affected line to improve user experience during de-linting -- just press Return (#735, @AshesITR).* * `no_tab_linter()`: use more reliable matching (e.g., excluding matches found in comments; #441, @russHyde). * `object_length_linter()`: correctly detect generics and only count the implementation class towards the length. This prevents false positive lints in the case of long generic names, e.g. `very_very_very_long_generic_name.short_class` no longer produces a lint (#871, @AshesITR). * `object_name_linter()`: + Improved generic detection -- in user-defined method `my_method.upstream.class`, `upstream.class` no longer throws a lint because the generic (`my_method`) properly uses `snake_case` (#737, @AshesITR). + Exclude special R namespace hook functions such as `.onLoad()` (#500, #614, @AshesITR and @MichaelChirico). + Correctly detect imported functions when linting packages (#642, @AshesITR). + Correctly detect assignment generics like `names<-.class_name` (#843, @jonkeane). + Added new styles `"symbols"` and `"SNAKE_CASE"` (#494, #495, #615, #670, @MichaelChirico and @AshesITR). - `"symbols"` is a new default style which won't lint all-symbol object names. In particular, that means operator names like `%+%` are allowed. + No longer lints names used in `$` extractions (#582, @AshesITR). * `object_usage_linter()`: + Detect global variables if there are top-level dollar-assignments (#666, @AshesITR). + Report usage warnings spanning multiple lines (#507, @AshesITR). + Detect usages inside `glue::glue()` constructs (#942, @AshesITR). + Extended to include functions assigned with `=` instead of `<-` (#1081, @MichaelChirico). + Detect functions exported by packages that are explicitly attached using `library()` or `require()` calls (#1127, @AshesITR). + Improved location information in some cases where the previous regex-based approach didn't work, e.g. unicode characters in variable names (#1285, @AshesITR). + Correctly detect functions declared within `assign()` and `setMethod()` (#1322, @AshesITR). * `spaces_inside_linter()`: ignore spaces preceding trailing comments (#636, @MichaelChirico). * `T_and_F_symbol_linter()`: + Added as a default because it enforces a tidyverse style guide rule (#517, @AshesITR). + No longer lint occurrences of `T` and `F` when used for subsetting, and give a better message when used as variable names (#657, @AshesITR). * `trailing_blank_lines_linter()`: + Extended to lint files without a terminal newline (#675, @AshesITR). + Also, running `lint()` on a file without a terminal newline no longer throws a `warning()`. * `trailing_whitespace_linter()`: + Extended to also lint completely blank lines by default (#1044, @AshesITR). + Added argument `allow_empty_lines` (`FALSE` by default) to toggle this behavior. + Improved so that trailing whitespace inside string literals does not trigger a lint (#1045, @AshesITR). + Added argument `allow_in_strings` (`TRUE` by default) to toggle this behavior. * `undesirable_function_linter()`: + Added new functions to `default_undesirable_functions` related to debugging (#876, @MichaelChirico): - `browser()` - `debug()` - `debugcall()` - `debugonce()` - `trace()` - `untrace()` + No longer lints `library()` and `require()` calls attaching a package with an undesired name, e.g. `library(foo)` (#814, @kpagacz and @MichaelChirico). + No longer lints undesirable symbols if they are used as names in `$` extractions (#1050, @AshesITR). + Added more explanation why certain functions might be undesirable and what alternatives to use; ditto for `undesirable_operator_linter()` (#1133, #1146, #1159, @AshesITR). ### Other noteworthy changes * `cyclocomp_linter()`: set the default `complexity_limit` to 15. This brings the default into sync with what is enforced via `default_linters` (#693, @AshesITR). * `lint_package()` now lints files in the `demo` directory by default (#703, @dmurdoch). * Moved the default lintr cache directory from `~/.R/lintr_cache` (which was a violation of CRAN policy) to `R_user_dir("lintr", "cache")`. Note that 3.0.0 is a major version update and invalidates the old cache anyway, so it can be safely deleted (#1062, @AshesITR). ## New and improved features ### New linters * `backport_linter()` for detecting mismatched R version dependencies (#506, #1316, #1318, #1319, @MichaelChirico and @AshesITR). * `duplicate_argument_linter()` similarly checks that there are no duplicate arguments supplied to function calls (#850, @renkun-ken). * `missing_argument_linter()` to check for empty (missing) arguments in function calls (#563, #1152, @renkun-ken and @AshesITR). * `missing_package_linter()` to check if packages in calls to `library()` and friends are missing (#536, #1037, @renkun-ken and @MichaelChirico). * `namespace_linter()` to check for common mistakes in `pkg::symbol` usages (#548, @renkun-ken). * `package_hooks_linter()` to run a series of checks also done by `R CMD check` on the `.onLoad()`, `.onAttach()`, `.Last.lib()` and `.onDetach()` hooks (#882, @MichaelChirico). * `pipe_call_linter()` to enforce that all steps of `magrittr` pipelines use explicit calls instead of symbols, e.g. `x %>% mean()` instead of `x %>% mean` (#801, @MichaelChirico). * `sprintf_linter()` to check for common mistakes in `sprintf()` usage (#544, #624, @renkun-ken and @AshesITR). * `unused_import_linter()` to detect unnecessary `library()` calls in R scripts (#239, @jimhester, @AshesITR). #### Google linters Google is a heavy user of lintr internally, and has developed a large set of linters improving code consistency and correcting common R usage mistakes. This release includes many of these linters that are of general interest to the broader R community. More will be included in future releases. See, e.g. #884, #979, #998, #1011, #1016, #1036, #1051, #1066, and #1067; special thanks to @MichaelChirico and @michaelquinn32. * `any_duplicated_linter()` Require usage of `anyDuplicated(x) > 0L` over `any(duplicated(x))` and similar. * `any_is_na_linter()` Require usage of `anyNA(x)` over `any(is.na(x))`. * `class_equals_linter()` Prevent comparing `class(x)` with `==`, `!=`, or `%in%`, where `inherits()` is typically preferred. * `condition_message_linter()` Prevent condition messages from being constructed like `stop(paste(...))` (where just `stop(...)` is preferable). * `conjunct_test_linter()` Require usage of `expect_true(x); expect_true(y)` over `expect_true(x && y)` and similar. * `consecutive_stopifnot_linter()` Require consecutive calls to `stopifnot()` to be unified into one. * `expect_comparison_linter()` Require usage of `expect_gt(x, y)` over `expect_true(x > y)` and similar. * `expect_identical_linter()` Require usage of `expect_identical()` by default, and `expect_equal()` only by exception. * `expect_length_linter()` Require usage of `expect_length(x, n)` over `expect_equal(length(x), n)` and similar. * `expect_named_linter()` Require usage of `expect_named(x, n)` over `expect_equal(names(x), n)` and similar. * `expect_not_linter()` Require usage of `expect_false(x)` over `expect_true(!x)`, and _vice versa_. * `expect_null_linter()` Require usage of `expect_null(x)` over `expect_equal(x, NULL)` and similar. * `expect_s3_class_linter()` Require usage of `expect_s3_class(x, k)` over `expect_equal(class(x), k)` and similar. * `expect_s4_class_linter()` Require usage of `expect_s4_class(x, k)` over `expect_true(methods::is(x, k))`. * `expect_true_false_linter()` Require usage of `expect_true(x)` over `expect_equal(x, TRUE)` and similar. * `expect_type_linter()` Require usage of `expect_type(x, t)` over `expect_equal(typeof(x), t)` and similar. * `fixed_regex_linter()` Require `fixed = TRUE` or `stringr::fixed()` for regular expressions that can be expressed statically, e.g. `strsplit(x, "[.]")` can be `strsplit(x, ".", fixed = TRUE)`. + Added parameter `allow_grepl` (default `FALSE`) to toggle whether `grepl()` usages should be linted. These might be treated separately because `grepl("^x", NA)` is `FALSE`; the `startsWith()` equivalent to get `FALSE` for missing input is clunkier, but more explicit: `!is.na(x) & startsWith(x, string)` (#1376, @MichaelChirico). * `ifelse_censor_linter()` Require usage of `pmax()` / `pmin()` where appropriate, e.g. `ifelse(x > y, x, y)` is `pmax(x, y)`. * `inner_combine_linter()` Require inputs to known-vectorized functions to be combined first rather than later, e.g. `as.Date(c(x, y))` over `c(as.Date(x), as.Date(y))`. * `literal_coercion_linter()` Require using correctly-typed literals instead of direct coercion, e.g. `1L` instead of `as.numeric(1)`. * `nested_ifelse_linter()` Prevent nested calls to `ifelse()` like `ifelse(A, x, ifelse(B, y, z))`, and similar. * `numeric_leading_zero_linter()` Require a leading `0` in fractional numeric constants, e.g. `0.1` instead of `.1`. * `outer_negation_linter()` Require usage of `!any(x)` over `all(!x)` and `!all(x)` over `any(!x)`. * `paste_linter()` lint for common mis-use of `paste()` and `paste0()`: + `paste0()` encouraged instead of `paste(sep = "")`. + `toString()` or `glue::glue_collapse()` encouraged instead of `paste(x, collapse = ", ")`. + Lint `sep=` passed to `paste0()` -- typically a mistake. * `redundant_ifelse_linter()` Prevent usage like `ifelse(A & B, TRUE, FALSE)` or `ifelse(C, 0, 1)` (the latter is `as.numeric(!C)`). * `regex_subset_linter()` Require usage of `grep(ptn, x, value = TRUE)` over `x[grep(ptn, x)]` and similar. * `string_boundary_linter()` Require usage of `startsWith(x, ptn)` over `grepl("^ptn", x)` or `substr(x, 1, 3) == ptn` and similar. * `strings_as_factors_linter()` Check for code designed to work before and after the `stringsAsFactors = FALSE` default change in R 4.0 by examining code for `data.frame()` usages susceptible to assumptions about the default value of `stringsAsFactors=`. * `system_file_linter()` Prevent usage like `file.path(system.file("A", package = "pkg"), "B")` where simply `system.file("A", "B", package = "pkg")` is more concise and readable. * `unreachable_code_linter()` Prevent code after `return()` and `stop()` statements that will never be reached (extended for #1051 thanks to early user testing, thanks @bersbersbers!). * `vector_logic_linter()` Require use of scalar logical operators (`&&` and `||`) inside `if()` conditions and similar. * `yoda_test_linter()` Require usage of `expect_identical(x, 1L)` over `expect_equal(1L, x)` and similar. ### Other features and improvements * **Documentation**: Reorganize linter documentation into new tag-based Rd pages (#888, #1015, @AshesITR). + Each linter has its own help page. + `?linters` also links to tag help pages, collecting linters with a similar goal. + Each linter can have multiple tags. + `available_linters()`: new function to list available linters and their tags. This feature is extensible by package authors providing add-on linters for {lintr}. + `available_tags()`: new function to list available tags. + `linters_with_tags()`: new function to help build a list of linters using tags. * **Encodings**: lintr now supports non-system character Encodings. The correct encoding is auto-detected from .Rproj or DESCRIPTION files in your project. Override the default in the `encoding` setting of lintr (#752, #782, @AshesITR). * **Jenkins CI**: Support for writing comments to GitHub repo when running in Jenkins CI (#488, @fdlk). * **Performance**: Optimized performance-critical functions in lintr, such as `get_source_expressions()` resulting in about 2x speedup in our test suite and even more for complex files (#1169, #1197, #1200, #1201, #1214, @MichaelChirico and @AshesITR). Average `lint_package()` execution time is down about 30% and the median package sees about 40% improvement. * **Raw strings**: Several linters tightened internal logic to allow for raw strings like `R"( a\string )"` (#1034, #1285, @MichaelChirico and @AshesITR). * **Selective exclusion syntax**: New syntax to exclude only selected linters from certain lines or passages. Use `# nolint: linter_name, linter2_name.` or `# nolint start: linter_name, linter2_name.` in source files or named lists of line numbers in `.lintr`. Note the terminal `.` is required. Also allows for partial matching as long as the supplied prefix is unique, e.g. `# nolint: infix_spaces.` works to exclude `infix_spaces_linter` (#605, #872, @AshesITR). + Added the linter name to lintrs output to facilitate discovery of the correct name (#1357, @AshesITR). * Improved S3 generic detection for non-standard S3 generics where `UseMethod()` is called after several preceding expressions (#846, @jonkeane). * `extraction_operator_linter()`: no longer lint `x[NULL]` (#1273, @AshesITR). * `is_lint_level()`: new exported helper for readably explaining which type of expression is required for a custom linter. Some linters are written to require the full file's parse tree (for example, `single_quotes_linter()`). Others only need single expressions, which is more cache-friendly (most linters are written this way to leverage caching) (#921, @MichaelChirico). * `lint_dir()` excludes the `renv` and `packrat` directories by default (#697, @AshesITR). * `lint()`: new optional argument `text` for supplying a line or lines directly, e.g. if the file is already in memory or linting is being done _ad hoc_ (#503, @renkun-ken). * `seq_linter()`: improve lint message to be clearer about the reason for linting (#522, @MichaelChirico). * `unneeded_concatenation_linter()`: + Correctly considers arguments in pipelines (`%>%` or `|>`; #573, #1270, @michaelquinn32 and @AshesITR). + New argument `allow_single_expression`, default `TRUE`, toggling whether `c(x)` should be linted, i.e., a call to `c()` with only one entry which is not a constant. In some such cases, `c()` can simply be dropped, e.g. `c(a:b)`; in others, the parentheses are still needed, e.g. `-c(a:b)` should be `-(a:b)`; and in still others, `c()` is used for the side-effect of stripping attributes, e.g. `c(factor(letters))` or `c(matrix(1:10, 5, 2))`. In this last case, `c()` can (and should) in most cases be replaced by `as.vector()` or `as.integer()` for readability. In fact, we suspect it is _always_ preferable to do so, and may change the default to `allow_single_expression = FALSE` in the future. Please report your use case if `as.vector()` does not suit your needs (#1344, @MichaelChirico). * `use_lintr()`: new exported helper for creating a minimal `.lintr` configuration (#902, @AshesITR). * `xml_nodes_to_lints()`: new exported helper for converting `xml_node` objects obtained using linter logic expressed in XPath into `Lint` objects (#1124, #1216, #1234, @MichaelChirico and @AshesITR). ## Bug fixes * **RStudio**: Source markers are cleared when there are no lints (#520, @AshesITR). * Error message for mismatched starts and ends of exclusion ranges is now more helpful. (#571, #860, @AshesITR and @danielinteractive). * Improved location information for R parse errors (#894, #892, @renkun-ken and @AshesITR). * `get_source_expressions()`: + Fix possible error on invalid XML produced by `xmlparsedata::xml_parse_data()` (#559, @renkun-ken). + Fix handling zero-length variable name error (#566, @renkun-ken). + Malformed Rmd files now cause a lint instead of an error (#571, @AshesITR). + No longer fails if `getParseData()` returns a truncated (invalid) Unicode character as parsed text (#815, @leogama). + Fixes the `text` value for `STR_CONST` nodes involving 1- or 2-width octal escapes (e.g. `"\1"`) to account for an R parser bug (https://bugs.r-project.org/show_bug.cgi?id=18323; #1056, @MichaelChirico). + Handle Rmd inputs containing unevaluated code blocks with named format specifiers (#472, @russHyde). * `line_length_linter()`: fix a bug causing duplicate lints for lines containing multiple expressions (#681, @AshesITR). * `lint_package()`: + Warns and returns `NULL` if no package is found (instead of giving a peculiar error message; #776, @MichaelChirico). + Stricter about what is considered to be a package -- folders named `DESCRIPTION` are ignored (#702, @MichaelChirico). * `linters_with_defaults()` (formerly `with_defaults()`): + No longer duplicates the `lintr_function` class when it is already present (#511, @AshesITR). + Warns if a named argument is `NULL` but its name is not in `defaults` (#1049, @AshesITR). * `linters_with_defaults()` handles automatic naming of very long arguments correctly (#774, @MichaelChirico). * `save_cache()` will now recursively create the cache directory; this avoids errors that could arise if any parent directories do not exist (#60, @dankessler). * `spaces_left_parentheses_linter()`: fix a bug causing warnings like "In `parent == parent[before_operator_idx]` longer object length is not a multiple of shorter object length" in nested expressions (#654, @AshesITR). ## Internals * Added a new, more restrictive test workflow - `test-package` - that fails on warnings emitted by tests (#1263, #1272, @AshesITR). * Added a secondary, more restrictive lint workflow - `lint-changed-files` - for newly written / modified code (#641, @dragosmg). * Several optional `Imported` packages have become `Suggested` dependencies: `httr`, `testthat`, and `rstudioapi`. This should allow snappier CI builds for usages not relying on some more "peripheral" features of the package. * Special thanks to @bersbersbers for early testing on the 3.0.0 changes. * Switched CI from Travis to GitHub Actions, using the full tidyverse recommended `R CMD check`. Code coverage and linting are implemented using separate GitHub Actions workflows (#572, @dragosmg). * Updated R CMD GitHub Actions workflow to check for R 3.6 on Ubuntu, instead of R 3.3, and for R 4.0 on Windows, instead of R 3.6 (#803, @ dragosmg). * `lintr` now uses the 3rd edition of `testthat` (@MichaelChirico, @AshesITR, #910, #967). # lintr 2.0.1 ## New features * lintr now supports GitHub Actions and will print the lints as warning messages if lints are printed during an action. * `lint_package()` will now lint vignettes and data-raw by default (#447, @AshesITR). * `lint_dir()` will now include Rmd and Rnw files by default (@AshesITR). ## Minor fixes and features * `single_quote_linter()` no longer causes a print issue when open quote appears at a column > than close quote (#457, @jamieRowen) * `absolute_path_linter()` and `nonportable_path_linter()` now handle file-paths that are wrapped with double-quotes (#433, #437, @russHyde). * `get_source_expressions()` has been changed to handle `expr_or_assign_or_help` tokens arising when parsing code containing equals-assignments in R-devel (#403, #456, @russHyde). * `object_usage_linter` has been changed to ensure lint-position is indicated relative to the start of the file, rather than the start of a defining function (#432, @russHyde). * `commas_linter` now allows spaces to come before a comma when used to denote a fall-through in a switch statement (#499, @MrMallIronmaker) # lintr 2.0.0 lintr 2.0.0 is a major release, and incorporates development changes since the last major release (1.0.0) in 2016-04-16. ## Deprecated functions * Deprecated `camel_case_linter()`, `snake_case_linter()` and `multiple_dots_linter()` in favor of `object_name_linter()` which enforce the given style: snake_case, dotted.case, lowerCamelCalse, UpperCamelCase, alllowercase or ALLUPPERCASE (#59, @fangly). * Deprecated absolute_paths_linter() in favor of the new `absolute_path_linter()`, with a lax mode for fewer false positive lints (#199, @fangly). ## New linters * New `cyclocomp_linter()` identifies overly complex functions (#361, @fabian-s) * New `equals_na_linter()` (#143, #326, @jabranham) * New `extraction_operator_linter()` checks that the `[[` operator is used when extracting a single element from an object, not `[` (subsetting) nor `$` (interactive use) (@fangly). * New `function_left_parentheses_linter()` to check that there is no space between a function name and its left parentheses (#204, @jrnold). * New `implicit_integer_linter()` detects round numbers not declared as integers, i.e. 1 instead of 1L (@fangly). * New `nonportable_path_linter()` identifies paths constructed without file.path() (@fangly). * New `paren_brace_linter()` checks that there is a space between right parenthesis and an opening curly brace (@bfgray3, #242). * New `pipe_continuation_linter()` to ensure there is a space before %>% and newline afterwards (#216). * New `semicolon_terminator_linter()` reports semicolons at the end of a line (#147, @gaborcsardi) and between expressions (#181, @fangly). * New `seq_linter()`, finds `1:length(...)` (and similar) expressions (#155, @gaborcsardi) * New `todo_comment_linter()` lints TODOs (@fangly). * New `T_and_F_symbol_linter()` warns when using T and F instead of TRUE and FALSE (@fangly). * New `undesirable_operator_linter()` and `undesirable_function_linter()` lint uses of user-specified functions and operators (#48, #149, @fangly). * New `unneeded_concatenation_linter()` lints uses of c() with a constant or no arguments (@fangly). ## New functions for writing linters * Export `expect_lint()` (#178, #210) * Export `ids_with_token()` and `with_id()` (#297 @stufield) * linters can use the XML parse tree as well now, via the https://github.com/MangoTheCat/xmlparsedata package (#154, @gaborcsardi) ## New functions for users * New `lint_dir()` function to lint files under a given directory (@arekbee, #360) * New `summary.lints()` function to summarize the linter results (#260, #262, @wlandau). * New `checkstyle_output()` function to output lints to checkstyle XML output (#156, @joshkgold) ## Linter fixes * `closed_curly_linter()` now allows closing parenthesis or comma after closing curly brace (#167, @Enchufa2) * `commas_linter()` now handles missing arguments calls properly (#145) * `commented_code_linter()` now relaxed, it no longer lints comments within roxygen blocks and does not consider "-" an R operator to avoid too many false positives. * `function_left_parentheses_linter()` now allows spaces if a function starts with a left parenthesis (#311) * `no_tab_linter()` now reports proper line in all cases (#134, @fangly) * `object_length_linter()` argument `length` now defaults to 30 for consistency (#325 @DragosMG) * `object_name_linter()` now works when passed multiple styles (#341, @infotroph) * `object_usage_linter()` has been changed to better detect lexical scoping of global variables (#27, #336, #91, #382) * `object_usage_linter()` now respects `utils::globalVariables()`, so it can be used to avoid false positive warnings due to non-standard evaluation (#352) * `object_usage_linter()` now ignores top level calls that contain function definitions (#26). * `object_linter*()`s now only lint objects declared in the current file (#76, #108, #136, #191, #194, #201, @fangly). * `open_curly_linter()` and `closed_curly_linter()` now do not lint double curly syntax (#388) * `open_curly_linter()` now allows comments after the curly braces (#188) * `pipe_continuation_linter()` now behaves better in nested expressions, functions etc. (#366 @russHyde) * `space_inside_linter()` now reports proper line and column numbers (#203, @fangly) ## General improvements and fixes * `expect_lint()` now no longer shows Rstudio markers and error messages are correctly preserved (#180, #211, @fangly) * `Lint()` / `as.data.frame()` error now fixed (#179, @fangly). * `lint()` no longer errors with inline `\\Sexpr` (#127). * `lint()` no longer errors with '<% %>' constructs (#185). * `lint_package()` now works with the cache, as intended (#146, @schloerke) * `lint_package()` now excludes `R/RcppExports.R` by default (#282) * `lint_package()` now removes fully excluded files as soon as possible * lintr now looks up its configuration in any parent directories as well as the package directory (#238, #345) * `seq_linter` is now one of the default linters (#316). * Fix issue in lintr's compatibility with R-devel, due to to a new version of the PCRE library (#411.) * `read_settings()` now has a better error message when the config file does not end with a newline (#160, #189) * `expect_lint_free()` is now automatically skipped when run on covr (#287) * Now lintr only tries to generate comments if running in wercker or travis CI (#166) * Add support for overriding GitHub API Token via `GITHUB_TOKEN` environment variable (#63, @mattyb) * Config files are now also searched for in the users' home directory (#266, @randy3k) * Fixed crash caused by ambiguous cache file paths (#212, @fangly). * RStudio addins to lint current source and project (fixes #264, @JhossePaul) * Added proper handling of tab characters (fixes #44, @fangly) * lintr does not need the igraph package any more (#152, @gaborcsardi) * Fixed cache not saved in a directory other than requested (#213, @fangly) avoid reading and pre-processing of ignored files (@mwaldstein) * Allow for any number of `#` to start a comment. Useful in ESS (#299, @prosoitos) * R Markdown files that do not contain chunks are no longer treated as code (#370). * Fixed plain-code-block bug in Rmarkdown (#252, @russHyde) * Fixed bug where non-R chunks using {lang} `engine format` were parsed from R-markdown (#322, @russHyde) * Ensured `lintr` runs / installs / tests on R-3.6: pinned to github `xmlparsedata`; ensure vectors are length-1 when compared using `&&` and `||` (#363 #377 #384 #391, @russHyde). # lintr 1.0.3 * Fix tests to work with changes in the parser in R 3.6 # lintr 1.0.2 * Fix tests to work with upcoming testthat release. # lintr 1.0.1 * bugfix to work with knitr 1.16.7 * `expect_lint_free()` now is always skipped on CRAN. This is necessary because the non-binary R source may not be available when running tests on CRAN, and those tests may not be run in the package directory. # lintr 1.0.0 * bugfix to work with testthat 1.0.0 # lintr 0.3.3 * infix_spaces_linter now properly checks `=` in named arguments. (#130, @saurfang). * commas_linter now properly recognizes lints when preceded by a blank line and points to the missing space rather than the comma (#111, #129, @saurfang). * Make spaces_left_parentheses_linter more robust when determining `(` type (#128, @saurfang) * commented_code_linter (#83, @jackwasey) * Now trims long comments (#55, reported by @paulstaab) * Automatic commenting of Github commits and pull requests when linting on Travis-CI * expect_lint_free expectation can be added to testthat unit tests. * Robust configuration system and exclusion logic * Emacs and Sublime Text 3 plugins now available from their respective package repositories. * add `names.lints`, `split.lints` (#49, @ttriche) * Fixed bug that caused vim syntastic plugin not to work properly in windows (#46, @abossenbroek) * allow lintr customization per project using `.lintr` config files. * use `globalenv()` instead of `baseenv()` for default parent environment so that `methods` will be included. * do not check object usage if eval fails. Fixes (#24, reported by @fabian-s) * `trailing_whitespace_linter` was reporting the incorrect line number * Use RStudio source marker API to display lints (#37, @jjallaire) * Permit single quotes if they quote literal double quotes (#28, @jackwasey) * `# nolint` comments are respected with caching (#68, @krlmlr) * Properly handle all knitr document formats * Allow for (( when linting (#259, @nathaneastwood) * Remove ^ from infix spaces to conform with tidyverse. (#302, @nathaneastwood) # lintr 0.2.0 * Initial release lintr/MD50000644000176200001440000010470314600212472011715 0ustar liggesusersed8a0b1f64da166a24cf21ca5034e90a *DESCRIPTION bc3a849060d69db9eb35174f4bc8bcef *LICENSE 88151e047f048710c045ee3a439038f5 *NAMESPACE d2e19763f7dd712d792e2cacc4db14aa *NEWS.md 68e8916d69e0550d36f4d9061452c384 *R/AAA.R a0a02216117eceef453eef49e79c6686 *R/T_and_F_symbol_linter.R 5eb77406e0b15c3ffb1ceb5cfa1ac105 *R/absolute_path_linter.R fcecf9f02c4c88da687d894e23168593 *R/actions.R 47ccb5fd3e1bc5afd204118840e8bd35 *R/addins.R c03e0ca8a8effde417bfa65061afd235 *R/any_duplicated_linter.R df94de47572c6c78c288b426a1444696 *R/any_is_na_linter.R 9afdadbebade2b8607a6b7534364f797 *R/assignment_linter.R d9e7d8b3f1a41a6cb025c983ce4c7594 *R/backport_linter.R eb23f1203e5477a567a659dabb073f85 *R/boolean_arithmetic_linter.R 3ffc7a176b80fe58a2ce47da177ccfcd *R/brace_linter.R 4d482e3cb46a26a83c8f8ef1cbef0aec *R/cache.R d3269c98545848c7c61cd3d60727fcbb *R/class_equals_linter.R 72d2f169d92467d7fde1af5d4865b712 *R/commas_linter.R 200d0cd72b6837f68e71c65fec51d8ea *R/comment_linters.R 3d197ee41e466451ed0db9a0a9e13aa3 *R/comments.R cfbbdb2f67ed11d92873455008bae97f *R/condition_message_linter.R ad05b66f74b5aacd750c6f98efc4ede3 *R/conjunct_test_linter.R e6b7feee3a431c74f765d4a80f8b8782 *R/consecutive_assertion_linter.R 7c1f18f6b55dfc1e8cb77f35f0bcf68d *R/cyclocomp_linter.R deafb9e16f1a98027ce6fa6239c8e4e2 *R/declared_functions.R 49b6fcd585e2276464cc231f285750f4 *R/deprecated.R b3fa2a430b4ac5d37d6d68f6135c9baf *R/duplicate_argument_linter.R 2b688ed88194182fab2bf7f0b0eace9d *R/empty_assignment_linter.R f3ff4bab93d3408f4aa4ebbd55d8f403 *R/equals_na_linter.R 4f4144b2da158ce9aeb07d5d0c7fce3d *R/exclude.R 7ae804ee545d50db2dd97aed782fc587 *R/expect_comparison_linter.R 2a61e0bd3e8e2d5cfef5f4382e674fc5 *R/expect_identical_linter.R 322cb550fb71dd0699ab6e632a8a5555 *R/expect_length_linter.R cb51e9042e42751d9b9dd87bd13920fa *R/expect_lint.R 5c731243cb8a10760e8225f7f6e1496a *R/expect_named_linter.R 0356eb433cf3ff4ac4f546179a1442e5 *R/expect_not_linter.R 4623402ea3472f1e0b4701b09fc188b6 *R/expect_null_linter.R 7d3bb3f0ed9be32054716f3f817556bc *R/expect_s3_class_linter.R 3956e2fd5c8a154c71fc1e33e12ae5ed *R/expect_s4_class_linter.R 77504ec04c8f3365cf0b1242863bd0c8 *R/expect_true_false_linter.R 0c42eacda7329e9bf35b69b1b7809eeb *R/expect_type_linter.R 9889149166d537c49f729dd9a87ac4fb *R/extract.R b0b8300b386c9996dccf636c086f74cb *R/extraction_operator_linter.R 5ea6fafde5d811496c81c49c16343784 *R/fixed_regex_linter.R 7452790eb222cdcce12ebbcaeb92554d *R/for_loop_index_linter.R b15296416cd20cf2354716fc5b05baf4 *R/function_argument_linter.R 41b1efb97d8a834d51d28270ab10d24e *R/function_left_parentheses_linter.R 337c5fee335e27c40e1630bd2fb7c891 *R/function_return_linter.R 8320051adf8c106719b1edddb532246d *R/get_source_expressions.R 9db920ea739f2aace1a95db7f96e87f2 *R/ids_with_token.R 46aaffed7117eaf56014273826919b84 *R/if_not_else_linter.R 86548e6b5b4404258793e977c957932c *R/ifelse_censor_linter.R 79ea70e79a233ceee8813dc4a9f89afa *R/implicit_assignment_linter.R e94ca3b1529c9508e0cfb9e642100732 *R/implicit_integer_linter.R cc53bdc9d2f29d4d4e3b3170d6d429f8 *R/indentation_linter.R beb021b911868289e48553f35f81c3fc *R/infix_spaces_linter.R adf906213c71c39787de8b945722110d *R/inner_combine_linter.R eac1ed726723d8227eaca515b3477830 *R/is_lint_level.R af344b7113a11ac9d854287ab7fb7349 *R/is_numeric_linter.R e001d0d48a09095895de357192f731af *R/keyword_quote_linter.R 3851d407d0777fed37229d97e247bf36 *R/length_levels_linter.R c79bb5707b88a42d90388c2fca72eab4 *R/length_test_linter.R 5f0d2866280c3e1626840ace8b646c21 *R/lengths_linter.R 4227bdec5fb6cc3261a2c8415a560ed4 *R/library_call_linter.R 190229a3496afb6399ab7c5d35fb48e2 *R/line_length_linter.R 34937244bd64d126d27fe45de184e28d *R/lint.R cf5165bbbd069ca27bb41bec5c0290f4 *R/linter_tag_docs.R 83d2dd66f6d85f254fb1b7559c32bfe6 *R/linter_tags.R bc9a55e70ea1b5f8062051b21b5870c8 *R/lintr-deprecated.R 47b6d901b79542dcb65394a6d1153f17 *R/lintr-package.R 78fed8229495dcf2c261d555b27c4797 *R/literal_coercion_linter.R f35199ceba5136674f3b159f2cc51a00 *R/make_linter_from_regex.R 1b5e254fa588802425fea1852e4cc9b0 *R/make_linter_from_xpath.R 1ecfc8d78d8edd566c882860973a4bc9 *R/matrix_apply_linter.R d86c8a4c7c976f061e34dfc03eab2f0c *R/methods.R 32d63f5f795b88363cbe4cb2edfb59a0 *R/missing_argument_linter.R 8e46a658c55b641b4531bd117804007b *R/missing_package_linter.R a6a7e371e642c7a27e280966984a62ba *R/namespace.R 03bb39f75a277dcad17e17d88d787eae *R/namespace_linter.R 5c2d4b64b2f3c8b604b5e2db63176ed0 *R/nested_ifelse_linter.R 46b8b982fe97bd4f5142ff1877bf31ac *R/nonportable_path_linter.R e557ed00ac6fa8052aa4fff06142c636 *R/numeric_leading_zero_linter.R eeea178cc75033b18d18c006fc23ef82 *R/object_length_linter.R f57d3ebe7e43519eaf49d3a9c08643e3 *R/object_name_linter.R e1186712a6bb1e40cadc3930de8eaa11 *R/object_usage_linter.R 199421d45d883cbfffcb950b5a8aa04e *R/outer_negation_linter.R c8906f06c27ca2fb5a3d5e1cd6221d46 *R/package_hooks_linter.R f5df2bbcb401670da48d14ac775a19a9 *R/paren_body_linter.R fb4090681d11a8091f0149919a5d61ae *R/paste_linter.R 80bcac75e4b9bf14da87cfd352da1f03 *R/path_utils.R a02d640104b2bd020231af922c2e1807 *R/pipe_call_linter.R f527e548a589d2f36011baf9bd503b22 *R/pipe_consistency_linter.R af26b547bca27b6d1663407f95dd6c33 *R/pipe_continuation_linter.R 9a05e37d2a8e1bba0faa8ce3a1c46ffb *R/quotes_linter.R 0fe512a994b443e3dcd45e51d6ab95db *R/redundant_equals_linter.R 0373cda9eb54d88fddd672a9079b61db *R/redundant_ifelse_linter.R 3d52561ab2b69878a020a9cc9d9ff6c2 *R/regex_subset_linter.R 9745379985fdf693080b5a487df68651 *R/repeat_linter.R 8e1c8357fcb759a2008f07bd93b16097 *R/routine_registration_linter.R 529fb1a95e84dc23f3dfe736070e6c37 *R/scalar_in_linter.R 5657c80ba3d125a6597af57cca7c20de *R/semicolon_linter.R 341f8b10255296c583a0b3302674be86 *R/seq_linter.R 3303798c3158d475eeb457d7afe56b5b *R/settings.R 679344958bb3b77f0745fd2126c30a37 *R/settings_utils.R 7a70f3941375d453ee1df7eabf3a975f *R/shared_constants.R fcc5cdca8033d158c5300981e620c059 *R/sort_linter.R e5032e72b38bb929a67d8ef53169d787 *R/spaces_inside_linter.R 1fecfdb8c8c47d157d2084f5fa0d52e4 *R/spaces_left_parentheses_linter.R 9384e35db3b61f7ce4f8d896fcec49c7 *R/sprintf_linter.R 4b48adec5f474798d71922d8360d2f44 *R/string_boundary_linter.R 5afd6cc467f3bcad316eb1ca0ff5d720 *R/strings_as_factors_linter.R ecb2e74283081d459795e32f6cadc0b9 *R/system_file_linter.R 09d413e422bba051c9b1bd65d51244c4 *R/trailing_blank_lines_linter.R b6530199954c055eaa3fe3b78ddcb01e *R/trailing_whitespace_linter.R 1fb5fa8e9c0d770149b988dde72497fe *R/tree_utils.R 6fe973a8a8bc368e0ec080ca6a4e2ca9 *R/undesirable_function_linter.R acf44bda37d516dee329006150cd2d29 *R/undesirable_operator_linter.R c5d195d1b6a3f1368e5b65cbda48d29e *R/unnecessary_concatenation_linter.R dc6dbdd21379bf684e554d4da6c128e8 *R/unnecessary_lambda_linter.R f2b9538ccded08cb1ab39922e37d7d61 *R/unnecessary_nested_if_linter.R d2d171b24ba7634570d0267961ccd9d0 *R/unnecessary_placeholder_linter.R e349e4651db96c17c2d44688c6e34aaa *R/unreachable_code_linter.R 235206e90f6e26844fde3ec73fee152a *R/unused_import_linter.R da53297c47d0c5dd2a6614418ae2d29a *R/use_lintr.R 95c7e96bed05e54b391f33ca12ba1c1d *R/utils.R c315401f7d145f4fc6f71e798fa351ee *R/vector_logic_linter.R 402e8486a9f9109fe1ec9d72dd0925d6 *R/whitespace_linter.R 35a9792db20af7763708c7901c406014 *R/with.R d2679f4145cbd5b7866bcc9415baf562 *R/with_id.R f87ae7c4dc428f20d787e0dee3f231b7 *R/xml_nodes_to_lints.R d96a3bc6afc0d16264df4fcfa3b1a07f *R/xp_utils.R c79fa28a77f8a35e2d14ac43f0329c5e *R/yoda_test_linter.R cb5f9869613ebefafb960ee39bb2f584 *R/zzz.R e754147f23fefe825d958b438b1435e4 *README.md 7332e069c990312306da79c64b480dbd *build/lintr.pdf b88ede75b1d205ecc46aa5143c290c54 *build/stage23.rdb 971c038425eb0ce08821d8ae60e8e3a4 *build/vignette.rds 6c8ccc95f15caa0d6fca57ead47f57d5 *inst/WORDLIST 5e270ae6034b339c2a13d20a5f7d2dec *inst/doc/continuous-integration.R 58897cfc8d2912d4da2c412a2bb5b2f1 *inst/doc/continuous-integration.Rmd bb81e38bad4ac0c0a079a6b42d2ffb29 *inst/doc/continuous-integration.html f79c7713bfac20a3e2b9fd5206c5ab20 *inst/doc/creating_linters.Rmd 2ab94177b716ebb80c9eb7cab945d5e9 *inst/doc/creating_linters.html 217947520a60dfbe342d51207149891c *inst/doc/editors.R 79b71c06f968aec2a875be4969f034e4 *inst/doc/editors.Rmd 2cd65c81c9ad743f278da99a799956fa *inst/doc/editors.html 2c9a2ec86e1eb394aa0e4703a3cb2997 *inst/doc/lintr.R fc4d04b6291c0246512374b6327e3f48 *inst/doc/lintr.Rmd f642df180454b3cc8e080ede601bdafc *inst/doc/lintr.html 99e2dcf364f1a17436a52bac9e87e84a *inst/example/bad.R 63c773397517932b7c85faadad73a7fe *inst/example/complexity.R 6f515ea057170dc538df138369b0c3c7 *inst/extdata/sarif-template.json 0f799908172305388f0e240606f8ce7e *inst/lintr/linters.csv 4bfa1cd4531477ebc1ba01acdecb4d33 *inst/rstudio/addins.dcf 8b4d64c345bab131574d9b68861be1c1 *inst/syntastic/lintr.vim 6a6ad6f7f990d527b58d24f4e64d23ba *man/Linter.Rd 0311a6066f45023a8110556ad241afad *man/T_and_F_symbol_linter.Rd c97c8ddf671325dcb58de04b2180fd9e *man/absolute_path_linter.Rd 1fd5d53823bd6d9102edc50916dda8ba *man/all_linters.Rd 8ac2ecd2142ba77377796280fe696ee7 *man/any_duplicated_linter.Rd 81723ce2cfc9157fa5067c19bde05c59 *man/any_is_na_linter.Rd 5f8679e8be69ba5ad904efcef1783d1f *man/assignment_linter.Rd 4693e13749589cb5787aab0b095797a9 *man/available_linters.Rd b7f10454b716e33859497c1aecb504c2 *man/backport_linter.Rd d6b67e0d056d7c15a2910b5c593563ef *man/best_practices_linters.Rd ef5ed3c4baa6aa512b8261675ecb30b9 *man/boolean_arithmetic_linter.Rd 53fb472d643bc00059ba3a5e8c345111 *man/brace_linter.Rd 6b1bb38ef6923fe98d2518a57e1ab9f9 *man/checkstyle_output.Rd 1514cd47ae0934ee3ff6d2b3dc940fa9 *man/class_equals_linter.Rd ab3200819a810d785390dc805fea4429 *man/clear_cache.Rd 162a396d6fa9bdcfa2eec0c623ceb3d4 *man/commas_linter.Rd 5ce81fec82926df355c90a9972a20d26 *man/commented_code_linter.Rd b3d6014d198978d984919639ffb51161 *man/common_mistakes_linters.Rd d342fd5e07c6c09ce094dc3242b813b5 *man/condition_message_linter.Rd f10c73bcbbf2b7dd10f7d57f40d00057 *man/configurable_linters.Rd 21cac62e77b8cf3084d96e6ca6a63840 *man/conjunct_test_linter.Rd bb3d0775f67e7e11190f1822f222f3fa *man/consecutive_assertion_linter.Rd bc656646f1e086d441434c5b358f5ce3 *man/consistency_linters.Rd 6a1650b1ca64f3a46a465b54342a2d7c *man/correctness_linters.Rd 69b704c008f37ae1dd8b7c0221646033 *man/cyclocomp_linter.Rd 5f8c827ca1a4314131f4afd3357f3393 *man/default_linters.Rd cadfa5618cb6cf4c8d75d8f9595f8645 *man/default_settings.Rd 602dd118d3c39a4ed66570cf48a9f695 *man/default_undesirable_functions.Rd 504b937b9371beeb2fafab183a27b41f *man/deprecated_linters.Rd e46b77da4b5d76763d456bf5fcc14ebb *man/duplicate_argument_linter.Rd 7949af37bc98d0f08f98a3f59cd4a44d *man/efficiency_linters.Rd 053bfd358f4ebb322f0584745fe1a2dc *man/empty_assignment_linter.Rd d3fab73fdc2bb5df82d598bd23fb110b *man/equals_na_linter.Rd f232d333fdaf3e46bb0225be01705a4b *man/exclude.Rd 4c5f21aab4af937412f14933db47c15b *man/executing_linters.Rd fa7d9613cde22cefcc451fb7baca3b35 *man/expect_comparison_linter.Rd 8376df4ca9455aed1fe1b3f95ebc54fe *man/expect_identical_linter.Rd d651d862fced0b2fa6f784dedb20a3a2 *man/expect_length_linter.Rd 4d42b4a1e7f6484a5dd1a911cbeee986 *man/expect_lint.Rd 1f8890ebcfb9712352e4d90960b03db5 *man/expect_lint_free.Rd 15cdbd081729d359896d7555fa4dc8ac *man/expect_named_linter.Rd 57f8842de58c61f11dfc690dc1f1d097 *man/expect_not_linter.Rd c622d08bb7e3e338f13ffa39795102bb *man/expect_null_linter.Rd 9399ae2745289d595ccbee024d5011c5 *man/expect_s3_class_linter.Rd af02b24e756edb4c11c9f3936f874d28 *man/expect_s4_class_linter.Rd 40bae057da5e2e65364f6914d3eba8f1 *man/expect_true_false_linter.Rd c47af3e9397a0fccf48c2a9a4b488df6 *man/expect_type_linter.Rd d3c943fa60c95f1635f1b6c8097103fc *man/extraction_operator_linter.Rd 9e1674c355416c5123fbd7caf59f7e90 *man/figures/demo.gif d2349751170fee825b38b96dc73b0199 *man/fixed_regex_linter.Rd 420b1e957c1b6a0499ab354717243578 *man/for_loop_index_linter.Rd ccf6c6e60110b0191d602986fcdca8d0 *man/function_argument_linter.Rd 15d0084ebd9798326c6015def0192dd9 *man/function_left_parentheses_linter.Rd d2a67fc30a7db23eef1007bc63d9e772 *man/function_return_linter.Rd 02f5ef1714fe2dd0af2435e933dd49fe *man/get_r_string.Rd be8fc2b10d0bfe0a82635f8f55543aee *man/get_source_expressions.Rd 7253c9742806f3605658c97c61a5e4b4 *man/ids_with_token.Rd 41ec2a66ce0f43f57d9f799e01b43ebf *man/if_not_else_linter.Rd fca8e4340b30b480de0d1a32ccdba253 *man/ifelse_censor_linter.Rd 78a5db9fb7d25337f344234a95ead0e7 *man/implicit_assignment_linter.Rd ce3c6e1dca99c36cbd2ea97bb5738b5b *man/implicit_integer_linter.Rd 58deffd23f30acfdc543c990be0d9eb1 *man/indentation_linter.Rd 3b8211065fa6ed4ce9cee277656dcf13 *man/infix_spaces_linter.Rd 85399ceed985050a4c3508abe1a9e35e *man/inner_combine_linter.Rd 6f8a45725ab7b3a7fd586cf080040077 *man/is_lint_level.Rd 172d23f4e36eeaf917e3f7d1e9be7162 *man/is_numeric_linter.Rd 6934ff905dc5d5e461eb819a93170d9b *man/keyword_quote_linter.Rd 226c797456f0383884e2af419006a5c5 *man/length_levels_linter.Rd ab248aeb32ae8333fd1e8958586aede5 *man/length_test_linter.Rd 2a674616b6af294d01b5437cbb5f0672 *man/lengths_linter.Rd a58c4f2ee2f8b698fb8ec153e2a97a0d *man/library_call_linter.Rd a6e5442a6ad487732b03c81e6a452329 *man/line_length_linter.Rd 7e65203707e985c9a8f6f178376933dd *man/lint-s3.Rd 847cb49a39cde26307d0cbf93f613042 *man/lint.Rd 32013a3dc21cdb912881863ffc00b717 *man/linters.Rd c61baf20ed387a9b6870c292bc508738 *man/linters_with_defaults.Rd e023b9d2632cbfcf158e5ba09a286b2b *man/linters_with_tags.Rd 210d55d9f5ec1faedd32cdf5327d8257 *man/lintr-deprecated.Rd 440d6a4909d908f8773dd521b3fe0d5a *man/lintr-package.Rd 9653060007f3ea1d0adfb6a4df80e8c3 *man/literal_coercion_linter.Rd cdfd7fcd033e84e63028f7d9c28d86a1 *man/make_linter_from_xpath.Rd 056b36cc16cabb85cea9984e06b1ce0b *man/matrix_apply_linter.Rd c879319b4810b5960ef8b02d510e98a5 *man/missing_argument_linter.Rd f5451e230bcd931f579e5f8ee000dc98 *man/missing_package_linter.Rd 37a3ecc2d045faa6a198ab79dfaf4b8d *man/modify_defaults.Rd 4e67d67b2488b9f1b6796a9857c97669 *man/namespace_linter.Rd 9fedf31c9262d271ed46c7a240f2d686 *man/nested_ifelse_linter.Rd 3571ba34df51fe6b19b8b9b11dab9928 *man/nonportable_path_linter.Rd 4af5840531b973046aaaeb2e2c65ff2d *man/normalize_exclusions.Rd 152303cb47153cd52492db05bcd69064 *man/numeric_leading_zero_linter.Rd 8a349a0b89e6a1fd92b2ef4130df9e1e *man/object_length_linter.Rd 36dcae2f6d9f78f6417a8ff881162552 *man/object_name_linter.Rd 3c420658f57754f4dd6d2edd6eab3359 *man/object_usage_linter.Rd 0afc7fda9e38cc53b5b3014b1aa777d4 *man/outer_negation_linter.Rd fd6c00d85b50b4f51ed6b0326245f2fb *man/package_development_linters.Rd 6baa8b010184c44e3766b52c2c9f2da2 *man/package_hooks_linter.Rd 6288eacba5c41c7f35f9a3c2066673cd *man/paren_body_linter.Rd c6b91b9930ebc8c2c0a84ccd8b17c79f *man/parse_exclusions.Rd ff3d752bc1a0e0a3081475806997f795 *man/paste_linter.Rd c44b47c40357bfd98434796ac7fbaf61 *man/pipe_call_linter.Rd 6faf7b62bc371f20f49d930c8ade1a63 *man/pipe_consistency_linter.Rd ed406b3297b64eaf5bd4403431f5897d *man/pipe_continuation_linter.Rd f4c44d4526652bc2d923cfbe144c577e *man/pkg_testthat_linters.Rd 3c13c489a19a6928bac8ece207d54733 *man/quotes_linter.Rd e6fbf00c15e48d28238a08c0c2963d79 *man/read_settings.Rd 6fec01e75f42867e0b81b9fe340a5de3 *man/readability_linters.Rd 9bda71c9d40f9ba3e3747b7013253f8b *man/redundant_equals_linter.Rd 0c8ae3933a06817197595d2b9206781e *man/redundant_ifelse_linter.Rd bdbd5db46ed160ab781aaa0ab941da02 *man/regex_subset_linter.Rd 18b1d05a113267fa9d3c5320e856aa42 *man/repeat_linter.Rd b66ee3f4a59850e21a924cd65431796a *man/robustness_linters.Rd 31542647d63a182fa5b1c2264cf63eea *man/routine_registration_linter.Rd ccef70b8eee150be011d950bf65fda7a *man/sarif_output.Rd 1bdfa40e2a65e7d8c2716b9787b201fd *man/scalar_in_linter.Rd c1e7a754a8a9e44286e5445c3fe3447a *man/semicolon_linter.Rd cfb13562cb448471768134d7cc12d55e *man/seq_linter.Rd f20cb94256a81112e5066a13fba4924f *man/sort_linter.Rd 2018483d18081ccee090a330b292e63c *man/spaces_inside_linter.Rd d164dd87f5b2b10dd9fb563132dd2822 *man/spaces_left_parentheses_linter.Rd 98f0bafe788f9f3566594c950c5d5186 *man/sprintf_linter.Rd 83137d060051094c927497f58aa2f42b *man/string_boundary_linter.Rd e6ff99f4c58968b51d6311423a992979 *man/strings_as_factors_linter.Rd 06d5d57eaa11ee58223e5ead55da0e5c *man/style_linters.Rd db3decd3a0a1f280f4773ebb3117018e *man/system_file_linter.Rd c10b20ece3b2ee83a2d15ee5240439ca *man/todo_comment_linter.Rd 510519d96ceeca9dbe8c1a0f9487d766 *man/trailing_blank_lines_linter.Rd 283fe4b6547d0bebe6a7fe62e318bf19 *man/trailing_whitespace_linter.Rd 1dd91d7527457fd09affb54254409f5a *man/undesirable_function_linter.Rd 9d7d8c155cd09d47ea46b3c32182761e *man/undesirable_operator_linter.Rd 9a389186117e6f68bdb42949c5bde772 *man/unnecessary_concatenation_linter.Rd f775c2d42cb1d5d72ffb4a6ccbf27b68 *man/unnecessary_lambda_linter.Rd 5a80fb2d220f1381e3bb3413d825ed7f *man/unnecessary_nested_if_linter.Rd 33d79693da4413a9404a49198f2efeb8 *man/unnecessary_placeholder_linter.Rd d9217fccc311ff9ac8f973d573772d7b *man/unreachable_code_linter.Rd 3d9ef254fb0daaa8001c70d19e774516 *man/unused_import_linter.Rd 7afd7071fcbf7a73218b5825261649de *man/use_lintr.Rd ad53e0bde63fac41d03a354e1e16df9c *man/vector_logic_linter.Rd 9aab6b22558ae3665eeef7ad57cd50e4 *man/whitespace_linter.Rd b38c6aaf1218f5f020e50a676830dc96 *man/xml_nodes_to_lints.Rd b43e46607943efae86448b4fca494786 *man/xp_call_name.Rd 3cd4f66d5f3e73560571a19b735c85f3 *man/yoda_test_linter.Rd cd0784aeed7386695d7553d17ef4c340 *tests/testthat.R 26d15b1f61ca44e70148edc81254d441 *tests/testthat/checkstyle.xml bd4efc115d08e7634146c5c954004097 *tests/testthat/default_linter_testcode.R aaed27820d9ea879bd76df3ff4751bd9 *tests/testthat/dummy_packages/RConfig/DESCRIPTION 8c7c02352670449350b4d1bdef021768 *tests/testthat/dummy_packages/RConfig/R/lint_me.R b29540a2f21f8192c776b7c363a98379 *tests/testthat/dummy_packages/RConfig/lintr_test_config.R 21dbf7f39cbf41256fbf18e254f99a7d *tests/testthat/dummy_packages/RConfig/lintr_test_config_conflict 19646a96b4ad2271c1c0b1a29c02353f *tests/testthat/dummy_packages/RConfig/lintr_test_config_conflict.R 8320d1ad1f6cac28bee45881e8979290 *tests/testthat/dummy_packages/RConfig/lintr_test_config_extraneous.R bfa2bf7f8a8b8e53c02fbafa848bf076 *tests/testthat/dummy_packages/RConfig/tests/testthat.R 6839e5cac8d2ad6beef39fc461e4b028 *tests/testthat/dummy_packages/RConfigInvalid/DESCRIPTION 26813d0f7f8e272af05102662163c53b *tests/testthat/dummy_packages/RConfigInvalid/R/lint_me.R c3ce8bfe939aab9148629f67b79a8847 *tests/testthat/dummy_packages/RConfigInvalid/lintr_test_config.R b64bf1efeb5e911288ecd23887b3bfd6 *tests/testthat/dummy_packages/assignmentLinter/DESCRIPTION 8b54e5a89fbda3af5e077053d40bec76 *tests/testthat/dummy_packages/assignmentLinter/NAMESPACE 3ae082cb6fd2047c7ccaa6e08c71838c *tests/testthat/dummy_packages/assignmentLinter/R/abc.R adb6f9135ff547493ee5bef6e5aa6ad0 *tests/testthat/dummy_packages/assignmentLinter/R/jkl.R 6a9d3d5c14bb032d2ec02d3685093b56 *tests/testthat/dummy_packages/assignmentLinter/exec/script.R 78a21cb50559555815798f6c2bc9884a *tests/testthat/dummy_packages/assignmentLinter/tests/testthat.R f6dcb71f287240b7e28dcaceb13942b9 *tests/testthat/dummy_packages/assignmentLinter/tests/testthat/test-abc.R fb24a4e2f1f9627c3908b83bfa3970f9 *tests/testthat/dummy_packages/clean/DESCRIPTION 4868f6d5a7fb6dbf050df0a5bf2cbc53 *tests/testthat/dummy_packages/clean/NAMESPACE c5dde05e81ae3cca126311141e85bab7 *tests/testthat/dummy_packages/clean/R/clean_generics.R c4fbf9794416b9abfbfd9494a9fde0ed *tests/testthat/dummy_packages/clean/R/default_linter_testcode.R 035388fd16a4b82cdd37efbceeaf47f2 *tests/testthat/dummy_packages/clean/R/eat_me.R 4c61569079b46263ca9b09d41d360086 *tests/testthat/dummy_packages/clean/lintr_test_config 4c61569079b46263ca9b09d41d360086 *tests/testthat/dummy_packages/clean_subdir/lintr_test_config c9239a50affa8bff3b03273f421bc254 *tests/testthat/dummy_packages/clean_subdir/r/DESCRIPTION 6b01f49f6db592a0ff9d92e399d242c3 *tests/testthat/dummy_packages/clean_subdir/r/NAMESPACE c4fbf9794416b9abfbfd9494a9fde0ed *tests/testthat/dummy_packages/clean_subdir/r/R/default_linter_testcode.R 74655ea81ee2a9c5db45d5f6c57e07da *tests/testthat/dummy_packages/clean_subdir/r/R/imported_methods.R d287dd2446cdf35da5530ae99bb312f3 *tests/testthat/dummy_packages/cp1252/DESCRIPTION 8b54e5a89fbda3af5e077053d40bec76 *tests/testthat/dummy_packages/cp1252/NAMESPACE fa310f3f657122643ad3b020f755d415 *tests/testthat/dummy_packages/cp1252/R/cp1252.R 9c202a90ee3e421e55821643ccb7210f *tests/testthat/dummy_packages/cp1252/cp1252.Rproj 863ab39602c4002968c7978b77ed0a49 *tests/testthat/dummy_packages/desc_dir_pkg/DESCRIPTION/R/foo.R 2cf4b050b4ec4d6a9a10b60cd1c2515e *tests/testthat/dummy_packages/github_lintr_file/DESCRIPTION 8b54e5a89fbda3af5e077053d40bec76 *tests/testthat/dummy_packages/github_lintr_file/NAMESPACE 387ce1901b5304468abb91bbfcf3a31e *tests/testthat/dummy_packages/github_lintr_file/R/abc.R 3116b9cb6e55dbc52682ffe45521933c *tests/testthat/dummy_packages/github_lintr_file/tests/testthat.R 387ce1901b5304468abb91bbfcf3a31e *tests/testthat/dummy_packages/github_lintr_file/tests/testthat/test-abc.R 5fa5a362fe0926b3763a9d676c023b02 *tests/testthat/dummy_packages/missing_dep/DESCRIPTION 21b783bc993c520768c42c81d1a31aa1 *tests/testthat/dummy_packages/missing_dep/NAMESPACE ee665b88f1a4bd01eeedf1cd9b7f17fc *tests/testthat/dummy_packages/missing_dep/R/foo.R 347aa60736542d22f771cd3a915a2e20 *tests/testthat/dummy_packages/no_export_dep/DESCRIPTION ac0b9e0144a121b592555541bc124458 *tests/testthat/dummy_packages/no_export_dep/NAMESPACE 1e1dff52d75c5815c8877535cb0aab48 *tests/testthat/dummy_packages/no_export_dep/R/foo.R 4e4ece613a3cab8b7960c27fbbcbc0d5 *tests/testthat/dummy_packages/package/DESCRIPTION 53a143d3ff54cf4db5a4ffd57e848364 *tests/testthat/dummy_packages/package/NAMESPACE 23d3b4a21d5a8c89cc2bead0d25d6391 *tests/testthat/dummy_packages/package/R/default_linter_testcode.R 23d3b4a21d5a8c89cc2bead0d25d6391 *tests/testthat/dummy_packages/package/data-raw/default_linter_testcode.R 84c672fd8da1f137384cdda85fd36ccd *tests/testthat/dummy_packages/package/exec/script.R 23d3b4a21d5a8c89cc2bead0d25d6391 *tests/testthat/dummy_packages/package/inst/data-raw/default_linter_testcode.R d1c3e86a77bba5f384b5279bb9bd7086 *tests/testthat/dummy_packages/package/lintr_test_config a22ea92b08e06a4bbbb38cfd5693e080 *tests/testthat/dummy_packages/package/package.Rproj 944ac51d76c95bd11df93d252b7c009e *tests/testthat/dummy_packages/package/vignettes/test.Qmd b4ecb1ec1dbb7d7c629c578cbebcce5a *tests/testthat/dummy_packages/package/vignettes/test.Rhtml 1885ebd2d20240bb193f44b6b8e30765 *tests/testthat/dummy_packages/package/vignettes/test.Rmd c37efc49e0294190561047a63ba49607 *tests/testthat/dummy_packages/package/vignettes/test.Rnw 93db4dc8ed588c87ff94566b00e6891e *tests/testthat/dummy_packages/package/vignettes/test.Rrst 4ecc24216384c642ab5abbbc7537a907 *tests/testthat/dummy_packages/package/vignettes/test.Rtex 52a1f46b64423940155df41059342c51 *tests/testthat/dummy_packages/package/vignettes/test.Rtxt fa310f3f657122643ad3b020f755d415 *tests/testthat/dummy_projects/project/cp1252.R fa09e2c168d037d278f62d9c476ed416 *tests/testthat/dummy_projects/project/cp1252_parseable.R 23d3b4a21d5a8c89cc2bead0d25d6391 *tests/testthat/dummy_projects/project/default_linter_testcode.R 9aee4ffbb277a4e6613bdba74fe61e06 *tests/testthat/dummy_projects/project/mismatched_starts_ends.R 3f932aa4067f423825a53dd6eb0bda87 *tests/testthat/dummy_projects/project/one_start_no_end.R cee103d26493854043b9fc3357e23dec *tests/testthat/dummy_projects/project/partially_matched_exclusions.R f823822886b798e97fdb6d693323cb56 *tests/testthat/dummy_projects/project/project.Rproj d1bf27337e9d99679909f450d930c5cf *tests/testthat/exclusions-test 40d13194ad202d175528578df9d06e4a *tests/testthat/helper.R 8b63d69e1187cc4725c41da250164940 *tests/testthat/knitr_extended_formats/bookdown.Rmd c244da997cbdd2a7d0d73eef2c9b5d4a *tests/testthat/knitr_extended_formats/tufte.Rmd b4ecb1ec1dbb7d7c629c578cbebcce5a *tests/testthat/knitr_formats/test.Rhtml ea73828bc2e38386950d7f9425dfe2ea *tests/testthat/knitr_formats/test.Rmd c37efc49e0294190561047a63ba49607 *tests/testthat/knitr_formats/test.Rnw 93db4dc8ed588c87ff94566b00e6891e *tests/testthat/knitr_formats/test.Rrst 4ecc24216384c642ab5abbbc7537a907 *tests/testthat/knitr_formats/test.Rtex 52a1f46b64423940155df41059342c51 *tests/testthat/knitr_formats/test.Rtxt 944ac51d76c95bd11df93d252b7c009e *tests/testthat/knitr_formats/test.qmd d006c5b4e5ddac8fcb4696e9939d7c36 *tests/testthat/knitr_malformed/incomplete_r_block.Rmd d006c5b4e5ddac8fcb4696e9939d7c36 *tests/testthat/knitr_malformed/incomplete_r_block.qmd 5cc611f6b1e16a64dfffae5bcff2e704 *tests/testthat/lints 3d0d90c969dc237631b38934f0591b7b *tests/testthat/test-Lint-builder.R cd78bfbb40f6b80ec585318df78e80b4 *tests/testthat/test-T_and_F_symbol_linter.R 1833fdce9d0b36524aa7bf0396ccfba6 *tests/testthat/test-absolute_path_linter.R c22ad57135818156c673e96c155105c6 *tests/testthat/test-any_duplicated_linter.R 937eaf8fd07d472591e84081022931a7 *tests/testthat/test-any_is_na_linter.R 6354be087d5a4c7e55a2f5c1037d721b *tests/testthat/test-assignment_linter.R b1de98fed4ff71da5e222c4a54191f3d *tests/testthat/test-backport_linter.R 1e143db8cdd51b3ac1e493de317677ce *tests/testthat/test-boolean_arithmetic_linter.R dc737dfda8b5740aee7370224ee166b4 *tests/testthat/test-brace_linter.R 8abd5bb4f0e80433a666e715f74e7c10 *tests/testthat/test-cache.R 9fd5ddbfc33b3950968db2b966abc387 *tests/testthat/test-checkstyle_output.R eb7305549b7eb51584a84f35cebf92f9 *tests/testthat/test-ci.R 7aa96c188fb8105a5424d86e9b00cea0 *tests/testthat/test-class_equals_linter.R 30ef1d18c3efc26d01895f9e3cdbfb80 *tests/testthat/test-closed_curly_linter.R c064fd21c290f553041f16e8af2d7a55 *tests/testthat/test-commas_linter.R 056b57f08842a41e57b3ad1e7dcd33dc *tests/testthat/test-commented_code_linter.R 120413b367b2824cda53cfd1ad1f2b26 *tests/testthat/test-comments.R 6c0535e62950b0bc90213eda41fa9f7b *tests/testthat/test-condition_message_linter.R 053ad45cc93f1bf71b42c1c3f704101d *tests/testthat/test-conjunct_test_linter.R 78fa457b6a019f650acb5a6135c3fe05 *tests/testthat/test-consecutive_assertion_linter.R 736d730f3c2739867f29edff2987788f *tests/testthat/test-cyclocomp_linter.R e2909eceb405d8887eaa50e800c7a76b *tests/testthat/test-defaults.R 743dc2292e005dbb66feab88cfc225ed *tests/testthat/test-duplicate_argument_linter.R 21d9797dafeed939ab3cab854e3d2730 *tests/testthat/test-empty_assignment_linter.R ec6b34063e7a7fd636638114f9dff61d *tests/testthat/test-equals_na_linter.R e5eac50c38bc15a50fd5042bcec62829 *tests/testthat/test-error.R ccbd33bd1d41d8d5f646d72e39d107b7 *tests/testthat/test-exclusions.R 82bf90cd6c1a0c42bb4d0a0df50c2de2 *tests/testthat/test-expect_comparison_linter.R 0239492925f91e9a2b9340558d932d77 *tests/testthat/test-expect_identical_linter.R afb99da299518cff04556e515ebb870e *tests/testthat/test-expect_length_linter.R a7bb285162398d97d00f9fc7ce5e2df6 *tests/testthat/test-expect_lint.R 14ee9a7d1c89e0b451e3bee791348510 *tests/testthat/test-expect_named_linter.R dce96fba3d2dba69db5a22eac4d2a00d *tests/testthat/test-expect_not_linter.R 58129c322d8d52bc8d66e9e6b4eb74d3 *tests/testthat/test-expect_null_linter.R e5bab2d5eb19a973d07313879b5d7351 *tests/testthat/test-expect_s3_class_linter.R c301a2dfc8853cb4470adb9bb5a1f87a *tests/testthat/test-expect_s4_class.R 7e47a7c70b674dfcaeef9ac842d9b95c *tests/testthat/test-expect_true_false_linter.R 2d0cbfbe81795d691327d308354d171c *tests/testthat/test-expect_type_linter.R 001ae3ecc41804e9002fd38971fc517d *tests/testthat/test-extraction_operator_linter.R 2b997bb47eb1f37c5d81d435e7c7daff *tests/testthat/test-fixed_regex_linter.R ea7360c897ea92114b43805850d9976d *tests/testthat/test-for_loop_index_linter.R cf909ccea303beb52fbaa5ae2fd85e10 *tests/testthat/test-function_argument_linter.R 1c83773033bbb88702a3f4fcadb7c010 *tests/testthat/test-function_left_parentheses_linter.R 7246671a193b9f36b1f3dab7b950aacd *tests/testthat/test-function_return_linter.R 255c83a67525d4f4104f9cb88454f22b *tests/testthat/test-get_source_expressions.R 48c806718e4c7a1676c90abb8f7a7ebd *tests/testthat/test-ids_with_token.R 2e27c98a9f53bd36bb21d865f88e7786 *tests/testthat/test-if_not_else_linter.R d188f573466b6e0f9445c79e467087a3 *tests/testthat/test-ifelse_censor_linter.R 75aa7232056bca01c8fa9a231385923e *tests/testthat/test-implicit_assignment_linter.R f631b8e1dcbb90a8f0ca999499ea740b *tests/testthat/test-implicit_integer_linter.R 9c6ef2c99e6363d21e338a0ac43734e0 *tests/testthat/test-indentation_linter.R d0b632eb20360f34aea30c724567fcbe *tests/testthat/test-infix_spaces_linter.R fa96134379cdf8176c600311065d38d0 *tests/testthat/test-inner_combine_linter.R af61184ddcec5e080e002b9526773e49 *tests/testthat/test-is_lint_level.R 28187d135b1ff694efa41f24becd2688 *tests/testthat/test-is_numeric_linter.R 64fdf71e27b6c61199d2f4d3e5b3a702 *tests/testthat/test-keyword_quote_linter.R ffc991941c90472e6375fd49aea03f89 *tests/testthat/test-knitr_extended_formats.R 56fa72d227494b7595b26d777c86a5ca *tests/testthat/test-knitr_formats.R 5b1621572f9e904e6cd3acb76ca117f9 *tests/testthat/test-length_levels_linter.R 15b6141b9c86254c0eaab8a07c76f8d1 *tests/testthat/test-length_test_linter.R de06fc95b818a96c61331baea1b23cae *tests/testthat/test-lengths_linter.R 3a6d2930e9bacb84dc5a52f2f082fc15 *tests/testthat/test-library_call_linter.R 42bf400453de364e5119d7f4d52f2260 *tests/testthat/test-line_length_linter.R db1eccda53440a190ddf376f3d9c4613 *tests/testthat/test-lint.R 27b2caa33cf37c58885c28a91f22bd81 *tests/testthat/test-lint_dir.R 9b3d1396e91a9da25a5f08b7b242269b *tests/testthat/test-lint_package.R 7708b431d89b22bc9d708bd769d02e91 *tests/testthat/test-linter_tags.R a9e81e143e612b7ccc705238d0dd1990 *tests/testthat/test-literal_coercion_linter.R a5f72271eefd5c9791da697fc075bdb9 *tests/testthat/test-make_linter_from_regex.R f486a7dfc1da22b14e02f1f11a6849d8 *tests/testthat/test-make_linter_from_xpath.R 1acdbd5e5b0b1c900f8db9eb6eba0ad2 *tests/testthat/test-matrix_apply_linter.R 7fd55db83fe93b89f0ae00ae44417528 *tests/testthat/test-methods.R 17d85acf45e4d3ff270160de9653d5a3 *tests/testthat/test-missing_argument_linter.R fb310a665894ae1130468a04b4b2f7fc *tests/testthat/test-missing_package_linter.R 56bfa179b2f723ce383e4177adf5d575 *tests/testthat/test-namespace_linter.R 05166ab706d3e48faa43a2e376939feb *tests/testthat/test-nested_ifelse_linter.R 6b74519653be448ac2f0beefcb182cdb *tests/testthat/test-nonportable_path_linter.R 81a829dbcabc6e8fb40a8f2fc10fbc08 *tests/testthat/test-normalize_exclusions.R 9d39c1ed78f561c913d84b3b00d92780 *tests/testthat/test-numeric_leading_zero_linter.R 896562ec1865be16eaee5382723531c4 *tests/testthat/test-object_length_linter.R 8c7672e9e471efd9d6279deb2aae5044 *tests/testthat/test-object_name_linter.R 81aa38ab550aec0dd7085fdbef3fcaac *tests/testthat/test-object_usage_linter.R afcf31a349a6118fe3ddfb1959dcb8d4 *tests/testthat/test-open_curly_linter.R 6c0f92fd833bbe9a80d053dccd9f1d41 *tests/testthat/test-outer_negation_linter.R 7e1a9740eedc2a4ec29018c82c4bc89a *tests/testthat/test-package_hooks_linter.R 99378a94709249d52862cd7dfc2ccdf4 *tests/testthat/test-paren_body_linter.R 8f60db17e9b1319223d037120693a74f *tests/testthat/test-paren_brace_linter.R 93363a93164fd149611488cadd44a96d *tests/testthat/test-parse_exclusions.R 9cb2c1c1fd2419b87de5e030f5c7acc0 *tests/testthat/test-paste_linter.R 87bf990d1e01367ad2856435d53ddb88 *tests/testthat/test-pipe-consistency-linter.R 24d16293067a9102a0d86927a62029a4 *tests/testthat/test-pipe_call_linter.R 92199092e982018a09f3d887f6039c47 *tests/testthat/test-pipe_continuation_linter.R 2f9090d1dbc470f38b8f8d1f541e45df *tests/testthat/test-quotes_linter.R 8fc9d9c86094ab70609371505c05e34e *tests/testthat/test-redundant_equals_linter.R aaf32eedd97958defbb84d7a361ca76b *tests/testthat/test-redundant_ifelse_linter.R be295265ce82b8fb523027c89fea912e *tests/testthat/test-regex_subset_linter.R b43b41e81391c1425f3bc2ef6d3f7560 *tests/testthat/test-repeat_linter.R ec67908605b604bde31388addf49270b *tests/testthat/test-routine_registration_linter.R 0f868bc3bb610e449589ec5f152bd06f *tests/testthat/test-rstudio_markers.R d219ab739fc8f3977131d69ef9797fc1 *tests/testthat/test-sarif_output.R f223f0d07e3927be6978fef78d56f5c8 *tests/testthat/test-scalar_in_linter.R e81d8f8c1a6b0720ecfa1cd51d6b4573 *tests/testthat/test-semicolon_linter.R 8e130f9df3bc1a4927be6075839aa675 *tests/testthat/test-seq_linter.R 08614454409be3ce44c6a7d2598cb104 *tests/testthat/test-settings.R 2bbe5c943384a0380a23853f4f95f16e *tests/testthat/test-sort_linter.R ace3439cb54cd0960f77bc3bf48b21a1 *tests/testthat/test-spaces_inside_linter.R d3b7fcd39f43836bcbdb1f6fd9562af1 *tests/testthat/test-spaces_left_parentheses_linter.R 93ebc9e6d39b50141402d9aa33add9c4 *tests/testthat/test-sprintf_linter.R ee34bbf3dcf993dfd1320702049f67fe *tests/testthat/test-string_boundary_linter.R ec99e3e1a7bf53da2945adebb969fa8a *tests/testthat/test-strings_as_factors_linter.R e81fdc207ea406154624793dd0e7bfed *tests/testthat/test-system_file_linter.R 1188ae9cd185fe19fe87b580e6e44bfc *tests/testthat/test-todo_comment_linter.R e47134ca45f23d6a00a89da20e5e7672 *tests/testthat/test-trailing_blank_lines_linter.R 0d44091d30457b0315c14d382eb2f696 *tests/testthat/test-trailing_whitespace_linter.R 468fa23d7aafa723d9c1016c97b902ff *tests/testthat/test-undesirable_function_linter.R 779b149f0dd59617a08d3860d9ecce80 *tests/testthat/test-undesirable_operator_linter.R 8406ecf956bc6c0420d6afd3ac3b1676 *tests/testthat/test-unnecessary_concatenation_linter.R 86bf3b615b33ada0d49a5b5b1fbe1373 *tests/testthat/test-unnecessary_lambda_linter.R 911bb80e3adced5a68e000d05117d69d *tests/testthat/test-unnecessary_nested_if_linter.R 91e585a2c854df05e106e8ca72cb22cf *tests/testthat/test-unnecessary_placeholder_linter.R 0e7dd5e1a86666a96f238782a2b3081c *tests/testthat/test-unneeded_concatenation_linter.R d141ddd75f7dbd05a5a2efa7ce9a5db1 *tests/testthat/test-unreachable_code_linter.R a88086d4aa7f8bc8a5db0f7a96eecce1 *tests/testthat/test-unused_import_linter.R c8adc3b7d7f9c3a5a43b8adfa4be8222 *tests/testthat/test-use_lintr.R 7abb5c407878d0e7ee81e1cf25735efb *tests/testthat/test-vector_logic_linter.R 206b5b7fe86cd25b931ca1832266cdc2 *tests/testthat/test-whitespace_linter.R ab61cc4456544b1e7c6e3716536a6cd5 *tests/testthat/test-with.R 68bc1563d44444908867886cd06d7303 *tests/testthat/test-with_id.R a40f716469405aba787de56914784510 *tests/testthat/test-xml_nodes_to_lints.R 0fc9639a883880cc44a8558f7be05626 *tests/testthat/test-xp_utils.R 1708bfb4b8a6928249e2c86c14810e41 *tests/testthat/test-yoda_test_linter.R e687b7d16ef3d9dcda5d11eb01e9bfb4 *tests/testthat/testthat-problems.rds a0ad092a8b252ffc3e939ab0f9cbb665 *vignettes/atom.png 58897cfc8d2912d4da2c412a2bb5b2f1 *vignettes/continuous-integration.Rmd f79c7713bfac20a3e2b9fd5206c5ab20 *vignettes/creating_linters.Rmd 79b71c06f968aec2a875be4969f034e4 *vignettes/editors.Rmd a0e9aec3dc3cda3a9436bbb9312ecd0d *vignettes/emacs-still.gif fc4d04b6291c0246512374b6327e3f48 *vignettes/lintr.Rmd c65b3525f3ac2d3f825a443a18500dff *vignettes/rstudio.png c2e39f07dbe4df2f901ed095d6658ecb *vignettes/sublime-still.gif aa43e719e7e82fa8f132b34827391d82 *vignettes/vim-syntastic-still.gif b117b8cd1795d24f91c192567bd48455 *vignettes/vim-syntastic.gif 32bdb35e5ff7098c72c66f21c92b75ae *vignettes/vscode.png lintr/inst/0000755000176200001440000000000014600122247012355 5ustar liggesuserslintr/inst/example/0000755000176200001440000000000014577052532014024 5ustar liggesuserslintr/inst/example/bad.R0000644000176200001440000000024413740716603014672 0ustar liggesusersfun = function(one) { one.plus.one <- oen + 1 four <- newVar <- matrix(1:10,nrow = 2) four[ 1, ] txt <- 'hi' three <- two+ 1 if(txt == 'hi') 4 5} { lintr/inst/example/complexity.R0000644000176200001440000000053214577052532016344 0ustar liggesuserscomplexity <- function(x) { if (x > 0.0) { if (x > 10.0) { if (x > 20.0) { x <- x / 2.0 } else { return(x) } } else { return(x) } } else { if (x < -10.0) { if (x < -20.0) { x <- x * 2.0 } else { return(x) } } else { return(x) } } x } lintr/inst/syntastic/0000755000176200001440000000000014577054033014410 5ustar liggesuserslintr/inst/syntastic/lintr.vim0000644000176200001440000000610314577054033016255 0ustar liggesusers"============================================================================ "File: lintr.vim "Description: Syntax checking plugin for syntastic.vim "Maintainer: Michael Chirico "License: This program is free software. It comes without any warranty, " to the extent permitted by applicable law. You can redistribute " it and/or modify it under the terms of the Do What The Fuck You " Want To Public License, Version 2, as published by Sam Hocevar. " See http://sam.zoy.org/wtfpl/COPYING for more details. " "============================================================================ " " Security: " " This checker runs the code in your file. This is probably fine if you " wrote the file yourself, but it can be a problem if you're trying to " check third party files. If you are 100% willing to let Vim run the " code in your file, set g:syntastic_enable_r_lintr_checker to 1 in " your vimrc to enable this checker: " let g:syntastic_enable_r_lintr_checker = 1 if exists("g:loaded_syntastic_r_lintr_checker") finish endif let g:loaded_syntastic_r_lintr_checker = 1 if !exists('g:syntastic_r_lintr_linters') let g:syntastic_r_lintr_linters = 'default_linters' endif if !exists('g:syntastic_r_lintr_cache') let g:syntastic_r_lintr_cache = 'FALSE' endif let s:save_cpo = &cpo set cpo&vim function! SyntaxCheckers_r_lintr_GetHighlightRegex(item) let term = matchstr(a:item['text'], "\\m'\\zs[^']\\+\\ze'") return term != '' ? '\V' . escape(term, '\') : '' endfunction function! SyntaxCheckers_r_lintr_IsAvailable() dict if !executable(self.getExec()) return 0 endif call system(self.getExecEscaped() . ' --slave --no-restore --no-save -e ' . syntastic#util#shescape('library(lintr)')) return v:shell_error == 0 endfunction function! SyntaxCheckers_r_lintr_GetLocList() dict if !exists('g:syntastic_enable_r_lintr_checker') || !g:syntastic_enable_r_lintr_checker call syntastic#log#error('checker r/lintr: checks disabled for security reasons; set g:syntastic_enable_r_lintr_checker to 1 to override') return [] endif let setwd = syntastic#util#isRunningWindows() ? 'setwd(''' . escape(getcwd(), '"\') . '''); ' : '' let makeprg = self.getExecEscaped() . ' --slave --no-restore --no-save' . \ ' -e ' . syntastic#util#shescape(setwd . 'suppressPackageStartupMessages(library(lintr)); ' . \ 'lint(cache = ' . g:syntastic_r_lintr_cache . ', commandArgs(TRUE), ' . g:syntastic_r_lintr_linters . ')') . \ ' --args ' . syntastic#util#shexpand('%') let errorformat = \ '%W%f:%l:%c: style: %m,' . \ '%W%f:%l:%c: warning: %m,' . \ '%E%f:%l:%c: error: %m,' call self.setWantSort(1) return SyntasticMake({ \ 'makeprg': makeprg, \ 'errorformat': errorformat, \ 'returns': [0] }) endfunction call g:SyntasticRegistry.CreateAndRegisterChecker({ \ 'filetype': 'r', \ 'name': 'lintr', \ 'exec': 'R' }) let &cpo = s:save_cpo unlet s:save_cpo " vim: set et sts=4 sw=4: lintr/inst/rstudio/0000755000176200001440000000000014122042072014042 5ustar liggesuserslintr/inst/rstudio/addins.dcf0000644000176200001440000000034014122042072015757 0ustar liggesusersName: Lint current file Description: Runs lintr::lint on the current file Binding: addin_lint Interative: false Name: Lint current package Description: Runs lintr::lint_package Binding: addin_lint_package Interative: false lintr/inst/doc/0000755000176200001440000000000014600122247013122 5ustar liggesuserslintr/inst/doc/creating_linters.html0000644000176200001440000007726714600122245017365 0ustar liggesusers Creating new linters

    Creating new linters

    lintr maintainers

    2024-03-24

    This vignette describes the steps necessary to create a new linter.

    See the last section for some details specific to writing new linters for {lintr}.

    A good example of a simple linter is the pipe_call_linter.

    #' Pipe call linter
    #'
    #' Force explicit calls in magrittr pipes, e.g.,
    #' `1:3 %>% sum()` instead of `1:3 %>% sum`.
    #'
    #' @evalRd rd_tags("pipe_call_linter")
    #' @seealso [linters] for a complete list of linters available in lintr.
    #' @export
    pipe_call_linter <- function() {
      xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]"
    
      Linter(function(source_expression) {
        if (!is_lint_level(source_expression, "expression")) {
          return(list())
        }
    
        xml <- source_expression$xml_parsed_content
    
        bad_expr <- xml2::xml_find_all(xml, xpath)
    
        xml_nodes_to_lints(
          bad_expr,
          source_expression = source_expression,
          lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.",
          type = "warning"
        )
      })
    }

    Let’s walk through the parts of the linter individually.

    Writing the linter

    #' Pipe call linter
    #'
    #' Force explicit calls in magrittr pipes, e.g.,
    #' `1:3 %>% sum()` instead of `1:3 %>% sum`.

    Describe the linter, giving it a title and briefly covering the usages that are discouraged when the linter is active.

    #' @evalRd rd_tags("pipe_call_linter")
    #' @seealso [linters] for a complete list of linters available in lintr.
    #' @export

    These lines (1) generate a Tags section in the documentation for the linter1; (2) link to the full table of available linters; and (3) mark the function for export. The most unfamiliar here is probably (1), which can be skipped outside of lintr itself.

    pipe_call_linter <- function() {

    Next, we define the name of the new linter. The convention is to suffix all linter names with _linter. All _linter functions are function factories that return a closure that will do the actual linting function. We could define additional parameters that are useful for the linter in this function declaration (see, e.g. assignment_linter), but pipe_call_linter requires no additional arguments.

    xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]"

    Here is the core linter logic. xpath is an XPath expression for expressions matching the discouraged usage. xpath is saved inside the factory code (as opposed to inside the linter itself) for efficiency. Often, the xpath will be somewhat complicated / involve some assembly code using paste() or glue::glue()[^See infix_spaces_linter() for an example of this], in which case it is preferable to execute this code only once when creating the linter; the cached XPath is then re-used on each expression in each file where the linter is run.

    Let’s examine the XPath a bit more closely:

    //expr                  # global search (//) for 'expr' nodes (R expressions), at any nesting level
    [                       # node[...] looks for any 'node' satisfying conditions in ...
      preceding-sibling::   # "siblings" are at the same nesting level in XML
        SPECIAL[            # 'SPECIAL' is the parse token for infix operators like %% or %+%
          text() = '%>%'    # text() returns the string associated with this node
        ]                   #
      and                   # combine conditions with 'and'
      *                     # match any node
      [1]                   # match the first such node
      [self::SYMBOL]        # match if the current node is a 'SYMBOL' (i.e., a 'name' in R)
    ]                       #

    Taken together, that means we want to match expr nodes preceded by the %>% infix operator whose first child node is a name. That maps pretty closely to the description of what the pipe_call_linter is looking for, but there is subtlety in mapping between the R code you’re used to and how they show up in the XML representation. expr nodes in particular take some practice to get accustomed to – use the plentiful XPath-based linters in lintr as a guide to get extra practice2. Note: xml2 implements XPath 1.0, which lacks some helpful features available in XPath 2.0.

    Linter(function(source_expression) {

    This is the closure. It will be called on the source_expression variable that contains the top level expressions in the file to be linted. The call to Linter() gives this closure the class ‘linter’ (it also guesses the name of the linter; see ?Linter for more details).

    The raw text of the expression is available from source_file$content. However, it is not generally possible to implement linters from the raw text – consider equals_na_linter. If we just look for == NA in the text of the file, we’ll generate many false positives, e.g. in comments (such as # note: is.na() is the proper way to check == NA) or inside character literals (such as warning("don't use == NA to check missingness")). We’re also likely to generate false negatives, for example when == and NA appear on different lines! Working around these issues using only the un-parsed text in every situation amounts to re-implementing the parser.

    Therefore it is recommended to work with the tokens from source_file$parsed_content or source_file$xml_parsed_content, as they are tokenized from the R parser. These tokens are obtained from parse() and utils::getParseData() calls done prior to calling the new linter. getParseData() returns a data.frame with information from the source parse tree of the file being linted. A list of tokens is available from r-source/src/main/gram.y.

    source_file$xml_parsed_content uses xmlparsedata::xml_parse_data() to convert the getParseData() output into an XML tree, which enables writing linter logic in XPath, a powerful language for expressing paths within the nested XML data structure. Most linters in lintr are built using XPath because it is a powerful language for computation on the abstract syntax tree / parse tree.

    if (!is_lint_level(source_expression, "expression")) {
      return(list())
    }

    Here, we return early if source_expression is not the expression-level object. get_source_expression() returns an object that parses the input file in two ways – once is done expression-by-expression, the other contains all of the expressions in the file. This is done to facilitate caching. Suppose your package has a long source file (e.g., 100s of expressions) – rather than run linters on every expression every time the file is updated, when caching is activated lintr will only run the linter again on expressions that have changed.

    Therefore, it is preferable to write expression-level linters whenever possible. Two types of exceptions observed in lintr are (1) when several or all expressions are required to ensure the linter logic applies (e.g., conjunct_test_linter looks for consecutive calls to stopifnot(), which will typically appear on consecutive expressions) or (2) when the linter logic is very simple & fast to compute, so that the overhead of re-running the linter is low (e.g., single_quotes_linter). In those cases, use is_lint_level(source_expression, "file").

    xml <- source_expression$xml_parsed_content
    
    bad_expr <- xml2::xml_find_all(xml, xpath)

    source_expression$xml_parsed_content is copied to a local variable (this is not strictly necessary but facilitates debugging). Then xml2::xml_find_all() is used to execute the XPath on this particular expression. Keep in mind that it is typically possible for a single expression to generate more than one lint – for example, x %>% na.omit %>% sum will trigger the pipe_call_linter() twice3.

    xml_nodes_to_lints(
      bad_expr,
      source_expression = source_expression,
      lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.",
      type = "warning"
    )

    Finally, we pass the matching XML node(s) to xml_nodes_to_lints(), which returns Lint objects corresponding to any “bad” usages found in source_expression. See ?Lint and ?xml_nodes_to_lints for details about the arguments. Note that here, the message for the lint is always the same, but for many linters, the message is customized to more closely match the observed usage. In such cases, xml_nodes_to_lint() can conveniently accept a function in lint_message which takes a node as input and converts it to a customized message. See, for example, seq_linter.

    Writing linter tests

    (NB: this section uses the assignment_linter() which has simpler examples than pipe_continuation_linter().)

    {lintr} works best inside the {testthat} unit testing framework, in particular, {lintr} exports lintr::expect_lint() which is designed as a companion to other testthat expectations.

    You can define tests inside separate test_that calls. Most of the linters use the same default form.

    test_that("returns the correct linting", {

    You then test a series of expectations for the linter using expect_lint. Please see ?expect_lint for a full description of the parameters.

    The main three aspects to test are:

    1. Linter returns no lints when there is nothing to lint, e.g.
    expect_lint("blah", NULL, assignment_linter())
    1. Linter returns a lint when there is something to lint, e.g.
    expect_lint("blah=1",
      rex("Use <-, not =, for assignment."),
      assignment_linter()
    )
    1. As many edge cases as you can think of that might break it, e.g.
    expect_lint("fun((blah = fun(1)))",
      rex("Use <-, not =, for assignment."),
      assignment_linter()
    )

    You may want to test that additional lint attributes are correct, such as the type, line number, column number, e.g.

    expect_lint("blah=1",
      list(message = "assignment", line_number = 1, column_number = 5, type = "style"),
      assignment_linter()
    )

    Finally, it is a good idea to test that your linter reports multiple lints if needed, e.g.

    expect_lint("blah=1; blah=2",
      list(
        list(line_number = 1, column_number = 5),
        list(line_number = 1, column_number = 13),
      )
      assignment_linter()
    )

    It is always better to write too many tests rather than too few.

    Other utilities for writing custom linters

    Besides is_lint_level(), {lintr} also exports some other helpers generally useful for writing custom linters; these are used a lot in the internals of our own helpers, and so they’ve been tested and demonstrated their utility already.

    • get_r_string(): Whenever your linter needs to examine the value of a character literal (e.g., whether an argument value is set to some string), use this to extract the string exactly as R will see it. This is especially important to make your logic robust to R-4-style raw strings like R"-(hello)-", which is otherwise difficult to express, for example as an XPath.

    Contributing to {lintr}

    More details about writing tests for new {lintr} linters

    The {lintr} package uses testthat for testing. You can run all of the currently available tests using devtools::test(). If you want to run only the tests in a given file use the filter argument to devtools::test().

    Linter tests should be put in the tests/testthat/ folder. The test filename should be the linter name prefixed by test-, e.g. test-pipe_continuation_linter.R.

    Adding your linter to the default_linters

    If your linter implements part of the tidyverse style guide you can add it to default_linters. This object is created in the file zzz.R (this name ensures that it will always run after all the linters are defined). Add your linter name to the default_linters list before the NULL at the end, and add a corresponding test case to the test script ./tests/testthat/default_linter_testcode.R.

    Submit pull request

    Push your changes to a branch of your fork of the lintr repository, and submit a pull request to get your linter merged into lintr!


    1. NB: this is a helper function for generating custom Rd styling. See R/linter_tags.R.↩︎

    2. The W3schools tutorials are also quite helpful; see https://www.w3schools.com/xml/xpath_intro.asp↩︎

    3. This is particularly important if you want the message field in the resulting Lint() to vary depending on the exact violation that’s found. For pipe_call_linter(), the message is always the same. See assignment_linter() for an example where the message can vary.↩︎

    lintr/inst/doc/editors.html0000644000176200001440000163054214600122246015473 0ustar liggesusers Editor setup

    Editor setup

    Note: This vignette is best viewed online, where we can render full animations of editor flows.

    RStudio

    lintr lints are automatically displayed in the RStudio Markers pane (RStudio versions > v0.99.206).

    RStudio Example
    RStudio Example

    In order to show the “Markers” pane in RStudio: Menu “Tools” -> “Global Options…”, a window with title “Options” will pop up. In that window: click “Code” on the left; click “Diagnostics” tab; check “Show diagnostics for R”.

    To lint a source file test.R type in the Console lintr::lint("test.R") and look at the result in the “Markers” pane.

    This package also includes two addins for linting the current source and package. To bind the addin to a keyboard shortcut navigate to Tools > addins > Browse Addins > Keyboard Shortcuts. It’s recommended to use Alt+Shift+L for linting the current source lint and Ctrl+Shift+Alt+L to code the package. These are easy to remember as you are Alt+Shift+L(int) ;)

    Emacs

    lintr has built-in integration with flycheck versions greater than 0.23. Emacs Example

    Installation

    lintr is fully integrated into flycheck when using ESS. See the installation documentation for those packages for more information.

    Configuration

    You can also configure what linters are used. e.g. using a different line length cutoff. - M-x customize-option -> flycheck-lintr-linters -> linters_with_defaults(line_length_linter(120))

    Vim - syntastic

    lintr can be integrated with syntastic for on-the-fly linting.

    Vim Example
    Vim Example

    Installation

    Put the file syntastic/lintr.vim in syntastic/syntax_checkers/r. If you are using pathogen this directory is ~/.vim/bundles/syntastic/syntax_checkers/r.

    You will also need to add the following lines to your .vimrc.

    let g:syntastic_enable_r_lintr_checker = 1
    let g:syntastic_r_checkers = ['lintr']

    Configuration

    You can also configure what linters are used. e.g. using a different line length cutoff.

    let g:syntastic_r_lintr_linters = "linters_with_defaults(line_length_linter(120))"

    Vim - ALE

    lintr can be integrated with ALE for on the fly linting.

    Installation

    lintr is integrated with ALE and requires no additional installation.

    Configuration

    You can configure what linters are used, e.g. using a different line length cutoff.

    let g:ale_r_lintr_options = "linters_with_defaults(line_length_linter(120))"

    You can also configure whether lint or lint_package is used. Set to 1 for lint_package and 0 (default) for lint.

    let g:ale_r_lintr_lint_package = 1

    See :h ale_r_lintr for more information.

    Note that configuration through .lintr files are not supported.

    There is a work around that can be used to read the contents of a .lintr file in the root of the working directory. This would allow the use of configuration through .lintr files.

    if filereadable(".lintr")
      let g:ale_r_lintr_options = join(readfile('.lintr'))
    endif

    Sublime Text 3

    lintr can be integrated with Sublime Linter for on-the-fly linting.

    Sublime Example
    Sublime Example

    Installation

    Simply install sublimeLinter-contrib-lintr using Package Control.

    For more information see Sublime Linter Docs

    Configuration

    You can also configure what linters are used. e.g. disabling the assignment linter and using a different line length cutoff. In the SublimeLinter User Settings

        {
          "linters": {
            "lintr": {
              "linters": "linters_with_defaults(assignment_linter = NULL, line_length_linter(120))"
            }
          }
        }

    Atom

    lintr can be integrated with Linter for on the fly linting.

    Atom Example
    Atom Example

    Installation

    Simply install linter-lintr from within Atom or on the command line with:

    apm install linter-lintr

    For more information and bug reports see Atom linter-lintr.

    Visual Studio Code

    In Visual Studio Code, vscode-R presents the lintr diagnostics from languageserver.

    VS Code Example
    VS Code Example

    Installation

    Installing languageserver package in R and vscode-R extension in VS Code will enable lintr in VS Code by default or run the following command lines:

    Rscript -e 'install.packages("languageserver")'
    code --install-extension reditorsupport.r
    lintr/inst/doc/continuous-integration.R0000644000176200001440000000021714600122244017771 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) lintr/inst/doc/editors.Rmd0000644000176200001440000001324114457657444015266 0ustar liggesusers--- title: "Editor setup" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Editor setup} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) in_pkgdown <- identical(Sys.getenv("IN_PKGDOWN"), "true") maybe_still <- function(url) { if (in_pkgdown) { url } else { gsub("\\.gif$", "-still.gif", url) } } ``` ```{r, echo = FALSE, results = 'asis'} if (!in_pkgdown) { cat( "Note: This vignette is best viewed [online](https://lintr.r-lib.org/articles/editors.html),", "where we can render full animations of editor flows.\n" ) } ``` ## RStudio lintr lints are automatically displayed in the RStudio Markers pane (RStudio versions \> v0.99.206). ![RStudio Example](rstudio.png "Rstudio Example") In order to show the "Markers" pane in RStudio: Menu "Tools" -\> "Global Options...", a window with title "Options" will pop up. In that window: click "Code" on the left; click "Diagnostics" tab; check "Show diagnostics for R". To lint a source file `test.R` type in the Console `lintr::lint("test.R")` and look at the result in the "Markers" pane. This package also includes two addins for linting the current source and package. To bind the addin to a keyboard shortcut navigate to Tools \> addins \> Browse Addins \> Keyboard Shortcuts. It's recommended to use Alt+Shift+L for linting the current source lint and Ctrl+Shift+Alt+L to code the package. These are easy to remember as you are Alt+Shift+L(int) ;) ## Emacs lintr has [built-in integration](http://www.flycheck.org/en/latest/languages.html#r) with [flycheck](https://github.com/flycheck/flycheck) versions greater than `0.23`. ![Emacs Example](`r maybe_still("emacs.gif")` "Emacs Example") ### Installation lintr is fully integrated into flycheck when using [ESS](http://ess.r-project.org/). See the installation documentation for those packages for more information. ### Configuration You can also configure what linters are used. e.g. using a different line length cutoff. - `M-x customize-option` -\> `flycheck-lintr-linters` -\> `linters_with_defaults(line_length_linter(120))` ## Vim - syntastic lintr can be integrated with [syntastic](https://github.com/vim-syntastic/syntastic) for on-the-fly linting. ![Vim Example](`r maybe_still("vim-syntastic.gif")` "Vim Example") ### Installation Put the file [syntastic/lintr.vim](https://raw.githubusercontent.com/r-lib/lintr/v2.0.1/inst/syntastic/lintr.vim) in `syntastic/syntax_checkers/r`. If you are using [pathogen](https://github.com/tpope/vim-pathogen) this directory is `~/.vim/bundles/syntastic/syntax_checkers/r`. You will also need to add the following lines to your `.vimrc`. ``` vim let g:syntastic_enable_r_lintr_checker = 1 let g:syntastic_r_checkers = ['lintr'] ``` ### Configuration You can also configure what linters are used. e.g. using a different line length cutoff. ``` vim let g:syntastic_r_lintr_linters = "linters_with_defaults(line_length_linter(120))" ``` ## Vim - ALE lintr can be integrated with [ALE](https://github.com/dense-analysis/ale) for on the fly linting. ### Installation lintr is integrated with ALE and requires no additional installation. ### Configuration You can configure what linters are used, e.g. using a different line length cutoff. ``` vim let g:ale_r_lintr_options = "linters_with_defaults(line_length_linter(120))" ``` You can also configure whether `lint` or `lint_package` is used. Set to 1 for `lint_package` and 0 (default) for `lint`. ``` vim let g:ale_r_lintr_lint_package = 1 ``` See `:h ale_r_lintr` for more information. Note that configuration through `.lintr` files are not supported. There is a work around that can be used to read the contents of a `.lintr` file in the root of the working directory. This would allow the use of configuration through `.lintr` files. ``` vim if filereadable(".lintr") let g:ale_r_lintr_options = join(readfile('.lintr')) endif ``` ## Sublime Text 3 lintr can be integrated with [Sublime Linter](https://github.com/SublimeLinter/SublimeLinter) for on-the-fly linting. ![Sublime Example](`r maybe_still("sublime.gif")` "Sublime Example") ### Installation Simply install `sublimeLinter-contrib-lintr` using [Package Control](https://packagecontrol.io/). For more information see [Sublime Linter Docs](http://sublimelinter.readthedocs.io/en/latest/installation.html#installing-via-pc) ### Configuration You can also configure what linters are used. e.g. disabling the assignment linter and using a different line length cutoff. In the SublimeLinter User Settings ```json { "linters": { "lintr": { "linters": "linters_with_defaults(assignment_linter = NULL, line_length_linter(120))" } } } ``` ## Atom lintr can be integrated with [Linter](https://github.com/steelbrain/linter) for on the fly linting. ![Atom Example](atom.png "Atom Example") ### Installation Simply install `linter-lintr` from within Atom or on the command line with: ``` bash apm install linter-lintr ``` For more information and bug reports see [Atom linter-lintr](https://github.com/AtomLinter/linter-lintr). ## Visual Studio Code In Visual Studio Code, [vscode-R](https://github.com/REditorSupport/vscode-R#r-extension-for-visual-studio-code) presents the lintr diagnostics from [languageserver](https://github.com/REditorSupport/languageserver). ![VS Code Example](vscode.png "VS Code Example") ### Installation Installing `languageserver` package in R and `vscode-R` extension in VS Code will enable lintr in VS Code by default or run the following command lines: ``` bash Rscript -e 'install.packages("languageserver")' code --install-extension reditorsupport.r ``` lintr/inst/doc/lintr.html0000644000176200001440000016371014600122247015150 0ustar liggesusers Using lintr

    Using lintr

    Alexander Rosenstock

    This vignette describes how to set up and configure lintr for use with projects or packages.

    Running lintr on a project

    Checking an R project for lints can be done with three different functions:

    • Lint a single file using lint():

      lint(filename = "R/bad.R")
    • Lint a directory using lint_dir():

      lint_dir(path = "R")

      This will apply lint() to all R source files matching the pattern argument. By default, this means all .R files as well as knitr formats (e.g. .Rmd, .Rnw).

      lint_dir is vectorized over path, so multiple directories can be linted at the same time.

    • Lint all relevant directories of an R package using lint_package():

      lint_package(path = ".")

      This will apply lint_dir() to all subdirectories usually containing R code in packages:

      • R containing the package implementation.
      • tests containing test code.
      • inst containing sample code or vignettes that will be installed along with the package.
      • vignettes containing package vignettes.
      • data-raw containing code to produce data files.

    For more information about the assumed package structure, see R Packages.

    Note that some linters (e.g. object_usage_linter()) require the package to be installed to function properly. pkgload::load_all() will also suffice. See ?executing_linters for more details.

    Configuring linters

    The .lintr file

    The canonical way to configure R projects and packages for linting is to create a .lintr file in the project root. This is a file in debian control format (?read.dcf), each value of which is evaluated as R code by lintr when reading the settings. A minimal .lintr file can be generated by running use_lintr() in the project directory. Lintr supports per-project configuration of the following fields.

    • linters - see ?linters_with_defaults for example of specifying only a few non-default linters and ?linters_with_tags for more fine-grained control.
    • exclusions - a list of filenames to exclude from linting. You can use a named item to exclude only certain lines from a file.
    • exclude - a regex pattern for lines to exclude from linting. Default is “# nolint”
    • exclude_start - a regex pattern to start exclusion range. Default is “# nolint start”
    • exclude_end - a regex pattern to end exclusion range. Default is “# nolint end”
    • encoding - the encoding used for source files. Default inferred from .Rproj or DESCRIPTION files, fallback to UTF-8

    .lintr File Example

    Below is an example .lintr file that uses 120 character line lengths, disables commented_code_linter, excludes a couple of files.

    linters: linters_with_defaults(
        line_length_linter(120),
        commented_code_linter = NULL
      )
    exclusions: list(
        "inst/doc/creating_linters.R" = 1,
        "inst/example/bad.R",
        "tests/testthat/exclusions-test"
      )

    Other configuration options

    More generally, lintr searches for a settings file according to following prioritized list. The first one found, if any, will be used:

    1. If options("lintr.linter_file") is an absolute path, this file will be used. The default for this option is ".lintr" or the value of the environment variable R_LINTR_LINTER_FILE, if set.
    2. A project-local linter file; that is, either
      1. a linter file (that is, a file named like lintr.linter_file) in the currently-searched directory, i.e. the directory of the file passed to lint(); or
      2. a linter file in the .github/linters child directory of the currently-searched directory.
    3. A project-local linter file in the closest parent directory of the currently-searched directory, starting from the deepest path, moving upwards one level at a time. When run from lint_package(), this directory can differ for each linted file.
    4. A linter file in the user’s HOME directory.
    5. A linter file called config in the user’s configuration path (given by tools::R_user_dir("lintr", which = "config")).

    If no linter file is found, only default settings take effect (see defaults).

    Using options()

    Values in options(), if they are not NULL, take precedence over those in the linter file (e.g. .lintr). Note that the key option_name in the linter file translates to an R option lintr.option_name. For example, options(lintr.exclude = "# skip lint") will take precedence over exclude: # nolint in the linter file.

    Using arguments to lint()

    The settings can also be passed as arguments to linting functions directly. In case of exclusions, these will be combined with the globally parsed settings. Other settings will be overridden.

    If only the specified settings should be changed, and the remaining settings should be taken directly from the defaults, the argument parse_settings = FALSE can be added to the function calls. This will suppress reading of the .lintr configuration. This is particularly useful for tests which should not exclude example files containing lints while the package-level .lintr excludes those files because the lints are intentional.

    Defaults

    The default settings of lintr are intended to conform to the tidyverse style guide. However, the behavior can be customized using different methods.

    Apart from lintr.linter_file, which defaults to ".lintr", there are the following settings:

    default
    linters lintr::default_linters
    encoding UTF-8
    exclude regex: #[[:space:]]*nolint
    exclude_next regex: #[[:space:]]*nolint next
    exclude_start regex: #[[:space:]]*nolint start
    exclude_end regex: #[[:space:]]*nolint end
    exclude_linter regex: ^[[:space:]]*:[[:space:]]*(?<linters>(?:(?:[^,.])+[[:space:]]*,[[:space:]]*)*(?:[^,.])+)\.
    exclude_linter_sep regex: [[:space:]]*,[[:space:]]*
    exclusions (empty)
    cache_directory /home/michael/.cache/R/lintr
    comment_token (lintr-bot comment token for automatic GitHub comments)
    comment_bot TRUE
    error_on_lint FALSE

    Note that the default encoding setting depends on the file to be linted. If an Encoding is found in a .Rproj file or a DESCRIPTION file, that encoding overrides the default of UTF-8.

    Customizing active linters

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

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

    linters: linters_with_defaults(
        line_length_linter = line_length_linter(120L),
        whitespace_linter = NULL
      )

    By default, the following linters are enabled. Where applicable, the default settings are also shown.

    settings
    assignment_linter allow_cascading_assign = TRUE, allow_right_assign = FALSE, allow_trailing = TRUE, allow_pipe_assign = FALSE
    brace_linter allow_single_line = FALSE
    commas_linter allow_trailing = FALSE
    commented_code_linter
    cyclocomp_linter complexity_limit = 15L
    equals_na_linter
    function_left_parentheses_linter
    indentation_linter indent = 2L, hanging_indent_style = “tidy”, assignment_as_infix = TRUE
    infix_spaces_linter exclude_operators = NULL, allow_multiple_spaces = TRUE
    line_length_linter length = 80L
    object_length_linter length = 30L
    object_name_linter styles = c(“snake_case”, “symbols”), regexes = character(0)
    object_usage_linter interpret_glue = TRUE, skip_with = TRUE
    paren_body_linter
    pipe_continuation_linter
    quotes_linter delimiter = “"”
    semicolon_linter allow_compound = FALSE, allow_trailing = FALSE
    seq_linter
    spaces_inside_linter
    spaces_left_parentheses_linter
    T_and_F_symbol_linter
    trailing_blank_lines_linter
    trailing_whitespace_linter allow_empty_lines = FALSE, allow_in_strings = TRUE
    vector_logic_linter
    whitespace_linter

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

    lintr::available_tags(packages = "lintr")
    #>  [1] "best_practices"      "common_mistakes"     "configurable"       
    #>  [4] "consistency"         "correctness"         "default"            
    #>  [7] "deprecated"          "efficiency"          "executing"          
    #> [10] "package_development" "pkg_testthat"        "readability"        
    #> [13] "robustness"          "style"

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

    linters <- lintr::linters_with_tags(tags = c("package_development", "readability"))
    names(linters)
    #>  [1] "backport_linter"                  "boolean_arithmetic_linter"       
    #>  [3] "brace_linter"                     "commas_linter"                   
    #>  [5] "commented_code_linter"            "conjunct_test_linter"            
    #>  [7] "consecutive_assertion_linter"     "cyclocomp_linter"                
    #>  [9] "empty_assignment_linter"          "expect_comparison_linter"        
    #> [11] "expect_identical_linter"          "expect_length_linter"            
    #> [13] "expect_named_linter"              "expect_not_linter"               
    #> [15] "expect_null_linter"               "expect_s3_class_linter"          
    #> [17] "expect_s4_class_linter"           "expect_true_false_linter"        
    #> [19] "expect_type_linter"               "fixed_regex_linter"              
    #> [21] "for_loop_index_linter"            "function_left_parentheses_linter"
    #> [23] "function_return_linter"           "if_not_else_linter"              
    #> [25] "implicit_assignment_linter"       "indentation_linter"              
    #> [27] "infix_spaces_linter"              "inner_combine_linter"            
    #> [29] "is_numeric_linter"                "keyword_quote_linter"            
    #> [31] "length_levels_linter"             "lengths_linter"                  
    #> [33] "library_call_linter"              "line_length_linter"              
    #> [35] "matrix_apply_linter"              "nested_ifelse_linter"            
    #> [37] "numeric_leading_zero_linter"      "object_length_linter"            
    #> [39] "object_usage_linter"              "outer_negation_linter"           
    #> [41] "package_hooks_linter"             "paren_body_linter"               
    #> [43] "pipe_call_linter"                 "pipe_consistency_linter"         
    #> [45] "pipe_continuation_linter"         "quotes_linter"                   
    #> [47] "redundant_equals_linter"          "repeat_linter"                   
    #> [49] "scalar_in_linter"                 "semicolon_linter"                
    #> [51] "sort_linter"                      "spaces_inside_linter"            
    #> [53] "spaces_left_parentheses_linter"   "string_boundary_linter"          
    #> [55] "system_file_linter"               "T_and_F_symbol_linter"           
    #> [57] "unnecessary_concatenation_linter" "unnecessary_lambda_linter"       
    #> [59] "unnecessary_nested_if_linter"     "unnecessary_placeholder_linter"  
    #> [61] "unreachable_code_linter"          "yoda_test_linter"

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

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

    Using all available linters

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

    names(lintr::all_linters())
    #>  [1] "absolute_path_linter"             "any_duplicated_linter"           
    #>  [3] "any_is_na_linter"                 "assignment_linter"               
    #>  [5] "backport_linter"                  "boolean_arithmetic_linter"       
    #>  [7] "brace_linter"                     "class_equals_linter"             
    #>  [9] "commas_linter"                    "commented_code_linter"           
    #> [11] "condition_message_linter"         "conjunct_test_linter"            
    #> [13] "consecutive_assertion_linter"     "cyclocomp_linter"                
    #> [15] "duplicate_argument_linter"        "empty_assignment_linter"         
    #> [17] "equals_na_linter"                 "expect_comparison_linter"        
    #> [19] "expect_identical_linter"          "expect_length_linter"            
    #> [21] "expect_named_linter"              "expect_not_linter"               
    #> [23] "expect_null_linter"               "expect_s3_class_linter"          
    #> [25] "expect_s4_class_linter"           "expect_true_false_linter"        
    #> [27] "expect_type_linter"               "extraction_operator_linter"      
    #> [29] "fixed_regex_linter"               "for_loop_index_linter"           
    #> [31] "function_argument_linter"         "function_left_parentheses_linter"
    #> [33] "function_return_linter"           "if_not_else_linter"              
    #> [35] "ifelse_censor_linter"             "implicit_assignment_linter"      
    #> [37] "implicit_integer_linter"          "indentation_linter"              
    #> [39] "infix_spaces_linter"              "inner_combine_linter"            
    #> [41] "is_numeric_linter"                "keyword_quote_linter"            
    #> [43] "length_levels_linter"             "length_test_linter"              
    #> [45] "lengths_linter"                   "library_call_linter"             
    #> [47] "line_length_linter"               "literal_coercion_linter"         
    #> [49] "matrix_apply_linter"              "missing_argument_linter"         
    #> [51] "missing_package_linter"           "namespace_linter"                
    #> [53] "nested_ifelse_linter"             "nonportable_path_linter"         
    #> [55] "numeric_leading_zero_linter"      "object_length_linter"            
    #> [57] "object_name_linter"               "object_usage_linter"             
    #> [59] "outer_negation_linter"            "package_hooks_linter"            
    #> [61] "paren_body_linter"                "paste_linter"                    
    #> [63] "pipe_call_linter"                 "pipe_consistency_linter"         
    #> [65] "pipe_continuation_linter"         "quotes_linter"                   
    #> [67] "redundant_equals_linter"          "redundant_ifelse_linter"         
    #> [69] "regex_subset_linter"              "repeat_linter"                   
    #> [71] "routine_registration_linter"      "scalar_in_linter"                
    #> [73] "semicolon_linter"                 "seq_linter"                      
    #> [75] "sort_linter"                      "spaces_inside_linter"            
    #> [77] "spaces_left_parentheses_linter"   "sprintf_linter"                  
    #> [79] "string_boundary_linter"           "strings_as_factors_linter"       
    #> [81] "system_file_linter"               "T_and_F_symbol_linter"           
    #> [83] "todo_comment_linter"              "trailing_blank_lines_linter"     
    #> [85] "trailing_whitespace_linter"       "undesirable_function_linter"     
    #> [87] "undesirable_operator_linter"      "unnecessary_concatenation_linter"
    #> [89] "unnecessary_lambda_linter"        "unnecessary_nested_if_linter"    
    #> [91] "unnecessary_placeholder_linter"   "unreachable_code_linter"         
    #> [93] "unused_import_linter"             "vector_logic_linter"             
    #> [95] "whitespace_linter"                "yoda_test_linter"

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

    linters: all_linters()

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

    linters: all_linters(
        commented_code_linter = NULL,
        implicit_integer_linter = NULL
      )

    Advanced: programmatic retrieval of linters

    For some use cases, it may be useful to specify linters by string instead of by name, i.e. "assignment_linter" instead of writing out assignment_linter().

    Beware that in such cases, a simple get() is not enough:

    library(lintr)
    linter_name <- "assignment_linter"
    
    show_lint <- function(l) {
      lint_df <- as.data.frame(l)
      print(lint_df[, c("line_number", "message", "linter")])
    }
    hline <- function() cat(strrep("-", getOption("width") - 5L), "\n", sep = "")
    
    withr::with_tempfile("tmp", {
      writeLines("a = 1", tmp)
    
      # linter column is just 'get'
      show_lint(lint(tmp, linters = get(linter_name)()))
      hline()
    
      this_linter <- get(linter_name)()
      attr(this_linter, "name") <- linter_name
      # linter column is 'assignment_linter'
      show_lint(lint(tmp, linters = this_linter))
      hline()
    
      # more concise alternative for this case: use eval(call(.))
      show_lint(lint(tmp, linters = eval(call(linter_name))))
    })
    #>   line_number                        message linter
    #> 1           1 Use <-, not =, for assignment.    get
    #> ---------------------------------------------------------------------------
    #>   line_number                        message            linter
    #> 1           1 Use <-, not =, for assignment. assignment_linter
    #> ---------------------------------------------------------------------------
    #>   line_number                        message            linter
    #> 1           1 Use <-, not =, for assignment. assignment_linter

    Exclusions

    Sometimes, linters should not be globally disabled. Instead, one might want to exclude some code from linting altogether or selectively disable some linters on some part of the code.

    Note that the preferred way of excluding lints from source code is to use the narrowest possible scope and specify exactly which linters should not throw a lint on a marked line. This prevents accidental suppression of justified lints that happen to be on the same line as a lint that needs to be suppressed.

    Excluding lines of code

    Within source files, special comments can be used to exclude single lines of code from linting. All lints produced on the marked line are excluded from the results.

    By default, this special comment is # nolint:

    file.R

    X = 42L # -------------- this comment overflows the default 80 chars line length.

    > lint("file.R")

    #> <text>:1:1: style: [object_name_linter] Variable and function name style should match snake_case or symbols.
    #> X = 42L # -------------- this comment overflows the default 80 chars line length.
    #> ^
    #> <text>:1:3: style: [assignment_linter] Use <-, not =, for assignment.
    #> X = 42L # -------------- this comment overflows the default 80 chars line length.
    #>   ^
    #> <text>:1:81: style: [line_length_linter] Lines should not be more than 80 characters. This line is 81 characters.
    #> X = 42L # -------------- this comment overflows the default 80 chars line length.
    #> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

    file2.R

    X = 42L # nolint ------ this comment overflows the default 80 chars line length.

    > lint("file2.R")

    Observe how all lints were suppressed and no output is shown. Sometimes, only a specific linter needs to be excluded. In this case, the name of the linter can be appended to the # nolint comment preceded by a colon and terminated by a dot.

    Excluding only some linters

    file3.R

    X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.

    > lint("file3.R")

    #> <text>:1:3: style: [assignment_linter] Use <-, not =, for assignment.
    #> X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.
    #>   ^
    #> <text>:1:81: style: [line_length_linter] Lines should not be more than 80 characters. This line is 94 characters.
    #> X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.
    #> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~

    Observe how only the object_name_linter was suppressed. This is preferable to blanket # nolint statements because blanket exclusions may accidentally silence a linter that was not intentionally suppressed.

    Multiple linters can be specified by listing them with a comma as a separator:

    file4.R

    X = 42L # nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length.

    > lint("file4.R")

    #> <text>:1:3: style: [assignment_linter] Use <-, not =, for assignment.
    #> X = 42L # nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length.
    #>   ^

    You can also specify the linter names by a unique prefix instead of their full name:

    file5.R

    X = 42L # nolint: object_name, line_len. this comment still overflows the default 80 chars line length.

    > lint("file5.R")

    #> <text>:1:3: style: [assignment_linter] Use <-, not =, for assignment.
    #> X = 42L # nolint: object_name, line_len. this comment still overflows the default 80 chars line length.
    #>   ^

    Excluding multiple lines of codes

    If any or all linters should be disabled for a contiguous block of code, the exclude_start and exclude_end patterns can be used. They default to # nolint start and # nolint end respectively.

    # nolint start accepts the same syntax as # nolint to disable specific linters in the following lines until a # nolint end is encountered.

    # x <- 42L
    # print(x)
    #> <text>:1:3: style: [commented_code_linter] Commented code should be removed.
    #> # x <- 42L
    #>   ^~~~~~~~
    #> <text>:2:3: style: [commented_code_linter] Commented code should be removed.
    #> # print(x)
    #>   ^~~~~~~~
    # nolint start: commented_code_linter.
    # x <- 42L
    # print(x)
    # nolint end

    (No lints)

    Excluding lines via the config file

    Individual lines can also be excluded via the config file by setting the key exclusions to a list with elements corresponding to different files. To exclude all lints for line 1 of file R/bad.R and line_length_linter for lines 4 to 6 of the same file, one can set

    exclusions: list(
        "R/bad.R" = list(
          1, # global exclusions are unnamed
          line_length_linter = 4:6
        )
      )

    All paths are interpreted relative to the location of the .lintr file.

    Excluding files completely

    The linter configuration can also be used to exclude a file entirely, or a linter for a file entirely. Use the sentinel line number Inf to mark all lines as excluded within a file. If a file is only given as a character vector, full exclusion is implied.

    exclusions: list(
        # excluded from all lints:
        "R/excluded1.R",
        "R/excluded2.R" = Inf,
        "R/excluded3.R" = list(Inf),
        # excluded from line_length_linter:
        "R/no-line-length.R" = list(
          line_length_linter = Inf
        )
      )

    Excluding directories completely

    Entire directories are also recognized when supplied as strings in the exclusions key. For example, to exclude the renv folder from linting in a R project using renv, set

    exclusions: list(
        "renv"
      )

    This is particularly useful for projects which include external code in subdirectories.

    lintr/inst/doc/continuous-integration.Rmd0000644000176200001440000001010314577052532020324 0ustar liggesusers--- title: "Continuous integration" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Continuous integration} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` You can configure `lintr` to run as part of continuous integration (either for a package or a general project containing R files) in order to automatically check that commits and pull requests do not deteriorate code style. ## For packages First, take special note of the proviso in `?executing_linters` about the need to have your package and its dependencies installed or loaded (e.g. with `pkgload::load_all()`) in order for certain linters (e.g. `object_usage_linter()`) to function as intended. ### GitHub Actions If your package is on GitHub, the easiest way to do this is with GitHub Actions. The workflow configuration files use YAML syntax. The `usethis` package has some great functionality that can help you with workflow files. The most straightforward way to add a `lintr` workflow to your package is to use the [r-lib/actions](https://github.com/r-lib/actions/)'s [`lint` example](https://github.com/r-lib/actions/tree/v2-branch/examples#lint-workflow). To do this with `usethis`, you need to call ```r usethis::use_github_action("lint") ``` This will create a workflow file called `lint.yaml` and place it in the correct location, namely in the `.github/workflows` directory of your repository. This file configures all the steps required to run `lintr::lint_package()` on your package. Alternatively you can use the eponymous [`lint-changed-files.yaml`](https://github.com/r-lib/actions/blob/v2-branch/examples/lint-changed-files.yaml) to only lint any changed files: ```r usethis::use_github_action("lint-changed-files") ``` Comments to the commit or pull request will be printed as [annotations](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-status-checks#types-of-status-checks-on-github) along side the status check on GitHub. If you want the builds to produce an error instead of just a warning, you can set the environment variable `LINTR_ERROR_ON_LINT=true`. This is set by default for both [r-lib/actions](https://github.com/r-lib/actions/)'s `lint.yaml` and `lint-changed-files.yaml`. Note that this will kill the R process in case of a lint. If your project is in a subdirectory and you would like to use GitHub Actions annotations, you can set `options(lintr.github_annotation_project_dir = "path/to/project")` which will make sure that the annotations point to the correct paths. ### Travis CI If you want to run `lintr` on [Travis-CI](https://www.travis-ci.com/), you will need to have Travis install the package first. This can be done by adding the following line to your `.travis.yml` ``` yaml r_github_packages: - r-lib/lintr ``` We recommend running `lintr::lint_package()` as an after_success step in your build process: ``` yaml after_success: - R CMD INSTALL $PKG_TARBALL - Rscript -e 'lintr::lint_package()' ``` If lints are found in the commit or pull request they will be printed on Travis-CI. The environment variable `LINTR_ERROR_ON_LINT` mentioned for GitHub actions also works with Travis CI builds. ## For projects You are not limited to using `lintr` for packages -- you can use it in combination with continuous integration for any other project. ### GitHub Actions If your project is on GitHub, you could take advantage of GitHub Actions and the `usethis` functionality. [r-lib/actions](https://github.com/r-lib/actions/) includes a [`lint-project` example](https://github.com/r-lib/actions/tree/v2-branch/examples#lint-project-workflow), which you can use by calling: ``` r usethis::use_github_action("lint-project") ``` ### Super-Linter `lintr` powers R lints for [Super-Linter](https://github.com/github/super-linter) and [MegaLinter](https://megalinter.io/latest/), which provide a unified linting experience across many languages. Specifically, they execute `lintr::lint()` on the R and R Markdown files included in a given project. lintr/inst/doc/editors.R0000644000176200001440000000112014600122245014706 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) in_pkgdown <- identical(Sys.getenv("IN_PKGDOWN"), "true") maybe_still <- function(url) { if (in_pkgdown) { url } else { gsub("\\.gif$", "-still.gif", url) } } ## ----echo = FALSE, results = 'asis'------------------------------------------- if (!in_pkgdown) { cat( "Note: This vignette is best viewed [online](https://lintr.r-lib.org/articles/editors.html),", "where we can render full animations of editor flows.\n" ) } lintr/inst/doc/creating_linters.Rmd0000644000176200001440000003124214577052532017140 0ustar liggesusers--- title: "Creating new linters" author: "lintr maintainers" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Creating new linters} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- This vignette describes the steps necessary to create a new linter. See the last section for some details specific to writing new linters for `{lintr}`. A good example of a simple linter is the `pipe_call_linter`. ```r #' Pipe call linter #' #' Force explicit calls in magrittr pipes, e.g., #' `1:3 %>% sum()` instead of `1:3 %>% sum`. #' #' @evalRd rd_tags("pipe_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export pipe_call_linter <- function() { xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]" Linter(function(source_expression) { if (!is_lint_level(source_expression, "expression")) { return(list()) } xml <- source_expression$xml_parsed_content bad_expr <- xml2::xml_find_all(xml, xpath) xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.", type = "warning" ) }) } ``` Let's walk through the parts of the linter individually. ## Writing the linter ## ```r #' Pipe call linter #' #' Force explicit calls in magrittr pipes, e.g., #' `1:3 %>% sum()` instead of `1:3 %>% sum`. ``` Describe the linter, giving it a title and briefly covering the usages that are discouraged when the linter is active. ```r #' @evalRd rd_tags("pipe_call_linter") #' @seealso [linters] for a complete list of linters available in lintr. #' @export ``` These lines (1) generate a Tags section in the documentation for the linter^[NB: this is a helper function for generating custom Rd styling. See R/linter_tags.R.]; (2) link to the full table of available linters; and (3) mark the function for export. The most unfamiliar here is probably (1), which can be skipped outside of `lintr` itself. ```r pipe_call_linter <- function() { ``` Next, we define the name of the new linter. The convention is to suffix all linter names with `_linter`. All `_linter` functions are function factories that return a closure that will do the actual linting function. We could define additional parameters that are useful for the linter in this function declaration (see, e.g. `assignment_linter`), but `pipe_call_linter` requires no additional arguments. ```r xpath <- "//expr[preceding-sibling::SPECIAL[text() = '%>%'] and *[1][self::SYMBOL]]" ``` Here is the core linter logic. `xpath` is an XPath expression for expressions matching the discouraged usage. `xpath` is saved inside the factory code (as opposed to inside the linter itself) for efficiency. Often, the `xpath` will be somewhat complicated / involve some assembly code using `paste()` or `glue::glue()`[^See `infix_spaces_linter()` for an example of this], in which case it is preferable to execute this code only once when creating the linter; the cached XPath is then re-used on each expression in each file where the linter is run. Let's examine the XPath a bit more closely: ```xpath //expr # global search (//) for 'expr' nodes (R expressions), at any nesting level [ # node[...] looks for any 'node' satisfying conditions in ... preceding-sibling:: # "siblings" are at the same nesting level in XML SPECIAL[ # 'SPECIAL' is the parse token for infix operators like %% or %+% text() = '%>%' # text() returns the string associated with this node ] # and # combine conditions with 'and' * # match any node [1] # match the first such node [self::SYMBOL] # match if the current node is a 'SYMBOL' (i.e., a 'name' in R) ] # ``` Taken together, that means we want to match `expr` nodes preceded by the `%>%` infix operator whose first child node is a `name`. That maps pretty closely to the description of what the `pipe_call_linter` is looking for, but there is subtlety in mapping between the R code you're used to and how they show up in the XML representation. `expr` nodes in particular take some practice to get accustomed to -- use the plentiful XPath-based linters in `lintr` as a guide to get extra practice^[The W3schools tutorials are also quite helpful; see https://www.w3schools.com/xml/xpath_intro.asp]. Note: `xml2` implements XPath 1.0, which lacks some helpful features available in XPath 2.0. ```r Linter(function(source_expression) { ``` This is the closure. It will be called on the `source_expression` variable that contains the top level expressions in the file to be linted. The call to `Linter()` gives this closure the class 'linter' (it also guesses the name of the linter; see `?Linter` for more details). The raw text of the expression is available from `source_file$content`. However, it is not generally possible to implement linters from the raw text -- consider `equals_na_linter`. If we just look for `== NA` in the text of the file, we'll generate many false positives, e.g. in comments (such as `# note: is.na() is the proper way to check == NA`) or inside character literals (such as `warning("don't use == NA to check missingness")`). We're also likely to generate false negatives, for example when `==` and `NA` appear on different lines! Working around these issues using only the un-parsed text in every situation amounts to re-implementing the parser. Therefore it is recommended to work with the tokens from `source_file$parsed_content` or `source_file$xml_parsed_content`, as they are tokenized from the `R` parser. These tokens are obtained from `parse()` and `utils::getParseData()` calls done prior to calling the new linter. `getParseData()` returns a `data.frame` with information from the source parse tree of the file being linted. A list of tokens is available from [r-source/src/main/gram.y](https://github.com/r-devel/r-svn/blob/master/src/main/gram.y#L395-L412). `source_file$xml_parsed_content` uses `xmlparsedata::xml_parse_data()` to convert the `getParseData()` output into an XML tree, which enables writing linter logic in [XPath](https://www.w3schools.com/xml/xpath_intro.asp), a powerful language for expressing paths within the nested XML data structure. Most linters in `lintr` are built using XPath because it is a powerful language for computation on the abstract syntax tree / parse tree. ```r if (!is_lint_level(source_expression, "expression")) { return(list()) } ``` Here, we return early if `source_expression` is not the expression-level object. `get_source_expression()` returns an object that parses the input file in two ways -- once is done expression-by-expression, the other contains all of the expressions in the file. This is done to facilitate caching. Suppose your package has a long source file (e.g., 100s of expressions) -- rather than run linters on every expression every time the file is updated, when caching is activated `lintr` will only run the linter again on expressions that have changed. Therefore, it is preferable to write expression-level linters whenever possible. Two types of exceptions observed in `lintr` are (1) when several or all expressions are _required_ to ensure the linter logic applies (e.g., `conjunct_test_linter` looks for consecutive calls to `stopifnot()`, which will typically appear on consecutive expressions) or (2) when the linter logic is very simple & fast to compute, so that the overhead of re-running the linter is low (e.g., `single_quotes_linter`). In those cases, use `is_lint_level(source_expression, "file")`. ```r xml <- source_expression$xml_parsed_content bad_expr <- xml2::xml_find_all(xml, xpath) ``` `source_expression$xml_parsed_content` is copied to a local variable (this is not strictly necessary but facilitates debugging). Then `xml2::xml_find_all()` is used to execute the XPath on this particular expression. Keep in mind that it is typically possible for a single expression to generate more than one lint -- for example, `x %>% na.omit %>% sum` will trigger the `pipe_call_linter()` twice^[This is particularly important if you want the `message` field in the resulting `Lint()` to vary depending on the exact violation that's found. For `pipe_call_linter()`, the message is always the same. See `assignment_linter()` for an example where the `message` can vary.]. ```r xml_nodes_to_lints( bad_expr, source_expression = source_expression, lint_message = "Use explicit calls in magrittr pipes, i.e., `a %>% foo` should be `a %>% foo()`.", type = "warning" ) ``` Finally, we pass the matching XML node(s) to `xml_nodes_to_lints()`, which returns `Lint` objects corresponding to any "bad" usages found in `source_expression`. See `?Lint` and `?xml_nodes_to_lints` for details about the arguments. Note that here, the `message` for the lint is always the same, but for many linters, the message is customized to more closely match the observed usage. In such cases, `xml_nodes_to_lint()` can conveniently accept a function in `lint_message` which takes a node as input and converts it to a customized message. See, for example, `seq_linter`. ## Writing linter tests (NB: this section uses the `assignment_linter()` which has simpler examples than `pipe_continuation_linter()`.) `{lintr}` works best inside the `{testthat}` unit testing framework, in particular, `{lintr}` exports `lintr::expect_lint()` which is designed as a companion to other testthat expectations. You can define tests inside separate `test_that` calls. Most of the linters use the same default form. ```r test_that("returns the correct linting", { ``` You then test a series of expectations for the linter using `expect_lint`. Please see `?expect_lint` for a full description of the parameters. The main three aspects to test are: 1. Linter returns no lints when there is nothing to lint, e.g. ```r expect_lint("blah", NULL, assignment_linter()) ``` 2. Linter returns a lint when there is something to lint, e.g. ```r expect_lint("blah=1", rex("Use <-, not =, for assignment."), assignment_linter() ) ``` 3. As many edge cases as you can think of that might break it, e.g. ```r expect_lint("fun((blah = fun(1)))", rex("Use <-, not =, for assignment."), assignment_linter() ) ``` You may want to test that additional `lint` attributes are correct, such as the type, line number, column number, e.g. ```r expect_lint("blah=1", list(message = "assignment", line_number = 1, column_number = 5, type = "style"), assignment_linter() ) ``` Finally, it is a good idea to test that your linter reports multiple lints if needed, e.g. ```r expect_lint("blah=1; blah=2", list( list(line_number = 1, column_number = 5), list(line_number = 1, column_number = 13), ) assignment_linter() ) ``` It is always better to write too many tests rather than too few. ## Other utilities for writing custom linters Besides `is_lint_level()`, `{lintr}` also exports some other helpers generally useful for writing custom linters; these are used a lot in the internals of our own helpers, and so they've been tested and demonstrated their utility already. * `get_r_string()`: Whenever your linter needs to examine the value of a character literal (e.g., whether an argument value is set to some string), use this to extract the string exactly as R will see it. This is especially important to make your logic robust to R-4-style raw strings like `R"-(hello)-"`, which is otherwise difficult to express, for example as an XPath. ## Contributing to `{lintr}` ### More details about writing tests for new `{lintr}` linters The `{lintr}` package uses [testthat](https://github.com/r-lib/testthat) for testing. You can run all of the currently available tests using `devtools::test()`. If you want to run only the tests in a given file use the `filter` argument to `devtools::test()`. Linter tests should be put in the [tests/testthat/](https://github.com/r-lib/lintr/tree/main/tests/testthat) folder. The test filename should be the linter name prefixed by `test-`, e.g. `test-pipe_continuation_linter.R`. ### Adding your linter to the default_linters ## If your linter implements part of the tidyverse style guide you can add it to `default_linters`. This object is created in the file `zzz.R` (this name ensures that it will always run after all the linters are defined). Add your linter name to the `default_linters` list before the `NULL` at the end, and add a corresponding test case to the test script `./tests/testthat/default_linter_testcode.R`. ### Submit pull request ## Push your changes to a branch of your fork of the [lintr](https://github.com/r-lib/lintr) repository, and submit a pull request to get your linter merged into lintr! lintr/inst/doc/lintr.Rmd0000644000176200001440000003634414577052532014744 0ustar liggesusers--- title: "Using lintr" author: "Alexander Rosenstock" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using lintr} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This vignette describes how to set up and configure `lintr` for use with projects or packages. ## Running `lintr` on a project Checking an R project for lints can be done with three different functions: - Lint a single file using `lint()`: ``` r lint(filename = "R/bad.R") ``` - Lint a directory using `lint_dir()`: ``` r lint_dir(path = "R") ``` This will apply `lint()` to all R source files matching the `pattern` argument. By default, this means all `.R` files as well as `knitr` formats (e.g. `.Rmd`, `.Rnw`). `lint_dir` is vectorized over `path`, so multiple directories can be linted at the same time. - Lint all relevant directories of an R package using `lint_package()`: ``` r lint_package(path = ".") ``` This will apply `lint_dir()` to all subdirectories usually containing R code in packages: - `R` containing the package implementation. - `tests` containing test code. - `inst` containing sample code or vignettes that will be installed along with the package. - `vignettes` containing package vignettes. - `data-raw` containing code to produce `data` files. For more information about the assumed package structure, see [R Packages](https://r-pkgs.org/structure.html). Note that some linters (e.g. `object_usage_linter()`) require the package to be installed to function properly. `pkgload::load_all()` will also suffice. See `?executing_linters` for more details. ## Configuring linters ### The `.lintr` file The canonical way to configure R projects and packages for linting is to create a `.lintr` file in the project root. This is a file in debian control format (`?read.dcf`), each value of which is evaluated as R code by `lintr` when reading the settings. A minimal `.lintr` file can be generated by running `use_lintr()` in the project directory. Lintr supports per-project configuration of the following fields. - `linters` - see `?linters_with_defaults` for example of specifying only a few non-default linters and `?linters_with_tags` for more fine-grained control. - `exclusions` - a list of filenames to exclude from linting. You can use a named item to exclude only certain lines from a file. - `exclude` - a regex pattern for lines to exclude from linting. Default is "\# nolint" - `exclude_start` - a regex pattern to start exclusion range. Default is "\# nolint start" - `exclude_end` - a regex pattern to end exclusion range. Default is "\# nolint end" - `encoding` - the encoding used for source files. Default inferred from .Rproj or DESCRIPTION files, fallback to UTF-8 ### .lintr File Example Below is an example .lintr file that uses 120 character line lengths, disables `commented_code_linter`, excludes a couple of files. ``` yaml linters: linters_with_defaults( line_length_linter(120), commented_code_linter = NULL ) exclusions: list( "inst/doc/creating_linters.R" = 1, "inst/example/bad.R", "tests/testthat/exclusions-test" ) ``` ### Other configuration options More generally, `lintr` searches for a settings file according to following prioritized list. The first one found, if any, will be used: 1. If `options("lintr.linter_file")` is an absolute path, this file will be used. The default for this option is `".lintr"` or the value of the environment variable `R_LINTR_LINTER_FILE`, if set. 2. A project-local linter file; that is, either 1. a linter file (that is, a file named like `lintr.linter_file`) in the currently-searched directory, i.e. the directory of the file passed to `lint()`; or 2. a linter file in the `.github/linters` child directory of the currently-searched directory. 3. A project-local linter file in the closest parent directory of the currently-searched directory, starting from the deepest path, moving upwards one level at a time. When run from `lint_package()`, this directory can differ for each linted file. 4. A linter file in the user's `HOME` directory. 5. A linter file called `config` in the user's configuration path (given by `tools::R_user_dir("lintr", which = "config")`). If no linter file is found, only default settings take effect (see [defaults](#defaults)). ### Using `options()` Values in `options()`, if they are not `NULL`, take precedence over those in the linter file (e.g. `.lintr`). Note that the key `option_name` in the linter file translates to an R option `lintr.option_name`. For example, `options(lintr.exclude = "# skip lint")` will take precedence over `exclude: # nolint` in the linter file. ### Using arguments to `lint()` The settings can also be passed as arguments to linting functions directly. In case of `exclusions`, these will be combined with the globally parsed settings. Other settings will be overridden. If only the specified settings should be changed, and the remaining settings should be taken directly from the defaults, the argument `parse_settings = FALSE` can be added to the function calls. This will suppress reading of the `.lintr` configuration. This is particularly useful for tests which should not exclude example files containing lints while the package-level `.lintr` excludes those files because the lints are intentional. ### Defaults {#defaults} The default settings of `lintr` are intended to conform to the [tidyverse style guide](https://style.tidyverse.org/). However, the behavior can be customized using different methods. Apart from `lintr.linter_file`, which defaults to `".lintr"`, there are the following settings: ```{r show_default_settings, echo = FALSE} default_settings <- lintr::default_settings default_settings$linters <- "`lintr::default_linters`" default_settings$comment_token <- "(lintr-bot comment token for automatic GitHub comments)" default_settings$exclusions <- "(empty)" make_string <- function(x) { if (inherits(x, "regex")) { paste0("regex: `", x, "`") } else { as.character(x) } } defaults_table <- data.frame( default = vapply(default_settings, make_string, character(1L)), stringsAsFactors = FALSE ) # avoid conflict when loading lintr in echo=TRUE cell below rm(default_settings) knitr::kable(defaults_table) ``` Note that the default `encoding` setting depends on the file to be linted. If an Encoding is found in a `.Rproj` file or a `DESCRIPTION` file, that encoding overrides the default of UTF-8. #### Customizing active linters If you only want to customize some linters, you can use the helper function `linters_with_defaults()`, which will keep all unnamed linters with the default settings. Disable a linter by passing `NULL`. For example, to set the line length limit to 120 characters and globally disable the `whitespace_linter()`, you can put this into your `.lintr`: ``` r linters: linters_with_defaults( line_length_linter = line_length_linter(120L), whitespace_linter = NULL ) ``` By default, the following linters are enabled. Where applicable, the default settings are also shown. ```{r show_linter_defaults, echo = FALSE} library(lintr) # needed here for formalArgs default_linters <- lintr::default_linters linters_with_args <- lapply( setNames(nm = intersect(names(default_linters), lintr::available_linters(tags = "configurable")$linter)), formalArgs ) make_setting_string <- function(linter_name) { args <- linters_with_args[[linter_name]] if (is.null(args)) { return("") } arglist <- vapply(args, function(arg) { env <- environment(default_linters[[linter_name]]) deparse(env[[arg]]) }, character(1L)) paste0(args, " = ", arglist, collapse = ", ") } defaults_table <- data.frame( row.names = names(default_linters), settings = vapply(names(default_linters), make_setting_string, character(1L)), stringsAsFactors = FALSE ) knitr::kable(defaults_table) ``` Another way to customize linters is by specifying tags in `linters_with_tags()`. The available tags are listed below: ```{r show_tags} lintr::available_tags(packages = "lintr") ``` You can select tags of interest to see which linters are included: ```{r show_tag_linters} linters <- lintr::linters_with_tags(tags = c("package_development", "readability")) names(linters) ``` You can include tag-based linters in the configuration file, and customize them further: ```yaml linters: linters_with_tags( tags = c("package_development", "readability"), yoda_test_linter = NULL ) ``` #### Using all available linters The default lintr configuration includes only linters relevant to the tidyverse style guide, but there are many other linters available in `{lintr}`. You can see a list of all available linters using ```{r show_all_linter_names} names(lintr::all_linters()) ``` If you want to use all available linters, you can include this in your `.lintr` file: ```yaml linters: all_linters() ``` If you want to use all available linters *except* a few, you can exclude them using `NULL`: ```yaml linters: all_linters( commented_code_linter = NULL, implicit_integer_linter = NULL ) ``` #### Advanced: programmatic retrieval of linters For some use cases, it may be useful to specify linters by string instead of by name, i.e. `"assignment_linter"` instead of writing out `assignment_linter()`. Beware that in such cases, a simple `get()` is not enough: ```{r programmatic_lintr} library(lintr) linter_name <- "assignment_linter" show_lint <- function(l) { lint_df <- as.data.frame(l) print(lint_df[, c("line_number", "message", "linter")]) } hline <- function() cat(strrep("-", getOption("width") - 5L), "\n", sep = "") withr::with_tempfile("tmp", { writeLines("a = 1", tmp) # linter column is just 'get' show_lint(lint(tmp, linters = get(linter_name)())) hline() this_linter <- get(linter_name)() attr(this_linter, "name") <- linter_name # linter column is 'assignment_linter' show_lint(lint(tmp, linters = this_linter)) hline() # more concise alternative for this case: use eval(call(.)) show_lint(lint(tmp, linters = eval(call(linter_name)))) }) ``` ## Exclusions Sometimes, linters should not be globally disabled. Instead, one might want to exclude some code from linting altogether or selectively disable some linters on some part of the code. > Note that the preferred way of excluding lints from source code is to use the narrowest possible scope and specify exactly which linters should not throw a lint on a marked line. > This prevents accidental suppression of justified lints that happen to be on the same line as a lint that needs to be suppressed. ### Excluding lines of code Within source files, special comments can be used to exclude single lines of code from linting. All lints produced on the marked line are excluded from the results. By default, this special comment is `# nolint`: **file.R** ``` r X = 42L # -------------- this comment overflows the default 80 chars line length. ``` `> lint("file.R")` ```{r show_long_line_lint, echo = FALSE} lint("X = 42L # -------------- this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` **file2.R** ``` r X = 42L # nolint ------ this comment overflows the default 80 chars line length. ``` `> lint("file2.R")` ```{r show_nolint, echo = FALSE} lint("X = 42L # nolint ------ this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` Observe how all lints were suppressed and no output is shown. Sometimes, only a specific linter needs to be excluded. In this case, the *name* of the linter can be appended to the `# nolint` comment preceded by a colon and terminated by a dot. ### Excluding only some linters **file3.R** ``` r X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length. ``` `> lint("file3.R")` ```{r show_long_line_lint_not_skipped, echo = FALSE} lint("X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ``` Observe how only the `object_name_linter` was suppressed. This is preferable to blanket `# nolint` statements because blanket exclusions may accidentally silence a linter that was not intentionally suppressed. Multiple linters can be specified by listing them with a comma as a separator: **file4.R** ``` r X = 42L # nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length. ``` `> lint("file4.R")` ```{r show_nolint_multiple, echo = FALSE} lint( paste( "X = 42L", "# nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ``` You can also specify the linter names by a unique prefix instead of their full name: **file5.R** ``` r X = 42L # nolint: object_name, line_len. this comment still overflows the default 80 chars line length. ``` `> lint("file5.R")` ```{r show_nolint_abbrev, echo = FALSE} lint( paste( "X = 42L", "# nolint: object_name, line_len. this comment still overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ``` ### Excluding multiple lines of codes If any or all linters should be disabled for a contiguous block of code, the `exclude_start` and `exclude_end` patterns can be used. They default to `# nolint start` and `# nolint end` respectively. `# nolint start` accepts the same syntax as `# nolint` to disable specific linters in the following lines until a `# nolint end` is encountered. ``` r # x <- 42L # print(x) ``` ```{r show_comment_code_lint, echo = FALSE} lint("# x <- 42L\n# print(x)\n", parse_settings = FALSE) ``` ``` r # nolint start: commented_code_linter. # x <- 42L # print(x) # nolint end ``` ```{r show_comment_code_nolint, echo = FALSE} lint("# nolint start: commented_code_linter.\n# x <- 42L\n# print(x)\n# nolint end\n", parse_settings = FALSE ) ``` (No lints) ### Excluding lines via the config file Individual lines can also be excluded via the config file by setting the key `exclusions` to a list with elements corresponding to different files. To exclude all lints for line 1 of file `R/bad.R` and `line_length_linter` for lines 4 to 6 of the same file, one can set ``` r exclusions: list( "R/bad.R" = list( 1, # global exclusions are unnamed line_length_linter = 4:6 ) ) ``` All paths are interpreted relative to the location of the `.lintr` file. ### Excluding files completely The linter configuration can also be used to exclude a file entirely, or a linter for a file entirely. Use the sentinel line number `Inf` to mark all lines as excluded within a file. If a file is only given as a character vector, full exclusion is implied. ``` r exclusions: list( # excluded from all lints: "R/excluded1.R", "R/excluded2.R" = Inf, "R/excluded3.R" = list(Inf), # excluded from line_length_linter: "R/no-line-length.R" = list( line_length_linter = Inf ) ) ``` ### Excluding directories completely Entire directories are also recognized when supplied as strings in the `exclusions` key. For example, to exclude the `renv` folder from linting in a R project using `renv`, set ``` r exclusions: list( "renv" ) ``` This is particularly useful for projects which include external code in subdirectories. lintr/inst/doc/lintr.R0000644000176200001440000001035614600122247014402 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----show_default_settings, echo = FALSE-------------------------------------- default_settings <- lintr::default_settings default_settings$linters <- "`lintr::default_linters`" default_settings$comment_token <- "(lintr-bot comment token for automatic GitHub comments)" default_settings$exclusions <- "(empty)" make_string <- function(x) { if (inherits(x, "regex")) { paste0("regex: `", x, "`") } else { as.character(x) } } defaults_table <- data.frame( default = vapply(default_settings, make_string, character(1L)), stringsAsFactors = FALSE ) # avoid conflict when loading lintr in echo=TRUE cell below rm(default_settings) knitr::kable(defaults_table) ## ----show_linter_defaults, echo = FALSE--------------------------------------- library(lintr) # needed here for formalArgs default_linters <- lintr::default_linters linters_with_args <- lapply( setNames(nm = intersect(names(default_linters), lintr::available_linters(tags = "configurable")$linter)), formalArgs ) make_setting_string <- function(linter_name) { args <- linters_with_args[[linter_name]] if (is.null(args)) { return("") } arglist <- vapply(args, function(arg) { env <- environment(default_linters[[linter_name]]) deparse(env[[arg]]) }, character(1L)) paste0(args, " = ", arglist, collapse = ", ") } defaults_table <- data.frame( row.names = names(default_linters), settings = vapply(names(default_linters), make_setting_string, character(1L)), stringsAsFactors = FALSE ) knitr::kable(defaults_table) ## ----show_tags---------------------------------------------------------------- lintr::available_tags(packages = "lintr") ## ----show_tag_linters--------------------------------------------------------- linters <- lintr::linters_with_tags(tags = c("package_development", "readability")) names(linters) ## ----show_all_linter_names---------------------------------------------------- names(lintr::all_linters()) ## ----programmatic_lintr------------------------------------------------------- library(lintr) linter_name <- "assignment_linter" show_lint <- function(l) { lint_df <- as.data.frame(l) print(lint_df[, c("line_number", "message", "linter")]) } hline <- function() cat(strrep("-", getOption("width") - 5L), "\n", sep = "") withr::with_tempfile("tmp", { writeLines("a = 1", tmp) # linter column is just 'get' show_lint(lint(tmp, linters = get(linter_name)())) hline() this_linter <- get(linter_name)() attr(this_linter, "name") <- linter_name # linter column is 'assignment_linter' show_lint(lint(tmp, linters = this_linter)) hline() # more concise alternative for this case: use eval(call(.)) show_lint(lint(tmp, linters = eval(call(linter_name)))) }) ## ----show_long_line_lint, echo = FALSE---------------------------------------- lint("X = 42L # -------------- this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ## ----show_nolint, echo = FALSE------------------------------------------------ lint("X = 42L # nolint ------ this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ## ----show_long_line_lint_not_skipped, echo = FALSE---------------------------- lint("X = 42L # nolint: object_name_linter. this comment overflows the default 80 chars line length.\n", parse_settings = FALSE ) ## ----show_nolint_multiple, echo = FALSE--------------------------------------- lint( paste( "X = 42L", "# nolint: object_name_linter, line_length_linter. this comment overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ## ----show_nolint_abbrev, echo = FALSE----------------------------------------- lint( paste( "X = 42L", "# nolint: object_name, line_len. this comment still overflows the default 80 chars line length.\n" ), parse_settings = FALSE ) ## ----show_comment_code_lint, echo = FALSE------------------------------------- lint("# x <- 42L\n# print(x)\n", parse_settings = FALSE) ## ----show_comment_code_nolint, echo = FALSE----------------------------------- lint("# nolint start: commented_code_linter.\n# x <- 42L\n# print(x)\n# nolint end\n", parse_settings = FALSE ) lintr/inst/doc/continuous-integration.html0000644000176200001440000003433314600122245020543 0ustar liggesusers Continuous integration

    Continuous integration

    You can configure lintr to run as part of continuous integration (either for a package or a general project containing R files) in order to automatically check that commits and pull requests do not deteriorate code style.

    For packages

    First, take special note of the proviso in ?executing_linters about the need to have your package and its dependencies installed or loaded (e.g. with pkgload::load_all()) in order for certain linters (e.g. object_usage_linter()) to function as intended.

    GitHub Actions

    If your package is on GitHub, the easiest way to do this is with GitHub Actions. The workflow configuration files use YAML syntax. The usethis package has some great functionality that can help you with workflow files. The most straightforward way to add a lintr workflow to your package is to use the r-lib/actions’s lint example. To do this with usethis, you need to call

    usethis::use_github_action("lint")

    This will create a workflow file called lint.yaml and place it in the correct location, namely in the .github/workflows directory of your repository. This file configures all the steps required to run lintr::lint_package() on your package.

    Alternatively you can use the eponymous lint-changed-files.yaml to only lint any changed files:

    usethis::use_github_action("lint-changed-files")

    Comments to the commit or pull request will be printed as annotations along side the status check on GitHub. If you want the builds to produce an error instead of just a warning, you can set the environment variable LINTR_ERROR_ON_LINT=true. This is set by default for both r-lib/actions’s lint.yaml and lint-changed-files.yaml. Note that this will kill the R process in case of a lint.

    If your project is in a subdirectory and you would like to use GitHub Actions annotations, you can set options(lintr.github_annotation_project_dir = "path/to/project") which will make sure that the annotations point to the correct paths.

    Travis CI

    If you want to run lintr on Travis-CI, you will need to have Travis install the package first. This can be done by adding the following line to your .travis.yml

    r_github_packages:
      - r-lib/lintr

    We recommend running lintr::lint_package() as an after_success step in your build process:

    after_success:
      - R CMD INSTALL $PKG_TARBALL
      - Rscript -e 'lintr::lint_package()'

    If lints are found in the commit or pull request they will be printed on Travis-CI. The environment variable LINTR_ERROR_ON_LINT mentioned for GitHub actions also works with Travis CI builds.

    For projects

    You are not limited to using lintr for packages – you can use it in combination with continuous integration for any other project.

    GitHub Actions

    If your project is on GitHub, you could take advantage of GitHub Actions and the usethis functionality. r-lib/actions includes a lint-project example, which you can use by calling:

    usethis::use_github_action("lint-project")

    Super-Linter

    lintr powers R lints for Super-Linter and MegaLinter, which provide a unified linting experience across many languages. Specifically, they execute lintr::lint() on the R and R Markdown files included in a given project.

    lintr/inst/extdata/0000755000176200001440000000000014517602346014021 5ustar liggesuserslintr/inst/extdata/sarif-template.json0000644000176200001440000000271714517602346017640 0ustar liggesusers{ "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json", "version": "2.1.0", "runs": [ { "tool": { "driver": { "name": "lintr", "informationUri": "https://lintr.r-lib.org/", "version": "2.0.1", "rules": [ { "id": "trailing_whitespace_linter", "fullDescription": { "text": "Trailing whitespace is superfluous." }, "defaultConfiguration": { "level": "note" } } ] } }, "results": [ { "ruleId": "trailing_whitespace_linter", "ruleIndex": 0, "message": { "text": "Trailing blank lines are superfluous." }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "TestFileFolder/hello.r", "uriBaseId": "ROOTPATH" }, "region": { "startLine": 2, "startColumn": 22, "snippet": { "text": "print(Hello World!) " } } } } ] } ], "columnKind": "utf16CodeUnits", "originalUriBaseIds": { "ROOTPATH": { "uri": "file:///C:/repos/repototest/" } } } ] } lintr/inst/WORDLIST0000644000176200001440000000163714457657444013604 0ustar liggesusersALLUPPERCASE Addins Backport CMD Checkstyle Config Ctrl Cyclomatic Deprecations EQ ESS Github Linter Linters MegaLinter ORCID RMarkdown RStudio Rhtml Rmarkdown Rmd Rnw Rproj Rrst Rstudio Rtex Rtxt SARIF SublimeLinter Tidyverse UpperCamelCase Wickham XPath XPaths YAML Yihui addin addins alllowercase backports backticked bookdown bugfix checkstyle codecov codinghorror coercions config covr customizable customizations cyclomatic dcf de debian dplyr dragosmg envvar eval flycheck github https igraph importFrom infixes io jwz knitr labelled lang languageserver linter linters lintr's lowerCamelCalse magrittr michaelchirico mis nd nodeset nolint patilindrajeets pre programmatically qmd readably regexes repo rlang roxygen sandboxing src stopifnot styler subdir syntastic syntatic testthat th tibble tibbles tidyverse tokenized travis tufte un unevaluated unicode unparseable untrusted vectorized vscode wercker www xpath yoda lintr/inst/lintr/0000755000176200001440000000000014577052532013521 5ustar liggesuserslintr/inst/lintr/linters.csv0000644000176200001440000001421114577052532015715 0ustar liggesuserslinter,tags absolute_path_linter,robustness best_practices configurable any_duplicated_linter,efficiency best_practices any_is_na_linter,efficiency best_practices assignment_linter,style consistency default configurable backport_linter,robustness configurable package_development boolean_arithmetic_linter,efficiency best_practices readability brace_linter,style readability default configurable class_equals_linter,best_practices robustness consistency closed_curly_linter,style readability deprecated configurable commas_linter,style readability default configurable commented_code_linter,style readability best_practices default condition_message_linter,best_practices consistency conjunct_test_linter,package_development best_practices readability configurable pkg_testthat consecutive_assertion_linter,style readability consistency consecutive_stopifnot_linter,style readability consistency deprecated cyclocomp_linter,style readability best_practices default configurable duplicate_argument_linter,correctness common_mistakes configurable empty_assignment_linter,readability best_practices equals_na_linter,robustness correctness common_mistakes default expect_comparison_linter,package_development best_practices pkg_testthat expect_identical_linter,package_development pkg_testthat expect_length_linter,package_development best_practices readability pkg_testthat expect_named_linter,package_development best_practices readability pkg_testthat expect_not_linter,package_development best_practices readability pkg_testthat expect_null_linter,package_development best_practices pkg_testthat expect_s3_class_linter,package_development best_practices pkg_testthat expect_s4_class_linter,package_development best_practices pkg_testthat expect_true_false_linter,package_development best_practices readability pkg_testthat expect_type_linter,package_development best_practices pkg_testthat extraction_operator_linter,style best_practices fixed_regex_linter,best_practices readability efficiency configurable for_loop_index_linter,best_practices readability robustness function_argument_linter,style consistency best_practices function_left_parentheses_linter,style readability default function_return_linter,readability best_practices if_not_else_linter,readability consistency configurable ifelse_censor_linter,best_practices efficiency implicit_assignment_linter,style best_practices readability configurable implicit_integer_linter,style consistency best_practices configurable indentation_linter,style readability default configurable infix_spaces_linter,style readability default configurable inner_combine_linter,efficiency consistency readability is_numeric_linter,readability best_practices consistency keyword_quote_linter,readability consistency style length_levels_linter,readability best_practices consistency length_test_linter,common_mistakes efficiency lengths_linter,efficiency readability best_practices library_call_linter,style best_practices readability configurable line_length_linter,style readability default configurable literal_coercion_linter,best_practices consistency efficiency matrix_apply_linter,readability efficiency missing_argument_linter,correctness common_mistakes configurable missing_package_linter,robustness common_mistakes namespace_linter,correctness robustness configurable executing nested_ifelse_linter,efficiency readability no_tab_linter,style consistency deprecated nonportable_path_linter,robustness best_practices configurable numeric_leading_zero_linter,style consistency readability object_length_linter,style readability default configurable executing object_name_linter,style consistency default configurable executing object_usage_linter,style readability correctness default executing configurable open_curly_linter,style readability deprecated configurable outer_negation_linter,readability efficiency best_practices package_hooks_linter,style correctness package_development paren_body_linter,style readability default paren_brace_linter,style readability deprecated paste_linter,best_practices consistency configurable pipe_call_linter,style readability pipe_consistency_linter,style readability configurable pipe_continuation_linter,style readability default quotes_linter,style consistency readability default configurable redundant_equals_linter,best_practices readability efficiency common_mistakes redundant_ifelse_linter,best_practices efficiency consistency configurable regex_subset_linter,best_practices efficiency repeat_linter,style readability routine_registration_linter,best_practices efficiency robustness scalar_in_linter,readability consistency best_practices efficiency semicolon_linter,style readability default configurable semicolon_terminator_linter,style readability deprecated configurable seq_linter,robustness efficiency consistency best_practices default single_quotes_linter,style consistency readability deprecated sort_linter,readability best_practices efficiency spaces_inside_linter,style readability default spaces_left_parentheses_linter,style readability default sprintf_linter,correctness common_mistakes string_boundary_linter,readability efficiency configurable strings_as_factors_linter,robustness system_file_linter,consistency readability best_practices T_and_F_symbol_linter,style readability robustness consistency best_practices default todo_comment_linter,style configurable trailing_blank_lines_linter,style default trailing_whitespace_linter,style default configurable undesirable_function_linter,style efficiency configurable robustness best_practices undesirable_operator_linter,style efficiency configurable robustness best_practices unnecessary_concatenation_linter,style readability efficiency configurable unnecessary_lambda_linter,best_practices efficiency readability unnecessary_nested_if_linter,readability best_practices unnecessary_placeholder_linter,readability best_practices unneeded_concatenation_linter,style readability efficiency configurable deprecated unreachable_code_linter,best_practices readability unused_import_linter,best_practices common_mistakes configurable executing vector_logic_linter,default efficiency best_practices whitespace_linter,style consistency default yoda_test_linter,package_development best_practices readability pkg_testthat