├── .circleci ├── config.yml ├── crucible.sh ├── crucible_prep.sh ├── deps_project.clj ├── example-green-project │ ├── deps.edn │ ├── project.clj │ └── src │ │ └── example_project │ │ └── core.clj ├── example-red-project │ ├── deps.edn │ ├── project.clj │ └── src │ │ └── example_project │ │ └── core.clj ├── example-refresh-project │ ├── deps.edn │ ├── project.clj │ ├── red │ │ └── red.clj │ └── src │ │ └── example_project │ │ └── core.clj ├── integration_test.sh ├── lint.sh ├── maybe_deploy.clj ├── nvd │ ├── deps.edn │ ├── nvd-config.edn │ ├── nvd_suppressions.xml │ └── run.sh └── reflection-example │ ├── project.clj │ └── src │ └── reflection_example │ └── core.clj ├── .clj-kondo └── config.edn ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitmodules ├── README.md ├── cases └── testcases │ ├── analyzererror.clj │ ├── are_true │ ├── green.clj │ ├── red_one.clj │ ├── red_three.clj │ └── red_two.clj │ ├── arglists.clj │ ├── boxed_math │ ├── green.clj │ └── red.clj │ ├── byte_array │ └── green.clj │ ├── bytes_class │ ├── green.clj │ └── green2.clj │ ├── clojure_1_11.clj │ ├── clojure_test.clj │ ├── clojure_test │ └── are │ │ └── red.clj │ ├── cond │ └── green.clj │ ├── const.clj │ ├── const │ └── unused_namespaces │ │ ├── consumer.clj │ │ └── producer.clj │ ├── constant_test │ ├── if_some │ │ ├── green.clj │ │ └── red.clj │ ├── some_thread_first │ │ └── green.clj │ ├── some_thread_last │ │ └── green.clj │ ├── spec_every │ │ └── green.clj │ └── when_some │ │ ├── green.clj │ │ └── red.clj │ ├── constanttestexpr.clj │ ├── data_readers.clj │ ├── def_in_def │ ├── red1.clj │ ├── red2.clj │ └── red3.clj │ ├── deprecated.clj │ ├── deprecations │ ├── overloading │ │ ├── green1.clj │ │ ├── green2.clj │ │ └── red.clj │ └── own_ns │ │ ├── green.clj │ │ └── red.clj │ ├── duplicateparams.clj │ ├── duplicateparams2.clj │ ├── dynamic_earmuffs │ ├── no_dynamic │ │ ├── green.clj │ │ └── red.clj │ └── no_earmuffs │ │ ├── green.clj │ │ └── red.clj │ ├── eastwood-testing-config.clj │ ├── f01.clj │ ├── f02.clj │ ├── f03.clj │ ├── f04.clj │ ├── f05.clj │ ├── f06.clj │ ├── f07.clj │ ├── f08.clj │ ├── foreign_macroexpansions │ └── red.clj │ ├── ignored_faults_example.clj │ ├── implicit_dependencies.clj │ ├── implicit_dependencies │ ├── cljc │ │ ├── green.cljc │ │ └── red.cljc │ └── explicit_require │ │ ├── green1.clj │ │ ├── green2.clj │ │ ├── green3.clj │ │ ├── green4.clj │ │ ├── green5.clj │ │ └── red.clj │ ├── imported_var.clj │ ├── in_ns_switching.clj │ ├── is_false │ └── green.clj │ ├── isformsok.clj │ ├── keyword_typos.clj │ ├── large_defprotocol.clj │ ├── let │ ├── green.clj │ └── red.clj │ ├── macrometa.clj │ ├── performance │ ├── green │ │ ├── case.clj │ │ ├── hash.clj │ │ └── recur.clj │ └── red │ │ ├── case.clj │ │ ├── hash.clj │ │ └── recur.clj │ ├── refer_clojure_exclude │ ├── green.clj │ └── red.clj │ ├── subkind_silencing │ └── red.clj │ ├── suspicious.clj │ ├── tanal_27.clj │ ├── tanal_9.clj │ ├── test_metadata_example.clj │ ├── testtest.clj │ ├── unhinted_reflective_call │ ├── defn_call_inside_refresh_dirs.clj │ ├── defn_call_outside_refresh_dirs.clj │ ├── depends_on_example_defn.clj │ ├── example_defmacro.clj │ ├── example_defn.clj │ ├── foreign_defn_call.clj │ ├── foreign_macro_call.clj │ ├── macro_call_inside_refresh_dirs.clj │ ├── macro_call_outside_refresh_dirs.clj │ ├── red.clj │ └── unused_foreign_reflective_code.clj │ ├── unknown_reify.clj │ ├── unlimiteduse.clj │ ├── unused_fn_args │ └── multimethods │ │ ├── green.clj │ │ └── red.clj │ ├── unused_ret_vals │ ├── green1.clj │ ├── red1.clj │ ├── red2.clj │ ├── red3.clj │ ├── red4.clj │ └── red5.clj │ ├── unusedlocals.clj │ ├── unusednsimport │ ├── consumer1.clj │ ├── consumer2.clj │ ├── consumer3.clj │ ├── consumer4.clj │ ├── consumer5.clj │ └── defrecord.clj │ ├── unusednss.clj │ ├── unusednss2.clj │ ├── unusednss3.clj │ ├── unusednss4.clj │ ├── warnloc1.clj │ ├── warnloc2.clj │ ├── while_true │ ├── green.clj │ └── red.clj │ ├── wrong_meta_on_macro_example.clj │ ├── wrong_require.clj │ ├── wrong_tag_example.clj │ ├── wrongnsform.clj │ ├── wrongprepost.clj │ ├── wrongprepost2.clj │ ├── wrongtag.clj │ └── wrongtag2.clj ├── changes.md ├── clojure-core-test └── eastwood │ └── clojure_core_test.clj ├── copied-deps └── eastwood │ └── copieddeps │ ├── dep1 │ └── clojure │ │ └── tools │ │ ├── analyzer.clj │ │ └── analyzer │ │ ├── ast.clj │ │ ├── ast │ │ └── query.clj │ │ ├── env.clj │ │ ├── passes.clj │ │ ├── passes │ │ ├── add_binding_atom.clj │ │ ├── cleanup.clj │ │ ├── collect_closed_overs.clj │ │ ├── constant_lifter.clj │ │ ├── elide_meta.clj │ │ ├── emit_form.clj │ │ ├── index_vector_nodes.clj │ │ ├── source_info.clj │ │ ├── trim.clj │ │ ├── uniquify.clj │ │ └── warn_earmuff.clj │ │ └── utils.clj │ ├── dep10 │ └── clojure │ │ └── tools │ │ ├── reader.clj │ │ └── reader │ │ ├── default_data_readers.clj │ │ ├── edn.clj │ │ ├── impl │ │ ├── commons.clj │ │ ├── errors.clj │ │ ├── inspect.clj │ │ └── utils.clj │ │ └── reader_types.clj │ ├── dep11 │ └── clojure │ │ └── java │ │ └── classpath.clj │ ├── dep2 │ └── clojure │ │ └── tools │ │ └── analyzer │ │ ├── jvm.clj │ │ ├── jvm │ │ └── utils.clj │ │ └── passes │ │ └── jvm │ │ ├── analyze_host_expr.clj │ │ ├── annotate_branch.clj │ │ ├── annotate_host_info.clj │ │ ├── annotate_loops.clj │ │ ├── annotate_tag.clj │ │ ├── box.clj │ │ ├── classify_invoke.clj │ │ ├── constant_lifter.clj │ │ ├── emit_form.clj │ │ ├── fix_case_test.clj │ │ ├── infer_tag.clj │ │ ├── validate.clj │ │ ├── validate_loop_locals.clj │ │ ├── validate_recur.clj │ │ └── warn_on_reflection.clj │ ├── dep3 │ └── clojure │ │ └── core │ │ └── memoize.clj │ ├── dep4 │ └── clojure │ │ └── core │ │ ├── cache.clj │ │ └── cache │ │ └── wrapped.clj │ ├── dep5 │ └── clojure │ │ └── data │ │ └── priority_map.clj │ └── dep9 │ └── clojure │ └── tools │ ├── namespace.clj │ └── namespace │ ├── dependency.clj │ ├── dir.clj │ ├── file.clj │ ├── find.clj │ ├── move.clj │ ├── parse.clj │ ├── reload.clj │ ├── repl.clj │ └── track.clj ├── copy-deps-scripts ├── README.md ├── clone.sh └── dolly │ └── dolly.clj ├── deps.edn ├── doc ├── Clint_Eastwood_-_1960s_small.jpg └── README-creating-new-release.md ├── lein-eastwood └── leiningen │ └── eastwood.clj ├── project.clj ├── resource ├── eastwood │ └── config │ │ ├── clojure-contrib.clj │ │ ├── clojure.clj │ │ └── third-party-libs.clj ├── jvm-method-info.edn └── var-info.edn ├── resources └── EASTWOOD_VERSION ├── src └── eastwood │ ├── analyze_ns.clj │ ├── error_messages.clj │ ├── exit.clj │ ├── lint.clj │ ├── linters │ ├── boxed_math.clj │ ├── deprecated.clj │ ├── implicit_dependencies.clj │ ├── misc.clj │ ├── performance.clj │ ├── reflection.clj │ ├── typetags.clj │ ├── typos.clj │ └── unused.clj │ ├── passes.clj │ ├── reporting_callbacks.clj │ ├── util.clj │ ├── util │ └── ns.clj │ ├── version.clj │ └── versioncheck.clj ├── test-resources ├── eastwood │ ├── config │ │ ├── disable_date_deprecation.clj │ │ ├── disable_def_in_def.clj │ │ ├── disable_def_in_def_unrelated.clj │ │ ├── disable_unused_meta_on_macro.clj │ │ ├── disable_unused_meta_on_macro_unrelated.clj │ │ ├── disable_wrong_tag.clj │ │ ├── disable_wrong_tag_unrelated.clj │ │ └── linter_executor.clj │ └── test │ │ ├── linter_executor │ │ └── sample_ns.clj │ │ └── outside_test_paths │ │ ├── defn_reflection_warning.clj │ │ ├── example.clj │ │ └── reflection_warning.clj └── invalid │ └── syntax.clj ├── test-third-party-deps ├── eastwood │ └── third_party_deps_test.clj └── testcases │ ├── core_async_example.clj │ ├── core_async_example │ ├── alt.clj │ ├── assert.clj │ └── misc.clj │ ├── jdbc_example.clj │ ├── manifold_example.clj │ ├── spec_tools_example.clj │ ├── speced_def_example.clj │ ├── timbre_example.clj │ └── tufte_example.clj ├── test └── eastwood │ ├── analyze_ns_test.clj │ ├── lint_test.clj │ ├── linter_executor_test.clj │ ├── linters │ └── typos_test.clj │ ├── test │ ├── analyze_ns_test.clj │ ├── cc_compare.clj │ ├── implicit_dependencies_linter_test.clj │ └── linters_test.clj │ └── util_test.clj └── var-info-test └── eastwood └── var_info_test.clj /.circleci/crucible.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Euxo pipefail 3 | 4 | lein with-profile -user,-dev,+eastwood-plugin install || exit 1 5 | 6 | cd .circleci || exit 1 7 | 8 | git submodule update --init --recursive 9 | 10 | # Exercise core.async because it's been historically a difficult-to-analyse one 11 | cd ./core.async || exit 1 12 | 13 | if lein with-profile -user,+test"$EXTRA_PROFILE" update-in :plugins conj "[jonase/eastwood \"RELEASE\"]" -- eastwood | tee output; then 14 | echo "Should have failed! Emitted output:" 15 | cat output 16 | exit 1 17 | fi 18 | 19 | if grep --silent "Reflection warning" output; then 20 | echo "Reflection warnings should have been detected as an Eastwood linter and not as mere stdout!" 21 | exit 1 22 | fi 23 | 24 | grep --silent "== Warnings: 29. Exceptions thrown: 0" output || exit 1 25 | 26 | # Exercise tools.reader, see https://github.com/jonase/eastwood/issues/413 27 | cd ../tools.reader || exit 1 28 | 29 | if lein with-profile -user,+test"$EXTRA_PROFILE" update-in :plugins conj "[jonase/eastwood \"RELEASE\"]" -- eastwood | tee output; then 30 | echo "Should have failed! Emitted output:" 31 | cat output 32 | exit 1 33 | fi 34 | 35 | if grep --silent "Reflection warning" output; then 36 | echo "Reflection warnings should have been detected as an Eastwood linter and not as mere stdout!" 37 | exit 1 38 | fi 39 | 40 | grep --silent "== Warnings: 14. Exceptions thrown: 0" output || exit 1 41 | 42 | # Exercise clojurescript as it's large and interesting 43 | cd ../clojurescript || exit 1 44 | 45 | if lein with-profile -user,+test"$EXTRA_PROFILE" update-in :plugins conj "[jonase/eastwood \"RELEASE\"]" -- eastwood | tee output; then 46 | echo "Should have failed! Emitted output:" 47 | cat output 48 | exit 1 49 | fi 50 | 51 | if grep --silent "Reflection warning" output; then 52 | echo "Reflection warnings should have been detected as an Eastwood linter and not as mere stdout!" 53 | exit 1 54 | fi 55 | 56 | # (Warnings vary per JDK) 57 | grep --silent "== Warnings: 339. Exceptions thrown: 0" output || grep --silent "== Warnings: 338. Exceptions thrown: 0" output || grep --silent "== Warnings: 337. Exceptions thrown: 0" output || exit 1 58 | 59 | # Exercise malli because Eastwood used to choke on that project due to lack of explicit topo order for ns analysis 60 | cd ../malli || exit 1 61 | 62 | cp ../deps_project.clj project.clj 63 | 64 | if lein with-profile -user,+test"$EXTRA_PROFILE" update-in :plugins conj "[jonase/eastwood \"RELEASE\"]" -- eastwood | tee output; then 65 | echo "Should have failed! Emitted output:" 66 | cat output 67 | exit 1 68 | fi 69 | 70 | if grep --silent "Reflection warning" output; then 71 | echo "Reflection warnings should have been detected as an Eastwood linter and not as mere stdout!" 72 | exit 1 73 | fi 74 | 75 | grep --silent "== Warnings: 111. Exceptions thrown: 0" output || exit 1 76 | 77 | # Exercise crux simply because it's a large project with plenty of Java hints, interop etc 78 | cd ../crux || exit 1 79 | ./lein-sub install 80 | ./lein-sub with-profile -user,+test"$EXTRA_PROFILE" update-in :dependencies conj "[jonase/eastwood \"RELEASE\"]" -- run -m eastwood.lint "{:source-paths [\"src\"], :test-paths [\"test\"], :forced-exit-code 0}" | tee output 81 | 82 | # Assert that no exceptions are thrown, and that useful insights are found: 83 | ex_marker="Exceptions thrown:" 84 | 85 | if grep --silent "$ex_marker [1-9]" output; then 86 | echo "Should not have thrown an exception!" 87 | exit 1 88 | fi 89 | 90 | if grep --silent "Reflection warning" output; then 91 | echo "Reflection warnings should have been detected as an Eastwood linter and not as mere stdout!" 92 | exit 1 93 | fi 94 | 95 | grep --silent "Warnings: 3. $ex_marker 0" output || exit 1 96 | grep --silent "Warnings: 4. $ex_marker 0" output || exit 1 97 | grep --silent "Warnings: 10. $ex_marker 0" output || exit 1 98 | grep --silent "Warnings: 16. $ex_marker 0" output || exit 1 99 | grep --silent "Warnings: 36. $ex_marker 0" output || exit 1 100 | grep --silent "Warnings: 43. $ex_marker 0" output || exit 1 101 | 102 | zero_warns=$(grep -c "Warnings: 0. $ex_marker 0" output) 103 | zero_warns=${zero_warns// /} 104 | # Result depends on the JDK: 105 | if [ "$zero_warns" != "11" ] && [ "$zero_warns" != "10" ]; then 106 | exit 1 107 | fi 108 | 109 | one_warns=$(grep -c "Warnings: 1. $ex_marker 0" output) 110 | one_warns=${one_warns// /} 111 | # Result depends on the JDK: 112 | if [ "$one_warns" != "1" ] && [ "$one_warns" != "2" ]; then 113 | exit 1 114 | fi 115 | 116 | exit 0 117 | -------------------------------------------------------------------------------- /.circleci/crucible_prep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuxo pipefail 3 | 4 | mkdir -p ~/.lein 5 | 6 | echo '{:eastwood-ci-clojure-1-10 {:dependencies [[org.clojure/clojure "1.10.3"]]} :eastwood-ci-clojure-1-11 {:dependencies [[org.clojure/clojure "1.11.0-alpha3"], [org.clojure/spec.alpha "0.3.214"]]}}' > ~/.lein/profiles.clj 7 | -------------------------------------------------------------------------------- /.circleci/example-green-project/deps.edn: -------------------------------------------------------------------------------- 1 | {:aliases {:eastwood {:main-opts ["-m" "eastwood.lint" {}] 2 | :extra-deps {jonase/eastwood {:mvn/version "RELEASE"} 3 | org.clojure/clojure {:mvn/version "1.10.3"}}}}} 4 | -------------------------------------------------------------------------------- /.circleci/example-green-project/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example-project "n/a" 2 | :dependencies [[jonase/eastwood "RELEASE"] 3 | [org.clojure/clojure "1.10.3"]] 4 | :plugins [[jonase/eastwood "RELEASE"]]) 5 | -------------------------------------------------------------------------------- /.circleci/example-green-project/src/example_project/core.clj: -------------------------------------------------------------------------------- 1 | (ns example-project.core) 2 | -------------------------------------------------------------------------------- /.circleci/example-red-project/deps.edn: -------------------------------------------------------------------------------- 1 | {:aliases {:eastwood {:main-opts ["-m" "eastwood.lint" {}] 2 | :extra-deps {jonase/eastwood {:mvn/version "RELEASE"} 3 | org.clojure/clojure {:mvn/version "1.10.3"}}}}} 4 | -------------------------------------------------------------------------------- /.circleci/example-red-project/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example-project "n/a" 2 | :dependencies [[jonase/eastwood "RELEASE"] 3 | [org.clojure/clojure "1.10.3"]] 4 | :plugins [[jonase/eastwood "RELEASE"]]) 5 | -------------------------------------------------------------------------------- /.circleci/example-red-project/src/example_project/core.clj: -------------------------------------------------------------------------------- 1 | (ns example-project.core) 2 | 3 | clojure.set/difference 4 | -------------------------------------------------------------------------------- /.circleci/example-refresh-project/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/tools.namespace {:mvn/version "1.1.1"}} 2 | :paths ["src" "red"] 3 | :aliases {:eastwood {:main-opts ["-m" "eastwood.lint" 4 | ;; no red: 5 | ;; red will be ignored because it's not set passed as Eastwood config/arguments, 6 | ;; so its reflection warning won't show up in Eastwood output. 7 | {:source-paths ["src"]}] 8 | :extra-deps {jonase/eastwood {:mvn/version "RELEASE"} 9 | org.clojure/clojure {:mvn/version "1.10.3"}}}}} 10 | -------------------------------------------------------------------------------- /.circleci/example-refresh-project/project.clj: -------------------------------------------------------------------------------- 1 | (defproject example-project "n/a" 2 | :source-paths ["src" "red"] 3 | :dependencies [[jonase/eastwood "RELEASE"] 4 | [org.clojure/clojure "1.10.3"] 5 | [org.clojure/tools.namespace "1.1.1"]] 6 | :plugins [[jonase/eastwood "RELEASE"]] 7 | ;; no red: 8 | ;; red will be ignored because it's not set passed as Eastwood config/arguments, 9 | ;; so its reflection warning won't show up in Eastwood output. 10 | :eastwood {:source-paths ["src"]}) 11 | -------------------------------------------------------------------------------- /.circleci/example-refresh-project/red/red.clj: -------------------------------------------------------------------------------- 1 | (ns red) 2 | 3 | (defn reflection [x] 4 | (.method x)) 5 | -------------------------------------------------------------------------------- /.circleci/example-refresh-project/src/example_project/core.clj: -------------------------------------------------------------------------------- 1 | (ns example-project.core 2 | (:require 3 | [clojure.tools.namespace.repl :refer [set-refresh-dirs]])) 4 | 5 | (set-refresh-dirs "src" "red") 6 | -------------------------------------------------------------------------------- /.circleci/integration_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Euxo pipefail 3 | 4 | lein with-profile -user,-dev,+eastwood-plugin install || exit 1 5 | 6 | # Red project 7 | 8 | cd .circleci/example-red-project || exit 1 9 | 10 | rm -rf .eastwood* 11 | if lein with-profile -user eastwood > output; then 12 | echo "Should have failed! Emitted output:" 13 | cat output 14 | exit 1 15 | fi 16 | 17 | grep --silent "== Warnings: 1. Exceptions thrown: 0" output || exit 1 18 | 19 | rm -rf .eastwood* 20 | if lein with-profile -user run -m eastwood.lint > output; then 21 | echo "Should have failed! Emitted output:" 22 | cat output 23 | exit 1 24 | fi 25 | 26 | grep --silent "== Warnings: 1. Exceptions thrown: 0" output || exit 1 27 | 28 | rm -rf .eastwood* 29 | if clojure -M:eastwood > output; then 30 | echo "Should have failed! Emitted output:" 31 | cat output 32 | exit 1 33 | fi 34 | 35 | grep --silent "== Warnings: 1. Exceptions thrown: 0" output || exit 1 36 | 37 | # Green project 38 | 39 | cd ../example-green-project || exit 1 40 | 41 | rm -rf .eastwood* 42 | if ! lein with-profile -user eastwood > output; then 43 | echo "Should have passed!" 44 | exit 1 45 | fi 46 | 47 | grep --silent "== Warnings: 0. Exceptions thrown: 0" output || exit 1 48 | 49 | rm -rf .eastwood* 50 | if ! lein with-profile -user run -m eastwood.lint > output; then 51 | echo "Should have passed!" 52 | exit 1 53 | fi 54 | 55 | grep --silent "== Warnings: 0. Exceptions thrown: 0" output || exit 1 56 | 57 | rm -rf .eastwood* 58 | if ! clojure -M:eastwood > output; then 59 | echo "Should have passed!" 60 | exit 1 61 | fi 62 | 63 | grep --silent "== Warnings: 0. Exceptions thrown: 0" output || exit 1 64 | 65 | # Refresh project 66 | 67 | cd ../example-refresh-project || exit 1 68 | 69 | rm -rf .eastwood* 70 | if ! lein with-profile -user eastwood > output; then 71 | echo "Should have passed!" 72 | exit 1 73 | fi 74 | 75 | grep --silent "== Warnings: 0. Exceptions thrown: 0" output || exit 1 76 | 77 | rm -rf .eastwood* 78 | if ! clojure -M:eastwood > output; then 79 | echo "Should have passed!" 80 | exit 1 81 | fi 82 | 83 | grep --silent "== Warnings: 0. Exceptions thrown: 0" output || exit 1 84 | 85 | exit 0 86 | -------------------------------------------------------------------------------- /.circleci/lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuxo pipefail 3 | 4 | classpath="$(lein with-profile -user,+test classpath)" 5 | # populate a clj-kondo cache per https://github.com/clj-kondo/clj-kondo/tree/4f1252748b128da6ea23033f14b2bec8662dc5fd#project-setup : 6 | lein with-profile -user,+test,+clj-kondo run -m clj-kondo.main --lint "$classpath" --dependencies --parallel --copy-configs 7 | lein with-profile -user,+test,+clj-kondo run -m clj-kondo.main --lint src test lein-eastwood var-info-test clojure-core-test 8 | 9 | lein with-profile -user,-dev,+antq,+clj-kondo trampoline antq 10 | lein with-profile -user,-dev,+antq,+check-var-info trampoline antq 11 | 12 | NVD_SCRIPT=".circleci/nvd/run.sh" 13 | shellcheck $NVD_SCRIPT 14 | $NVD_SCRIPT 15 | -------------------------------------------------------------------------------- /.circleci/maybe_deploy.clj: -------------------------------------------------------------------------------- 1 | (ns maybe-deploy 2 | (:require 3 | [clojure.java.shell :refer [sh]] 4 | [clojure.string :as str])) 5 | 6 | (def release-marker "v") 7 | 8 | (defn make-version [tag] 9 | (str/replace-first tag release-marker "")) 10 | 11 | (defn log-result [m] 12 | (println m) 13 | m) 14 | 15 | (defn -main [& _] 16 | (let [tag (System/getenv "CIRCLE_TAG")] 17 | (if-not tag 18 | (System/exit 1) 19 | (if-not (re-find (re-pattern release-marker) tag) 20 | (System/exit 1) 21 | (do 22 | (apply println "Executing" *command-line-args*) 23 | (->> [:env (into {"PROJECT_VERSION" (make-version tag)} (System/getenv))] 24 | (into (vec *command-line-args*)) 25 | (apply sh) 26 | log-result 27 | :exit 28 | (System/exit))))))) 29 | -------------------------------------------------------------------------------- /.circleci/nvd/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths [] 2 | :deps {} 3 | 4 | :aliases 5 | {:nvd 6 | ;; if updating this, please make sure - ~/.m2/repository/org/owasp/dependency-check-utils//data remains valid in our CircleCI file: 7 | {:extra-deps {nvd-clojure/nvd-clojure {:mvn/version "3.2.0"}} 8 | :jvm-opts ["-Dclojure.main.report=stderr"] 9 | :main-opts ["-m" "nvd.task.check"]}}} 10 | -------------------------------------------------------------------------------- /.circleci/nvd/nvd-config.edn: -------------------------------------------------------------------------------- 1 | {:suppression-file "nvd_suppressions.xml"} 2 | -------------------------------------------------------------------------------- /.circleci/nvd/nvd_suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.circleci/nvd/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuxo pipefail 3 | 4 | NVD_DIR="$PWD/.circleci/nvd" 5 | # calculate a production classpath within the main project: 6 | CLASSPATH="$(LEIN_SILENT=true lein with-profile -user,-dev classpath)" 7 | 8 | DEPENDENCY_CHECK_COMMAND="clojure -M:nvd $NVD_DIR/nvd-config.edn $CLASSPATH" 9 | 10 | # cd into a different project defining its own deps.edn: 11 | cd "$NVD_DIR" || exit 1 12 | 13 | # Try a few times in face of flakiness: 14 | eval "$DEPENDENCY_CHECK_COMMAND" || eval "$DEPENDENCY_CHECK_COMMAND" || eval "$DEPENDENCY_CHECK_COMMAND" 15 | -------------------------------------------------------------------------------- /.circleci/reflection-example/project.clj: -------------------------------------------------------------------------------- 1 | (defproject reflection-example "0.1.0" 2 | :description "Exercises reflection warnings coming from third-party macros located in .jars" 3 | :dependencies [[org.clojure/clojure "1.10.3"]]) 4 | -------------------------------------------------------------------------------- /.circleci/reflection-example/src/reflection_example/core.clj: -------------------------------------------------------------------------------- 1 | (ns reflection-example.core) 2 | 3 | (defn- omg [x] 4 | (.toString x)) 5 | 6 | (omg 2) 7 | (omg "") 8 | 9 | (defn boo [] 10 | (omg 2) 11 | (omg "")) 12 | 13 | (defmacro foo [x] 14 | `(.thing ~x)) 15 | 16 | (defn bar [x] 17 | (.refl x)) 18 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:linters {:docstring-leading-trailing-whitespace {:level :warning} 2 | :reduce-without-init {:level :warning} 3 | :single-key-in {:level :warning} 4 | :used-underscored-binding {:level :warning}}} 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Replace this placeholder text with a summary of the changes in your PR. 2 | The more detailed you are, the better.** 3 | 4 | ----------------- 5 | 6 | Before submitting the PR make sure the following things have been done (and denote this 7 | by checking the relevant checkboxes): 8 | 9 | 10 | - [ ] You've updated the [changelog](../blob/master/changes.md) (if adding/changing user-visible functionality) 11 | - [ ] You've updated the [readme](../blob/master/README.md) (if eg adding a new linter) 12 | 13 | Thanks! 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | /checkouts/ 6 | .lein-failures 7 | .lein-deps-sum 8 | .lein-repl-history 9 | *~ 10 | /target/ 11 | /copy-deps-scripts/repos/ 12 | /.circleci/example-red-project/output 13 | /.circleci/example-green-project/output 14 | /.circleci/example-refresh-project/output 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".circleci/core.async"] 2 | path = .circleci/core.async 3 | url = git@github.com:clojure/core.async.git 4 | [submodule ".circleci/crux"] 5 | path = .circleci/crux 6 | url = git@github.com:juxt/crux.git 7 | [submodule ".circleci/malli"] 8 | path = .circleci/malli 9 | url = git@github.com:metosin/malli.git 10 | [submodule ".circleci/clojurescript"] 11 | path = .circleci/clojurescript 12 | url = git@github.com:clojure/clojurescript.git 13 | [submodule ".circleci/tools.reader"] 14 | path = .circleci/tools.reader 15 | url = git@github.com:clojure/tools.reader.git 16 | -------------------------------------------------------------------------------- /cases/testcases/analyzererror.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.analyzererror) 2 | 3 | ;;This will cause an exception to be thrown when this file is analyzed. 4 | no.such.java.class 5 | -------------------------------------------------------------------------------- /cases/testcases/are_true/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.are-true.green 2 | (:require 3 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]])) 4 | 5 | (deftest foo 6 | (are [a b expected] (testing [a b] 7 | (is (= expected 8 | (+ a b))) 9 | true) 10 | 1 2 3)) 11 | 12 | (deftest bar 13 | (are [a b expected] (do 14 | (is (= expected 15 | (+ a b))) 16 | true) 17 | 1 2 3)) 18 | 19 | (deftest baz 20 | (are [a b expected] (let [x [a b]] 21 | (is (= expected 22 | (+ a b)) 23 | (pr-str x)) 24 | true) 25 | 1 2 3)) 26 | -------------------------------------------------------------------------------- /cases/testcases/are_true/red_one.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.are-true.red-one 2 | (:require 3 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]])) 4 | 5 | (deftest foo 6 | (are [a b expected] true 7 | 1 2 3)) 8 | -------------------------------------------------------------------------------- /cases/testcases/are_true/red_three.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.are-true.red-three 2 | "Uses `::true` instead of `true` (the `:qualifier` value) so this corpus will fail Eastwood." 3 | (:require 4 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]])) 5 | 6 | (deftest foo 7 | (are [a b expected] (testing [a b] 8 | (is (= expected 9 | (+ a b))) 10 | ::true) 11 | 1 2 3)) 12 | 13 | (deftest bar 14 | (are [a b expected] (do 15 | (is (= expected 16 | (+ a b))) 17 | ::true) 18 | 1 2 3)) 19 | 20 | (deftest baz 21 | (are [a b expected] (let [x [a b]] 22 | (is (= expected 23 | (+ a b)) 24 | (pr-str x)) 25 | ::true) 26 | 1 2 3)) 27 | -------------------------------------------------------------------------------- /cases/testcases/are_true/red_two.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.are-true.red-two 2 | (:require 3 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]])) 4 | 5 | (deftest foo 6 | (are [a b expected] (do 7 | (is true) 8 | true) 9 | 1 2 3)) 10 | -------------------------------------------------------------------------------- /cases/testcases/arglists.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.arglists 2 | (:require [clojure.test :refer :all])) 3 | 4 | 5 | (defn fn-no-arglists1 6 | [name] 7 | :fn-no-arglists1) 8 | 9 | (defn fn-with-arglists1 10 | {:arglists '([name expr])} 11 | [name] 12 | :fn-with-arglists1) 13 | 14 | (defn fn-no-arglists2 15 | [name & sigs] 16 | :fn-no-arglists2) 17 | 18 | (defn fn-no-arglists3 19 | ([a1] :fn-no-arglists3-1) 20 | ([a1 a2 a3] :fn-no-arglists3-2)) 21 | 22 | (defn fn-with-arglists3 23 | {:arglists '([x y] [x y z w])} 24 | ([a1] :fn-no-arglists3-1) 25 | ([a1 a2 a3] :fn-no-arglists3-2)) 26 | 27 | (defn fn-no-arglists4 28 | [name & sigs] 29 | :fn-no-arglists1) 30 | 31 | (defn fn-with-arglists4 32 | {:arglists '([name expr])} 33 | [name & sigs] 34 | :fn-with-arglists1) 35 | 36 | (defmacro macro-no-arglists1 37 | [name & sigs] 38 | (let [[name [expr]] (cons name sigs)] 39 | `(def ~name ~expr))) 40 | 41 | (defmacro macro-with-arglists1 42 | {:arglists '([name expr])} 43 | [name & sigs] 44 | (let [[name [expr]] (cons name sigs)] 45 | `(def ~name ~expr))) 46 | 47 | ;; This is adapted from a macro named with-replies* in the 48 | ;; carmine library. It has given warnings at one time with Eastwood, 49 | ;; but I think it should not. 50 | 51 | (defmacro macro-with-arglists-no-warn1 52 | {:arglists '([:as-pipeline & body] [& body])} 53 | [& [s1 & sn :as sigs]] 54 | (let [as-pipeline? (identical? s1 :as-pipeline) 55 | body (if as-pipeline? sn sigs)] 56 | `(do ~@body (do :get-replies ~as-pipeline?)))) 57 | 58 | ;; Adapted from a macro named fnm in the core.logic library. I 59 | ;; think it should not give any warnings, because the developer-supplied 60 | ;; :arglists is more restrictive than the actual arg vector(s) allow. 61 | 62 | (defmacro macro-with-arglists-no-warn2 63 | {:arglists '([t as tabled? & cs])} 64 | [t as & cs] 65 | :macro-with-arglists-no-warn2) 66 | -------------------------------------------------------------------------------- /cases/testcases/boxed_math/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.boxed-math.green) 2 | 3 | (defn foo [] 4 | (let [a (long 2) 5 | b (long 4)] 6 | (+ a b))) 7 | -------------------------------------------------------------------------------- /cases/testcases/boxed_math/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.boxed-math.red) 2 | 3 | (defn sum-squares [a b] 4 | (+ (* a a) (* b b))) 5 | -------------------------------------------------------------------------------- /cases/testcases/byte_array/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.byte-array.green 2 | "https://github.com/jonase/eastwood/issues/188") 3 | 4 | (def class-byte-array (.getClass (byte-array 0))) 5 | -------------------------------------------------------------------------------- /cases/testcases/bytes_class/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.bytes-class.green) 2 | 3 | (defprotocol ToBytes 4 | (to-bytes [x])) 5 | 6 | (extend-protocol ToBytes 7 | (Class/forName "[B") 8 | (to-bytes [x] x)) 9 | 10 | ;; ensure that the construct in fact works (else we could be linting something that doesn't work in the first place): 11 | (assert (-> "" .getBytes to-bytes)) 12 | -------------------------------------------------------------------------------- /cases/testcases/bytes_class/green2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.bytes-class.green2) 2 | 3 | (defprotocol ToBytes 4 | (to-bytes [x])) 5 | 6 | ;; An usage in the wild: git.io/JnMOT 7 | (extend-protocol ToBytes 8 | (class (byte-array 0)) 9 | (to-bytes [x] x)) 10 | 11 | ;; ensure that the construct in fact works (else we could be linting something that doesn't work in the first place): 12 | (assert (-> "" .getBytes to-bytes)) 13 | -------------------------------------------------------------------------------- /cases/testcases/clojure_1_11.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.clojure-1-11 2 | "A namespace that exercises all new features in Clojure 1.11.0." 3 | (:require 4 | [clojure.math :as math] 5 | [clojure.test :refer [deftest is]] 6 | [totally-does-not-exist :as-alias does-not-exist])) 7 | 8 | ;; https://clojure.org/news/2021/03/18/apis-serving-people-and-programs 9 | (defn destr [& {:keys [a b] :as opts}] 10 | [a b opts]) 11 | 12 | (defn uses-destr [] 13 | [(destr :a 1) 14 | (destr {:a 1 :b 2})]) 15 | 16 | (defn uses-non-existing [] 17 | ::does-not-exist/foo) 18 | 19 | (defn uses-misc [] 20 | [(abs -1) 21 | (random-uuid) 22 | (update-keys (fn [v] 23 | (+ v v)) 24 | {1 2 25 | 3 4}) 26 | (update-vals (fn [v] 27 | (+ v v)) 28 | {1 2 29 | 3 4}) 30 | (parse-long "1") 31 | (parse-double "1.1") 32 | (parse-uuid "fail") 33 | (parse-uuid "true") 34 | (NaN? :not-a-nan) 35 | (infinite? 42) 36 | (math/random) 37 | (math/round 2.4)]) 38 | 39 | (deftest test-iteration 40 | ;; equivalence to line-seq 41 | (let [readme #(java.nio.file.Files/newBufferedReader (.toPath (java.io.File. "project.clj")))] 42 | (is (= (with-open [^java.io.BufferedReader r (readme)] 43 | (vec (iteration (fn [_] (.readLine r))))) 44 | (with-open [^java.io.BufferedReader r (readme)] 45 | (doall (line-seq r)))))) 46 | 47 | ;; paginated API 48 | (let [items 12 pgsize 5 49 | src (vec (repeatedly items #(java.util.UUID/randomUUID))) 50 | api (fn [tok] 51 | (let [tok (or tok 0)] 52 | (when (< tok items) 53 | {:tok (+ tok pgsize) 54 | :ret (subvec src tok (min (+ tok pgsize) items))})))] 55 | (is (= src 56 | (mapcat identity (iteration api :kf :tok :vf :ret)) 57 | (into [] cat (iteration api :kf :tok :vf :ret))))) 58 | 59 | (let [src [:a :b :c :d :e] 60 | api (fn [k] 61 | (let [k (or k 0)] 62 | (if (< k (count src)) 63 | {:item (nth src k) 64 | :k (inc k)})))] 65 | (is (= [:a :b :c] 66 | (vec (iteration api 67 | :some? (comp #{:a :b :c} :item) 68 | :kf :k 69 | :vf :item)) 70 | (vec (iteration api 71 | :kf #(some-> % :k #{0 1 2}) 72 | :vf :item)))))) 73 | -------------------------------------------------------------------------------- /cases/testcases/clojure_test.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.clojure-test 2 | (:require 3 | [clojure.test :refer [are assert-expr deftest do-report is join-fixtures testing use-fixtures]])) 4 | 5 | (defmethod assert-expr 'truthy? 6 | [msg [pred v]] 7 | `(do-report (if ~v 8 | {:type :pass, :message ~msg} 9 | {:type :fail, :message ~msg}))) 10 | 11 | (defn throws-exception [] 12 | (throw (ex-info "" {}))) 13 | 14 | (defn func [f] "") 15 | 16 | ;; https://github.com/jonase/eastwood/issues/116 17 | (deftest exercises-thrown 18 | (is (thrown? Exception (throws-exception)))) 19 | 20 | ;; https://github.com/jonase/eastwood/issues/313 21 | (deftest exercises-truthy 22 | (is (truthy? 42))) 23 | 24 | ;; https://github.com/jonase/eastwood/issues/207 25 | (deftest b-test 26 | (is (instance? String (func +)))) 27 | 28 | ;; https://github.com/jonase/eastwood/issues/206 29 | (deftest a-test-singular 30 | ;; a test with a single `is` 31 | (is (thrown? Exception (compare [] 1)))) 32 | 33 | ;; https://github.com/jonase/eastwood/issues/206 34 | (deftest a-test-plural 35 | ;; a test with multiple `is`es 36 | (is (thrown? Exception (compare [] 1))) 37 | (is (thrown? Exception (+ 2 3))) 38 | (is (thrown? Exception (Long/parseLong "1")))) 39 | 40 | ;; https://github.com/jonase/eastwood/issues/206 41 | (deftest a-test-are-1 42 | (are [ex] (thrown? ex (+ 1 2)) 43 | Exception 44 | Error)) 45 | 46 | ;; https://github.com/jonase/eastwood/issues/206 47 | (deftest a-test-are-2 48 | (are [op] (thrown? Exception op) 49 | (+ 3 2) 50 | (Long/parseLong "31") 51 | (compare [] 1))) 52 | 53 | ;; `=` within `testing` within `are`, at least as shown in this example, is correct. 54 | ;; Eastwood should not trigger a warning for it: 55 | (deftest are-with-testing 56 | (are [f] (testing f 57 | (= "1" (f 1))) 58 | str 59 | pr-str)) 60 | -------------------------------------------------------------------------------- /cases/testcases/clojure_test/are/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.clojure-test.are.red 2 | (:require 3 | [clojure.test :refer [are deftest testing]])) 4 | 5 | (deftest are-with-testing 6 | (are [f] (testing f 7 | ;; should trigger a warning: 8 | (= "3" (f 1)) 9 | ;; should trigger a warning: 10 | (= "2" (f 1)) 11 | ;; should not trigger a warning (since it's located at tail position): 12 | (= "1" (f 1))) 13 | str 14 | pr-str)) 15 | -------------------------------------------------------------------------------- /cases/testcases/cond/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.cond.green) 2 | 3 | (defn foo [] 4 | ;; https://github.com/jonase/eastwood/issues/169 5 | (cond-> {} 6 | true (assoc 1 1)) 7 | 8 | (cond 9 | (< (rand) 0.5) 10 | 1 11 | 12 | ;; https://github.com/jonase/eastwood/issues/169#issuecomment-328940985 13 | true 14 | false) 15 | 16 | (cond 17 | (< (rand) 0.5) 18 | 1 19 | 20 | ;; https://github.com/jonase/eastwood/issues/169#issuecomment-144686370 21 | :default 22 | false)) 23 | -------------------------------------------------------------------------------- /cases/testcases/const.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.const 2 | "Exercises https://github.com/jonase/eastwood/issues/341") 3 | 4 | ;; Makes this test case more deterministic when re-running the associated `deftest` from a repl: 5 | (ns-unmap (-> ::_ namespace symbol the-ns) 6 | 'balanced?) 7 | 8 | (defn ^:const balanced? 9 | "Returns whether brackets contained in expr are balanced" 10 | ([expr] (balanced? expr 0)) 11 | ([[x & xs] count] 12 | (cond (neg? count) false 13 | (nil? x) (zero? count) 14 | (= x \[) (recur xs (inc count)) 15 | (= x \]) (recur xs (dec count)) 16 | :else (recur xs count)))) 17 | -------------------------------------------------------------------------------- /cases/testcases/const/unused_namespaces/consumer.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.const.unused-namespaces.consumer 2 | "https://github.com/jonase/eastwood/issues/192" 3 | (:require [testcases.const.unused-namespaces.producer :as a])) 4 | 5 | (defn project-info [] 6 | {:version a/version}) 7 | -------------------------------------------------------------------------------- /cases/testcases/const/unused_namespaces/producer.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.const.unused-namespaces.producer 2 | "https://github.com/jonase/eastwood/issues/192") 3 | 4 | (def ^:const version "1.0.0") 5 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/if_some/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.if-some.green) 2 | 3 | (if-some [v (when (< (rand) 0.5) 4 | 2)] 5 | v 6 | :whoops) 7 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/if_some/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.if-some.red) 2 | 3 | (if-some [v nil] 4 | v 5 | :whoops) 6 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/some_thread_first/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.some-thread-first.green) 2 | 3 | (some-> (symbol "clojure.tools.namespace.repl" "refresh-dirs") 4 | resolve 5 | deref) 6 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/some_thread_last/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.some-thread-last.green) 2 | 3 | (some->> ["https.proxyPort" "http.proxyPort"] 4 | (some #(System/getProperty %)) 5 | Integer/parseInt) 6 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/spec_every/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.spec-every.green 2 | (:require 3 | [clojure.spec.gen.alpha :as gen] 4 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]] 5 | [clojure.spec.alpha :as spec])) 6 | 7 | ;; https://github.com/jonase/eastwood/issues/435 8 | (spec/def ::some-spec (spec/map-of 9 | (spec/spec string? 10 | :gen (fn [] (gen/fmap 11 | #(apply str %) 12 | (gen/vector 13 | gen/char-alpha 14 | 5 20)))) 15 | (spec/coll-of 16 | (spec/tuple ::source ::sub-type) 17 | :into #{} 18 | :min-count 2 19 | :gen-max 7) 20 | :min-count 2 21 | :gen-max 8)) 22 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/when_some/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.when-some.green) 2 | 3 | (when-some [v (when (< (rand) 0.5) 4 | 2)] 5 | v) 6 | -------------------------------------------------------------------------------- /cases/testcases/constant_test/when_some/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.constant-test.when-some.red) 2 | 3 | (when-some [v nil] 4 | v) 5 | -------------------------------------------------------------------------------- /cases/testcases/data_readers.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.data-readers) 2 | 3 | (println #my-int "42") 4 | -------------------------------------------------------------------------------- /cases/testcases/def_in_def/red1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.def-in-def.red1) 2 | 3 | (def foo 4 | (let [x 1] 5 | (def y x) 6 | x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/def_in_def/red2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.def-in-def.red2) 2 | 3 | (defn foo [x] 4 | (def bar x) 5 | x) 6 | -------------------------------------------------------------------------------- /cases/testcases/def_in_def/red3.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.def-in-def.red3) 2 | 3 | (defmacro my-macro [& body] 4 | `(do 5 | ~@body)) 6 | 7 | (my-macro (def foo 8 | (let [x 1] 9 | (def bar x) 10 | x))) 11 | -------------------------------------------------------------------------------- /cases/testcases/deprecated.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecated 2 | (:import java.util.Date 3 | java.awt.Frame)) 4 | 5 | (defn deprecated-stuff [] 6 | ;; Constructor 7 | (Date. 2013 3 12) 8 | ;; Static Field 9 | Frame/TEXT_CURSOR 10 | ;; Method 11 | (.getMonth (Date.)) 12 | ;; Var 13 | (replicate 1 nil)) 14 | -------------------------------------------------------------------------------- /cases/testcases/deprecations/overloading/green1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecations.overloading.green1 2 | (:import 3 | (java.util Date))) 4 | 5 | (defn expiry-date [] 6 | (-> (Date.) .getTime inc Date.)) 7 | -------------------------------------------------------------------------------- /cases/testcases/deprecations/overloading/green2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecations.overloading.green2 2 | (:import 3 | (java.util Date))) 4 | 5 | (defn expiry-date [] 6 | (Date. 0)) 7 | -------------------------------------------------------------------------------- /cases/testcases/deprecations/overloading/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecations.overloading.red 2 | (:import 3 | (java.util Date))) 4 | 5 | (defn expiry-date [] 6 | (Date. "12/12/1999")) 7 | -------------------------------------------------------------------------------- /cases/testcases/deprecations/own_ns/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecations.own-ns.green) 2 | 3 | (defn ^:deprecated foo []) 4 | 5 | ;; https://github.com/jonase/eastwood/issues/402 6 | (defn bar [] 7 | (foo)) 8 | -------------------------------------------------------------------------------- /cases/testcases/deprecations/own_ns/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.deprecations.own-ns.red 2 | (:require 3 | [testcases.deprecations.own-ns.green :as green])) 4 | 5 | (defn foo [] 6 | (green/foo)) 7 | -------------------------------------------------------------------------------- /cases/testcases/duplicateparams2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.duplicateparams2) 2 | 3 | 4 | ;; foo8 gives an error when attempting to compile it with Clojure 5 | ;; 1.8.0 or earlier. It is OK with Clojure 1.9.0. 6 | 7 | (defn foo8 [{:my.ns/keys [a b] :other.ns/syms [a c]}] 8 | [a b c]) 9 | 10 | ;; user=> (foo8 {:my.ns/a 1 :my.ns/b 2 'other.ns/a 4 'other.ns/c 3}) 11 | ;; [4 2 3] 12 | -------------------------------------------------------------------------------- /cases/testcases/dynamic_earmuffs/no_dynamic/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.dynamic-earmuffs.no-dynamic.green) 2 | 3 | (def foo) 4 | -------------------------------------------------------------------------------- /cases/testcases/dynamic_earmuffs/no_dynamic/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.dynamic-earmuffs.no-dynamic.red) 2 | 3 | (def *foo*) 4 | -------------------------------------------------------------------------------- /cases/testcases/dynamic_earmuffs/no_earmuffs/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.dynamic-earmuffs.no-earmuffs.green) 2 | 3 | (def foo) 4 | -------------------------------------------------------------------------------- /cases/testcases/dynamic_earmuffs/no_earmuffs/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.dynamic-earmuffs.no-earmuffs.red) 2 | 3 | (def ^:dynamic foo) 4 | -------------------------------------------------------------------------------- /cases/testcases/eastwood-testing-config.clj: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;; Just for Eastwood testing 3 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 4 | 5 | (disable-warning 6 | {:linter :wrong-arity 7 | :function-symbol 'testcases.f01/fn-with-arglists-meta 8 | :arglists-for-linting 9 | '([x y z]) 10 | :reason "Only for Eastwood testing"}) 11 | -------------------------------------------------------------------------------- /cases/testcases/f01.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f01 2 | (:use clojure.test)) 3 | 4 | (def i-am-redefd 1) 5 | (def i-am-redefd (fn [y] (inc y))) 6 | 7 | (defn foo1 8 | "Good doc string placement" 9 | [x] 10 | (dec x)) 11 | 12 | (defn foo2 13 | [x] 14 | "Bad doc string placement" 15 | (* x x)) 16 | 17 | (defn foo3 ; No doc string 18 | [x] 19 | (/ x 3)) 20 | 21 | (defn foo4 22 | ([x] 23 | ;; This should not be warned as a misplaced doc string, because 24 | ;; it is in return position. 25 | "string1") 26 | ([x y] 27 | (str x y)) 28 | ([] 29 | 10)) 30 | 31 | (defn foo5 32 | ([x y] 33 | (str x y)) 34 | ([] 35 | ;; This should be warned as a misplaced doc string, because it is 36 | ;; the first of more than one expression. 37 | "string3" 38 | 10)) 39 | 40 | ;; This test will not fail, because it will not be run, because the 41 | ;; same name will be redefined later. 42 | (deftest test1-redefd 43 | (is (= 4 5))) 44 | 45 | ;; ditto 46 | (deftest test1-redefd 47 | (is (= 0 1))) 48 | 49 | (deftest test1-redefd 50 | (is (= 1 1))) 51 | 52 | ;; declare's should not count as def's for the purposes of detecting 53 | ;; repeated def's. 54 | (declare i-am-not-redefd) 55 | 56 | (defn i-am-also-not-redefd [x] 57 | (println x "Hello, World!") 58 | (if (zero? x) 59 | (i-am-not-redefd x))) 60 | 61 | (defn i-am-not-redefd [y] 62 | (if (= y 5) 63 | (i-am-also-not-redefd (inc y)) 64 | (inc y))) 65 | 66 | 67 | ;; def's not at the top level are harder to notice for analysis tools, 68 | ;; if you don't plan for it in advance. 69 | 70 | (def i-am-redefd2 1) 71 | 72 | (let [x 5] 73 | (def i-am-redefd2 (fn [y] (+ x y)))) 74 | 75 | 76 | ;; :def-in-def test case 77 | 78 | (def def-in-def1 1) 79 | 80 | (defn bar [x] 81 | (let [foo-before def-in-def1] 82 | (def def-in-def1 (dec x)) 83 | {:x x 84 | :def-in-def1-before foo-before 85 | :def-in-def1-now def-in-def1})) 86 | 87 | 88 | ;; :wrong-arity test cases 89 | 90 | (defn call-with-wrong-num-of-args [x] 91 | (assoc x)) 92 | 93 | (defn call-with-wrong-num-of-args-2 [x] 94 | (assoc)) 95 | 96 | (defn call-local-fn-with-wrong-arity [] 97 | (let [f1 (fn [x] (inc x))] 98 | (f1))) 99 | 100 | (defn call-local-fn-with-wrong-arity2 [] 101 | (let [f1 #(inc %2)] 102 | (f1))) 103 | 104 | 105 | (defn catch 106 | "The Clojure compiler allows catch to be defined as a function and 107 | called, as long as you do it from outside of a try form. Test case 108 | for tools.analyzer, which at one time did not permit this. Zach 109 | Tellman is to be credited/blamed for this test case." 110 | ([x y] 111 | (catch x Throwable y)) 112 | ([x y z] 113 | [x y z])) 114 | 115 | 116 | (defn fn-with-arglists-meta 117 | {:arglists '([x y])} 118 | [x y z] 119 | (+ x y z)) 120 | 121 | 122 | ;; Eastwood can now detect the call to fn-with-arglists-meta below as 123 | ;; having the wrong number of arguments, if you add appropriate 124 | ;; configuration to override the :arglists metadata given above, which 125 | ;; makes it appear that it is called with the correct number of args 126 | ;; below. 127 | 128 | ;; This is a toy test example that I don't think anyone would create 129 | ;; in real development. It is only intended to verify that Eastwood 130 | ;; is actually using the configuration correctly. 131 | 132 | (defn wrong-args1 [x] 133 | (fn-with-arglists-meta x (* 2 x))) 134 | 135 | 136 | (defn wrong-args-threw-exception-before-bugfix1 [x] 137 | ((if x 138 | (fn [coll x] (conj coll x)) 139 | (fn [coll x] (disj coll x))) 140 | #{:a :b} :c :d)) 141 | 142 | 143 | (defn wrong-args-threw-exception-before-bugfix2 [x] 144 | ((do 145 | (println "foo") 146 | (fn [coll x] (disj coll x))) 147 | #{:a :b} :c :d)) 148 | 149 | 150 | (def ^:dynamic *var1* []) 151 | 152 | (def *var2* []) 153 | 154 | (def ^:dynamic var3 []) 155 | 156 | (def var4 []) 157 | -------------------------------------------------------------------------------- /cases/testcases/f02.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f02 2 | (:use clojure.test)) 3 | 4 | 5 | ;; defonce and defmulti currently require special handling in the 6 | ;; eastwood implementation to avoid :redefd-vars warnings. 7 | 8 | (defonce i-am-defonced-and-defmultid 'foo) 9 | 10 | (defmulti i-am-defonced-and-defmultid) 11 | 12 | (defonce i-am-defonced 7) 13 | 14 | (defmulti i-am-a-defmulti-done-once) 15 | 16 | (defmulti i-am-a-redefd-defmulti) 17 | 18 | (defmulti i-am-a-redefd-defmulti) 19 | 20 | (defonce i-am-outer-defonce-sym (defonce i-am-inner-defonce-sym 7)) 21 | 22 | 23 | ;; It seems defprotocol macroexpands into a do with quite a few 24 | ;; sequential forms, each with side effects. It is probably necessary 25 | ;; to analyze, and then eval, each of them one at a time, in order for 26 | ;; things to work. 27 | 28 | (defprotocol ProtocolNameNotRedefd 29 | "This protocol is only defined once." 30 | (as-file [x] "Coerce argument to a file.")) 31 | 32 | 33 | ;; This used to cause a problem for tools.analyzer, but it has since 34 | ;; been filed as ticket TANAL-6 and fixed. 35 | 36 | (defn pprint-matrix [] 37 | (doseq [[i row] (map (fn [i] [i 1]) [5 6 7])] 38 | (doseq [p (take i (range))] 39 | (print p row)) 40 | (print i))) 41 | 42 | 43 | ;; This was a test case that used to be a problem for tools.analyzer 44 | ;; before ticket TANAL-10 was fixed. 45 | 46 | (deftest update2 47 | (str #'update2)) 48 | -------------------------------------------------------------------------------- /cases/testcases/f03.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f03 2 | (:import (java.io InputStream File FileOutputStream) 3 | (java.net URI)) 4 | (:require clojure.main clojure.tools.macro) 5 | (:use [clojure.tools.macro 6 | :only (with-symbol-macros defsymbolmacro name-with-attributes)]) 7 | (:gen-class)) ; used to cause :unused-ret-vals warning 8 | 9 | ;; This case used to cause tools.analyzer to throw an exception before 10 | ;; ticket TANAL-11 was fixed. 11 | 12 | (defmacro defmonadfn 13 | "Like defn, but for functions that use monad operations and are used inside 14 | a with-monad block." 15 | {:arglists '([name docstring? attr-map? args expr] 16 | [name docstring? attr-map? (args expr) ...])} 17 | [name & options] 18 | (let [[name options] (name-with-attributes name options) 19 | fn-name (symbol (str *ns*) (format "m+%s+m" (str name))) 20 | make-fn-body (fn [args expr] 21 | (list (vec (concat ['m-bind 'm-result 22 | 'm-zero 'm-plus] args)) 23 | (list `with-symbol-macros expr)))] 24 | (if (list? (first options)) 25 | ; multiple arities 26 | (let [arglists (map first options) 27 | exprs (map second options) 28 | ] 29 | `(do 30 | (defsymbolmacro ~name (partial ~fn-name ~'m-bind ~'m-result 31 | ~'m-zero ~'m-plus)) 32 | (defn ~fn-name ~@(map make-fn-body arglists exprs)))) 33 | ; single arity 34 | (let [[args expr] options] 35 | `(do 36 | (defsymbolmacro ~name (partial ~fn-name ~'m-bind ~'m-result 37 | ~'m-zero ~'m-plus)) 38 | (defn ~fn-name ~@(make-fn-body args expr))))))) 39 | 40 | (defsymbolmacro m-bind m-bind) 41 | 42 | (defmonadfn m-join 43 | "Converts a monadic value containing a monadic value into a 'simple' 44 | monadic value." 45 | [m] 46 | (m-bind m identity)) 47 | 48 | 49 | ;; Excerpted from clojure.java.io. Once caused tools.analyzer to 50 | ;; throw an exception. Reported and fixed as ticket TANAL-19. 51 | 52 | (defmulti do-copy 53 | (fn [input output _opts] [(type input) (type output)])) 54 | 55 | (defmethod do-copy [InputStream File] [^InputStream input ^File output opts] 56 | (with-open [out (FileOutputStream. output)] 57 | (do-copy input out opts))) 58 | 59 | 60 | ;; This function, excerpted and cut down from clojure.java.browse, 61 | ;; once caused tools.analyzer to throw an exception. It was reported 62 | ;; and fixed as ticket TANAL-20. 63 | 64 | (defn open-url-in-browser [url] 65 | (try 66 | (-> (clojure.lang.Reflector/invokeStaticMethod "java.awt.Desktop" 67 | "getDesktop" (to-array nil)) 68 | (.browse (URI. url))) 69 | url 70 | (catch ClassNotFoundException e 71 | nil))) 72 | 73 | 74 | ;; Stripped down from a longer example in namespace clojure.data.xml. 75 | ;; Exception in tools.analyzer was reported and fixed as ticket 76 | ;; TANAL-21. 77 | 78 | (defn foo1 [] 79 | javax.xml.transform.OutputKeys/INDENT) 80 | 81 | (defn foo2 [] 82 | (javax.xml.transform.OutputKeys/INDENT)) 83 | 84 | 85 | ;; Stripped down from some example found somewhere in a contrib 86 | ;; library, I think. Formerly caused exception with tools.analyzer, 87 | ;; but reported and fixed as ticket TANAL-25. 88 | 89 | (defn foo [e] 90 | (clojure.main/repl-caught e)) 91 | 92 | ;; The comment below used to cause an :unused-ret-vals warning in 93 | ;; earlier versions of Eastwood. Starting with version 0.1.4, 94 | ;; tools.analyzer(.jvm) added :raw-forms in the returned ASTs, making 95 | ;; it easy for Eastwood to discover that the nil return value was 96 | ;; produced from a comment macro invocation. 97 | 98 | ;; The other local bindings of comment as fn and macro are there 99 | ;; simply to see what tools.analyzer(.jvm) will return for them. 100 | 101 | (defn bar [x] 102 | (comment 1 2 3) 103 | (let [comment (fn [y] (println y))] 104 | (comment 7)) 105 | (clojure.tools.macro/macrolet [(comment [y] `(println ~y))] 106 | (comment 9)) 107 | (inc x)) 108 | -------------------------------------------------------------------------------- /cases/testcases/f04.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f04) 2 | 3 | 4 | ;; This case used to cause tools.analyzer to throw an exception before 5 | ;; ticket TANAL-12 was fixed. 6 | 7 | (try 8 | (fn foo 9 | ([] 10 | nil) 11 | ([x] 12 | (if (< x 5) 13 | (println x) 14 | (recur (inc x))))) 15 | (catch Exception e 16 | (println "Exception occurred"))) 17 | 18 | 19 | ;; Below are test cases for :local-shadows-var linter 20 | (defn bar [x] 21 | (comment 1 2 3) 22 | ;; The let-binding of name should not cause a :local-shadows-var 23 | ;; linter warning, since it is never referenced in the body of the 24 | ;; let except as a value that is not a function. 25 | (let [name 'foo 26 | pmap {:a 1 :b 2} 27 | comment (fn [y] (println name map y)) 28 | remove #(inc %) 29 | replace (comp str biginteger) 30 | shuffle distinct] 31 | (println "name" name) 32 | ;; should cause a warning for pmap, but current linter doesn't 33 | ;; detect that pmap is being used as a function here. 34 | (println (map pmap [1 2 3])) 35 | ;; No warning, because local binding to (fn ...) is detected by 36 | ;; tools.analyzer(.jvm) as a function. 37 | (comment 7) 38 | ;; No warning, because local binding to (inc %) is detected as a 39 | ;; function. 40 | (println (remove 5)) 41 | ;; Eastwood currently isn't clever enough to recognize that (comp 42 | ;; ...) returns a function value. Thus unlike the examples 43 | ;; above, it warns about this call to replace. 44 | (println (replace 5)) 45 | ;; Similarly for shuffle being bound locally to the value of 46 | ;; distinct, which should resolve to clojure.core/distinct. 47 | (println (shuffle [1 2 2 3])))) 48 | 49 | ;; core.logic intentionally shadows the name loop using letfn. 50 | ;; Hopefully the result of analyzing this code makes it easy to 51 | ;; determine that loop's value is a function in this case, so I can 52 | ;; suppress the warning. 53 | 54 | (defn shadowed-loop [v] 55 | (letfn [(loop [ys] 56 | (if ys 57 | (loop (next ys)) 58 | 28))] 59 | (loop v))) 60 | 61 | (defn user-reported-example [fetched-data] 62 | (let [{count :count 63 | data :data} fetched-data 64 | real-count (count data)] 65 | (inc real-count))) 66 | 67 | 68 | ;; Example code exhibiting the issue from Benjamin Peter, submitted in 69 | ;; description of Clojure ticket CLJ-1249 70 | 71 | (defprotocol HasPets 72 | (dogs [this]) 73 | (cats [this]) 74 | (octopus [this]) 75 | (cute-ones [this])) 76 | 77 | ;; Here the field "dogs" is added with the same name as the protocol 78 | 79 | (defrecord Petshop [dogs] 80 | HasPets 81 | (dogs [this] 82 | [:pluto :bethoven]) 83 | (cats [this] 84 | [:tom]) 85 | (octopus [this] 86 | [:henry]) 87 | (cute-ones [this] 88 | ;; Here it was intended to call the function "dogs", instead the 89 | ;; field is used. 90 | (concat (dogs this) (cats this)))) 91 | -------------------------------------------------------------------------------- /cases/testcases/f05.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f05 2 | (:require [clojure.set :as set])) 3 | 4 | ;; The combination of two classes named IInterval and IIntervals, with 5 | ;; one being the same as the other except with an "s" appended, used 6 | ;; to exhibit a bug in tools.analyzer before ticket TANAL-7 was fixed. 7 | ;; The code is copied and modified from part of the library 8 | ;; core.logic, which I was testing Eastwood on when I discovered this 9 | ;; issue. 10 | 11 | (alias 'core 'clojure.core) 12 | 13 | (defprotocol IMergeDomains 14 | (-merge-doms [a b])) 15 | 16 | (defprotocol IMemberCount 17 | (-member-count [dom])) 18 | 19 | (defprotocol IInterval 20 | (-lb [this]) 21 | (-ub [this])) 22 | 23 | (defprotocol IIntervals 24 | (-intervals [this])) 25 | 26 | (defprotocol ISortedDomain 27 | (-drop-one [this]) 28 | (-drop-before [this n]) 29 | (-keep-before [this n])) 30 | 31 | (defprotocol ISet 32 | (-member? [this n]) 33 | (-disjoint? [this that]) 34 | (-intersection [this that]) 35 | (-difference [this that])) 36 | 37 | 38 | (declare finite-domain? domain disjoint?* sorted-set->domain 39 | intersection* difference*) 40 | 41 | (deftype FiniteDomain [s min max] 42 | Object 43 | (equals [this that] 44 | (if (finite-domain? that) 45 | (if (= (-member-count this) (-member-count that)) 46 | (= s (:s that)) 47 | false) 48 | false)) 49 | 50 | clojure.lang.ILookup 51 | (valAt [this k] 52 | (.valAt this k nil)) 53 | (valAt [this k not-found] 54 | (case k 55 | :s s 56 | :min min 57 | :max max 58 | not-found)) 59 | 60 | IMemberCount 61 | (-member-count [this] (count s)) 62 | 63 | IInterval 64 | (-lb [_] min) 65 | (-ub [_] max) 66 | 67 | ISortedDomain 68 | (-drop-one [_] 69 | (let [s (disj s min) 70 | c (count s)] 71 | (cond 72 | (= c 1) (first s) 73 | (core/> c 1) (FiniteDomain. s (first s) max) 74 | :else nil))) 75 | 76 | (-drop-before [_ n] 77 | (apply domain (drop-while #(core/< % n) s))) 78 | 79 | (-keep-before [this n] 80 | (apply domain (take-while #(core/< % n) s))) 81 | 82 | ISet 83 | (-member? [this n] 84 | (if (s n) true false)) 85 | 86 | (-disjoint? [this that] 87 | (cond 88 | (integer? that) 89 | (if (s that) false true) 90 | (instance? FiniteDomain that) 91 | (cond 92 | (core/< max (:min that)) true 93 | (core/> min (:max that)) true 94 | :else (empty? (set/intersection s (:s that)))) 95 | :else (disjoint?* this that))) 96 | 97 | (-intersection [this that] 98 | (cond 99 | (integer? that) 100 | (when (-member? this that) that) 101 | (instance? FiniteDomain that) 102 | (sorted-set->domain (set/intersection s (:s that))) 103 | :else 104 | (intersection* this that))) 105 | 106 | (-difference [this that] 107 | (cond 108 | (integer? that) 109 | (sorted-set->domain (disj s that)) 110 | (instance? FiniteDomain that) 111 | (sorted-set->domain (set/difference s (:s that))) 112 | :else 113 | (difference* this that))) 114 | 115 | IIntervals 116 | (-intervals [_] (seq s)) 117 | 118 | IMergeDomains 119 | (-merge-doms [this that] 120 | (-intersection this that)) 121 | 122 | ) 123 | 124 | 125 | (defn finite-domain? [x] 126 | (instance? FiniteDomain x)) 127 | -------------------------------------------------------------------------------- /cases/testcases/f06.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f06) 2 | 3 | 4 | ;; y is obviously unused here 5 | (defn fn-with-unused-args [x y] 6 | (* x x)) 7 | 8 | ;; y is still an unused arg in this fn, too 9 | (defn fn-with-unused-args2 [x y] 10 | (* x 11 | (let [y (dec x)] 12 | (* y 2)))) 13 | 14 | ;; all used here 15 | (defn fn-with-opt-args [x & y] 16 | [x y]) 17 | 18 | ;; Inner anonymous function has unused args 19 | (defn fn-with-unused-args3 [x] 20 | (let [foo (fn [y w z] 21 | (* y z))] 22 | (foo x (inc x) (dec x)))) 23 | 24 | 25 | ;; Macros have implicit &form and &env args that should never be 26 | ;; warned about if they are not used, since they are rarely used. 27 | (defmacro macro1 [x & body] 28 | `(when-not ~x 29 | ~@body)) 30 | 31 | 32 | ;; body is unused 33 | (defmacro macro2 [x & body] 34 | `(if ~x 1 2)) 35 | 36 | 37 | ;; No warning for args with name _ 38 | (defn ignore-underline-args [_] 39 | (+ 5 7)) 40 | 41 | 42 | ;; No warnings for args whose names begin with _ character 43 | (defn default-value-fn [_k v] 44 | v) 45 | 46 | 47 | ;; There was a bug in tools.analyzer in the way it renamed the last 48 | ;; occurrence of y in this function -- it had a different :name than 49 | ;; the arg y. Ticket TANAL-15 was filed and Nicola fixed it. 50 | (defn fn-with-unused-args4 [x y z] 51 | (let [foo (fn [y z] 52 | (* y z))] 53 | (foo x y))) 54 | 55 | 56 | (defprotocol CollReduce 57 | (coll-reduce [coll f] [coll f val])) 58 | 59 | 60 | (extend-protocol CollReduce 61 | nil 62 | (coll-reduce 63 | ([coll f] (f)) 64 | ([coll f val] (cons val coll))) 65 | 66 | clojure.lang.ASeq 67 | (coll-reduce 68 | ([coll f] [coll]) 69 | ([coll f val] [coll f]))) 70 | -------------------------------------------------------------------------------- /cases/testcases/f08.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.f08 2 | "Testcase for issue 307. It uses a dependency with an imported var" 3 | (:require 4 | [testcases.imported-var :as imported-var])) 5 | 6 | (imported-var/join "z") 7 | 8 | -------------------------------------------------------------------------------- /cases/testcases/foreign_macroexpansions/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.foreign-macroexpansions.red 2 | (:require [eastwood.test.outside-test-paths.example :refer [faulty]])) 3 | 4 | (defn foo [x] 5 | (faulty x)) 6 | -------------------------------------------------------------------------------- /cases/testcases/ignored_faults_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.ignored-faults-example 2 | "A sample namespace for the 'ignored-faults' feature") 3 | 4 | clojure.set/difference 5 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies) 2 | 3 | (def x 4 | clojure.string/join) 5 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/cljc/green.cljc: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.cljc.green) 2 | 3 | #?(:clj (require '[clojure [string :as s]])) 4 | 5 | s/blank? 6 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/cljc/red.cljc: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.cljc.red) 2 | 3 | #?(:clj clojure.string/blank?) 4 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/green1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.green1 2 | "Exercises an explicit `require`, with aliasing") 3 | 4 | (require '[clojure.string :as string]) 5 | 6 | string/blank? 7 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/green2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.green2 2 | "Exercises an explicit `require`, without aliasing") 3 | 4 | (require 'clojure.string) 5 | 6 | clojure.string/blank? 7 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/green3.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.green3 2 | "Simulates `require`s placed in deeply nested code") 3 | 4 | (when (= true 5 | (read-string (System/getProperty (-> (java.util.UUID/randomUUID) str) 6 | "true"))) 7 | (doseq [_ [1]] 8 | (require 'clojure.string))) 9 | 10 | clojure.string/blank? 11 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/green4.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.green4 2 | "Exercises prefix notation, without aliases") 3 | 4 | (require '[clojure [string]]) 5 | 6 | clojure.string/blank? 7 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/green5.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.green5 2 | "Exercises prefix notation, with aliases") 3 | 4 | (require '[clojure [string :as s]]) 5 | 6 | s/blank? 7 | -------------------------------------------------------------------------------- /cases/testcases/implicit_dependencies/explicit_require/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.implicit-dependencies.explicit-require.red) 2 | 3 | clojure.string/blank? 4 | -------------------------------------------------------------------------------- /cases/testcases/imported_var.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.imported-var 2 | "mimics a var imported from another namespace. i.e. by potemkin/import-vars" 3 | (:require [clojure.string :as str])) 4 | 5 | (def join 6 | str/join) 7 | 8 | (alter-meta! #'join merge (meta #'str/join)) 9 | -------------------------------------------------------------------------------- /cases/testcases/in_ns_switching.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.in-ns-switching 2 | ;;(:require [testcases.f01 :as t1]) 3 | (:use clojure.test 4 | clojure.set 5 | [testcases.f01 :as t1] 6 | )) 7 | 8 | ;; This test is inspired by a problem found when analyzing Incanter's 9 | ;; namespace incanter.sql-tests. It has a similar use of in-ns to 10 | ;; change namespaces, fiddle with something in the other namespace, 11 | ;; then switch back to the original namespace with another in-ns call. 12 | ;; Earlier versions of Eastwood would correctly process the first 13 | ;; in-ns, but would not process the second. 14 | 15 | (in-ns 'testcases.f01) 16 | 17 | (def statements-made (clojure.core/atom [])) 18 | 19 | (in-ns 'testcases.in-ns-switching) 20 | 21 | ;; Verify that the second in-ns was processed correctly by referring 22 | ;; to a var by a symbol that would only work if the second in-ns 23 | ;; worked. 24 | 25 | (def s1 (union #{1 2} #{3 4})) 26 | -------------------------------------------------------------------------------- /cases/testcases/is_false/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.is-false.green 2 | (:require 3 | [clojure.test :refer [are deftest is join-fixtures testing use-fixtures]])) 4 | 5 | (deftest foo 6 | (is false)) 7 | -------------------------------------------------------------------------------- /cases/testcases/isformsok.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.isformsok 2 | (:require [clojure.test :as t])) 3 | 4 | (defmacro is [& args] 5 | `(println ~@args)) 6 | 7 | (t/deftest testing-clojure-test 8 | ;; This should not warn, because it is testcases.isformsok/is, not 9 | ;; clojure.test/is 10 | (is "2+4 is 6 in the wrong place" (= 6 (+ 2 4))) ; backwards args, but test passes because string is logical true 11 | ) 12 | 13 | (comment 14 | ;; Best not to warn for things inside of (comment ...) 15 | (is "2+4 is 6 in the wrong place" (= (count [:flood :flood :floob :flood :gates :agtes]) (+ 2 4))) ; backwards args, but test passes because string is logical true 16 | ) 17 | -------------------------------------------------------------------------------- /cases/testcases/keyword_typos.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.keyword-typos) 2 | 3 | ;; Just a simple test that the :keyword-typos linter can actually 4 | ;; issue warnings. 5 | 6 | (def my-records 7 | [ {:name "John Q. Public", :address "123 Elm St", :occupation "fool"} 8 | {:name "Jane Doe", :address "678 Vine Rd", :occupation "Accountant"} 9 | {:name "Rajesh Koothrapali", :address "Los Angeles", :occuption "physicist"} 10 | ]) 11 | 12 | ;; Issue #163: George Simms pointed out that keywords that differ only 13 | ;; in the presence or absence of an initial '_' character are 14 | ;; reasonably common when interacting with Datomic. 15 | 16 | ;; The example below is completely made up. I have never worked with 17 | ;; Datomic before. It simply contains a pair of keywords that differ 18 | ;; only in presence/absence of a leading '_' character. 19 | 20 | (def datomic-common-pattern 21 | [ :contoso/foo :contoso/_foo ]) 22 | -------------------------------------------------------------------------------- /cases/testcases/large_defprotocol.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.large-defprotocol) 2 | 3 | (defprotocol Foo 4 | "Exercises https://github.com/jonase/eastwood/issues/191" 5 | 6 | (aa [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 7 | (ab [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 8 | (ac [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 9 | (ad [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 10 | (ae [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 11 | (af [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 12 | (ag [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 13 | (ah [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 14 | (ai [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 15 | (aj [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 16 | (ak [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 17 | (al [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 18 | (am [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 19 | (an [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 20 | (ao [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 21 | (ap [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 22 | (aq [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 23 | (ar [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 24 | (as [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 25 | (at [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 26 | (au [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 27 | (av [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 28 | (aw [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 29 | (ax [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 30 | (ay [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl]) 31 | (az [_ aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl aasdl] 32 | "dfskasfdasfdf adfkj adfskjadfs lkjdaf lkjadf lkadjsf aldkfj aldsfkj adlskfj aldkfja")) 33 | -------------------------------------------------------------------------------- /cases/testcases/let/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.let.green) 2 | 3 | ;; https://github.com/jonase/eastwood/issues/383 4 | (defn ok [] 5 | (let [{:keys [x] 6 | :as y} 7 | {:x 9}] 8 | 1)) 9 | -------------------------------------------------------------------------------- /cases/testcases/let/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.let.red) 2 | 3 | (defn faulty [] 4 | (let [a (if (seq? {}) 5 | 1 6 | 2)] 7 | a)) 8 | -------------------------------------------------------------------------------- /cases/testcases/performance/green/case.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.green.case) 2 | 3 | (defn foo [x] 4 | (case (long x) 5 | 1 :one)) 6 | -------------------------------------------------------------------------------- /cases/testcases/performance/green/hash.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.green.hash) 2 | 3 | (case 1 1 :long 922337203900225948N :big 2) 4 | -------------------------------------------------------------------------------- /cases/testcases/performance/green/recur.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.green.recur 2 | "Like `testcases.performance.green.recur` but adds a `long`` call 3 | to address the recur warning." 4 | (:require 5 | [clojure.string :as string])) 6 | 7 | (def ^:const vlq-base-shift 5) 8 | (def ^:const vlq-base (bit-shift-left 1 vlq-base-shift)) 9 | (def ^:const vlq-base-mask (dec vlq-base)) 10 | (def ^:const vlq-continuation-bit vlq-base) 11 | 12 | (defn to-vlq-signed [v] 13 | (if (neg? v) 14 | (inc (bit-shift-left (- v) 1)) 15 | (+ (bit-shift-left v 1) 0))) 16 | 17 | (defn from-vlq-signed [v] 18 | (let [neg? (= (bit-and v 1) 1) 19 | shifted (bit-shift-right v 1)] 20 | (if neg? 21 | (- shifted) 22 | shifted))) 23 | 24 | (defn decode [^String s] 25 | (let [l (.length s)] 26 | (loop [i 0 result 0 shift 0] 27 | (when (>= i l) 28 | (throw (Error. "Expected more digits in base 64 VLQ value."))) 29 | (let [digit (rand-int 10)] 30 | (let [i (inc i) 31 | continuation? (pos? (bit-and digit vlq-continuation-bit)) 32 | digit (bit-and digit vlq-base-mask) 33 | result (+ result (bit-shift-left digit shift)) 34 | shift (long (+ shift vlq-base-shift))] 35 | (if continuation? 36 | (recur i result shift) 37 | (lazy-seq 38 | (cons (from-vlq-signed result) 39 | (let [s (.substring s i)] 40 | (when-not (string/blank? s) 41 | (decode s))))))))))) 42 | -------------------------------------------------------------------------------- /cases/testcases/performance/red/case.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.red.case) 2 | 3 | (defn foo [x] 4 | (case x 5 | 1 :one)) 6 | -------------------------------------------------------------------------------- /cases/testcases/performance/red/hash.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.red.hash) 2 | 3 | (case 1 1 :long 9223372039002259457N :big 2) 4 | -------------------------------------------------------------------------------- /cases/testcases/performance/red/recur.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.performance.red.recur 2 | (:require 3 | [clojure.string :as string])) 4 | 5 | (def ^:const vlq-base-shift 5) 6 | (def ^:const vlq-base (bit-shift-left 1 vlq-base-shift)) 7 | (def ^:const vlq-base-mask (dec vlq-base)) 8 | (def ^:const vlq-continuation-bit vlq-base) 9 | 10 | (defn to-vlq-signed [v] 11 | (if (neg? v) 12 | (inc (bit-shift-left (- v) 1)) 13 | (+ (bit-shift-left v 1) 0))) 14 | 15 | (defn from-vlq-signed [v] 16 | (let [neg? (= (bit-and v 1) 1) 17 | shifted (bit-shift-right v 1)] 18 | (if neg? 19 | (- shifted) 20 | shifted))) 21 | 22 | (defn decode [^String s] 23 | (let [l (.length s)] 24 | (loop [i 0 result 0 shift 0] 25 | (when (>= i l) 26 | (throw (Error. "Expected more digits in base 64 VLQ value."))) 27 | (let [digit (rand-int 10)] 28 | (let [i (inc i) 29 | continuation? (pos? (bit-and digit vlq-continuation-bit)) 30 | digit (bit-and digit vlq-base-mask) 31 | result (+ result (bit-shift-left digit shift)) 32 | shift (+ shift vlq-base-shift)] 33 | (if continuation? 34 | (recur i result shift) 35 | (lazy-seq 36 | (cons (from-vlq-signed result) 37 | (let [s (.substring s i)] 38 | (when-not (string/blank? s) 39 | (decode s))))))))))) 40 | -------------------------------------------------------------------------------- /cases/testcases/refer_clojure_exclude/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.refer-clojure-exclude.green 2 | (:refer-clojure :exclude [update]) 3 | (:require 4 | [clojure.test :refer [deftest is]])) 5 | 6 | (defn update [m k f] 7 | (println "A side effect!" m k f)) 8 | 9 | (defn sut [x] 10 | (* x 2)) 11 | 12 | (deftest uses-update 13 | (update {} :f inc) 14 | (is (= 42 (sut 21)))) 15 | -------------------------------------------------------------------------------- /cases/testcases/refer_clojure_exclude/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.refer-clojure-exclude.red 2 | (:require 3 | [clojure.test :refer [deftest is]])) 4 | 5 | (defn sut [x] 6 | (* x 2)) 7 | 8 | (deftest uses-update 9 | (update {} :f inc) 10 | (is (= 42 (sut 21)))) 11 | -------------------------------------------------------------------------------- /cases/testcases/subkind_silencing/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.subkind-silencing.red 2 | (:require 3 | [clojure.test :refer [is]])) 4 | 5 | (defn foo [x] 6 | (is (= 42 (x)) 7 | [:not :a :string])) 8 | -------------------------------------------------------------------------------- /cases/testcases/suspicious.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.suspicious 2 | (:import (java.io StringReader)) 3 | (:use [clojure.test])) 4 | 5 | ;; Test cases targeted at :suspicious-expression linter. There are 6 | ;; some more in testcases.testtest namespace. 7 | (defmacro compile-if [test then] (if (eval test) then)) 8 | ;; Empty defrecords have macroexpansions containing suspicious-looking 9 | ;; macro invocations at intermediate steps, which would be good not to 10 | ;; warn about. 11 | (defrecord zero-type []) 12 | 13 | ;; and/or have 1-arg and/or at penultimate expansion step 14 | (if (and (> 5 3) (> 5 7)) 3 4) 15 | (if (or (> 5 7) (> 5 3)) 5 6) 16 | 17 | ;; cond has 0-arg version in itsexpansion 18 | (cond (> 5 3) 7 19 | (> 5 7) 3) 20 | 21 | ;; Earlier I had a bug that suppressed warnings for these 22 | (or (> 5 3) 23 | (doto (StringBuffer.))) 24 | 25 | (-> 1) 26 | (compile-if (resolve 'clojure.core/if-some) (->> 1)) ;; compile-if is because this arity was not defined for ->> macro in Clojure 1.5.1 27 | (and) 28 | (and 1) 29 | (as-> 1 x) 30 | (case 5 2) 31 | (cond) 32 | (let [x 5] (cond-> x)) 33 | (let [x 5] (cond->> x)) 34 | (condp = 5 2) 35 | (declare) 36 | (delay) 37 | (doseq [x [1 2 3]]) 38 | (dotimes [i 10]) 39 | (doto (Object.)) 40 | (import) 41 | (lazy-cat) 42 | (let [x 5]) 43 | (letfn [(foo [x] (inc x))]) 44 | (locking (Object.)) 45 | (loop [x 1]) 46 | (or) 47 | (or 1) 48 | (pvalues) 49 | (some-> 5) 50 | (some->> 5) 51 | (when 5) 52 | (when-first [x [5]]) ; tbd: Extra let warning to be suppressed 53 | (when-let [x 5]) ; tbd: Extra let warning to be suppressed 54 | (when-not 5) 55 | (compile-if (resolve 'clojure.core/when-some) (when-some [x 5])) ; tbd: Extra let warning to be suppressed 56 | (with-bindings {#'*warn-on-reflection* false}) 57 | (with-in-str "foo") 58 | (with-local-vars [x 5]) 59 | (with-open [rdr (java.io.StringReader. "foo")]) 60 | (with-out-str) 61 | (with-precision 10) 62 | (with-redefs [*warn-on-reflection* false]) 63 | -------------------------------------------------------------------------------- /cases/testcases/tanal_27.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.tanal-27) 2 | 3 | 4 | ;; tools.analyzer.jvm threw an exception when analyzing the following 5 | ;; function. Filed ticket TANAL-27 for this issue. 6 | 7 | (defn foo [a] 8 | (let [err (fn [& msg] (apply str msg))] 9 | (err "Invalid arg 'a'" a))) 10 | -------------------------------------------------------------------------------- /cases/testcases/tanal_9.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.tanal-9) 2 | 3 | (defrecord RecordTest [a b]) 4 | 5 | (defn mytest [] 6 | (= (RecordTest. 1 2) #testcases.tanal_9.RecordTest{:a 1, :b 2})) 7 | -------------------------------------------------------------------------------- /cases/testcases/test_metadata_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.test-metadata-example 2 | "Exercises a defn with `:test` metadata" 3 | (:require 4 | [clojure.test :refer [is]])) 5 | 6 | (defn foo 7 | {:test (fn [] 8 | (is (= 42 9 | (foo 21))))} 10 | [x] 11 | (* 2 x)) 12 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/defn_call_inside_refresh_dirs.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.defn-call-inside-refresh-dirs 2 | (:require 3 | [testcases.unhinted-reflective-call.example-defn])) 4 | 5 | (defn foo [x] 6 | (testcases.unhinted-reflective-call.example-defn/foo x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/defn_call_outside_refresh_dirs.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.defn-call-outside-refresh-dirs 2 | (:require 3 | [eastwood.test.outside-test-paths.defn-reflection-warning])) 4 | 5 | (defn foo [x] 6 | (eastwood.test.outside-test-paths.defn-reflection-warning/foo x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/depends_on_example_defn.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.depends-on-example-defn 2 | "A ns that depends on another, it being one that triggers reflection warnings." 3 | (:require 4 | [testcases.unhinted-reflective-call.example-defn])) 5 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/example_defmacro.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.example-defmacro) 2 | 3 | (defmacro foo [x] 4 | `(.sdfsdf ~x)) 5 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/example_defn.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.example-defn) 2 | 3 | (defn foo [x] 4 | (.fooasdlfjk x)) 5 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/foreign_defn_call.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.foreign-defn-call 2 | (:require 3 | [reflection-example.core])) 4 | 5 | (defn bar [x] 6 | (reflection-example.core/bar x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/foreign_macro_call.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.foreign-macro-call 2 | (:require 3 | [reflection-example.core])) 4 | 5 | (defn foo [x] 6 | (reflection-example.core/foo x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/macro_call_inside_refresh_dirs.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.macro-call-inside-refresh-dirs 2 | (:require 3 | [testcases.unhinted-reflective-call.example-defmacro])) 4 | 5 | (defn foo [x] 6 | (testcases.unhinted-reflective-call.example-defmacro/foo x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/macro_call_outside_refresh_dirs.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.macro-call-outside-refresh-dirs 2 | (:require 3 | [eastwood.test.outside-test-paths.reflection-warning])) 4 | 5 | (defn foo [x] 6 | (eastwood.test.outside-test-paths.reflection-warning/foo x)) 7 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.red) 2 | 3 | ;; exercise that Eastwood can work even in face of these: 4 | (set! *warn-on-reflection* false) 5 | 6 | (defn foo [x y] 7 | [(-> x .theReflectiveCall) 8 | (-> x (.theReflectiveCall y))]) 9 | -------------------------------------------------------------------------------- /cases/testcases/unhinted_reflective_call/unused_foreign_reflective_code.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unhinted-reflective-call.unused-foreign-reflective-code 2 | "A ns that merely `require`s a ns that is supposed to emit reflection warnings at compiler time." 3 | (:require 4 | [reflection-example.core])) 5 | -------------------------------------------------------------------------------- /cases/testcases/unknown_reify.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unknown-reify 2 | "https://github.com/jonase/eastwood/issues/205") 3 | 4 | (def foo (reify Unknown (foo [this]))) 5 | -------------------------------------------------------------------------------- /cases/testcases/unlimiteduse.clj: -------------------------------------------------------------------------------- 1 | 2 | 3 | (ns testcases.unlimiteduse 4 | (:refer-clojure :exclude [read-string]) 5 | (:use clojure.test ; warn 6 | [clojure.reflect] ; warn 7 | [clojure inspector] ; warn 8 | [clojure [set]] ; warn 9 | [clojure.java.io :as io] ; warn because no :only or :refer 10 | [clojure.java.browse :only [browse-url]] ; no warning because :only 11 | [clojure.walk :as w :refer [postwalk-demo]]) ; no warning because :refer 12 | (:use (clojure [xml :only [emit]] ; no warn because :only 13 | [edn :only [read-string]])) ; ditto 14 | (:use (clojure [pprint :as pp] ; warn because no :only or :refer 15 | [uuid :as u])) ; warn because no :only or :refer 16 | ) 17 | -------------------------------------------------------------------------------- /cases/testcases/unused_fn_args/multimethods/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-fn-args.multimethods.green) 2 | 3 | (defmulti foo 4 | (fn [a b c] 5 | ;; all args unintentionally unused 6 | (rand))) 7 | -------------------------------------------------------------------------------- /cases/testcases/unused_fn_args/multimethods/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-fn-args.multimethods.red) 2 | 3 | (defn foo [] 4 | (fn [a b c] 5 | ;; all args unintentionally unused 6 | (rand))) 7 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/green1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.green1 2 | (:import 3 | (java.io File))) 4 | 5 | (defn foo [] 6 | ;; Exercise `:unused-ret-vals` (static method call): 7 | (File/createTempFile "a" "a") 8 | 9 | (try 10 | ;; Exercise `:unused-ret-vals-in-try` (static method call): 11 | (File/createTempFile "a" "a") 12 | 1 13 | (catch Exception _)) 14 | 15 | ;; Exercise `:unused-ret-vals` (instance method call): 16 | (-> "a" File. .delete) 17 | 18 | (try 19 | ;; Exercise `:unused-ret-vals-in-try` (instance method call): 20 | (-> "a" File. .delete) 21 | 1 22 | (catch Exception _)) 23 | 24 | 42) 25 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/red1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.red1 2 | (:import 3 | (java.io File))) 4 | 5 | (defn foo [] 6 | ;; Exercise `:unused-ret-vals` (static method call): 7 | (Class/forName "a") 8 | 9 | 3) 10 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/red2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.red2 2 | (:import 3 | (java.io File))) 4 | 5 | (defn foo [] 6 | (try 7 | ;; Exercise `:unused-ret-vals-in-try` (static method call): 8 | (Class/forName "a") 9 | 1 10 | (catch Exception _))) 11 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/red3.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.red3 2 | (:import 3 | (java.io File))) 4 | 5 | (defn foo [] 6 | ;; Exercise `:unused-ret-vals` (instance method call): 7 | (-> "afs" .getBytes String. .toString) 8 | 9 | 42) 10 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/red4.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.red4 2 | (:import 3 | (java.io File))) 4 | 5 | (defn foo [] 6 | (try 7 | ;; Exercise `:unused-ret-vals-in-try` (instance method call): 8 | (-> "afs" .getBytes String. .toString) 9 | 1 10 | (catch Exception _))) 11 | -------------------------------------------------------------------------------- /cases/testcases/unused_ret_vals/red5.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unused-ret-vals.red5 2 | "https://github.com/jonase/eastwood/issues/441") 3 | 4 | (defn bad1 [x] 5 | (conj! x 2) 6 | 42) 7 | 8 | (defn bad2 [x] 9 | (assoc! x :a 2) 10 | 42) 11 | 12 | (defn bad3 [x] 13 | (pop! x) 14 | 42) 15 | 16 | (defn bad4 [x] 17 | (dissoc! x :a) 18 | 42) 19 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/consumer1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.consumer1 2 | ;; This require is needed to properly import the record below 3 | (:require [testcases.unusednsimport.defrecord]) 4 | (:import (testcases.unusednsimport.defrecord A))) 5 | 6 | ;; Exercises top-level forms: 7 | (A. 1) 8 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/consumer2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.consumer2 2 | ;; This require is needed to properly import the record below 3 | (:require [testcases.unusednsimport.defrecord]) 4 | (:import (testcases.unusednsimport.defrecord A))) 5 | 6 | ;; Should be deemed unused: 7 | (comment 8 | (A. 1)) 9 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/consumer3.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.consumer3 2 | ;; This require is needed to properly import the record below 3 | (:require [testcases.unusednsimport.defrecord]) 4 | (:import (testcases.unusednsimport.defrecord A))) 5 | 6 | ;; Exercises forms nested deep into arbitrary code: 7 | (defn sample [] 8 | (for [x [(A. 1)]] 9 | x)) 10 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/consumer4.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.consumer4 2 | ;; This require is needed to properly import the record below 3 | (:require [testcases.unusednsimport.defrecord]) 4 | (:import (testcases.unusednsimport.defrecord A))) 5 | 6 | ;; Exercises metadata: 7 | (defn ^A sample [] 8 | ;; return nil (and not `(A.)` so that we are certain that only metadata is being exercised) 9 | nil) 10 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/consumer5.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.consumer5 2 | ;; This require is needed to properly import the record below 3 | (:require [testcases.unusednsimport.defrecord]) 4 | (:import (testcases.unusednsimport.defrecord A))) 5 | 6 | ;; Exercises simple defs: 7 | (def thing (A. 1)) 8 | -------------------------------------------------------------------------------- /cases/testcases/unusednsimport/defrecord.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednsimport.defrecord) 2 | 3 | (defrecord A [a]) 4 | -------------------------------------------------------------------------------- /cases/testcases/unusednss.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednss 2 | (:use testcases.unusednss2) 3 | (:require [clojure [string :as s]] 4 | [clojure [repl :as r]] 5 | [clojure.data :as d])) 6 | 7 | ;; clojure.repl should be considered used because of the macro 8 | ;; invocation r/doc in foo1. 9 | 10 | (defn foo1 [x] 11 | (r/doc x)) 12 | 13 | 14 | ;; clojure.data _should_ be considered used because of the function 15 | ;; call d/diff in macro foo2. 16 | 17 | (defmacro foo2 [x y] 18 | `(d/diff ~x ~y)) 19 | 20 | 21 | ;(defn foo4 [a b] 22 | ; [(+ a b) (foo2 a b)]) 23 | 24 | 25 | ;; testcases.unusednss2 should be considered used because of the use 26 | ;; of atom1 in foo3. 27 | 28 | (defn foo3 [n] 29 | (swap! atom1 conj n)) 30 | 31 | 32 | ;; Should be no warning for this require, because it is outside of ns 33 | ;; form. 34 | 35 | (defn foo4 [ns] 36 | (require ns)) 37 | -------------------------------------------------------------------------------- /cases/testcases/unusednss2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednss2) 2 | 3 | (def atom1 (atom [])) 4 | -------------------------------------------------------------------------------- /cases/testcases/unusednss3.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednss3 2 | (:require [clojure.core.protocols :as protocols] 3 | [clojure.core.reducers :as reducers] 4 | [clojure.data :as data] 5 | [clojure.java.io :as io] 6 | [clojure.reflect :as reflect])) 7 | 8 | (extend String 9 | protocols/IKVReduce 10 | {:kv-reduce (fn [amap f init] nil)}) 11 | 12 | (extend-protocol reducers/CollFold 13 | String 14 | (coll-fold [coll n combinef reducef] nil)) 15 | 16 | (extend-type String 17 | data/EqualityPartition 18 | (equality-partition [x] nil)) 19 | 20 | (deftype Foo [whatever] 21 | io/Coercions 22 | (as-file [x] nil) 23 | (as-url [x] nil)) 24 | 25 | (deftype Bar [whatever] 26 | reflect/Reflector 27 | (do-reflect [reflector typeref] nil)) 28 | -------------------------------------------------------------------------------- /cases/testcases/unusednss4.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.unusednss4 2 | (:use testcases.unusednss2) 3 | (:require [clojure [string :as s]] 4 | [clojure [repl :as r]] 5 | [clojure.data :as d])) 6 | 7 | 8 | 9 | ;; This file should only reference the use'd and require'd namespaces 10 | ;; in the ns form above via their occurrence in keywords, either as an 11 | ;; alias, or as the full namespace name. Eastwood does not 12 | ;; distinguish which of those 2 methods is used. 13 | 14 | 15 | ;; Does _not_ reference namespace testcases.unusednss2, because this 16 | ;; keyword has no namespace, only the keyword name. 17 | (def x :testcases.unusednss2) 18 | 19 | ;; alias for clojure.string 20 | (def y ::s/foo) 21 | 22 | ;; Keyword with namespace clojure.repl inside syntax-quote 23 | (defmacro foo2 [x y] 24 | `(let [x2# ~x y2# ~y z# :clojure.repl/bar] 25 | (list x2# y2# z#))) 26 | 27 | (defmacro macro1 28 | [options] 29 | (if (map? options) 30 | `(str (d/diff (:x options) (:y options))) 31 | `(str (d/diff (first options) (second options))))) 32 | -------------------------------------------------------------------------------- /cases/testcases/warnloc1.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.warnloc1) 2 | 3 | ;; There are many blank lines in this file before the actual code, 4 | ;; because I want the line numbers to be larger than any line numbers 5 | ;; possible in the namespace testcases.warnloc2 6 | 7 | 8 | ;; The sole purpose of this namespace and testcases.warnloc2 is to 9 | ;; debug, and eventually fix and provide ongoing test cases for, 10 | ;; incorrect file/line/column combinations that as of just before 11 | ;; Eastwood 0.1.5 release are still there. That is, sometimes a file 12 | ;; name is reported with a line number that does not exist in that 13 | ;; file at all. It appears that this is due to an example like this: 14 | 15 | ;; Namespace A defines a macro. 16 | 17 | ;; Namespace B requires namespace A, then uses the macro in A. 18 | 19 | ;; The macro invocation, when expanded, causes Eastwood to issue a 20 | ;; warning on something about the macroexpanded form. 21 | 22 | ;; When Eastwood looks for :file :line :column keys in metadata, it 23 | ;; finds a line number in namespace A, but the file name for namespace 24 | ;; B. 25 | 26 | ;; I am not sure exactly how this happens, but it might be when 27 | ;; namespace B is on the list of namespaces to be linted explicitly, 28 | ;; and is thus read with tools.reader, but namespace A is *not* on the 29 | ;; list of namespaces to be linted, and is thus read using Clojure's 30 | ;; built-in reader, which does not have as extensive of metadata on 31 | ;; it. 32 | 33 | 34 | ;; many blank lines here. Go to the end for actual code. 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | (defmacro mydo [& args] 83 | `(do ~@args)) 84 | -------------------------------------------------------------------------------- /cases/testcases/warnloc2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.warnloc2 2 | (:require [testcases.warnloc1 :as loc1])) 3 | 4 | (println (loc1/mydo 1 2)) 5 | -------------------------------------------------------------------------------- /cases/testcases/while_true/green.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.while-true.green) 2 | 3 | (defn foo [] 4 | (while true 5 | (println 1))) 6 | -------------------------------------------------------------------------------- /cases/testcases/while_true/red.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.while-true.red) 2 | 3 | (defn foo [] 4 | (while 1 5 | (println 1))) 6 | -------------------------------------------------------------------------------- /cases/testcases/wrong_meta_on_macro_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrong-meta-on-macro-example) 2 | 3 | (.toString ^String (let [v ""] 4 | v)) 5 | -------------------------------------------------------------------------------- /cases/testcases/wrong_require.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrong-require 2 | (:require [clojure.srting :as s])) 3 | 4 | (def b {::b/id 'a}) 5 | -------------------------------------------------------------------------------- /cases/testcases/wrong_tag_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrong-tag-example) 2 | 3 | (defmacro the-macro [n x] 4 | `(defn ~n ~(with-meta [] {:tag pos?}) 5 | ~x)) 6 | 7 | (the-macro foo 42) 8 | -------------------------------------------------------------------------------- /cases/testcases/wrongprepost.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrongprepost) 2 | 3 | 4 | (defn wrong1 [x] 5 | {:pre (pos? x)} 6 | x) 7 | 8 | 9 | (defn wrong1b 10 | ([x] x) 11 | ([x y] 12 | {:pre (> x y)} 13 | (- x y))) 14 | 15 | 16 | (defn ok2 [x] 17 | {:pre [(pos? x)]} 18 | x) 19 | 20 | 21 | (defn ok-pre-wrong-post-3 [x] 22 | {:pre [(number? x)] 23 | :post [number?]} 24 | (+ x 3)) 25 | 26 | 27 | (defn ok-pre-ok-post-4 [x] 28 | {:pre [(number? x)] 29 | :post [(number? %)]} 30 | (+ x 3)) 31 | 32 | 33 | (defn ok-pre-wrong-post-5 [x] 34 | {:pre [(number? x)] 35 | :post (number? %)} 36 | (+ x 3)) 37 | 38 | 39 | 40 | (defn wrong-pre-ok-post-6 [x] 41 | {:pre (number? x) 42 | :post [(number? %)]} 43 | (+ x 3)) 44 | 45 | 46 | (defn f [x] 47 | (inc x)) 48 | 49 | 50 | (defn wrong-pre-7 [y] 51 | {:pre [f]} 52 | (dec y)) 53 | 54 | 55 | ;; It would be good to give a different warning for the following case 56 | ;; than for wrong-pre-7, because the following may actually be what 57 | ;; the user intended, to assert that the argument y is neither nil nor 58 | ;; false. If they intended to assert that y was not nil, we could 59 | ;; recommend that they be explicit with (not (nil? y)). 60 | 61 | ;; To implement this, it seems necessary to check whether the symbol 62 | ;; in the condition is the name of an argument or not. 63 | 64 | (defn correct-pre-8-but-suspicious [y] 65 | {:pre [y]} 66 | (dec y)) 67 | 68 | 69 | (defn wrong-pre-9 [y] 70 | {:pre [>= y 7]} 71 | (dec y)) 72 | 73 | 74 | (defn wrong-pre-wrong-post-10 [y] 75 | {:pre [(>= y 7) :a] 76 | :post [(>= % 6) % "constant"]} 77 | (dec y)) 78 | 79 | 80 | 81 | ;; Certain kinds of destructuring in fn arg vectors cause an 82 | ;; additional let to be in the macroexpansion, which caused an earlier 83 | ;; version of the :wrong-pre-post linter to not find the assert form 84 | ;; ASTs. This example is stripped down a bit from a similar function 85 | ;; found in the Carmine library where this issue was first discovered. 86 | 87 | (defn ok-different-macroexpansion-11 88 | [datastore & [{:keys [tqname freezer redis-ttl-ms] 89 | :or {tqname :default}}]] 90 | {:pre [(instance? Number datastore) 91 | (or (nil? freezer) (instance? clojure.lang.IPersistentMap freezer)) 92 | (or (nil? redis-ttl-ms) (>= redis-ttl-ms (* 1000 60 60 10)))]} 93 | (assoc freezer :foo 7)) 94 | 95 | 96 | ;; Like the above, but include some preconditions that should warn, to 97 | ;; verify that Eastwood can find them. 98 | 99 | (defn wrong-pre-different-macroexpansion-12 100 | [datastore & [{:keys [tqname freezer redis-ttl-ms] 101 | :or {tqname :default}}]] 102 | {:pre [(instance? Number datastore) 103 | wrong-pre-9 104 | (or (nil? redis-ttl-ms) (>= redis-ttl-ms (* 1000 60 60 10)))]} 105 | (assoc freezer :foo 7)) 106 | 107 | 108 | (defn wrong-pre-different-macroexpansion-13 109 | [datastore & [{:keys [tqname freezer redis-ttl-ms] 110 | :or {tqname :default}}]] 111 | {:pre (instance? Number datastore)} 112 | (assoc freezer :foo 7)) 113 | -------------------------------------------------------------------------------- /cases/testcases/wrongprepost2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrongprepost2 2 | (:require [clojure.spec.alpha :as spec])) 3 | 4 | 5 | (defn issue-219-fn [data] 6 | {:pre [(spec/assert ::my-spec data)]} 7 | (assoc data 5 7)) 8 | 9 | (def ^:dynamic *dynvar* nil) 10 | 11 | (defn issue-190-fn [x] 12 | {:pre [*dynvar*]} 13 | x) 14 | -------------------------------------------------------------------------------- /cases/testcases/wrongtag2.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.wrongtag2 2 | ;;(:import (java.util LinkedList)) 3 | (:require [clojure.test :refer :all])) 4 | 5 | ;;(defn avlf4a ^LinkedList [coll] (java.util.LinkedList. coll)) 6 | ;;(defn avlf4b ^LinkedList [& coll] (java.util.LinkedList. coll)) 7 | (defn avlf4c ^LinkedList [& {:keys [coll]}] (java.util.LinkedList. coll)) 8 | ;;(defn avlf4d ^LinkedList [& {:keys [^LinkedList coll]}] (java.util.LinkedList. coll)) 9 | 10 | 11 | ;; Copied and modified from some core.matrix code 12 | 13 | (defprotocol PTypeInfo 14 | (element-type [m])) 15 | 16 | 17 | (extend-protocol PTypeInfo 18 | (Class/forName "[D") 19 | (element-type [m] :double)) 20 | 21 | (extend-protocol PTypeInfo 22 | (Class/forName "[F") 23 | (element-type [m] :float)) 24 | 25 | (element-type (float-array [1.0 2.0])) 26 | 27 | 28 | (defprotocol PGetElem 29 | (get-elem [m idx])) 30 | 31 | ;; Reflection warning: 32 | ;; Reflection warning, filename.clj:3:23 - call to static method aget on clojure.lang.RT can't be resolved (argument types: unknown, int). 33 | ;; Reflection: yes 34 | ;; Eastwood wrong-tag warning: yes 35 | (extend-protocol PGetElem 36 | (Class/forName "[D") 37 | (get-elem [m idx] (aget m idx))) 38 | 39 | ;; Same reflection warning as previous, even with ^doubles hint: 40 | ;; Reflection: yes 41 | ;; Eastwood wrong-tag warning: yes 42 | (extend-protocol PGetElem 43 | (Class/forName "[D") 44 | (get-elem [^doubles m idx] (aget m idx))) 45 | 46 | ;; Why? I think because the type tag addition in macro extend-type, 47 | ;; used by macro extend-protocol, replaces the ^doubles type tag 48 | ;; during macro expansion. See the REPL session below for 49 | ;; confirmation. Namespace alias u was created with this require in 50 | ;; Eastwood: 51 | 52 | ;; (require '[eastwood.util :as u]) 53 | 54 | ;; user=> (def x1 (macroexpand '(extend-protocol PGetElem 55 | ;; #_=> (Class/forName "[D") 56 | ;; #_=> (get-elem [^doubles m ^Integer idx] (aget m idx))))) 57 | ;; 58 | ;; user=> (def x2 (macroexpand (second x1))) 59 | ;; 60 | ;; user=> (u/pprint-meta x2) 61 | ;; (clojure.core/extend 62 | ;; ^{:line 2, :column 3} (Class/forName "[D") 63 | ;; PGetElem 64 | ;; {:get-elem 65 | ;; (fn 66 | ;; ([^{:tag ^{:line 2, :column 3} (Class/forName "[D")} m 67 | ;; ^{:tag Integer} idx] 68 | ;; ^{:line 3, :column 41} (aget m idx)))}) 69 | ;; nil 70 | 71 | ;; Same reflection warning as previous, even with ^doubles and ^Integer hint: 72 | ;; Reflection: yes 73 | ;; Eastwood wrong-tag warning: yes 74 | (extend-protocol PGetElem 75 | (Class/forName "[D") 76 | (get-elem [^doubles m ^Integer idx] (aget m idx))) 77 | 78 | ;; This is the extend-type macro invocation that the previous 79 | ;; extend-protocol expands to. 80 | ;; Reflection: yes 81 | ;; Eastwood wrong-tag warning: yes 82 | 83 | (extend-type (Class/forName "[D") 84 | PGetElem 85 | (get-elem [^doubles m ^Integer idx] (aget m idx))) 86 | 87 | ;; This is the extend function call that the previous extend-type 88 | ;; macro invocation expands to. Note that there is no mention of a 89 | ;; type tag doubles anywhere, only (Class/forName "[D"). 90 | ;; Reflection: yes 91 | ;; Eastwood wrong-tag warning: yes 92 | 93 | (extend (Class/forName "[D") 94 | PGetElem 95 | {:get-elem 96 | (fn ([^{:tag (Class/forName "[D")} m ^Integer idx] 97 | (aget m idx)))}) 98 | 99 | ;; No reflection warning, because ^doubles hint is inside of aget 100 | ;; call, and not overwritten as it would be if it is on the argument 101 | ;; m. 102 | 103 | ;; Reflection: no 104 | ;; Eastwood wrong-tag warning: yes 105 | (extend-protocol PGetElem 106 | (Class/forName "[D") 107 | (get-elem [m idx] (aget ^doubles m idx))) 108 | 109 | 110 | ;; Here is a way to do it with no reflection, but it can only be done 111 | ;; by using extend directly. Neither extend-type nor extend-protocol 112 | ;; can be used for this in Clojure 1.6.0 (and probably also cannot in 113 | ;; any other Clojure version, at least up through 1.7.0-alpha4). 114 | 115 | ;; no reflection warning 116 | ;; Reflection: no 117 | ;; Eastwood wrong-tag warning: no 118 | (extend (Class/forName "[D") 119 | PGetElem 120 | {:get-elem 121 | (fn ([^doubles m idx] 122 | (aget m idx)))}) 123 | 124 | ;; reflection warning 125 | ;; Reflection: yes 126 | ;; Eastwood wrong-tag warning: no 127 | (extend (Class/forName "[D") 128 | PGetElem 129 | {:get-elem 130 | (fn ([m idx] 131 | (aget m idx)))}) 132 | 133 | ;; no reflection warning 134 | ;; Reflection: no 135 | ;; Eastwood wrong-tag warning: no 136 | (extend (Class/forName "[D") 137 | PGetElem 138 | {:get-elem 139 | (fn ([m idx] 140 | (aget ^doubles m idx)))}) 141 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/ast/query.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.ast.query 10 | "Utilities for querying tools.analyzer ASTs with Datomic" 11 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :as ast] 12 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [compile-if]])) 13 | 14 | (defn query-map 15 | "Transforms a Datomic query from its vector representation to its map one. 16 | If the given query is already in its map representation, the original query 17 | is returned." 18 | [query] 19 | (if (map? query) 20 | query 21 | (loop [ret {:find [] :in [] :where []} query query op nil] 22 | (if-let [[el & query] (seq query)] 23 | (if (keyword? el) 24 | (recur ret query el) 25 | (recur (update-in ret [op] conj el) query op)) 26 | (reduce-kv (fn [m k v] (if (seq v) (assoc m k v) m)) {} ret))))) 27 | 28 | (defn unfold-expression-clauses 29 | "Given a Datomic query, walk the :where clauses searching for 30 | expression clauses with nested calls, unnesting those calls. 31 | 32 | E.g {:where [[(inc (dec ?foo)) ?bar] ..] ..} will be transformed into 33 | {:where [[(dec ?foo) ?1234] [(inc ?1234) ?bar] ..] ..}" 34 | [query] 35 | (let [{:keys [where] :as query} (query-map query)] 36 | (if-not where 37 | query 38 | (assoc query :where 39 | (mapcat (fn [[op & rest :as form]] 40 | (if-let [[f & args] (and (seq? op) op)] 41 | (if (some seq? args) 42 | (loop [args args to-ssa {} cur [f] binds rest ret []] 43 | (if-let [[a & args] (seq args)] 44 | (if (and (seq? a) 45 | (not= 'quote (first a))) 46 | (let [g (gensym "?")] 47 | (recur args (assoc to-ssa g a) (conj cur g) binds ret)) 48 | (recur args to-ssa (conj cur a) binds ret)) 49 | (let [ret (conj ret (into [(seq cur)] binds))] 50 | (if-let [[k [f & args]] (first to-ssa)] 51 | (recur args (dissoc to-ssa k) [f] [k] ret) 52 | ret)))) 53 | [form]) 54 | [form])) where))))) 55 | 56 | (defn resolve-calls 57 | "Automatically replace fn name symbols in expression clauses with 58 | their namespace qualified one if the symbol can be resolved in the 59 | current namespace." 60 | [query] 61 | (let [{:keys [where] :as query} (query-map query)] 62 | (if-not where 63 | query 64 | (assoc query :where 65 | (mapv (fn [[op & rest :as form]] 66 | (if-let [[f & args] (and (seq? op) op)] 67 | (if-let [f-var (and (symbol? f) (resolve f))] 68 | (into [(seq (into [(symbol (str (ns-name (.ns f-var))) 69 | (str (.sym f-var)))] args))] 70 | rest) 71 | form) 72 | form)) where))))) 73 | 74 | (defn db 75 | "Given a list of ASTs, returns a representation of those 76 | that can be used as a database in a Datomic Datalog query." 77 | [asts] 78 | (mapcat ast/ast->eav asts)) 79 | 80 | (defn q 81 | "Execute a Datomic Datalog query against the ASTs. 82 | The first input is always assumed to be an AST database, if more 83 | are required, it's required to call `db` on them. 84 | `unfold-expression-clauses` is automatically applied to the 85 | query." 86 | [query asts & inputs] 87 | (compile-if (Class/forName "datomic.Datom") 88 | (do (require '[datomic.api :as d]) 89 | (apply (resolve 'datomic.api/q) (unfold-expression-clauses query) (db asts) inputs)) 90 | (throw (Exception. "Datomic is required")))) 91 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/env.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.env 10 | (:refer-clojure :exclude [ensure])) 11 | 12 | (def ^:dynamic *env* 13 | "Global env atom containing a map. 14 | Required options: 15 | * :namespaces a map from namespace symbol to namespace map, 16 | the namespace map contains at least the following keys: 17 | ** :mappings a map of mappings of the namespace, symbol to var/class 18 | ** :aliases a map of the aliases of the namespace, symbol to symbol 19 | ** :ns a symbol representing the namespace" 20 | nil) 21 | 22 | (defmacro with-env 23 | "Binds the global env to env, then executes the body" 24 | [env & body] 25 | `(let [env# ~env 26 | env# (cond 27 | (map? env#) (atom env#) 28 | (and (instance? clojure.lang.Atom env#) 29 | (map? @env#)) env# 30 | :default (throw (ex-info (str "global env must be a map or atom containing a map, not " 31 | (class env#)) 32 | {:env env#})))] 33 | (binding [*env* env#] ~@body))) 34 | 35 | ;; if *env* is not bound, bind it to env 36 | (defmacro ensure 37 | "If *env* is not bound it binds it to env before executing the body" 38 | [env & body] 39 | `(if *env* 40 | (do ~@body) 41 | (with-env ~env 42 | ~@body))) 43 | 44 | (defn deref-env 45 | "Returns the value of the current global env if bound, otherwise 46 | throws an exception." 47 | [] 48 | (if *env* 49 | @*env* 50 | (throw (Exception. "global env not bound")))) 51 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/add_binding_atom.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.add-binding-atom 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [prewalk]] 11 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.uniquify :refer [uniquify-locals]])) 12 | 13 | (defn add-binding-atom 14 | "Adds an atom-backed-map to every local binding,the same 15 | atom will be shared between all occurences of that local. 16 | 17 | The atom is put in the :atom field of the node." 18 | {:pass-info {:walk :pre :depends #{#'uniquify-locals} :state (fn [] (atom {}))}} 19 | ([ast] (prewalk ast (partial add-binding-atom (atom {})))) 20 | ([state ast] 21 | (case (:op ast) 22 | :binding 23 | (let [a (atom {})] 24 | (swap! state assoc (:name ast) a) 25 | (assoc ast :atom a)) 26 | :local 27 | (if-let [a (@state (:name ast))] 28 | (assoc ast :atom a) 29 | ;; handle injected locals 30 | (let [a (get-in ast [:env :locals (:name ast) :atom] (atom {}))] 31 | (swap! state assoc (:name ast) a) 32 | (assoc ast :atom a))) 33 | ast))) 34 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/cleanup.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.cleanup) 10 | 11 | (defn cleanup 12 | {:pass-info {:walk :any :depends #{}}} 13 | [ast] 14 | (-> ast 15 | (update-in [:env] dissoc :loop-locals-casts) 16 | (update-in [:env :locals] #(reduce-kv (fn [m k l] (assoc m k (dissoc l :env :init))) {} %)) 17 | (dissoc :atom))) 18 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/collect_closed_overs.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.collect-closed-overs 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [update-children]] 11 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.env :as env] 12 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.cleanup :refer [cleanup]] 13 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.uniquify :refer [uniquify-locals]])) 14 | 15 | (def ^:private ^:dynamic *collects*) 16 | 17 | (declare collect-closed-overs*) 18 | (defn -collect-closed-overs 19 | [ast] 20 | (-> (case (:op ast) 21 | :letfn ;; seed letfn bindings 22 | (let [bindings (:bindings ast)] 23 | (doseq [{:keys [name]} bindings] 24 | (swap! *collects* #(update-in % [:locals] conj name))) 25 | ast) 26 | :binding 27 | (let [name (:name ast)] 28 | (if (= :field (:local ast)) 29 | (swap! *collects* #(assoc-in % [:closed-overs name] (cleanup ast))) ;; special-case: put directly as closed-overs 30 | (swap! *collects* #(update-in % [:locals] conj name))) ;; register the local as a frame-local locals 31 | ast) 32 | :local 33 | (let [name (:name ast)] 34 | (when-not ((:locals @*collects*) name) ;; if the local is not in the frame-local locals 35 | (swap! *collects* #(assoc-in % [:closed-overs name] (cleanup ast)))) ;; then it's from the outer frame locals, thus a closed-over 36 | ast) 37 | ast) 38 | (update-children collect-closed-overs*))) ;; recursively collect closed-overs in the children nodes 39 | 40 | (defn collect-closed-overs* 41 | [{:keys [op] :as ast}] 42 | (let [collects @*collects* 43 | collect? ((:where collects) op)] 44 | (if collect? 45 | (let [[ast {:keys [closed-overs locals]}] 46 | (binding [*collects* (atom (merge @*collects* 47 | {:closed-overs {} :locals #{}}))] 48 | [(update-children ast -collect-closed-overs) @*collects*])] 49 | (swap! *collects* #(update-in % [:closed-overs] merge ;; propagate closed-overs from the inner frame to the outer frame 50 | (into {} 51 | (remove (fn [[_ {:keys [local]}]] ;; remove deftype fields from the closed-over locals 52 | (and (= op :deftype) 53 | (= :field local))) 54 | (apply dissoc closed-overs ;; remove from the closed-overs locals that were 55 | (:locals @*collects*)))))) ;; local to the inner frame 56 | (assoc ast :closed-overs closed-overs)) 57 | (-collect-closed-overs ast)))) 58 | 59 | (defn collect-closed-overs 60 | "Attach closed-overs info to the AST as specified by the passes opts: 61 | * :where set of :op nodes where to attach the closed-overs 62 | * :top-level? if true attach closed-overs info to the top-level node 63 | 64 | The info will be attached in the :closed-overs field of the AST node 65 | and will be a map of local name -> binding AST node" 66 | {:pass-info {:walk :none :depends #{#'uniquify-locals}}} 67 | [ast] 68 | (let [passes-opts (:passes-opts (env/deref-env)) 69 | {:keys [top-level?] :as opts} {:where (or (:collect-closed-overs/where passes-opts) #{}) 70 | :top-level? (:collect-closed-overs/top-level? passes-opts)}] 71 | (binding [*collects* (atom (merge opts {:closed-overs {} :locals #{}}))] 72 | (let [ast (collect-closed-overs* ast)] 73 | (if top-level? 74 | (assoc ast :closed-overs (:closed-overs @*collects*)) 75 | ast))))) 76 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/constant_lifter.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.constant-lifter 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [const-val]])) 11 | 12 | (defmulti constant-lift 13 | "If the node represents a collection with no metadata, and every item of that 14 | collection is a literal, transform the node to an equivalent :const node." 15 | {:pass-info {:walk :post :depends #{}}} 16 | :op) 17 | 18 | (defmethod constant-lift :vector 19 | [{:keys [items form env] :as ast}] 20 | (if (and (every? :literal? items) 21 | (empty? (meta form))) 22 | (merge (dissoc ast :items :children) 23 | {:op :const 24 | :val (mapv const-val items) 25 | :type :vector 26 | :literal? true}) 27 | ast)) 28 | 29 | (defmethod constant-lift :map 30 | [{:keys [keys vals form env] :as ast}] 31 | (if (and (every? :literal? keys) 32 | (every? :literal? vals) 33 | (empty? (meta form))) 34 | (let [c (into (empty form) 35 | (zipmap (mapv const-val keys) 36 | (mapv const-val vals))) 37 | c (if (= (class c) (class form)) 38 | c 39 | (apply array-map (mapcat identity c)))] 40 | (merge (dissoc ast :keys :vals :children) 41 | {:op :const 42 | :val c 43 | :type :map 44 | :literal? true})) 45 | ast)) 46 | 47 | (defmethod constant-lift :set 48 | [{:keys [items form env] :as ast}] 49 | (if (and (every? :literal? items) 50 | (empty? (meta form))) 51 | (merge (dissoc ast :items :children) 52 | {:op :const 53 | :val (into (empty form) (mapv const-val items)) 54 | :type :set 55 | :literal? true}) 56 | ast)) 57 | 58 | (defmethod constant-lift :default [ast] ast) 59 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/elide_meta.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.elide-meta 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.source-info :refer [source-info]])) 11 | 12 | (def ^:dynamic elides 13 | "A map of op keywords to predicate IFns. 14 | The predicate will be used to indicate what map keys should be elided on 15 | metadata of nodes for that op. 16 | :all can be used to indicate what should be elided for every node with 17 | metadata. 18 | Defaults to {:all (set (:elide-meta *compiler-options*))}" 19 | {:all (set (:elide-meta *compiler-options*))}) 20 | 21 | (defn replace-meta [meta new-meta] 22 | (if (= :const (:op meta)) 23 | (assoc meta :val new-meta) 24 | (let [meta-map (mapv (fn [k v] 25 | (when-not (elides (:form k)) 26 | [k v])) 27 | (:keys meta) (:vals meta))] 28 | (assoc meta 29 | :keys (vec (keep first meta-map)) 30 | :vals (vec (keep second meta-map)))))) 31 | 32 | (defn get-elides [{:keys [op expr type]}] 33 | (let [k (case op 34 | :with-meta 35 | (:op expr) 36 | 37 | :const 38 | type 39 | 40 | nil) 41 | f (get elides k)] 42 | (if f 43 | (some-fn (:all elides) f) 44 | (:all elides)))) 45 | 46 | (defn -elide-meta 47 | [{:keys [op meta expr env] :as ast}] 48 | (let [form (:form meta) 49 | new-meta (apply dissoc form (filter (get-elides ast) (keys form)))] 50 | (case op 51 | :const 52 | (if (or (not meta) 53 | (= new-meta (:form meta))) 54 | ast 55 | (if (not (empty? new-meta)) 56 | (assoc-in ast [:meta :val] new-meta) 57 | (-> ast 58 | (update-in [:val] with-meta nil) 59 | (dissoc :children :meta)))) 60 | :with-meta 61 | (if (not (empty? new-meta)) 62 | (if (= new-meta (:form meta)) 63 | ast 64 | (assoc ast :meta (replace-meta meta new-meta))) 65 | (merge (dissoc ast :meta :expr) 66 | {:op :do 67 | :body? true 68 | :ret expr 69 | :statements [] 70 | :children [:statements :ret]})) 71 | :def 72 | (if (not (empty? new-meta)) 73 | (if (= new-meta (:form meta)) 74 | ast 75 | (assoc ast :meta (replace-meta meta new-meta))) 76 | (let [ast (dissoc ast :meta)] 77 | (if-let [new-children (not-empty (filterv (complement #{:meta}) (:children ast)))] 78 | (assoc ast :children new-children) 79 | (dissoc ast :children)))) 80 | ast))) 81 | 82 | (defn elide-meta 83 | "If elides is not empty and the AST node contains metadata, 84 | dissoc all the keys in elides from the metadata." 85 | {:pass-info {:walk :any :depends #{} :after #{#'source-info}}} 86 | [ast] 87 | (if (some #(if (seq? %) (seq %) %) (vals elides)) 88 | (-elide-meta ast) 89 | ast)) 90 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/index_vector_nodes.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.index-vector-nodes) 10 | 11 | (defn index-vector-nodes 12 | "Adds an :idx attribute to nodes in a vector children, representing the position 13 | of the node vector." 14 | {:pass-info {:walk :any :depends #{}}} 15 | [ast] 16 | (merge ast 17 | (reduce (fn [m c] 18 | (let [v (c ast) 19 | v (if (vector? v) 20 | (mapv (fn [x i] (assoc x :idx i )) 21 | v (range)) 22 | v)] 23 | (assoc m c v))) {} (:children ast)))) 24 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/source_info.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.source-info 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [-source-info merge']] 11 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [update-children]])) 12 | 13 | (defn -merge-source-info [source-info] 14 | (fn [ast] 15 | (update-in ast [:env] merge' source-info))) 16 | 17 | (defn source-info 18 | "Adds (when avaliable) :line, :column, :end-line, :end-column and :file info to the AST :env" 19 | {:pass-info {:walk :pre :depends #{}}} 20 | [ast] 21 | (let [source-info (-source-info (:form ast) (:env ast)) 22 | merge-source-info (-merge-source-info source-info)] 23 | (update-children (merge-source-info ast) merge-source-info))) 24 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/trim.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.trim 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.elide-meta :refer [elide-meta]] 11 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [postwalk]])) 12 | 13 | (defmulti -trim :op) 14 | 15 | (defmethod -trim :default [ast] ast) 16 | 17 | (defn preserving-raw-forms [{:keys [form raw-forms] :as ast} body] 18 | (let [raw-forms (reverse (cons form raw-forms))] 19 | (update-in (into ast body) [:raw-forms] into raw-forms))) 20 | 21 | (defmethod -trim :do 22 | [{:keys [statements ret form] :as ast}] 23 | (if (and (every? :literal? statements) 24 | (not (:tag (meta form)))) 25 | (preserving-raw-forms (dissoc ast :children :statements :ret) ret) 26 | ast)) 27 | 28 | ;;TODO: letfn/loop 29 | (defmethod -trim :let 30 | [{:keys [bindings body form] :as ast}] 31 | (if (and (or (and (every? (comp :literal? :init) bindings) 32 | (:literal? body)) 33 | (empty? bindings)) 34 | (not (:tag (meta form)))) 35 | (preserving-raw-forms (dissoc ast :children :bindings :body) body) 36 | ast)) 37 | 38 | (defmethod -trim :try 39 | [{:keys [catches finally body form] :as ast}] 40 | (if (and (empty? catches) 41 | (empty? finally) 42 | (not (:tag (meta form)))) 43 | (preserving-raw-forms (dissoc ast :children :body :finally :catches) body) 44 | ast)) 45 | 46 | (defn trim 47 | "Trims the AST of unnecessary nodes, e.g. (do (do 1)) -> 1" 48 | {:pass-info {:walk :none :depends #{} :after #{#'elide-meta}}} 49 | [ast] 50 | (postwalk ast -trim)) 51 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/uniquify.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.uniquify 10 | (:refer-clojure :exclude [update-vals]) 11 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [update-children children]] 12 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [update-vals]] 13 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.env :as env])) 14 | 15 | (def ^:dynamic *locals-counter*) ;; global counter, map sym -> count 16 | (def ^:dynamic *locals-frame*) ;; holds the id for the locals in the current frame 17 | 18 | (defn normalize [name] 19 | (or (@*locals-frame* name) name)) 20 | 21 | (defn uniquify [name] 22 | (swap! *locals-counter* #(update-in % [name] (fnil inc -1))) 23 | (swap! *locals-frame* #(assoc-in % [name] (symbol (str name "__#" (@*locals-counter* name)))))) 24 | 25 | (defmulti -uniquify-locals :op) 26 | 27 | (defn uniquify-locals-around 28 | [ast] 29 | (let [ast (if (-> (env/deref-env) :passes-opts :uniquify/uniquify-env) 30 | (update-in ast [:env :locals] 31 | update-vals #(update-in % [:name] normalize)) 32 | ast)] 33 | (-uniquify-locals ast))) 34 | 35 | (defn uniquify-locals* [ast] 36 | (update-children ast uniquify-locals-around)) 37 | 38 | (defmethod -uniquify-locals :local 39 | [ast] 40 | (if (= :field (:local ast)) ;; deftype fields cannot be uniquified 41 | ast ;; to allow field access/set! to work 42 | (let [name (normalize (:name ast))] 43 | (assoc ast :name name)))) 44 | 45 | (defn uniquify-binding 46 | [b] 47 | (let [i (binding [*locals-frame* (atom @*locals-frame*)] ;; inits need to be uniquified before the local 48 | (uniquify-locals-around (:init b))) ;; to avoid potential shadowings 49 | name (:name b)] 50 | (uniquify name) 51 | (let [name (normalize name)] 52 | (assoc b 53 | :name name 54 | :init i)))) 55 | 56 | (defmethod -uniquify-locals :letfn 57 | [ast] 58 | (doseq [{:keys [name]} (:bindings ast)] ;; take into account that letfn 59 | (uniquify name)) ;; accepts parallel bindings 60 | (uniquify-locals* ast)) 61 | 62 | (defmethod -uniquify-locals :binding 63 | [{:keys [name local] :as ast}] 64 | (case local 65 | (:let :loop) 66 | (uniquify-binding ast) 67 | 68 | :letfn 69 | (-> ast 70 | (assoc :name (normalize name)) 71 | uniquify-locals*) 72 | 73 | :field 74 | ast 75 | 76 | (do (uniquify name) 77 | (assoc ast :name (normalize name))))) 78 | 79 | (defmethod -uniquify-locals :default 80 | [ast] 81 | (if (some #(= :binding (:op %)) (children ast)) 82 | (binding [*locals-frame* (atom @*locals-frame*)] ;; set up frame so locals won't leak 83 | (uniquify-locals* ast)) 84 | (uniquify-locals* ast))) 85 | 86 | (defn uniquify-locals 87 | "Walks the AST performing alpha-conversion on the :name field 88 | of :local/:binding nodes, invalidates :local map in :env field 89 | 90 | Passes opts: 91 | * :uniquify/uniquify-env If true, uniquifies the :env :locals map" 92 | {:pass-info {:walk :none :depends #{}}} 93 | [ast] 94 | (binding [*locals-counter* (atom {}) 95 | *locals-frame* (atom {})] 96 | (uniquify-locals-around ast))) 97 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep1/clojure/tools/analyzer/passes/warn_earmuff.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.warn-earmuff 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [dynamic?]])) 11 | 12 | (defn warn-earmuff 13 | "Prints a warning to *err* if the AST node is a :def node and the 14 | var name contains earmuffs but the var is not marked dynamic" 15 | {:pass-info {:walk :pre :depends #{}}} 16 | [ast] 17 | (let [name (str (:name ast))] 18 | (when (and (= :def (:op ast)) 19 | (> (count name) 2) ;; Allow * and ** as non-dynamic names 20 | (.startsWith name "*") 21 | (.endsWith name "*") 22 | (not (dynamic? (:var ast) (:val (:meta ast))))) 23 | (binding [*out* *err*] 24 | (println "Warning:" name "not declared dynamic and thus is not dynamically rebindable," 25 | "but its name suggests otherwise." 26 | "Please either indicate ^:dynamic" name "or change the name")))) 27 | ast) 28 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep10/clojure/tools/reader/impl/inspect.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Russ Olsen, Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep10.clojure.tools.reader.impl.inspect) 10 | 11 | (declare inspect*) 12 | 13 | (defn- inspect*-col [truncate col start end] 14 | (let [n (count col) 15 | l (if truncate 0 (min 10 n)) 16 | elements (map (partial inspect* true) (take l col)) 17 | content (apply str (interpose " " elements)) 18 | suffix (if (< l n) "...")] 19 | (str start content suffix end))) 20 | 21 | (defn- dispatch-inspect 22 | [_ x] 23 | (cond 24 | (nil? x) :nil 25 | (string? x) :string 26 | (keyword? x) :strable 27 | (number? x) :strable 28 | (symbol? x) :strable 29 | (vector? x) :vector 30 | (list? x) :list 31 | (map? x) :map 32 | (set? x) :set 33 | (= x true) :strable 34 | (= x false) :strable 35 | :default (class x))) 36 | 37 | (defmulti inspect* dispatch-inspect) 38 | 39 | (defmethod inspect* :string [truncate ^String x] 40 | (let [n (if truncate 5 20) 41 | suffix (if (> (.length x) n) "...\"" "\"")] 42 | (str 43 | \" 44 | (.substring ^String x 0 (min n (.length x))) 45 | suffix))) 46 | 47 | (defmethod inspect* :strable [truncate x] (str x)) 48 | 49 | (defmethod inspect* clojure.lang.PersistentVector$ChunkedSeq [truncate x] 50 | "") 51 | 52 | (defmethod inspect* clojure.lang.PersistentArrayMap$Seq [truncate x] 53 | "") 54 | 55 | (defmethod inspect* clojure.lang.PersistentHashMap$NodeSeq [truncate x] 56 | "") 57 | 58 | (defmethod inspect* clojure.lang.Cons [truncate x] "") 59 | 60 | (defmethod inspect* clojure.lang.LazySeq [truncate x] "") 61 | 62 | (defmethod inspect* :nil [_ _] "nil") 63 | 64 | (defmethod inspect* :list [truncate col] 65 | (inspect*-col truncate col \( \))) 66 | 67 | (defmethod inspect* :map [truncate m] 68 | (let [len (count m) 69 | n-shown (if truncate 0 len) 70 | contents (apply concat (take n-shown m)) 71 | suffix (if (> len n-shown) "...}" \})] 72 | (inspect*-col truncate contents \{ suffix))) 73 | 74 | (defmethod inspect* :set [truncate col] 75 | (inspect*-col truncate col "#{" \})) 76 | 77 | (defmethod inspect* :vector [truncate col] 78 | (inspect*-col truncate col \[ \])) 79 | 80 | (defmethod inspect* :default [truncate x] 81 | (let [classname (if (nil? x) "nil" (.getName (class x)))] 82 | (str "<" classname ">"))) 83 | 84 | (defn inspect 85 | "Return a string description of the value supplied. 86 | May be the a string version of the value itself (e.g. \"true\") 87 | or it may be a description (e.g. \"an instance of Foo\"). 88 | If truncate is true then return a very terse version of 89 | the inspection." 90 | ([x] (inspect* false x)) 91 | ([truncate x] (inspect* truncate x))) 92 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep10/clojure/tools/reader/impl/utils.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns ^:skip-wiki eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils 10 | (:refer-clojure :exclude [char reader-conditional tagged-literal])) 11 | 12 | (defn char [x] 13 | (when x 14 | (clojure.core/char x))) 15 | 16 | (def <=clojure-1-7-alpha5 17 | (let [{:keys [minor qualifier]} *clojure-version*] 18 | (or (< minor 7) 19 | (and (= minor 7) 20 | (= "alpha" 21 | (when qualifier 22 | (subs qualifier 0 (dec (count qualifier))))) 23 | (<= (read-string (subs qualifier (dec (count qualifier)))) 24 | 5))))) 25 | 26 | (defmacro compile-when [cond & then] 27 | (when (eval cond) 28 | `(do ~@then))) 29 | 30 | (defn ex-info? [ex] 31 | (instance? clojure.lang.ExceptionInfo ex)) 32 | 33 | (compile-when <=clojure-1-7-alpha5 34 | (defrecord TaggedLiteral [tag form]) 35 | 36 | (defn tagged-literal? 37 | "Return true if the value is the data representation of a tagged literal" 38 | [value] 39 | (instance? eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.TaggedLiteral value)) 40 | 41 | (defn tagged-literal 42 | "Construct a data representation of a tagged literal from a 43 | tag symbol and a form." 44 | [tag form] 45 | (eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.TaggedLiteral. tag form)) 46 | 47 | (ns-unmap *ns* '->TaggedLiteral) 48 | (ns-unmap *ns* 'map->TaggedLiteral) 49 | 50 | (defmethod print-method eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.TaggedLiteral [o ^java.io.Writer w] 51 | (.write w "#") 52 | (print-method (:tag o) w) 53 | (.write w " ") 54 | (print-method (:form o) w)) 55 | 56 | (defrecord ReaderConditional [splicing? form]) 57 | (ns-unmap *ns* '->ReaderConditional) 58 | (ns-unmap *ns* 'map->ReaderConditional) 59 | 60 | (defn reader-conditional? 61 | "Return true if the value is the data representation of a reader conditional" 62 | [value] 63 | (instance? eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.ReaderConditional value)) 64 | 65 | (defn reader-conditional 66 | "Construct a data representation of a reader conditional. 67 | If true, splicing? indicates read-cond-splicing." 68 | [form splicing?] 69 | (eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.ReaderConditional. splicing? form)) 70 | 71 | (defmethod print-method eastwood.copieddeps.dep10.clojure.tools.reader.impl.utils.ReaderConditional [o ^java.io.Writer w] 72 | (.write w "#?") 73 | (when (:splicing? o) (.write w "@")) 74 | (print-method (:form o) w))) 75 | 76 | (defn whitespace? 77 | "Checks whether a given character is whitespace" 78 | [ch] 79 | (when ch 80 | (or (Character/isWhitespace ^Character ch) 81 | (identical? \, ch)))) 82 | 83 | (defn numeric? 84 | "Checks whether a given character is numeric" 85 | [^Character ch] 86 | (when ch 87 | (Character/isDigit ch))) 88 | 89 | (defn newline? 90 | "Checks whether the character is a newline" 91 | [c] 92 | (or (identical? \newline c) 93 | (nil? c))) 94 | 95 | (defn desugar-meta 96 | "Resolves syntactical sugar in metadata" ;; could be combined with some other desugar? 97 | [f] 98 | (cond 99 | (keyword? f) {f true} 100 | (symbol? f) {:tag f} 101 | (string? f) {:tag f} 102 | :else f)) 103 | 104 | (defn make-var 105 | "Returns an anonymous unbound Var" 106 | [] 107 | (with-local-vars [x nil] x)) 108 | 109 | (defn namespace-keys [ns keys] 110 | (for [key keys] 111 | (if (or (symbol? key) 112 | (keyword? key)) 113 | (let [[key-ns key-name] ((juxt namespace name) key) 114 | ->key (if (symbol? key) symbol keyword)] 115 | (cond 116 | (nil? key-ns) 117 | (->key ns key-name) 118 | 119 | (= "_" key-ns) 120 | (->key key-name) 121 | 122 | :else 123 | key)) 124 | key))) 125 | 126 | (defn second' [[a b]] 127 | (when-not a b)) 128 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep11/clojure/java/classpath.clj: -------------------------------------------------------------------------------- 1 | ;;; classpath.clj: utilities for working with the Java class path 2 | 3 | ;; by Stuart Sierra, http://stuartsierra.com/ 4 | ;; April 19, 2009 5 | 6 | ;; Copyright (c) Rich Hickey, Stuart Sierra, and contributors. 7 | ;; All rights reserved. The use 8 | ;; and distribution terms for this software are covered by the Eclipse 9 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 10 | ;; which can be found in the file epl-v10.html at the root of this 11 | ;; distribution. By using this software in any fashion, you are 12 | ;; agreeing to be bound by the terms of this license. You must not 13 | ;; remove this notice, or any other, from this software. 14 | 15 | 16 | (ns 17 | ^{:author "Stuart Sierra" 18 | :doc "Utilities for dealing with the JVM's classpath"} 19 | eastwood.copieddeps.dep11.clojure.java.classpath 20 | (:require [clojure.java.io :as io]) 21 | (:import (java.io File) 22 | (java.util.jar JarFile JarEntry) 23 | (java.net URL URLClassLoader))) 24 | 25 | (defprotocol URLClasspath 26 | (urls [loader] 27 | "Returns a sequence of java.net.URL objects representing locations 28 | which this classloader will search for classes and resources.")) 29 | 30 | (extend-type java.net.URLClassLoader 31 | URLClasspath 32 | (urls [loader] (seq (.getURLs loader)))) 33 | 34 | (defn get-urls 35 | "Returns a sequence of java.net.URL objects used by this 36 | classloader, or nil if the classloader does not sastify the 37 | URLClasspath protocol." 38 | [loader] 39 | (when (satisfies? URLClasspath loader) 40 | (urls loader))) 41 | 42 | (defn jar-file? 43 | "Returns true if file is a normal file with a .jar or .JAR extension." 44 | [f] 45 | (let [file (io/file f)] 46 | (and (.isFile file) 47 | (or (.endsWith (.getName file) ".jar") 48 | (.endsWith (.getName file) ".JAR"))))) 49 | 50 | (defn filenames-in-jar 51 | "Returns a sequence of Strings naming the non-directory entries in 52 | the JAR file." 53 | [^JarFile jar-file] 54 | (map #(.getName ^JarEntry %) 55 | (filter #(not (.isDirectory ^JarEntry %)) 56 | (enumeration-seq (.entries jar-file))))) 57 | 58 | (defn system-classpath 59 | "Returns a sequence of File paths from the 'java.class.path' system 60 | property." 61 | [] 62 | (map #(File. ^String %) 63 | (.split (System/getProperty "java.class.path") 64 | (System/getProperty "path.separator")))) 65 | 66 | (defn loader-classpath 67 | "Returns a sequence of File paths from a classloader." 68 | [loader] 69 | (map io/as-file (get-urls loader))) 70 | 71 | (defn classpath 72 | "Returns a sequence of File objects of the elements on the 73 | classpath. Defaults to searching for instances of 74 | java.net.URLClassLoader in the classloader hierarchy above 75 | clojure.lang.RT/baseLoader or the given classloader. If no 76 | URLClassloader can be found, as on Java 9, falls back to the 77 | 'java.class'path' system property." 78 | ([classloader] 79 | (distinct 80 | (mapcat 81 | loader-classpath 82 | (take-while 83 | identity 84 | (iterate #(.getParent ^ClassLoader %) classloader))))) 85 | ([] 86 | (or (seq (classpath (clojure.lang.RT/baseLoader))) 87 | (system-classpath)))) 88 | 89 | (defn classpath-directories 90 | "Returns a sequence of File objects for the directories on classpath." 91 | [] 92 | (filter #(.isDirectory ^File %) (classpath))) 93 | 94 | (defn classpath-jarfiles 95 | "Returns a sequence of JarFile objects for the JAR files on classpath." 96 | [] 97 | (map #(JarFile. ^File %) (filter jar-file? (classpath)))) 98 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/annotate_branch.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.annotate-branch) 10 | 11 | (defmulti annotate-branch 12 | "Adds :branch? to branch AST nodes (if/case), :test? to the test children 13 | node of the branching op and :path? to the branching paths. 14 | 15 | Example: {:op if :branch? true :test {:test? true ..} :then {:path? true ..} ..}" 16 | {:pass-info {:walk :any :depends #{}}} 17 | :op) 18 | 19 | (defmethod annotate-branch :if 20 | [ast] 21 | (-> ast 22 | (assoc :branch? true) 23 | (assoc-in [:test :test?] true) 24 | (assoc-in [:then :path?] true) 25 | (assoc-in [:else :path?] true))) 26 | 27 | (defmethod annotate-branch :fn-method 28 | [ast] 29 | (assoc ast :path? true)) 30 | 31 | (defmethod annotate-branch :method 32 | [ast] 33 | (assoc ast :path? true)) 34 | 35 | (defmethod annotate-branch :case 36 | [ast] 37 | (-> ast 38 | (assoc :branch? true) 39 | (assoc-in [:test :test?] true) 40 | (assoc-in [:default :path?] true))) 41 | 42 | (defmethod annotate-branch :case-then 43 | [ast] 44 | (assoc ast :path? true)) 45 | 46 | (defmethod annotate-branch :default [ast] ast) 47 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/annotate_loops.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.annotate-loops 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [update-children]])) 11 | 12 | (defmulti annotate-loops 13 | "Adds a :loops field to nodes that represent a code path that 14 | might be visited more than once because of a recur. 15 | 16 | The field is a set of loop-ids representing the loops that might 17 | recur into that path 18 | 19 | Note that because (recur expr) is equivalent to (let [e expr] (recur e)) 20 | the node corresponting to expr will have the same :loops field 21 | as the nodes in the same code path of the recur" 22 | {:pass-info {:walk :pre :depends #{}}} 23 | :op) 24 | 25 | (defmulti check-recur :op) 26 | 27 | (defn -check-recur [ast k] 28 | (let [ast (update-in ast [k] check-recur)] 29 | (if (:recurs (k ast)) 30 | (assoc ast :recurs true) 31 | ast))) 32 | 33 | (defmethod check-recur :do 34 | [ast] 35 | (let [ast (-check-recur ast :ret)] 36 | (if (:recurs ast) 37 | (assoc ast :statements (mapv (fn [s] (assoc s :recurs true)) (:statements ast))) 38 | ast))) 39 | 40 | (defmethod check-recur :let 41 | [ast] 42 | (-check-recur ast :body)) 43 | 44 | (defmethod check-recur :letfn 45 | [ast] 46 | (-check-recur ast :body)) 47 | 48 | (defmethod check-recur :if 49 | [ast] 50 | (-> ast 51 | (-check-recur :then) 52 | (-check-recur :else))) 53 | 54 | (defmethod check-recur :case 55 | [ast] 56 | (let [ast (-> ast 57 | (-check-recur :default) 58 | (update-in [:thens] #(mapv check-recur %)))] 59 | (if (some :recurs (:thens ast)) 60 | (assoc ast :recurs true) 61 | ast))) 62 | 63 | (defmethod check-recur :case-then 64 | [ast] 65 | (-check-recur ast :then)) 66 | 67 | (defmethod check-recur :recur 68 | [ast] 69 | (assoc ast :recurs true)) 70 | 71 | (defmethod check-recur :default 72 | [ast] 73 | ast) 74 | 75 | (defn -loops [ast loop-id] 76 | (update-in ast [:loops] (fnil conj #{}) loop-id)) 77 | 78 | (defmethod annotate-loops :loop 79 | [{:keys [loops loop-id] :as ast}] 80 | (let [ast (if loops 81 | (update-children ast #(assoc % :loops loops)) 82 | ast) 83 | ast (update-in ast [:body] check-recur)] 84 | (if (-> ast :body :recurs) 85 | (update-in ast [:body] -loops loop-id) 86 | ast))) 87 | 88 | (defmethod annotate-loops :default 89 | [{:keys [loops] :as ast}] 90 | (if loops 91 | (update-children ast #(assoc % :loops loops)) 92 | ast)) 93 | 94 | (defmethod annotate-loops :if 95 | [{:keys [loops test then else env] :as ast}] 96 | (if loops 97 | (let [loop-id (:loop-id env) 98 | loops-no-recur (disj loops loop-id) 99 | branch-recurs? (or (:recurs then) (:recurs else)) 100 | then (if (or (:recurs then) ;; the recur is inside the then branch 101 | ;; the recur is in the same code path of the if expression 102 | (not branch-recurs?)) 103 | (assoc then :loops loops) 104 | (assoc then :loops loops-no-recur)) 105 | else (if (or (:recurs else) (not branch-recurs?)) 106 | (assoc else :loops loops) 107 | (assoc else :loops loops-no-recur))] 108 | (assoc ast 109 | :then then 110 | :else else 111 | :test (assoc test :loops loops))) 112 | ast)) 113 | 114 | (defmethod annotate-loops :case 115 | [{:keys [loops test default thens env] :as ast}] 116 | (if loops 117 | (let [loop-id (:loop-id env) 118 | loops-no-recur (disj loops loop-id) 119 | branch-recurs? (some :recurs (conj thens default)) 120 | 121 | default (if (or (:recurs default) (not branch-recurs?)) 122 | (assoc default :loops loops) 123 | (assoc default :loops loops-no-recur)) 124 | 125 | thens (mapv #(if (or (:recurs %) (not branch-recurs?)) 126 | (assoc % :loops loops) 127 | (assoc % :loops loops-no-recur)) thens)] 128 | (assoc ast 129 | :thens thens 130 | :default default 131 | :test (assoc test :loops loops))) 132 | ast)) 133 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/annotate_tag.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.annotate-tag 10 | (:require [eastwood.copieddeps.dep2.clojure.tools.analyzer.jvm.utils :refer [unbox maybe-class]] 11 | [eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.constant-lifter :refer [constant-lift]]) 12 | (:import (clojure.lang ISeq Var AFunction))) 13 | 14 | (defmulti -annotate-tag :op) 15 | 16 | (defmethod -annotate-tag :default [ast] ast) 17 | 18 | (defmethod -annotate-tag :map 19 | [{:keys [val form] :as ast}] 20 | (let [t (class (or val form))] 21 | (assoc ast :o-tag t :tag t))) 22 | 23 | (defmethod -annotate-tag :set 24 | [{:keys [val form] :as ast}] 25 | (let [t (class (or val form))] 26 | (assoc ast :o-tag t :tag t))) 27 | 28 | (defmethod -annotate-tag :vector 29 | [{:keys [val form] :as ast}] 30 | (let [t (class (or val form))] 31 | (assoc ast :o-tag t :tag t))) 32 | 33 | (defmethod -annotate-tag :the-var 34 | [ast] 35 | (assoc ast :o-tag Var :tag Var)) 36 | 37 | (defmethod -annotate-tag :const 38 | [ast] 39 | (case (:type ast) 40 | 41 | ;; char and numbers are unboxed by default 42 | :number 43 | (let [t (unbox (class (:val ast)))] 44 | (assoc ast :o-tag t :tag t)) 45 | 46 | :char 47 | (assoc ast :o-tag Character/TYPE :tag Character/TYPE) 48 | 49 | :seq 50 | (assoc ast :o-tag ISeq :tag ISeq) 51 | 52 | (let [t (class (:val ast))] 53 | (assoc ast :o-tag t :tag t)))) 54 | 55 | (defmethod -annotate-tag :binding 56 | [{:keys [form tag atom o-tag init local name variadic?] :as ast}] 57 | (let [o-tag (or (:tag init) ;; should defer to infer-tag? 58 | (and (= :fn local) AFunction) 59 | (and (= :arg local) variadic? ISeq) 60 | o-tag 61 | Object) 62 | o-tag (if (#{Void Void/TYPE} o-tag) 63 | Object 64 | o-tag)] 65 | (if-let [tag (or (:tag (meta form)) tag)] 66 | (let [ast (assoc ast :tag tag :o-tag tag)] 67 | (if init 68 | (assoc-in ast [:init :tag] (maybe-class tag)) 69 | ast)) 70 | (assoc ast :tag o-tag :o-tag o-tag)))) 71 | 72 | (defmethod -annotate-tag :local 73 | [{:keys [name form tag atom case-test] :as ast}] 74 | (let [o-tag (@atom :tag)] 75 | (assoc ast :o-tag o-tag :tag o-tag))) 76 | 77 | ;; TODO: move binding/local logic to infer-tag 78 | (defn annotate-tag 79 | "If the AST node type is a constant object or contains :tag metadata, 80 | attach the appropriate :tag and :o-tag to the node." 81 | {:pass-info {:walk :post :depends #{} :after #{#'constant-lift}}} 82 | [{:keys [op tag o-tag atom] :as ast}] 83 | (let [ast (if (and atom (:case-test @atom)) 84 | (update-in ast [:form] vary-meta dissoc :tag) 85 | ast) 86 | ast 87 | (if (and o-tag tag) 88 | ast 89 | (if-let [tag (or tag 90 | (-> ast :val meta :tag) 91 | (-> ast :form meta :tag))] 92 | (assoc (-annotate-tag ast) :tag tag) 93 | (-annotate-tag ast)))] 94 | (when (= op :binding) 95 | (swap! atom assoc :tag (:tag ast))) 96 | ast)) 97 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/classify_invoke.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.classify-invoke 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [arglist-for-arity protocol-node? source-info]] 11 | [eastwood.copieddeps.dep2.clojure.tools.analyzer.jvm.utils 12 | :refer [specials prim-interface]] 13 | [eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.validate :refer [validate]])) 14 | 15 | (defn classify-invoke 16 | "If the AST node is an :invoke, check the node in function position, 17 | * if it is a keyword, transform the node in a :keyword-invoke node; 18 | * if it is the clojure.core/instance? var and the first argument is a 19 | literal class, transform the node in a :instance? node to be inlined by 20 | the emitter 21 | * if it is a protocol function var, transform the node in a :protocol-invoke 22 | node 23 | * if it is a regular function with primitive type hints that match a 24 | clojure.lang.IFn$[primitive interface], transform the node in a :prim-invoke 25 | node" 26 | {:pass-info {:walk :post :depends #{#'validate}}} 27 | [{:keys [op args tag env form] :as ast}] 28 | (if-not (= op :invoke) 29 | ast 30 | (let [argc (count args) 31 | the-fn (:fn ast) 32 | op (:op the-fn) 33 | var? (= :var op) 34 | the-var (:var the-fn)] 35 | 36 | (cond 37 | 38 | (and (= :const op) 39 | (= :keyword (:type the-fn))) 40 | (if (<= 1 argc 2) 41 | (if (and (not (namespace (:val the-fn))) 42 | (= 1 argc)) 43 | (merge (dissoc ast :fn :args) 44 | {:op :keyword-invoke 45 | :target (first args) 46 | :keyword the-fn 47 | :children [:keyword :target]}) 48 | ast) 49 | (throw (ex-info (str "Cannot invoke keyword with " argc " arguments") 50 | (merge {:form form} 51 | (source-info env))))) 52 | (and (= 2 argc) 53 | var? 54 | (= #'clojure.core/instance? the-var) 55 | (= :const (:op (first args))) 56 | (= :class (:type (first args)))) 57 | (merge (dissoc ast :fn :args) 58 | {:op :instance? 59 | :class (:val (first args)) 60 | :target (second args) 61 | :form form 62 | :env env 63 | :o-tag Boolean/TYPE 64 | :tag (or tag Boolean/TYPE) 65 | :children [:target]}) 66 | 67 | (and var? (protocol-node? the-var (:meta the-fn))) 68 | (if (>= argc 1) 69 | (merge (dissoc ast :fn) 70 | {:op :protocol-invoke 71 | :protocol-fn the-fn 72 | :target (first args) 73 | :args (vec (rest args)) 74 | :children [:protocol-fn :target :args]}) 75 | (throw (ex-info "Cannot invoke protocol method with no args" 76 | (merge {:form form} 77 | (source-info env))))) 78 | 79 | :else 80 | (let [arglist (arglist-for-arity the-fn argc) 81 | arg-tags (mapv (comp specials str :tag meta) arglist) 82 | ret-tag (-> arglist meta :tag str specials) 83 | tags (conj arg-tags ret-tag)] 84 | (if-let [prim-interface (prim-interface (mapv #(if (nil? %) Object %) tags))] 85 | (merge ast 86 | {:op :prim-invoke 87 | :prim-interface prim-interface 88 | :args (mapv (fn [arg tag] (assoc arg :tag tag)) args arg-tags) 89 | :o-tag ret-tag 90 | :tag (or tag ret-tag)}) 91 | ast)))))) 92 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/constant_lifter.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.constant-lifter 2 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.constant-lifter :as orig] 3 | [eastwood.copieddeps.dep1.clojure.tools.analyzer :refer [analyze-const]] 4 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [constant? classify]] 5 | [eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.analyze-host-expr :refer [analyze-host-expr]] 6 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.elide-meta :refer [elide-meta]])) 7 | 8 | (defn constant-lift* 9 | [ast] 10 | (if (= :var (:op ast)) 11 | (let [{:keys [var env form meta]} ast] 12 | (if (constant? var meta) 13 | (let [val @var] 14 | (assoc (analyze-const val env (classify val)) 15 | :form form)) 16 | ast)) 17 | (orig/constant-lift ast))) 18 | 19 | (defn constant-lift 20 | "Like eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.constant-lifter/constant-lift but 21 | transforms also :var nodes where the var has :const in the metadata 22 | into :const nodes and preserves tag info" 23 | {:pass-info {:walk :post :depends #{} :after #{#'elide-meta #'analyze-host-expr}}} 24 | [ast] 25 | (merge (constant-lift* ast) 26 | (select-keys ast [:tag :o-tag :return-tag :arglists]))) 27 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/fix_case_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.fix-case-test 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.passes.add-binding-atom :refer [add-binding-atom]])) 11 | 12 | (defn fix-case-test 13 | "If the node is a :case-test, annotates in the atom shared 14 | by the binding and the local node with :case-test" 15 | {:pass-info {:walk :pre :depends #{#'add-binding-atom}}} 16 | [ast] 17 | (when (:case-test ast) 18 | (swap! (:atom ast) assoc :case-test true)) 19 | ast) 20 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/validate_recur.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.validate-recur 10 | (:require [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :refer [update-children]] 11 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.utils :refer [-source-info]])) 12 | 13 | (defmulti validate-recur 14 | "Ensures recurs don't cross try boundaries" 15 | {:pass-info {:walk :pre :depends #{}}} 16 | :op) 17 | 18 | (defmethod validate-recur :default [ast] 19 | (if (-> ast :env :no-recur) 20 | (update-children ast (fn [ast] (update-in ast [:env] assoc :no-recur true))) 21 | ast)) 22 | 23 | (defmethod validate-recur :try [ast] 24 | (update-children ast (fn [ast] (update-in ast [:env] assoc :no-recur true)))) 25 | 26 | (defmethod validate-recur :fn-method [ast] 27 | (update-in ast [:env] dissoc :no-recur)) 28 | 29 | (defmethod validate-recur :method [ast] 30 | (update-in ast [:env] dissoc :no-recur)) 31 | 32 | (defmethod validate-recur :loop [ast] 33 | (update-in ast [:env] dissoc :no-recur)) 34 | 35 | (defmethod validate-recur :recur [ast] 36 | (when (-> ast :env :no-recur) 37 | (throw (ex-info "Cannot recur across try" 38 | (merge {:form (:form ast)} 39 | (-source-info (:form ast) (:env ast)))))) 40 | ast) 41 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep2/clojure/tools/analyzer/passes/jvm/warn_on_reflection.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Nicola Mometto, Rich Hickey & contributors. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm.warn-on-reflection 10 | (:require [eastwood.copieddeps.dep2.clojure.tools.analyzer.passes.jvm 11 | [validate-loop-locals :refer [validate-loop-locals]] 12 | [validate :refer [validate]]])) 13 | 14 | (defn warn [what {:keys [file line column]}] 15 | (when *warn-on-reflection* 16 | (binding [*err* *out*] 17 | (println (str "Reflection warning: " 18 | (when file 19 | (str file ":")) 20 | (when line 21 | (str line ":")) 22 | (when column 23 | (str column " ")) 24 | "- " what))))) 25 | 26 | (defmulti warn-on-reflection 27 | "Prints a warning to *err* when *warn-on-reflection* is true 28 | and a node requires runtime reflection" 29 | {:pass-info {:walk :pre :depends #{#'validate} :after #{#'validate-loop-locals}}} 30 | :op) 31 | 32 | (defmethod warn-on-reflection :instance-call 33 | [ast] 34 | (when-not (:validated? ast) 35 | (warn (str "call to method " (:method ast) (when-let [class (:class ast)] 36 | (str " on " (.getName ^Class class))) 37 | " cannot be resolved") (:env ast))) 38 | ast) 39 | 40 | (defmethod warn-on-reflection :static-call 41 | [ast] 42 | (when-not (:validated? ast) 43 | (warn (str "call to static method " (:method ast) " on " 44 | (.getName ^Class (:class ast)) " cannot be resolved") 45 | (:env ast))) 46 | ast) 47 | 48 | (defmethod warn-on-reflection :host-interop 49 | [ast] 50 | (warn (str "reference to field or no args method call " (:m-or-f ast) 51 | " cannot be resolved") 52 | (:env ast)) 53 | ast) 54 | 55 | (defmethod warn-on-reflection :new 56 | [ast] 57 | (when-not (:validated? ast) 58 | (warn (str "call to " (.getName ^Class (:val (:class ast))) " ctor cannot be resolved") 59 | (:env ast))) 60 | ast) 61 | 62 | (defmethod warn-on-reflection :default [ast] ast) 63 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep9/clojure/tools/namespace/file.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and 2 | ;; distribution terms for this software are covered by the Eclipse 3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this 5 | ;; distribution. By using this software in any fashion, you are 6 | ;; agreeing to be bound by the terms of this license. You must not 7 | ;; remove this notice, or any other, from this software. 8 | 9 | (ns ^{:author "Stuart Sierra" 10 | :doc "Read and track namespace information from files"} 11 | eastwood.copieddeps.dep9.clojure.tools.namespace.file 12 | (:require [clojure.java.io :as io] 13 | [eastwood.copieddeps.dep9.clojure.tools.namespace.parse :as parse] 14 | [eastwood.copieddeps.dep9.clojure.tools.namespace.track :as track]) 15 | (:import (java.io PushbackReader))) 16 | 17 | (defn read-file-ns-decl 18 | "Attempts to read a (ns ...) declaration from file, and returns the 19 | unevaluated form. Returns nil if ns declaration cannot be found. 20 | read-opts is passed through to tools.reader/read." 21 | ([file] 22 | (read-file-ns-decl file nil)) 23 | ([file read-opts] 24 | (with-open [rdr (PushbackReader. (io/reader file))] 25 | (parse/*read-ns-decl* rdr read-opts)))) 26 | 27 | (def ^:dynamic *read-file-ns-decl* read-file-ns-decl) 28 | 29 | (defn file-with-extension? 30 | "Returns true if the java.io.File represents a file whose name ends 31 | with one of the Strings in extensions." 32 | {:added "0.3.0"} 33 | [^java.io.File file extensions] 34 | (and (.isFile file) 35 | (let [name (.getName file)] 36 | (some #(.endsWith name %) extensions)))) 37 | 38 | (def ^{:added "0.3.0"} 39 | clojure-extensions 40 | "File extensions for Clojure (JVM) files." 41 | (list ".clj" ".cljc")) 42 | 43 | (def ^{:added "0.3.0"} 44 | clojurescript-extensions 45 | "File extensions for ClojureScript files." 46 | (list ".cljs" ".cljc")) 47 | 48 | (defn clojure-file? 49 | "Returns true if the java.io.File represents a file which will be 50 | read by the Clojure (JVM) compiler." 51 | [^java.io.File file] 52 | (file-with-extension? file clojure-extensions)) 53 | 54 | (defn clojurescript-file? 55 | "Returns true if the java.io.File represents a file which will be 56 | read by the ClojureScript compiler." 57 | {:added "0.3.0"} 58 | [^java.io.File file] 59 | (file-with-extension? file clojurescript-extensions)) 60 | 61 | ;;; Dependency tracker 62 | 63 | (defn- files-and-deps [files read-opts] 64 | (let [files (->> files 65 | (pmap (fn [file] 66 | (when-let [decl (*read-file-ns-decl* file read-opts)] 67 | (let [deps (parse/deps-from-ns-decl decl) 68 | name (parse/name-from-ns-decl decl)] 69 | {:deps deps 70 | :name name 71 | :file file})))) 72 | (keep identity))] 73 | (->> files 74 | (reduce (fn [m {:keys [deps name file]}] 75 | (-> m 76 | (assoc-in [:depmap name] deps) 77 | (assoc-in [:filemap file] name))) 78 | {})))) 79 | 80 | (def ^:private merge-map (fnil merge {})) 81 | 82 | (defn add-files 83 | "Reads ns declarations from files; returns an updated dependency 84 | tracker with those files added. read-opts is passed through to 85 | tools.reader." 86 | ([tracker files] 87 | (add-files tracker files nil)) 88 | ([tracker files read-opts] 89 | (let [{:keys [depmap filemap]} (files-and-deps files read-opts)] 90 | (-> tracker 91 | (track/add depmap) 92 | (update-in [::filemap] merge-map filemap))))) 93 | 94 | (defn remove-files 95 | "Returns an updated dependency tracker with files removed. The files 96 | must have been previously added with add-files." 97 | [tracker files] 98 | (-> tracker 99 | (track/remove (keep (::filemap tracker {}) files)) 100 | (update-in [::filemap] #(apply dissoc % files)))) 101 | 102 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep9/clojure/tools/namespace/move.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and 2 | ;; distribution terms for this software are covered by the Eclipse 3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this 5 | ;; distribution. By using this software in any fashion, you are 6 | ;; agreeing to be bound by the terms of this license. You must not 7 | ;; remove this notice, or any other, from this software. 8 | 9 | (ns ^{:author "Stuart Sierra" 10 | :doc "Refactoring tool to move a Clojure namespace from one name/file to 11 | another, and update all references to that namespace in your other 12 | Clojure source files. 13 | 14 | WARNING: This code is ALPHA and subject to change. It also modifies 15 | and deletes your source files! Make sure you have a backup or 16 | version control."} 17 | eastwood.copieddeps.dep9.clojure.tools.namespace.move 18 | (:require [clojure.string :as str] 19 | [clojure.java.io :as io]) 20 | (:import (java.io File))) 21 | 22 | (defn- update-file 23 | "Reads file as a string, calls f on the string plus any args, then 24 | writes out return value of f as the new contents of file. Does not 25 | modify file if the content is unchanged." 26 | [file f & args] 27 | (let [old (slurp file) 28 | new (str (apply f old args))] 29 | (when-not (= old new) 30 | (spit file new)))) 31 | 32 | (defn- ns-file-name [sym] 33 | (str (-> (name sym) 34 | (str/replace "-" "_") 35 | (str/replace "." File/separator)) 36 | ".clj")) 37 | 38 | (defn- clojure-source-files [dirs] 39 | (->> dirs 40 | (map io/file) 41 | (filter #(.exists ^File %)) 42 | (mapcat file-seq) 43 | (filter (fn [^File file] 44 | (and (.isFile file) 45 | (.endsWith (.getName file) ".clj")))) 46 | (map #(.getCanonicalFile ^File %)))) 47 | 48 | (def ^:private symbol-regex 49 | ;; LispReader.java uses #"[:]?([\D&&[^/]].*/)?([\D&&[^/]][^/]*)" but 50 | ;; that's too broad; we don't want a whole namespace-qualified symbol, 51 | ;; just each part individually. 52 | #"[a-zA-Z0-9$%*+=?!<>_-]['.a-zA-Z0-9$%*+=?!<>_-]*") 53 | 54 | (defn replace-ns-symbol 55 | "ALPHA: subject to change. Given Clojure source as a string, replaces 56 | all occurrences of the namespace name old-sym with new-sym and 57 | returns modified source as a string." 58 | [source old-sym new-sym] 59 | (let [old-name (name old-sym) 60 | new-name (name new-sym)] 61 | ;; A lossless parser would be better, but this is adequate 62 | (str/replace source symbol-regex 63 | (fn [match] 64 | (if (= match old-name) 65 | new-name 66 | match))))) 67 | 68 | (defn move-ns-file 69 | "ALPHA: subject to change. Moves the .clj source file (found relative 70 | to source-path) for the namespace named old-sym to a file for a 71 | namespace named new-sym. 72 | 73 | WARNING: This function moves and deletes your source files! Make 74 | sure you have a backup or version control." 75 | [old-sym new-sym source-path] 76 | (let [old-file (io/file source-path (ns-file-name old-sym)) 77 | new-file (io/file source-path (ns-file-name new-sym))] 78 | (.mkdirs (.getParentFile new-file)) 79 | (io/copy old-file new-file) 80 | (.delete old-file) 81 | (loop [dir (.getParentFile old-file)] 82 | (when (empty? (.listFiles dir)) 83 | (.delete dir) 84 | (recur (.getParentFile dir)))))) 85 | 86 | (defn move-ns 87 | "ALPHA: subject to change. Moves the .clj source file (found relative 88 | to source-path) for the namespace named old-sym to new-sym and 89 | replace all occurrences of the old name with the new name in all 90 | Clojure source files found in dirs. 91 | 92 | This is a purely textual transformation. It does not work on 93 | namespaces require'd or use'd from a prefix list. 94 | 95 | WARNING: This function modifies and deletes your source files! Make 96 | sure you have a backup or version control." 97 | [old-sym new-sym source-path dirs] 98 | (move-ns-file old-sym new-sym source-path) 99 | (doseq [file (clojure-source-files dirs)] 100 | (update-file file replace-ns-symbol old-sym new-sym))) 101 | -------------------------------------------------------------------------------- /copied-deps/eastwood/copieddeps/dep9/clojure/tools/namespace/reload.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and 2 | ;; distribution terms for this software are covered by the Eclipse 3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this 5 | ;; distribution. By using this software in any fashion, you are 6 | ;; agreeing to be bound by the terms of this license. You must not 7 | ;; remove this notice, or any other, from this software. 8 | 9 | (ns ^{:author "Stuart Sierra" 10 | :doc "Force reloading namespaces on demand or through a 11 | dependency tracker"} 12 | eastwood.copieddeps.dep9.clojure.tools.namespace.reload 13 | (:require [eastwood.copieddeps.dep9.clojure.tools.namespace.track :as track])) 14 | 15 | (defn remove-lib 16 | "Remove lib's namespace and remove lib from the set of loaded libs." 17 | [lib] 18 | (remove-ns lib) 19 | (dosync (alter @#'clojure.core/*loaded-libs* disj lib))) 20 | 21 | (defn track-reload-one 22 | "Executes the next pending unload/reload operation in the dependency 23 | tracker. Returns the updated dependency tracker. If reloading caused 24 | an error, it is captured as ::error and the namespace which caused 25 | the error is ::error-ns." 26 | [tracker] 27 | (let [{unload ::track/unload, load ::track/load} tracker] 28 | (cond 29 | (seq unload) 30 | (let [n (first unload)] 31 | (remove-lib n) 32 | (update-in tracker [::track/unload] rest)) 33 | (seq load) 34 | (let [n (first load)] 35 | (try (require n :reload) 36 | (update-in tracker [::track/load] rest) 37 | (catch Throwable t 38 | (assoc tracker 39 | ::error t ::error-ns n ::track/unload load)))) 40 | :else 41 | tracker))) 42 | 43 | (defn track-reload 44 | "Executes all pending unload/reload operations on dependency tracker 45 | until either an error is encountered or there are no more pending 46 | operations." 47 | [tracker] 48 | (loop [tracker (dissoc tracker ::error ::error-ns)] 49 | (let [{error ::error, unload ::track/unload, load ::track/load} tracker] 50 | (if (and (not error) 51 | (or (seq load) (seq unload))) 52 | (recur (track-reload-one tracker)) 53 | tracker)))) 54 | -------------------------------------------------------------------------------- /copy-deps-scripts/clone.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuxo pipefail 3 | cd "${BASH_SOURCE%/*}" 4 | 5 | rm -rf repos 6 | mkdir -p repos 7 | cd repos 8 | 9 | git clone https://github.com/clojure/tools.analyzer.git 10 | cd tools.analyzer 11 | git checkout v1.1.0 12 | cd .. 13 | 14 | git clone https://github.com/clojure/tools.analyzer.jvm.git 15 | cd tools.analyzer.jvm 16 | git checkout v1.2.1 17 | cd .. 18 | 19 | git clone https://github.com/clojure/core.memoize.git 20 | cd core.memoize 21 | git checkout v1.0.257 22 | cd .. 23 | 24 | git clone https://github.com/clojure/core.cache.git 25 | cd core.cache 26 | git checkout v1.0.225 27 | cd .. 28 | 29 | git clone https://github.com/clojure/data.priority-map.git 30 | cd data.priority-map 31 | git checkout v1.1.0 32 | cd .. 33 | 34 | git clone https://github.com/clojure/tools.namespace.git 35 | cd tools.namespace 36 | git checkout v1.3.0 37 | cd .. 38 | 39 | git clone https://github.com/clojure/java.classpath.git 40 | cd java.classpath 41 | git checkout java.classpath-1.0.0 42 | cd .. 43 | 44 | git clone https://github.com/clojure/tools.reader.git 45 | cd tools.reader 46 | git checkout v1.3.6 47 | cd .. 48 | 49 | for j in * 50 | do 51 | cd $j 52 | echo "=== $j" 53 | git status . 54 | cd .. 55 | done 56 | -------------------------------------------------------------------------------- /copy-deps-scripts/dolly/dolly.clj: -------------------------------------------------------------------------------- 1 | (ns dolly 2 | (:require 3 | [dolly.clone :as c])) 4 | 5 | (def dry-run {:dry-run? true :print? true}) 6 | (def for-real {:dry-run? false :print? true}) 7 | 8 | (def e-root (System/getProperty "user.dir")) 9 | (def src-path (str e-root "/copied-deps")) 10 | (def staging-path (str e-root "/staging")) 11 | 12 | (def taj-src-path (str e-root "/copy-deps-scripts/repos/tools.analyzer.jvm/src/main/clojure")) 13 | (def ta-src-path (str e-root "/copy-deps-scripts/repos/tools.analyzer/src/main/clojure")) 14 | (def cm-src-path (str e-root "/copy-deps-scripts/repos/core.memoize/src/main/clojure")) 15 | (def ccache-src-path (str e-root "/copy-deps-scripts/repos/core.cache/src/main/clojure")) 16 | (def dp-src-path (str e-root "/copy-deps-scripts/repos/data.priority-map/src/main/clojure")) 17 | (def tr-src-path (str e-root "/copy-deps-scripts/repos/tools.reader/src/main/clojure")) 18 | (def tn-src-path (str e-root "/copy-deps-scripts/repos/tools.namespace/src/main/clojure")) 19 | (def jc-src-path (str e-root "/copy-deps-scripts/repos/java.classpath/src/main/clojure")) 20 | 21 | (defn -main [& _] 22 | ;; Change for-real to dry-run to see what will happen without 23 | ;; changing anything. Look it over to see if it is reasonable. 24 | (c/copy-namespaces-unmodified taj-src-path staging-path 'clojure.tools.analyzer for-real) 25 | 26 | ;; The copying above should make no modifications, so running a 27 | ;; diff command like the following from the dolly project root 28 | ;; directory in a command shell should show no differences. 29 | 30 | ;; diff -cr copy-deps-scripts/repos/tools.analyzer.jvm/src/main/clojure staging 31 | 32 | ;; Now move the files from the staging area into dolly's code and 33 | ;; rename the namespaces. 34 | 35 | (c/move-namespaces-and-rename staging-path src-path 'clojure.tools.analyzer 'eastwood.copieddeps.dep2.clojure.tools.analyzer [src-path] for-real) 36 | 37 | ;; tools.analyzer 38 | (c/copy-namespaces-unmodified ta-src-path staging-path 'clojure.tools.analyzer for-real) 39 | (c/move-namespaces-and-rename staging-path src-path 'clojure.tools.analyzer 'eastwood.copieddeps.dep1.clojure.tools.analyzer [src-path] for-real) 40 | 41 | ;; core.memoize 42 | (c/copy-namespaces-unmodified cm-src-path staging-path 'clojure.core.memoize for-real) 43 | (c/move-namespaces-and-rename staging-path src-path 'clojure.core.memoize 'eastwood.copieddeps.dep3.clojure.core.memoize [src-path] for-real) 44 | 45 | ;; core.cache 46 | (c/copy-namespaces-unmodified ccache-src-path staging-path 'clojure.core.cache for-real) 47 | (c/move-namespaces-and-rename staging-path src-path 'clojure.core.cache 'eastwood.copieddeps.dep4.clojure.core.cache [src-path] for-real) 48 | 49 | ;; data.priority-map 50 | (c/copy-namespaces-unmodified dp-src-path staging-path 'clojure.data.priority-map for-real) 51 | (c/move-namespaces-and-rename staging-path src-path 'clojure.data.priority-map 'eastwood.copieddeps.dep5.clojure.data.priority-map [src-path] for-real) 52 | 53 | ;; tools.reader 54 | (c/copy-namespaces-unmodified tr-src-path staging-path 'clojure.tools.reader for-real) 55 | (c/move-namespaces-and-rename staging-path src-path 'clojure.tools.reader 'eastwood.copieddeps.dep10.clojure.tools.reader [src-path] for-real) 56 | 57 | ;; tools.namespace 58 | (c/copy-namespaces-unmodified tn-src-path staging-path 'clojure.tools.namespace for-real) 59 | (c/move-namespaces-and-rename staging-path src-path 'clojure.tools.namespace 'eastwood.copieddeps.dep9.clojure.tools.namespace [src-path] for-real) 60 | 61 | ;; java.classpath 62 | (c/copy-namespaces-unmodified jc-src-path staging-path 'clojure.java.classpath for-real) 63 | (c/move-namespaces-and-rename staging-path src-path 'clojure.java.classpath 'eastwood.copieddeps.dep11.clojure.java.classpath [src-path] for-real)) 64 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "lein-eastwood" "copied-deps" "resource" "resources"] 2 | :deps {org.clojure/clojure {:mvn/version "1.10.3"} 3 | org.clojars.brenton/google-diff-match-patch {:mvn/version "0.1"} 4 | org.ow2.asm/asm-all {:mvn/version "5.2"}}} 5 | -------------------------------------------------------------------------------- /doc/Clint_Eastwood_-_1960s_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonase/eastwood/4c6b01f1926c91f9f8452568b5385f2fa56f88bf/doc/Clint_Eastwood_-_1960s_small.jpg -------------------------------------------------------------------------------- /doc/README-creating-new-release.md: -------------------------------------------------------------------------------- 1 | Please don't create "snapshot" releases. Consider instead: 2 | 3 | * can you / people perform `lein install`? 4 | * can one gain confidence through better tests? 5 | 6 | --- 7 | 8 | Steps to prepare in making a new release: 9 | 10 | Make sure CI is green. 11 | 12 | Update changes.md with any differences in behavior. 13 | 14 | Places where version number should be updated: 15 | 16 | * `resources/EASTWOOD_VERSION` 17 | * README.md 18 | * changes.md (header) 19 | 20 | Update the changelog in changes.md 21 | 22 | Commit all of those changes. 23 | 24 | Tag it with a version tag, e.g.: 25 | 26 | % git tag -a v1.4.3 -m "1.4.3" 27 | 28 | 'git push' by default does not push tags to the remote server. To 29 | cause that to happen, use: 30 | 31 | % git push origin --tags 32 | 33 | ---------------------------------------------------------------------- 34 | Then release to Clojars.org via the CI integration. 35 | 36 | ---------------------------------------------------------------------- 37 | 38 | -------------------------------------------------------------------------------- /resources/EASTWOOD_VERSION: -------------------------------------------------------------------------------- 1 | { 2 | :major 1 3 | :minor 4 4 | :patch 3 5 | } 6 | -------------------------------------------------------------------------------- /src/eastwood/exit.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.exit) 2 | ;; This is a dedicated ns because `eastwood.versioncheck` performs `(require 'eastwood.lint)` carefully. 3 | ;; We want to provide the `exit-fn` without providing more defns unawarely. 4 | 5 | ;; This is a defn because the result of `find-ns` can change over time 6 | (defn exit-fn [] 7 | (if (find-ns 'leiningen.core.main) 8 | @(resolve 'leiningen.core.main/exit) 9 | (fn [n] 10 | (System/exit n)))) 11 | -------------------------------------------------------------------------------- /src/eastwood/linters/boxed_math.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linters.boxed-math 2 | (:require [clojure.string :as string])) 3 | 4 | (defn linter [{:keys [boxed-math-warnings]} _] 5 | (->> boxed-math-warnings 6 | (map (fn [{:keys [post uri line column]}] 7 | {:linter :boxed-math 8 | :uri-or-file-name uri 9 | :loc {:line line 10 | :column column 11 | :file uri} 12 | :msg (-> post 13 | (string/replace #"^ - call: " ""))})))) 14 | -------------------------------------------------------------------------------- /src/eastwood/linters/implicit_dependencies.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linters.implicit-dependencies 2 | (:require 3 | [clojure.walk :as walk] 4 | [eastwood.copieddeps.dep1.clojure.tools.analyzer.ast :as ast] 5 | [eastwood.copieddeps.dep9.clojure.tools.namespace.parse :as parse] 6 | [eastwood.util :as util]) 7 | (:import 8 | (clojure.lang Namespace))) 9 | 10 | (defn var->ns-symbol [var] 11 | (if (util/clojure-1-10-or-later) 12 | ;; Use the most accurate method: 13 | (-> var symbol namespace symbol) 14 | (let [^Namespace ns (-> var meta :ns)] 15 | (-> ns .-name)))) 16 | 17 | (defn within-other-ns-macro? 18 | [ast ns-sym] 19 | (let [macro-namespace-syms (->> ast 20 | util/enclosing-macros 21 | (keep #(some-> % :macro namespace symbol)) 22 | set)] 23 | (-> macro-namespace-syms 24 | (disj ns-sym 'clojure.core) 25 | seq 26 | boolean))) 27 | 28 | (defn implicit-dependencies [{:keys [asts]} _] 29 | (let [explicit-requires (atom []) 30 | _ (when-let [s (some->> asts (keep :eastwood/ns-source) first)] 31 | (when-let [n (some->> asts (keep :eastwood/ns-sym) first find-ns)] 32 | (let [require-forms #{'require `require} 33 | all (util/string->forms s n false)] 34 | (->> all 35 | (run! (fn [form] 36 | (->> form 37 | (walk/postwalk (fn [x] 38 | (when (and (list? x) 39 | (-> x first require-forms) 40 | (and (-> x second sequential?) 41 | (-> x second first #{'quote}))) 42 | (let [require-forms (->> x 43 | (rest) 44 | (remove keyword?) 45 | (mapv (fn [thing] 46 | (cond-> thing 47 | (and (sequential? thing) 48 | (-> thing first #{'quote})) 49 | last)))) 50 | ns-form (list 'ns '_ (apply list :require require-forms)) 51 | ns-names (parse/deps-from-ns-decl ns-form)] 52 | (->> ns-names 53 | (run! (partial swap! explicit-requires conj))))) 54 | x))))))))) 55 | ns-ast (first (util/ns-form-asts asts)) 56 | ns-decl (first (:raw-forms ns-ast)) 57 | ns-sym (parse/name-from-ns-decl ns-decl) 58 | namespace-dependency? (-> ns-decl 59 | parse/deps-from-ns-decl 60 | (into @explicit-requires) 61 | (conj ns-sym ;; consider namespace as part of itself 62 | ;; clojure core is always included in every namespace, no need to warn about it: 63 | 'clojure.core))] 64 | (->> asts 65 | (mapcat ast/nodes) 66 | (keep (fn [{:keys [op env form] 67 | var-ref :var 68 | :as expr}] 69 | (when (and (= op :var) 70 | (not (within-other-ns-macro? expr ns-sym))) 71 | (let [implicit-ns-sym (var->ns-symbol var-ref)] 72 | (when-not (namespace-dependency? implicit-ns-sym) 73 | {:linter :implicit-dependencies 74 | :loc env 75 | :implicit-namespace-sym implicit-ns-sym 76 | :msg (format "Var %s refers to namespace %s that isn't explicitly required." 77 | form 78 | implicit-ns-sym)})))))))) 79 | -------------------------------------------------------------------------------- /src/eastwood/linters/performance.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linters.performance 2 | (:require 3 | [clojure.string :as string])) 4 | 5 | (defn linter [{:keys [performance-warnings]} _] 6 | (->> performance-warnings 7 | (map (fn [{:keys [post uri line column kind]}] 8 | {:linter :performance 9 | :kind kind 10 | :uri-or-file-name uri 11 | :loc {:line line 12 | :column column 13 | :file uri} 14 | :msg (-> post 15 | (string/replace #"^ - " "") 16 | (string/replace #"^ " " "))})))) 17 | -------------------------------------------------------------------------------- /src/eastwood/linters/reflection.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linters.reflection 2 | (:require 3 | [clojure.string :as string])) 4 | 5 | (defn linter [{:keys [reflection-warnings]} _] 6 | (->> reflection-warnings 7 | (map (fn [{:keys [post uri line column]}] 8 | {:linter :reflection 9 | :uri-or-file-name uri 10 | :loc {:line line 11 | :column column 12 | :file uri} 13 | :msg (-> post 14 | (string/replace #"^ - " "") 15 | (string/replace " (target class is unknown)" ""))})))) 16 | -------------------------------------------------------------------------------- /src/eastwood/util/ns.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.util.ns 2 | (:require 3 | [clojure.string :as string] 4 | [eastwood.copieddeps.dep9.clojure.tools.namespace.dependency :as dep] 5 | [eastwood.copieddeps.dep9.clojure.tools.namespace.file :as file] 6 | [eastwood.copieddeps.dep9.clojure.tools.namespace.parse :as parse] 7 | [eastwood.copieddeps.dep9.clojure.tools.namespace.track :as track])) 8 | 9 | (defn ns-name->resource-path [ns-name extension] 10 | (-> ns-name 11 | str 12 | munge 13 | (string/replace "." "/") 14 | (str extension))) 15 | 16 | (defn resource-path->filenames [resource-path] 17 | (->> (-> (Thread/currentThread) 18 | (.getContextClassLoader) 19 | (.getResources resource-path)) 20 | (enumeration-seq) 21 | (distinct) 22 | (mapv str))) 23 | 24 | (defn files-and-deps [project-namespaces] 25 | (->> project-namespaces 26 | (reduce (fn [m n] 27 | (let [file (or (some-> (ns-name->resource-path n ".clj") 28 | resource-path->filenames 29 | first) 30 | (some-> (ns-name->resource-path n ".cljc") 31 | resource-path->filenames 32 | first))] 33 | (if-let [decl (some-> file 34 | (file/*read-file-ns-decl* nil))] 35 | (let [deps (parse/deps-from-ns-decl decl) 36 | name (parse/name-from-ns-decl decl)] 37 | (-> m 38 | (assoc-in [:depmap name] deps))) 39 | m))) 40 | {}))) 41 | 42 | (defn add-files [tracker project-namespaces] 43 | (let [{:keys [depmap]} (files-and-deps project-namespaces)] 44 | (-> tracker 45 | (track/add depmap)))) 46 | 47 | (defn build-tracker [project-namespaces] 48 | (add-files (track/tracker) project-namespaces)) 49 | 50 | ;; Sort namespaces topographically. This increases the likelihood of evaluating defprotocols in the right order. 51 | (defn topo-sort [project-namespaces namespaces] 52 | {:post [(= (set namespaces) 53 | (set %))]} 54 | (let [inside-project (->> (build-tracker project-namespaces) 55 | ::track/deps 56 | dep/topo-sort 57 | (filterv (set namespaces))) 58 | outside-project (->> namespaces 59 | (remove (set inside-project)) 60 | vec)] 61 | (into inside-project outside-project))) 62 | -------------------------------------------------------------------------------- /src/eastwood/version.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.version 2 | (:require 3 | [clojure.java.io :refer [reader resource]] 4 | [clojure.string :refer [join]]) 5 | (:import 6 | (java.io PushbackReader))) 7 | 8 | (let [version-file (resource "EASTWOOD_VERSION")] 9 | (when version-file 10 | (with-open [rdr (reader version-file)] 11 | (binding [*read-eval* false] 12 | (def version (read (PushbackReader. rdr))) 13 | (def major (-> version :major (doto assert))) 14 | (def minor (-> version :minor (doto assert))) 15 | (def patch (-> version :patch (doto assert))) 16 | (def string (join "." [major minor patch])))))) 17 | 18 | (def ^:dynamic *eastwood-version* 19 | {:major major, :minor minor, :incremental patch}) 20 | 21 | (defn eastwood-version [] string) 22 | 23 | (defn versions [] 24 | {:eastwood-version-map *eastwood-version* 25 | :eastwood-version-string (eastwood-version) 26 | :clojure-version-map *clojure-version* 27 | :clojure-version-string (clojure-version) 28 | :jvm-version-string (get (System/getProperties) "java.version")}) 29 | 30 | (defn version-string [] 31 | (let [versions (versions)] 32 | (format "== Eastwood %s Clojure %s JVM %s ==" 33 | (:eastwood-version-string versions) 34 | (:clojure-version-string versions) 35 | (:jvm-version-string versions)))) 36 | -------------------------------------------------------------------------------- /src/eastwood/versioncheck.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.versioncheck 2 | (:require 3 | [eastwood.exit :refer [exit-fn]])) 4 | 5 | (def main 6 | (delay 7 | (do 8 | (require 'eastwood.lint) 9 | (resolve (symbol "eastwood.lint/eastwood-from-cmdline"))))) 10 | 11 | (defn run-eastwood [opts] 12 | (let [{:keys [major minor]} *clojure-version*] 13 | (when-not (>= (compare [major minor] [1 7]) 0) 14 | (println "Eastwood requires Clojure version >= 1.7.0. This project uses version" 15 | (clojure-version)) 16 | ((exit-fn) 1))) 17 | (@main opts)) 18 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_date_deprecation.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :deprecations 3 | :symbol-matches #{#"\sjava\.util\.Date\("}}) 4 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_def_in_def.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :def-in-def 3 | :if-inside-macroexpansion-of '#{testcases.def-in-def.red3/my-macro} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_def_in_def_unrelated.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :def-in-def 3 | :if-inside-macroexpansion-of '#{unrelated.namespace/my-macro} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_unused_meta_on_macro.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :unused-meta-on-macro 3 | :if-inside-macroexpansion-of '#{clojure.core/let} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_unused_meta_on_macro_unrelated.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :unused-meta-on-macro 3 | :if-inside-macroexpansion-of '#{unrelated.ns/let} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_wrong_tag.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :wrong-tag 3 | :if-inside-macroexpansion-of '#{testcases.wrong-tag-example/the-macro} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/disable_wrong_tag_unrelated.clj: -------------------------------------------------------------------------------- 1 | (disable-warning 2 | {:linter :wrong-tag 3 | :if-inside-macroexpansion-of '#{unrelated.ns/another-macro} 4 | :reason "Support a deftest"}) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/config/linter_executor.clj: -------------------------------------------------------------------------------- 1 | (defn custom-map [f & colls] 2 | (swap! @(resolve 'eastwood.linter-executor-test/proof) 3 | conj 4 | :hello) 5 | (apply map f colls)) 6 | 7 | (set-linter-executor! custom-map) 8 | -------------------------------------------------------------------------------- /test-resources/eastwood/test/linter_executor/sample_ns.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.linter-executor.sample-ns) 2 | 3 | ;; trigger at least 1 warning: 4 | (-> 1) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/test/outside_test_paths/defn_reflection_warning.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.outside-test-paths.defn-reflection-warning) 2 | 3 | (defn foo [x] 4 | (.fooasdlfjk x)) 5 | -------------------------------------------------------------------------------- /test-resources/eastwood/test/outside_test_paths/example.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.outside-test-paths.example 2 | "A ns that is not in :source-paths, :test-paths, or the t.n refresh-dirs") 3 | 4 | (defmacro faulty 5 | "A macro which expansion would cause an Eastwood warning" 6 | [x] 7 | `(-> ~x)) 8 | -------------------------------------------------------------------------------- /test-resources/eastwood/test/outside_test_paths/reflection_warning.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.outside-test-paths.reflection-warning) 2 | 3 | (defmacro foo [x] 4 | `(.bar ~x)) 5 | -------------------------------------------------------------------------------- /test-resources/invalid/syntax.clj: -------------------------------------------------------------------------------- 1 | (ns invalid.syntax) 2 | 3 | ( 4 | -------------------------------------------------------------------------------- /test-third-party-deps/eastwood/third_party_deps_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.third-party-deps-test 2 | (:require 3 | [clojure.test :refer [deftest is testing]] 4 | [eastwood.lint])) 5 | 6 | (when-not (System/getProperty "eastwood.internal.plugin-profile-active") 7 | (let [p "eastwood.internal.running-test-suite" 8 | v (System/getProperty p)] 9 | (assert (= "true" v) 10 | (format "The test suite should be running with the %s system property set, for extra test robustness" 11 | p)))) 12 | 13 | (deftest speced-def-handling 14 | (testing "Is able to process usages of the `nedap.speced.def` library without false positives" 15 | (is (= {:some-warnings false :some-errors false} 16 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.speced-def-example})))))) 17 | 18 | (deftest timbre-example 19 | (testing "Is able to process usages of the `taoensso.timbre` library without false positives" 20 | (is (= {:some-warnings false :some-errors false} 21 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.timbre-example})))))) 22 | 23 | (deftest tufte-example 24 | (testing "Is able to process usages of the `taoensso.tufte` library without false positives" 25 | (is (= {:some-warnings false :some-errors false} 26 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.tufte-example})))))) 27 | 28 | (deftest manifold-example 29 | (testing "Is able to process usages of the `manifold` library without false positives" 30 | (is (= {:some-warnings false :some-errors false} 31 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.manifold-example})))))) 32 | 33 | (deftest spec-tools-example 34 | (testing "Is able to process usages of the `spec-tools` library without false positives" 35 | (is (= {:some-warnings false :some-errors false} 36 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.spec-tools-example})))))) 37 | 38 | ;; https://github.com/jonase/eastwood/issues/436 39 | (deftest jdbc-example 40 | (testing "Is able to process usages of the `clojure.java.jdbc` library without false positives" 41 | (is (= {:some-warnings false :some-errors false} 42 | (eastwood.lint/eastwood (assoc eastwood.lint/default-opts :namespaces #{'testcases.jdbc-example})))))) 43 | 44 | (deftest core-async-example 45 | (testing "Handling of hard-to-analyze `go` forms" 46 | (let [opts (assoc eastwood.lint/default-opts :namespaces #{'testcases.core-async-example})] 47 | 48 | (testing "When encountering `go` usages that throw exceptions, it omits the exception" 49 | (is (= {:some-warnings false :some-errors false} 50 | (eastwood.lint/eastwood opts)))) 51 | 52 | ;; This counterexample is important not only for covering the option itself, 53 | ;; but also for making sure the previous test is logically valid: 54 | (testing "When encountering `go` usages that throw exceptions, and passing an opt-out flag, 55 | it doesn't omit the exception" 56 | (is (= {:some-warnings true :some-errors true} 57 | (eastwood.lint/eastwood (assoc opts :abort-on-core-async-exceptions? true))))))) 58 | 59 | 60 | (let [base-opts (assoc eastwood.lint/default-opts :abort-on-core-async-exceptions? true)] ;; Make test more logically robust 61 | 62 | (testing "`alt!` can be used without false positives, see https://github.com/jonase/eastwood/issues/411" 63 | (is (= {:some-warnings false :some-errors false} 64 | (eastwood.lint/eastwood (assoc base-opts 65 | :namespaces #{'testcases.core-async-example.alt}))))) 66 | 67 | (testing "`assert` can be used without false positives" 68 | (is (= {:some-warnings false :some-errors false} 69 | (eastwood.lint/eastwood (assoc base-opts 70 | :namespaces #{'testcases.core-async-example.assert}))))) 71 | 72 | (testing "All other (relevant) silencings present in resource/clojure.clj" 73 | (is (= {:some-warnings false :some-errors false} 74 | (eastwood.lint/eastwood (assoc base-opts 75 | :namespaces #{'testcases.core-async-example.misc}))))))) 76 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/core_async_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.core-async-example 2 | (:require 3 | [clojure.core.async :refer [go]])) 4 | 5 | (defprotocol P 6 | (x [p])) 7 | 8 | ;; taken from the core.async test suite 9 | (defrecord R [z] 10 | P 11 | (x [this] 12 | (go 13 | (loop [] 14 | (if (zero? (rand-int 3)) 15 | [z (.z this)] 16 | (recur)))))) 17 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/core_async_example/alt.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.core-async-example.alt 2 | (:require 3 | [clojure.core.async :refer [alt! alt!! go timeout]])) 4 | 5 | (defn sample 6 | "https://github.com/jonase/eastwood/issues/411" 7 | [trade-ch] 8 | (go 9 | (let [timeout-ch (timeout 1000) 10 | trade 100] 11 | (-> (alt! 12 | [[trade-ch trade]] :sent 13 | timeout-ch :timed-out) 14 | print)))) 15 | 16 | (defn sample2 17 | "https://github.com/jonase/eastwood/issues/422" 18 | [trade-ch] 19 | (let [timeout-ch (timeout 1000) 20 | trade 100] 21 | (-> (alt!! 22 | [[trade-ch trade]] :sent 23 | timeout-ch :timed-out) 24 | print))) 25 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/core_async_example/assert.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.core-async-example.assert 2 | (:require 3 | [clojure.core.async :refer [go]])) 4 | 5 | (defn sample [] 6 | (go 7 | (assert false) 8 | (assert true))) 9 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/core_async_example/misc.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.core-async-example.misc 2 | (:require 3 | [clojure.core.async :refer [go]] 4 | [clojure.spec.alpha :as spec] 5 | [clojure.test :refer [are assert-expr deftest do-report is join-fixtures testing use-fixtures]]) 6 | (:import 7 | (java.io PrintWriter))) 8 | 9 | (defn sample1 [] 10 | (go 11 | (spec/and ::foo ::bar))) 12 | 13 | (defn sample2 [] 14 | (go 15 | (spec/keys :req [::foo]))) 16 | 17 | (defn sample3 [] 18 | (go 19 | (spec/coll-of int?))) 20 | 21 | (defmethod assert-expr 'truthy? 22 | [msg [pred v]] 23 | `(do-report (if ~v 24 | {:type :pass, :message ~msg} 25 | {:type :fail, :message ~msg}))) 26 | 27 | (defn func [f] "") 28 | 29 | (defn throws-exception [] 30 | (throw (ex-info "" {}))) 31 | 32 | ;; https://github.com/jonase/eastwood/issues/116 33 | (deftest exercises-thrown 34 | (go 35 | (is (thrown? Exception (throws-exception))))) 36 | 37 | ;; https://github.com/jonase/eastwood/issues/313 38 | (deftest exercises-truthy 39 | (go 40 | (is (truthy? 42)))) 41 | 42 | ;; https://github.com/jonase/eastwood/issues/207 43 | (deftest b-test 44 | (go 45 | (is (instance? String (func +))))) 46 | 47 | (defn sample4 [] 48 | (go 49 | (is false))) 50 | 51 | (defn sample5 [] 52 | (go 53 | (cond-> 1 54 | true inc))) 55 | 56 | (defn sample6 [stream ^PrintWriter pw] 57 | (go 58 | (with-out-str 59 | (-> pw (.print "foo")) 60 | (spit stream 42) 61 | 1))) 62 | 63 | (defn sample7 [stream] 64 | (go 65 | (while true 66 | (throw (ex-info "." {}))))) 67 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/jdbc_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.jdbc-example 2 | (:require 3 | [clojure.java.jdbc :refer [with-db-connection]] 4 | [clojure.test :refer [testing]])) 5 | 6 | (defn foo [x y] 7 | (testing "Foo" 8 | (with-db-connection [x y] 9 | 42))) 10 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/manifold_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.manifold-example 2 | "https://github.com/jonase/eastwood/issues/197" 3 | (:require 4 | [manifold.deferred :refer [let-flow]])) 5 | 6 | (defn exceptional? [_x] 7 | (< (rand) 0.5)) 8 | 9 | (defn wrap-middlewares 10 | [handler] 11 | (fn [request] 12 | (let-flow [response (handler request)] 13 | (cond (exceptional? response) {:status 500 :body "my b"} 14 | (empty? response) {:status 204} 15 | :else {:body response})))) 16 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/spec_tools_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.spec-tools-example 2 | (:require 3 | [clojure.spec.alpha :as s] 4 | [spec-tools.core :as st])) 5 | 6 | (s/def ::query 7 | (st/spec 8 | {:spec any? 9 | :swagger/example '{} 10 | :description "Foo"})) 11 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/timbre_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.timbre-example 2 | (:require [taoensso.timbre :as log])) 3 | 4 | (defn uses-logging [] 5 | (log/error 42) 6 | (log/warn 42) 7 | (log/info 42) 8 | (log/debug 42) 9 | 10 | (log/errorf "%s" 42) 11 | (log/warnf "%s" 42) 12 | (log/infof "%s" 42) 13 | (log/debugf "%s" 42)) 14 | -------------------------------------------------------------------------------- /test-third-party-deps/testcases/tufte_example.clj: -------------------------------------------------------------------------------- 1 | (ns testcases.tufte-example 2 | (:require [taoensso.tufte :refer [defnp p profiled profile]])) 3 | 4 | ;; https://github.com/jonase/eastwood/issues/161 5 | (defnp my-fun 6 | [] 7 | (+ 1 1)) 8 | 9 | ;; https://github.com/ptaoussanis/tufte/tree/396ca0d5e05db9d9721635ca11bf4e00fb393ab8#10-second-example 10 | 11 | ;;; Let's define a couple dummy fns to simulate doing some expensive work 12 | (defn get-x [] (Thread/sleep 500) "x val") 13 | (defn get-y [] (Thread/sleep (rand-int 1000)) "y val") 14 | 15 | ;; How do these fns perform? Let's check: 16 | 17 | (profile ; Profile any `p` forms called during body execution 18 | {} ; Profiling options; we'll use the defaults for now 19 | (dotimes [_ 5] 20 | (p :get-x (get-x)) 21 | (p :get-y (get-y)))) 22 | -------------------------------------------------------------------------------- /test/eastwood/analyze_ns_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.analyze-ns-test 2 | (:require 3 | [clojure.test :refer [are deftest is testing]] 4 | [eastwood.analyze-ns :as sut])) 5 | 6 | (deftest cleanup 7 | (testing "Removes `:const` metadata" 8 | (are [f input expected] (let [result (f input)] 9 | (is (= result input) 10 | "Applying `f` should return an equivalent object (even after removing metadata)") 11 | (is (= expected 12 | (-> result second meta :const))) 13 | true) 14 | identity '(defn ^:const foo) true 15 | sut/cleanup '(defn ^:const foo) nil 16 | sut/cleanup '(defn foo) nil))) 17 | -------------------------------------------------------------------------------- /test/eastwood/linter_executor_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linter-executor-test 2 | (:require 3 | [clojure.test :refer [are deftest is testing]] 4 | [eastwood.lint])) 5 | 6 | (def proof (atom [])) 7 | 8 | (deftest works 9 | (are [desc input pred] (testing input 10 | (reset! proof []) 11 | (with-out-str 12 | (eastwood.lint/eastwood {:namespaces ['eastwood.test.linter-executor.sample-ns] 13 | :builtin-config-files input})) 14 | (is (pred @proof) 15 | desc) 16 | ;; Avoid duplicate failure reports, did the tests fail: 17 | true) 18 | "The custom `set-linter-executor!` successfully runs" 19 | ["linter_executor.clj"] 20 | seq 21 | 22 | "The effect of custom `:builtin-config-files` is not persistent" 23 | [] 24 | empty?)) 25 | -------------------------------------------------------------------------------- /test/eastwood/linters/typos_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.linters.typos-test 2 | (:require 3 | [clojure.test :refer [are deftest is testing]] 4 | [eastwood.linters.typos :as sut])) 5 | 6 | (deftest seq-call-from-destructuring? 7 | (testing "Detects a very specific call, for preventing false positives/negatives" 8 | (are [desc input expected] (testing input 9 | (is (= expected 10 | (sut/seq-call-from-destructuring? input)) 11 | desc) 12 | true) 13 | "Only the targeted shape returns `true`" 14 | '(if (clojure.core/seq? map__413572) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__413572)) map__413572) 15 | true 16 | 17 | "Placing an unexpected `seq?`" 18 | '(if (clojure.core/seq? map__413572) (clojure.lang.PersistentHashMap/create (clojure.core/seq? map__413572)) map__413572) 19 | false 20 | 21 | "Placing an unexpected `sequential?`" 22 | '(if (clojure.core/sequential? map__413572) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__413572)) map__413572) 23 | false 24 | 25 | "Using a naming other than `map__`" 26 | '(if (clojure.core/seq? mOp__413572) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__413572)) map__413572) 27 | false 28 | 29 | "Using a naming other than `map__`" 30 | '(if (clojure.core/seq? map__413572) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__413572)) m0p__413572) 31 | false))) 32 | 33 | (deftest inlined-identical-test 34 | (let [any-sym (gensym)] 35 | (are [input expected] (testing input 36 | (is (= expected 37 | (sut/inlined-identical-test {:op :static-call 38 | :form input}))) 39 | true) 40 | nil nil 41 | 42 nil 42 | '(. clojure.lang.Util (identical x nil)) 'x 43 | `(. clojure.lang.Util (~'identical ~any-sym nil)) any-sym 44 | '(. clojure.lang.Util (identical 42 nil)) nil 45 | '(. clojure.lang.Other (identical x nil)) nil 46 | '(. clojure.lang.Util (identical x 42)) nil))) 47 | -------------------------------------------------------------------------------- /test/eastwood/test/analyze_ns_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.analyze-ns-test 2 | (:require 3 | [clojure.test :refer [deftest is testing]] 4 | [eastwood.analyze-ns :as sut] 5 | [eastwood.lint :refer [with-memoization-bindings]])) 6 | 7 | (deftest test3 8 | (let [result (with-memoization-bindings 9 | (sut/analyze-ns 'testcases.wrong-require :opt {:debug #{}}))] 10 | (is (instance? java.io.FileNotFoundException (:exception result))))) 11 | 12 | (defn- int-reader [input] 13 | (Integer/parseInt input)) 14 | 15 | (deftest custom-data-readers 16 | (testing "it should fail if there are no data readers registered" 17 | (is (thrown-with-msg? Exception #"No reader function for tag my-int" 18 | (with-memoization-bindings 19 | (sut/analyze-ns 'testcases.data-readers))))) 20 | 21 | (testing "it should not fail when there is a data reader" 22 | ;; the parser reader uses the same data-readers from clojure.core (which in 23 | ;; practice are loaded by clojure.core/load-data-readers, but here in tests 24 | ;; has a manual binding. 25 | (binding [clojure.core/*data-readers* {'my-int int-reader}] 26 | (let [result (with-memoization-bindings 27 | (sut/analyze-ns 'testcases.data-readers))] 28 | (is (nil? (:exception result))))))) 29 | -------------------------------------------------------------------------------- /test/eastwood/test/implicit_dependencies_linter_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.test.implicit-dependencies-linter-test 2 | (:require 3 | [clojure.test :refer [deftest]] 4 | [eastwood.test.linters-test :as linters-test] 5 | [eastwood.util :as util])) 6 | 7 | (deftest implicit-dependency-linter 8 | (linters-test/lint-test 9 | 'testcases.implicit_dependencies 10 | [:implicit-dependencies] 11 | {} 12 | {{:linter :implicit-dependencies, 13 | :msg 14 | "Var clojure.string/join refers to namespace clojure.string that isn't explicitly required.", 15 | :file "testcases/implicit_dependencies.clj", 16 | :line 4, 17 | :column 3} 18 | 1}) 19 | (when (util/clojure-1-10-or-later) 20 | (linters-test/lint-test 21 | 'testcases.f08 22 | [:implicit-dependencies] 23 | {} 24 | {}))) 25 | -------------------------------------------------------------------------------- /test/eastwood/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.util-test 2 | (:require 3 | [clojure.test :refer [are deftest is testing]] 4 | [eastwood.util :as sut]) 5 | (:import 6 | (java.io File))) 7 | 8 | (deftest trim-thrown-form 9 | (testing "Removes the `Exception` from a `(is (thrown? Exception ...`" 10 | (are [input expected] (testing input 11 | (is (= expected 12 | (sut/trim-thrown-form input))) 13 | true) 14 | nil nil 15 | 1 1 16 | [] [] 17 | {} {} 18 | '(foo) '(foo) 19 | '(is (= 1 1)) '(is (= 1 1)) 20 | '(is (thrown? Exception (foo))) '(is (thrown? (foo))) 21 | '(is (thrown? ::anything (foo))) '(is (thrown? (foo))) 22 | '(do (is (thrown? Exception (foo)))) '(do (is (thrown? (foo)))) 23 | '(do (is (thrown? ::anything (foo)))) '(do (is (thrown? (foo))))))) 24 | 25 | (deftest in-thrown? 26 | (are [statement-form ancestor-form expected] (testing [statement-form ancestor-form] 27 | (is (= expected 28 | (sut/in-thrown? statement-form ancestor-form))) 29 | true) 30 | '(+ 3 2) 31 | '(do (is (thrown? Exception (+ 3 2)))) 32 | true 33 | 34 | '(+ 999 999) 35 | '(do (is (thrown? Exception (+ 3 2)))) 36 | false)) 37 | 38 | (deftest expand-exclude-linters 39 | (are [input expected] (testing input 40 | (is (= expected 41 | (sut/expand-exclude-linters input))) 42 | true) 43 | #{} #{} 44 | #{:foo} #{:foo} 45 | #{[:foo :bar]} #{[:foo :bar]} 46 | #{[:foo #{:bar :baz}]} #{[:foo :bar] [:foo :baz]} 47 | #{[:foo [:bar :baz]]} #{[:foo :bar] [:foo :baz]} 48 | #{:foo [:foo :bar]} #{:foo [:foo :bar]} 49 | #{:foo [:foo [:bar]]} #{:foo [:foo :bar]} 50 | #{:foo [:foo #{:bar}]} #{:foo [:foo :bar]} 51 | #{:foo [:foo [:bar :baz]]} #{:foo [:foo :bar] [:foo :baz]} 52 | #{:foo [:foo #{:bar :baz}]} #{:foo [:foo :bar] [:foo :baz]})) 53 | 54 | (deftest excludes-kind? 55 | (are [exclude-linters path expected] (testing [exclude-linters path] 56 | (is (= expected 57 | (sut/excludes-kind? path (sut/expand-exclude-linters exclude-linters)))) 58 | true) 59 | #_exclude-linters #_path #_expected 60 | #{} [:foo :bar] false 61 | #{:foo} [:foo :bar] true 62 | #{[:foo :bar]} [:foo :bar] true 63 | #{[:foo :bar]} [:foo :baz] false 64 | #{[:foo [:bar]]} [:foo :baz] false 65 | #{[:foo [:bar :baz]]} [:foo :baz] true 66 | #{[:foo [:bar :baz]]} [:foo :quux] false)) 67 | 68 | (deftest dir-outside-root-dir? 69 | (are [input expected] (testing input 70 | (is (= expected 71 | (sut/dir-outside-root-dir? input))) 72 | true) 73 | (File. (System/getProperty "user.dir")) false 74 | (File. ".") false 75 | (File. "src") false 76 | (File. "/") true)) 77 | 78 | (deftest dir-superset? 79 | (are [candidate other expected] (testing [candidate other] 80 | (is (= expected 81 | (sut/dir-superset? (File. candidate) (File. other)))) 82 | true) 83 | "/" "." true 84 | "/" "src" true 85 | "/" "src/eastwood" true 86 | ;; In theory an x is a superset of itself, but we'll disregard that - 87 | ;; we prefer to accept a duplicate entry over a confusing error message: 88 | "/" "/" false 89 | "." "." false 90 | "src" "src" false 91 | "src/eastwood" "src/eastwood" false 92 | 93 | "." "src" true 94 | "src" "." false 95 | "src" "test" false 96 | "src/eastwood" "src" false 97 | "src" "src/eastwood" true)) 98 | -------------------------------------------------------------------------------- /var-info-test/eastwood/var_info_test.clj: -------------------------------------------------------------------------------- 1 | (ns eastwood.var-info-test 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [eastwood.util] 5 | [eastwood.linters.typos] 6 | [clojure.string :as string])) 7 | 8 | (when-not (System/getProperty "eastwood.internal.plugin-profile-active") 9 | (let [p "eastwood.internal.running-test-suite" 10 | v (System/getProperty p)] 11 | (assert (= "true" v) 12 | (format "The test suite should be running with the %s system property set, for extra test robustness" 13 | p)))) 14 | 15 | (deftest var-info 16 | (let [v (with-out-str 17 | (eastwood.util/print-var-info-summary @eastwood.linters.typos/var-info-map-delayed))] 18 | (or (is (not (string/includes? v eastwood.util/loaded-but-not-in-file-marker)) 19 | "The var-info.edn file does not have missing entries") 20 | (println v)))) 21 | --------------------------------------------------------------------------------