├── .dockerignore ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── coverage.yml │ ├── release.yml │ ├── update_dependencies.yml │ ├── update_supply_chain.yml │ └── upgrade_examples.yml ├── .gitignore ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── cargo-dylint ├── Cargo.toml ├── README.md ├── src │ └── main.rs └── tests │ ├── ci.rs │ ├── integration │ ├── depinfo_dylint_libs.rs │ ├── dylint_driver_path.rs │ ├── fix.rs │ ├── library_packages.rs │ ├── list.rs │ ├── main.rs │ ├── nightly_toolchain.rs │ ├── no_deps.rs │ ├── package_options.rs │ └── warn.rs │ ├── markdown_link_check.json │ └── prettier_ignore.txt ├── clippy.toml ├── docs ├── 2024-10-11 Linting with Dylint (EuroRust).pdf └── how_dylint_works.md ├── driver ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── rust-toolchain └── src │ └── lib.rs ├── dylint-link ├── Cargo.toml ├── README.md ├── build.rs └── src │ └── main.rs ├── dylint.toml ├── dylint ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── driver_builder.rs │ ├── error.rs │ ├── lib.rs │ ├── library_packages │ ├── cargo_cli │ │ ├── mod.rs │ │ └── util │ │ │ ├── canonical_url.rs │ │ │ ├── hex.rs │ │ │ └── mod.rs │ ├── cargo_lib │ │ ├── mod.rs │ │ └── toml │ │ │ └── mod.rs │ └── mod.rs │ ├── name_toolchain_map │ ├── maybe_library.rs │ └── mod.rs │ ├── opts.rs │ └── package_options │ ├── auto_correct │ ├── highlight.rs │ ├── mod.rs │ ├── rewrite │ │ ├── diff.rs │ │ └── mod.rs │ ├── short_id.rs │ └── tokenization.rs │ ├── common.rs │ ├── mod.rs │ └── revs.rs ├── examples ├── Cargo.toml ├── README.md ├── experimental │ ├── README.md │ ├── derive_opportunity │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── rust-toolchain │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ ├── ui_at_least_one_field │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_ignore │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── missing_doc_comment_openai │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── rust-toolchain │ │ ├── src │ │ │ ├── lib.rs │ │ │ └── openai.rs │ │ └── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ └── overscoped_allow │ │ ├── .cargo │ │ └── config.toml │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── rust-toolchain │ │ ├── src │ │ └── lib.rs │ │ ├── ui_general │ │ ├── main.rs │ │ └── main.stderr │ │ └── ui_test │ │ ├── main.rs │ │ └── main.stderr ├── general │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── abs_home_path │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ ├── ui_build_script │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ └── src │ │ │ │ └── main.rs │ │ └── ui_test │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ └── main.rs │ ├── await_holding_span_guard │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── basic_dead_store │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── crate_wide_allow │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_manifest │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ └── main.rs │ ├── incorrect_matches_operation │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── non_local_effect_before_error_return │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── rvalue_places.rs │ │ │ └── visit_error_paths.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_public_only │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── non_thread_safe_call_in_test │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ ├── blacklist.rs │ │ │ ├── late.rs │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── interprocedural.rs │ │ │ ├── interprocedural.stderr │ │ │ ├── one_test.rs │ │ │ ├── one_test.stderr │ │ │ ├── set_current_dir.rs │ │ │ └── set_current_dir.stderr │ ├── rust-toolchain │ ├── src │ │ └── lib.rs │ └── wrong_serialize_struct_arg │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ └── lib.rs │ │ └── ui │ │ ├── main.rs │ │ └── main.stderr ├── restriction │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── assert_eq_arg_misordering │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── collapsible_unwrap │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── const_path_join │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── env_literal │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── inconsistent_qualification │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── misleading_variable_name │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── question_mark_in_expression │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── assign.rs │ │ │ ├── assign.stderr │ │ │ ├── clone.rs │ │ │ ├── clone.stderr │ │ │ ├── ls.rs │ │ │ ├── ls.stderr │ │ │ ├── non-empty.rs │ │ │ └── non-empty.stderr │ ├── ref_aware_redundant_closure_for_method_calls │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── eta.fixed │ │ │ ├── eta.rs │ │ │ ├── eta.stderr │ │ │ ├── ref_aware.fixed │ │ │ ├── ref_aware.rs │ │ │ └── ref_aware.stderr │ ├── register_lints_warn │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── rust-toolchain │ ├── suboptimal_pattern │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_no_explicit_deref_check │ │ │ ├── main.rs │ │ │ └── main.stderr │ └── try_io_result │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ └── lib.rs │ │ └── ui │ │ ├── main.rs │ │ └── main.stderr ├── src │ └── lib.rs ├── supplementary │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── arg_iter │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── commented_out_code │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── escaping_doc_link │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── inconsistent_struct_pattern │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.fixed │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── local_ref_cell │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── nonexistent_path_in_comment │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── redundant_reference │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_no_lifetime_check │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── rust-toolchain │ ├── src │ │ └── lib.rs │ ├── unnamed_constant │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ ├── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ │ └── ui_threshold │ │ │ ├── main.rs │ │ │ └── main.stderr │ ├── unnecessary_borrow_mut │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ │ └── lib.rs │ │ └── ui │ │ │ ├── main.rs │ │ │ └── main.stderr │ └── unnecessary_conversion_for_trait │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── src │ │ ├── check_inherents.rs │ │ └── lib.rs │ │ └── ui │ │ ├── general.fixed │ │ ├── general.rs │ │ ├── general.stderr │ │ ├── unnecessary_to_owned.fixed │ │ ├── unnecessary_to_owned.rs │ │ ├── unnecessary_to_owned.stderr │ │ ├── vec.rs │ │ └── vec.stderr └── testing │ ├── README.md │ ├── clippy │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── rust-toolchain │ ├── src │ │ └── lib.rs │ └── tests │ │ └── ui.rs │ └── straggler │ ├── .cargo │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── rust-toolchain │ └── src │ └── lib.rs ├── expensive ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── Dockerfile │ ├── alpine.rs │ ├── boundary_toolchains.rs │ └── custom_toolchain.rs ├── fixtures ├── .gitignore ├── array_pattern │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── depinfo_dylint_libs │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── library_packages_in_dylint_toml │ ├── Cargo.toml │ ├── dylint.toml │ └── src │ │ └── main.rs ├── library_packages_with_rust_toolchain │ ├── Cargo.toml │ ├── rust-toolchain │ └── src │ │ └── lib.rs ├── name_toolchain_map │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── no_deps │ ├── Cargo.toml │ ├── a │ ├── Cargo.toml │ └── src │ │ └── main.rs │ └── b │ ├── Cargo.toml │ └── src │ └── lib.rs ├── internal ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── src │ ├── bin │ │ └── preinstall-toolchains.rs │ ├── cargo.rs │ ├── clippy_utils.rs │ ├── command.rs │ ├── config.rs │ ├── env.rs │ ├── examples.rs │ ├── filename.rs │ ├── git.rs │ ├── home.rs │ ├── lib.rs │ ├── packaging.rs │ ├── paths.rs │ ├── rustup.rs │ ├── sed.rs │ └── testing.rs └── template │ ├── .cargo │ └── config.toml │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── rust-toolchain │ ├── src │ └── lib.rs │ └── ui │ ├── main.rs │ └── main.stderr ├── rustfmt.toml ├── scheduled ├── Cargo.toml ├── duplicate_dependencies │ ├── aarch64-apple-darwin.txt │ ├── x86_64-apple-darwin.txt │ ├── x86_64-pc-windows-msvc.txt │ └── x86_64-unknown-linux-gnu.txt ├── src │ └── lib.rs ├── supply_chain │ ├── aarch64-apple-darwin.json │ ├── x86_64-apple-darwin.json │ ├── x86_64-pc-windows-msvc.json │ └── x86_64-unknown-linux-gnu.json └── tests │ └── scheduled.rs ├── scripts ├── check_CHANGELOG.sh ├── check_examples.sh ├── lint.sh ├── lint_CHANGELOG.sh ├── publish.sh ├── remove_patch_versions.sh ├── sync_with_workspace_README.sh ├── toolchain_from_version.sh ├── update_example_READMEs.sh ├── update_lockfiles.sh ├── update_toml_rs.sh ├── update_versions.sh └── upgrade_examples.sh └── utils ├── linting ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── rust-toolchain ├── src │ └── lib.rs └── tests │ └── build.rs └── testing ├── Cargo.toml ├── README.md └── src ├── lib.rs └── ui.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | # smoelius: This file is needed for the `alpine` test. 2 | 3 | target 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # smoelius: Force `LF` line endings in all text files. 2 | # See: https://github.com/Manishearth/compiletest-rs/issues/154 3 | * text=auto eol=lf 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | day: sunday 8 | time: "03:00" 9 | 10 | - package-ecosystem: cargo 11 | directories: 12 | - / 13 | - /driver 14 | - /examples/experimental/* 15 | - /examples/general 16 | - /examples/restriction 17 | - /examples/supplementary 18 | - /examples/testing 19 | - /utils/linting 20 | schedule: 21 | interval: weekly 22 | day: sunday 23 | time: "03:00" 24 | allow: 25 | - dependency-type: direct 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Check CHANGELOG.md 17 | run: ./scripts/check_CHANGELOG.sh "${{ github.ref }}" 18 | 19 | - name: Login 20 | run: echo ${{ secrets.CRATES_IO_TOKEN }} | cargo login 21 | 22 | - name: Publish 23 | run: ./scripts/publish.sh 24 | 25 | - name: Get version 26 | id: get-version 27 | run: echo "version=${GITHUB_REF/refs\/tags\/v/}" >> "$GITHUB_OUTPUT" 28 | 29 | - name: Create release notes 30 | run: git log -p -1 CHANGELOG.md | grep '^+\($\|[^+]\)' | cut -c 2- | tee body.md 31 | 32 | - name: Create release 33 | uses: softprops/action-gh-release@v2 34 | with: 35 | tag_name: ${{ github.ref }} 36 | name: Release ${{ steps.get-version.outputs.version }} 37 | body_path: body.md 38 | draft: false 39 | prerelease: ${{ contains(github.ref, 'pre') || contains(github.ref, 'rc') }} 40 | token: ${{ secrets.REPO_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/update_supply_chain.yml: -------------------------------------------------------------------------------- 1 | name: Update supply chain 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | update: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | # https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs 14 | # https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-using-ssh-deploy-keys 15 | ssh-key: ${{ secrets.SSH_KEY }} 16 | 17 | # smoelius: The next use of `actions/cache` should match what is in ci.yml. 18 | - uses: actions/cache@v4 19 | with: 20 | path: | 21 | ~/.cargo/bin/ 22 | ~/.cargo/registry/index/ 23 | ~/.cargo/registry/cache/ 24 | ~/.cargo/git/db/ 25 | ~/.dylint_drivers/ 26 | ~/.rustup/toolchains/ 27 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 28 | 29 | - name: Install cargo-supply-chain 30 | run: cargo install cargo-supply-chain || true 31 | 32 | - name: Update supply_chain.json 33 | run: | 34 | cargo test -p cargo-dylint --test ci supply_chain 35 | env: 36 | BLESS: 1 37 | 38 | - name: Create pull request 39 | uses: peter-evans/create-pull-request@v7 40 | with: 41 | title: Update supply chain files 42 | branch: update-supply-chain 43 | branch-suffix: random 44 | commit-message: Update supply chain files 45 | token: ${{ secrets.REPO_TOKEN }} 46 | -------------------------------------------------------------------------------- /.github/workflows/upgrade_examples.yml: -------------------------------------------------------------------------------- 1 | name: Upgrade examples 2 | 3 | # smoelius: Every Saturday at 3:00 UTC (Friday at 22:00 EST), create a PR to update the example 4 | # libraries to the latest version of `clippy_utils`. 5 | 6 | on: 7 | schedule: 8 | - cron: 0 3 * * sat 9 | workflow_dispatch: 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | upgrade: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | # https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs 22 | # https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-using-ssh-deploy-keys 23 | ssh-key: ${{ secrets.SSH_KEY }} 24 | 25 | - name: Rustup 26 | run: rustup update 27 | 28 | - name: Install dylint-link 29 | run: cargo install --path ./dylint-link --force 30 | 31 | - name: Upgrade examples 32 | run: | 33 | scripts/upgrade_examples.sh 34 | git diff --name-only 35 | 36 | - name: Create pull request 37 | uses: peter-evans/create-pull-request@v7 38 | with: 39 | title: Upgrade examples 40 | branch: upgrade-examples 41 | branch-suffix: random 42 | commit-message: Upgrade examples 43 | token: ${{ secrets.REPO_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rust.git/ 2 | target*/ 3 | warnings.json 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @smoelius 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | WIP 2 | 3 | - Please prefix all non-rustdoc comments with your GitHub username. 4 | 5 | Examples: 6 | 7 | ```rust 8 | /// This is a rustdoc comment, so no username is required. 9 | fn foo() {} 10 | 11 | // smoelius: This is a non-rustdoc comment. 12 | fn bar() {} 13 | ``` 14 | 15 | ```sh 16 | #! /bin/bash 17 | # smoelius: This is a Bash comment, and therefore non-rustdoc. 18 | ``` 19 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Trail of Bits 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /cargo-dylint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-dylint" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A tool for running Rust lints from dynamic libraries" 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/trailofbits/dylint" 9 | 10 | [[test]] 11 | name = "ci" 12 | required-features = ["__ci"] 13 | 14 | [dependencies] 15 | anyhow = { workspace = true, features = ["backtrace"] } 16 | clap = { workspace = true, features = ["cargo", "derive", "wrap_help"] } 17 | env_logger = { workspace = true } 18 | 19 | dylint = { version = "=4.1.0", path = "../dylint", features = [ 20 | "package_options", 21 | ] } 22 | dylint_internal = { version = "=4.1.0", path = "../internal" } 23 | 24 | [dev-dependencies] 25 | assert_cmd = { workspace = true } 26 | cargo_metadata = { workspace = true } 27 | ctor = { workspace = true } 28 | glob = { workspace = true } 29 | log = { workspace = true } 30 | predicates = { workspace = true } 31 | regex = { workspace = true } 32 | semver = { workspace = true } 33 | serde_json = { workspace = true } 34 | similar-asserts = { workspace = true } 35 | tempfile = { workspace = true } 36 | walkdir = { workspace = true } 37 | 38 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 39 | "examples", 40 | "testing", 41 | ] } 42 | 43 | [features] 44 | default = ["cargo-cli", "__ci"] 45 | cargo-cli = ["dylint/__cargo_cli"] 46 | cargo-lib = ["dylint/__cargo_lib"] 47 | __ci = [] 48 | __clap_headings = [] 49 | 50 | [lints] 51 | workspace = true 52 | -------------------------------------------------------------------------------- /cargo-dylint/tests/integration/depinfo_dylint_libs.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use dylint_internal::env; 3 | use predicates::prelude::*; 4 | use std::{env::remove_var, process::Command}; 5 | 6 | #[ctor::ctor] 7 | fn initialize() { 8 | unsafe { 9 | remove_var(env::CARGO_TERM_COLOR); 10 | } 11 | } 12 | 13 | #[test] 14 | fn depinfo_dylint_libs() { 15 | Command::cargo_bin("cargo-dylint") 16 | .unwrap() 17 | .current_dir("../fixtures/depinfo_dylint_libs") 18 | .args(["dylint", "--lib", "question_mark_in_expression"]) 19 | .assert() 20 | .stderr(predicate::str::contains( 21 | "\nwarning: using the `?` operator within an expression\n", 22 | )); 23 | 24 | Command::cargo_bin("cargo-dylint") 25 | .unwrap() 26 | .current_dir("../fixtures/depinfo_dylint_libs") 27 | .args(["dylint", "--lib", "try_io_result"]) 28 | .assert() 29 | .stderr(predicate::str::contains( 30 | "\nwarning: returning a `std::io::Result` could discard relevant context (e.g., files \ 31 | or paths involved)\n", 32 | )); 33 | } 34 | -------------------------------------------------------------------------------- /cargo-dylint/tests/integration/dylint_driver_path.rs: -------------------------------------------------------------------------------- 1 | use dylint_internal::{ 2 | CommandExt, driver as dylint_driver, env, 3 | rustup::{SanitizeEnvironment, toolchain_path}, 4 | testing::new_template, 5 | }; 6 | use std::fs::create_dir_all; 7 | use tempfile::tempdir_in; 8 | 9 | #[cfg_attr(dylint_lib = "general", allow(non_thread_safe_call_in_test))] 10 | #[test] 11 | fn dylint_driver_path() { 12 | let tempdir = tempdir_in(env!("CARGO_MANIFEST_DIR")).unwrap(); 13 | 14 | new_template(tempdir.path()).unwrap(); 15 | 16 | let dylint_driver_path = tempdir.path().join("target/dylint_drivers"); 17 | 18 | create_dir_all(&dylint_driver_path).unwrap(); 19 | 20 | dylint_internal::cargo::test("dylint-template") 21 | .build() 22 | .sanitize_environment() 23 | .current_dir(&tempdir) 24 | .envs([(env::DYLINT_DRIVER_PATH, &*dylint_driver_path)]) 25 | .success() 26 | .unwrap(); 27 | 28 | // smoelius: Verify that the driver can be run directly. 29 | // https://github.com/trailofbits/dylint/issues/54 30 | let toolchain_path = toolchain_path(tempdir.path()).unwrap(); 31 | let toolchain = toolchain_path.iter().next_back().unwrap(); 32 | let mut command = dylint_driver( 33 | &toolchain.to_string_lossy(), 34 | &dylint_driver_path.join(toolchain).join("dylint-driver"), 35 | ) 36 | .unwrap(); 37 | command.success().unwrap(); 38 | } 39 | -------------------------------------------------------------------------------- /cargo-dylint/tests/integration/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(dylint_lib = "general", allow(crate_wide_allow))] 2 | #![cfg_attr(dylint_lib = "supplementary", allow(nonexistent_path_in_comment))] 3 | 4 | mod depinfo_dylint_libs; 5 | mod dylint_driver_path; 6 | mod fix; 7 | mod library_packages; 8 | mod list; 9 | mod nightly_toolchain; 10 | mod no_deps; 11 | mod package_options; 12 | mod warn; 13 | -------------------------------------------------------------------------------- /cargo-dylint/tests/integration/nightly_toolchain.rs: -------------------------------------------------------------------------------- 1 | // smoelius: On Windows, `rustup update nightly` generates "could not create link" errors similar to 2 | // this one: https://github.com/rust-lang/rustup/issues/1316 3 | #![cfg(not(target_os = "windows"))] 4 | 5 | use anyhow::Result; 6 | use dylint_internal::CommandExt; 7 | use std::process::Command; 8 | 9 | #[test] 10 | fn nightly_toolchain() { 11 | update_nightly().unwrap(); 12 | 13 | #[allow(let_underscore_drop)] 14 | let _ = dylint::driver_builder::get(&dylint::opts::Dylint::default(), "nightly").unwrap(); 15 | } 16 | 17 | fn update_nightly() -> Result<()> { 18 | Command::new("rustup").args(["update", "nightly"]).success() 19 | } 20 | -------------------------------------------------------------------------------- /cargo-dylint/tests/integration/no_deps.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use dylint_internal::env; 3 | use std::process::Command; 4 | 5 | #[test] 6 | fn current_dir() { 7 | test(|command| { 8 | command.current_dir("../fixtures/no_deps/a"); 9 | }); 10 | } 11 | 12 | #[test] 13 | fn manifest_path() { 14 | test(|command| { 15 | command 16 | .current_dir("../fixtures/no_deps") 17 | .args(["--manifest-path", "a/Cargo.toml"]); 18 | }); 19 | } 20 | 21 | #[test] 22 | fn package() { 23 | test(|command| { 24 | command 25 | .current_dir("../fixtures/no_deps") 26 | .args(["--package", "a"]); 27 | }); 28 | } 29 | 30 | fn test(f: impl Fn(&mut Command)) { 31 | for no_deps in [false, true] { 32 | let mut command = base_command(); 33 | 34 | f(&mut command); 35 | 36 | if no_deps { 37 | command.arg("--no-deps").assert().success(); 38 | } else { 39 | command.assert().failure(); 40 | } 41 | } 42 | } 43 | 44 | fn base_command() -> Command { 45 | let mut command = Command::cargo_bin("cargo-dylint").unwrap(); 46 | command.env(env::RUSTFLAGS, "-D warnings").args([ 47 | "dylint", 48 | "--lib", 49 | "question_mark_in_expression", 50 | ]); 51 | command 52 | } 53 | -------------------------------------------------------------------------------- /cargo-dylint/tests/markdown_link_check.json: -------------------------------------------------------------------------------- 1 | { 2 | "httpHeaders": [ 3 | { "urls": ["https://crates.io"], "headers": { "Accept": "text/html" } }, 4 | { 5 | "urls": ["https://github.com"], 6 | "headers": { 7 | "Accept": "text/html" 8 | } 9 | }, 10 | { 11 | "urls": ["https://api.github.com"], 12 | "headers": { 13 | "Accept": "application/vnd.github.v3+json", 14 | "Authorization": "token ${GITHUB_TOKEN}" 15 | } 16 | } 17 | ], 18 | "ignorePatterns": [ 19 | { 20 | "pattern": "^https://[^/]*\\.openai\\.com/" 21 | } 22 | ], 23 | "replacementPatterns": [ 24 | { 25 | "pattern": "^https://github.com/([^/]+)/([^/]+)/blob/([^/]+)/(.+)", 26 | "replacement": "https://api.github.com/repos/$1/$2/contents/$4?ref=$3" 27 | }, 28 | { 29 | "pattern": "^https://github.com/([^/]+)/([^/]+)/tree/([^/]+)/(.+)", 30 | "replacement": "https://api.github.com/repos/$1/$2/contents/$4?ref=$3" 31 | }, 32 | { 33 | "pattern": "^https://github.com/([^/]+)/([^/]+)$", 34 | "replacement": "https://api.github.com/repos/$1/$2" 35 | }, 36 | { 37 | "pattern": "^https://github.com/([^/]+)/([^/]+)/commit/(.+)", 38 | "replacement": "https://api.github.com/repos/$1/$2/commits/$3" 39 | }, 40 | { 41 | "pattern": "^https://github.com/([^/]+)/([^/]+)/issue/(.+)", 42 | "replacement": "https://api.github.com/repos/$1/$2/issues/$3" 43 | }, 44 | { 45 | "pattern": "^https://github.com/([^/]+)/([^/]+)/pull/(.+)", 46 | "replacement": "https://api.github.com/repos/$1/$2/pulls/$3" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /cargo-dylint/tests/prettier_ignore.txt: -------------------------------------------------------------------------------- 1 | examples 2 | template 3 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | disallowed-methods = [ 2 | { path = "std::process::Command::output", reason = "use `CommandExt::logged_output`" }, 3 | { path = "std::result::Result::expect", reason = "hides errors; use `unwrap_or_else(|error| panic!(...))`" }, 4 | ] 5 | -------------------------------------------------------------------------------- /docs/2024-10-11 Linting with Dylint (EuroRust).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/dylint/f176ee4b8bf6c05ccd69b61742f18cac1bc7711d/docs/2024-10-11 Linting with Dylint (EuroRust).pdf -------------------------------------------------------------------------------- /driver/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../target/nightly" 3 | -------------------------------------------------------------------------------- /driver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dylint_driver" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "Dylint driver library" 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/trailofbits/dylint" 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | libc = "0.2" 13 | libloading = "0.8" 14 | log = "0.4" 15 | rustversion = "1.0" 16 | serde_json = "1.0" 17 | 18 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 19 | "rustup", 20 | ] } 21 | 22 | [dev-dependencies] 23 | rustc_version = "0.4" 24 | 25 | [lints.rust.unexpected_cfgs] 26 | level = "deny" 27 | check-cfg = ["cfg(dylint_lib, values(any()))"] 28 | 29 | [workspace] 30 | 31 | [workspace.metadata.dylint] 32 | libraries = [ 33 | { path = "../examples/general" }, 34 | { path = "../examples/supplementary" }, 35 | { path = "../examples/testing/clippy" }, 36 | { path = "../examples/restriction/*" }, 37 | ] 38 | -------------------------------------------------------------------------------- /driver/README.md: -------------------------------------------------------------------------------- 1 | # dylint_driver 2 | 3 | The package is used by [Dylint] drivers. Users should not need to refer to this package directly. 4 | 5 | [Dylint]: .. 6 | -------------------------------------------------------------------------------- /driver/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /dylint-link/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dylint-link" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A wrapper around Rust's default linker to help create Dyling libraries" 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/trailofbits/dylint" 9 | 10 | [dependencies] 11 | anyhow = { workspace = true } 12 | env_logger = { workspace = true } 13 | if_chain = { workspace = true } 14 | toml = { workspace = true } 15 | toml_edit = { workspace = true } 16 | 17 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 18 | "cargo", 19 | ] } 20 | 21 | [build-dependencies] 22 | dylint_internal = { version = "=4.1.0", path = "../internal" } 23 | 24 | [dev-dependencies] 25 | assert_cmd = { workspace = true } 26 | predicates = { workspace = true } 27 | tempfile = { workspace = true } 28 | 29 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 30 | "packaging", 31 | ] } 32 | 33 | [lints] 34 | workspace = true 35 | 36 | [target.'cfg(target_os = "windows")'.dependencies] 37 | cc = "1.2" 38 | -------------------------------------------------------------------------------- /dylint-link/README.md: -------------------------------------------------------------------------------- 1 | # dylint-link 2 | 3 | `dylint-link` is a wrapper around Rust's default linker (`cc`) to help create [Dylint] libraries. 4 | 5 | When you link a dynamic library with the same name as your package, `dylint-link` creates a copy of your library with a filename that Dylint recognizes, i.e.: 6 | 7 | ``` 8 | DLL_PREFIX LIBRARY_NAME '@' TOOLCHAIN DLL_SUFFIX 9 | ``` 10 | 11 | To use `dylint-link`, install it: 12 | 13 | ```sh 14 | cargo-install dylint-link 15 | ``` 16 | 17 | And set it as the linker in your library's `.cargo/config.toml` file, e.g.: 18 | 19 | ```toml 20 | [target.x86_64-unknown-linux-gnu] 21 | linker = "dylint-link" 22 | ``` 23 | 24 | If your library uses `dylint-link` and the [`dylint_library!`] macro, then all you should have to do is implement the [`register_lints`] function. See the [examples] in this repository. 25 | 26 | [Dylint]: .. 27 | [`dylint_library!`]: ../utils/linting 28 | [`register_lints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints 29 | [examples]: ../examples 30 | -------------------------------------------------------------------------------- /dylint-link/build.rs: -------------------------------------------------------------------------------- 1 | use dylint_internal::env; 2 | 3 | fn main() { 4 | println!("cargo:rustc-env=TARGET={}", env::var(env::TARGET).unwrap()); 5 | } 6 | -------------------------------------------------------------------------------- /dylint.toml: -------------------------------------------------------------------------------- 1 | [non_local_effect_before_error_return] 2 | public_only = false 3 | 4 | [redundant_reference] 5 | lifetime_check = false 6 | -------------------------------------------------------------------------------- /dylint/src/library_packages/cargo_cli/util/hex.rs: -------------------------------------------------------------------------------- 1 | // smoelius: This file is a slight modification of: 2 | // https://github.com/rust-lang/cargo/blob/674e609a0ec2dc431575c48989a7bd1953ff2ab0/src/cargo/util/hex.rs 3 | 4 | #![allow(deprecated)] 5 | #![allow(clippy::large_stack_arrays, clippy::module_name_repetitions)] 6 | #![cfg_attr(dylint_lib = "supplementary", allow(unnamed_constant))] 7 | 8 | type StableHasher = rustc_stable_hash::StableSipHasher128; 9 | 10 | use std::fs::File; 11 | use std::hash::{Hash, Hasher}; 12 | use std::io::Read; 13 | 14 | pub fn to_hex(num: u64) -> String { 15 | hex::encode(num.to_le_bytes()) 16 | } 17 | 18 | pub fn hash_u64(hashable: H) -> u64 { 19 | let mut hasher = StableHasher::new(); 20 | hashable.hash(&mut hasher); 21 | Hasher::finish(&hasher) 22 | } 23 | 24 | pub fn hash_u64_file(mut file: &File) -> std::io::Result { 25 | let mut hasher = StableHasher::new(); 26 | let mut buf = [0; 64 * 1024]; 27 | loop { 28 | let n = file.read(&mut buf)?; 29 | if n == 0 { 30 | break; 31 | } 32 | hasher.write(&buf[..n]); 33 | } 34 | Ok(Hasher::finish(&hasher)) 35 | } 36 | 37 | pub fn short_hash(hashable: &H) -> String { 38 | to_hex(hash_u64(hashable)) 39 | } 40 | -------------------------------------------------------------------------------- /dylint/src/library_packages/cargo_cli/util/mod.rs: -------------------------------------------------------------------------------- 1 | mod canonical_url; 2 | pub use canonical_url::CanonicalUrl; 3 | 4 | #[allow(dead_code)] 5 | mod hex; 6 | pub use self::hex::short_hash; 7 | -------------------------------------------------------------------------------- /dylint/src/name_toolchain_map/maybe_library.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::path::PathBuf; 3 | 4 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 5 | pub struct MaybeLibrary { 6 | inner: Inner, 7 | } 8 | 9 | impl MaybeLibrary { 10 | pub fn path(&self) -> PathBuf { 11 | self.inner.path() 12 | } 13 | 14 | pub fn build(&self, opts: &crate::opts::Dylint) -> Result { 15 | self.inner.build(opts) 16 | } 17 | } 18 | 19 | impl From for MaybeLibrary { 20 | fn from(path: PathBuf) -> Self { 21 | Self { 22 | inner: Inner::Path(path), 23 | } 24 | } 25 | } 26 | 27 | #[cfg(__library_packages)] 28 | impl From for MaybeLibrary { 29 | fn from(package: crate::library_packages::Package) -> Self { 30 | Self { 31 | inner: Inner::Package(package), 32 | } 33 | } 34 | } 35 | 36 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 37 | pub enum Inner { 38 | Path(PathBuf), 39 | 40 | #[cfg(__library_packages)] 41 | Package(crate::library_packages::Package), 42 | } 43 | 44 | impl Inner { 45 | pub fn path(&self) -> PathBuf { 46 | match self { 47 | Self::Path(path) => path.clone(), 48 | 49 | #[cfg(__library_packages)] 50 | Self::Package(package) => package.path(), 51 | } 52 | } 53 | 54 | #[cfg_attr( 55 | not(__library_packages), 56 | allow(unused_variables, clippy::unnecessary_wraps) 57 | )] 58 | fn build(&self, opts: &crate::opts::Dylint) -> Result { 59 | match self { 60 | Self::Path(path) => Ok(path.clone()), 61 | 62 | #[cfg(__library_packages)] 63 | Self::Package(package) => crate::library_packages::build_library(opts, package), 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /dylint/src/package_options/auto_correct/short_id.rs: -------------------------------------------------------------------------------- 1 | use dylint_internal::git2::{Commit, Oid}; 2 | 3 | const SHORT_ID_LEN: usize = 7; 4 | 5 | pub trait ShortId { 6 | fn short_id(&self) -> String; 7 | } 8 | 9 | impl ShortId for Commit<'_> { 10 | fn short_id(&self) -> String { 11 | self.id().short_id() 12 | } 13 | } 14 | 15 | impl ShortId for Oid { 16 | fn short_id(&self) -> String { 17 | self.to_string()[..SHORT_ID_LEN].to_owned() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dylint/src/package_options/auto_correct/tokenization.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::sync::LazyLock; 3 | use syntect::parsing::{ParseState, ScopeStackOp, SyntaxReference, SyntaxSet}; 4 | 5 | static SYNTAX_SET: LazyLock = LazyLock::new(SyntaxSet::load_defaults_nonewlines); 6 | 7 | static SYNTAX: LazyLock<&SyntaxReference> = 8 | LazyLock::new(|| SYNTAX_SET.find_syntax_by_extension("rs").unwrap()); 9 | 10 | pub fn tokenize_lines>(lines: &[T]) -> Result> { 11 | let tokens = lines 12 | .iter() 13 | .map(|line| -> Result<_> { tokenize_fragment(line.as_ref().trim()) }) 14 | .collect::>>>()?; 15 | 16 | Ok(tokens.into_iter().flatten().collect()) 17 | } 18 | 19 | /// Tokenize a Rust fragment, e.g., code containing unbalanced delimiters. 20 | pub fn tokenize_fragment(line: &str) -> Result> { 21 | let mut state = ParseState::new(*SYNTAX); 22 | let mut offsets_and_ops = state.parse_line(line, &SYNTAX_SET)?; 23 | offsets_and_ops.push((line.len(), ScopeStackOp::Noop)); 24 | let tokens = offsets_and_ops 25 | .windows(2) 26 | .filter_map(|w| { 27 | let &[(start, _), (end, _)] = w else { 28 | unreachable!(); 29 | }; 30 | let token = &line[start..end]; 31 | if token.chars().all(char::is_whitespace) { 32 | return None; 33 | } 34 | // smoelius: Several of the "tokens" that `syntect` identifies begin with whitespace, 35 | // hence the use of `trim_start`. 36 | Some(token.trim_start()) 37 | }) 38 | .collect(); 39 | Ok(tokens) 40 | } 41 | -------------------------------------------------------------------------------- /dylint/src/package_options/common.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use dylint_internal::{clone, git2::Repository}; 3 | use std::{cell::RefCell, rc::Rc}; 4 | use tempfile::{TempDir, tempdir}; 5 | 6 | const RUST_CLIPPY_URL: &str = "https://github.com/rust-lang/rust-clippy"; 7 | 8 | // smoelius: `thread_local!` because `git2::Repository` cannot be shared between threads safely. 9 | thread_local! { 10 | static TMPDIR_AND_REPOSITORY: RefCell)>> = const { RefCell::new(None) }; 11 | } 12 | 13 | pub fn clippy_repository(quiet: bool) -> Result> { 14 | TMPDIR_AND_REPOSITORY.with_borrow_mut(|cell| { 15 | if let Some((_, repository)) = cell { 16 | return Ok(repository.clone()); 17 | } 18 | 19 | let tempdir = tempdir().with_context(|| "`tempdir` failed")?; 20 | 21 | let repository = clone(RUST_CLIPPY_URL, "master", tempdir.path(), quiet).map(Rc::new)?; 22 | 23 | cell.replace((tempdir, repository.clone())); 24 | 25 | Ok(repository) 26 | }) 27 | } 28 | 29 | pub fn parse_as_nightly(channel: &str) -> Option<[u32; 3]> { 30 | channel.strip_prefix("nightly-").and_then(parse_date) 31 | } 32 | 33 | fn parse_date(date_str: &str) -> Option<[u32; 3]> { 34 | date_str 35 | .split('-') 36 | .map(str::parse::) 37 | .map(Result::ok) 38 | .collect::>>() 39 | .map(<[u32; 3]>::try_from) 40 | .and_then(Result::ok) 41 | } 42 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dylint_examples" 3 | version = "4.1.0" 4 | description = "A dummy package for testing the example Dylint libraries" 5 | edition = "2024" 6 | license = "MIT OR Apache-2.0" 7 | publish = false 8 | 9 | [dev-dependencies] 10 | cargo-util = { workspace = true } 11 | cargo_metadata = { workspace = true } 12 | regex = { workspace = true } 13 | toml = { workspace = true } 14 | toml_edit = { workspace = true } 15 | walkdir = { workspace = true } 16 | 17 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 18 | "clippy_utils", 19 | "examples", 20 | ] } 21 | 22 | [lints] 23 | workspace = true 24 | -------------------------------------------------------------------------------- /examples/experimental/README.md: -------------------------------------------------------------------------------- 1 | # Experimental lints 2 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "derive_opportunity" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for traits that could be derived" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [[example]] 17 | name = "ui_at_least_one_field" 18 | path = "ui_at_least_one_field/main.rs" 19 | 20 | [[example]] 21 | name = "ui_ignore" 22 | path = "ui_ignore/main.rs" 23 | 24 | [dependencies] 25 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 26 | once_cell = "1.21" 27 | serde = "1.0" 28 | 29 | dylint_internal = { path = "../../../internal" } 30 | dylint_linting = { path = "../../../utils/linting" } 31 | 32 | [dev-dependencies] 33 | bitflags = "2.9" 34 | serde_derive = "1.0" 35 | 36 | dylint_testing = { path = "../../../utils/testing" } 37 | 38 | [lints.rust.unexpected_cfgs] 39 | level = "deny" 40 | check-cfg = ["cfg(dylint_lib, values(any()))"] 41 | 42 | [package.metadata.rust-analyzer] 43 | rustc_private = true 44 | 45 | [workspace] 46 | 47 | [workspace.metadata.dylint] 48 | libraries = [ 49 | { path = "../../general" }, 50 | { path = "../../supplementary" }, 51 | { path = "../../testing/clippy" }, 52 | { path = "../../restriction/*" }, 53 | ] 54 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/README.md: -------------------------------------------------------------------------------- 1 | # derive_opportunity 2 | 3 | ### What it does 4 | 5 | Checks for data structures that could derive additional traits. 6 | 7 | ### Why is this bad? 8 | 9 | Not deriving the additional traits could be a missed opportunity. 10 | 11 | ### Known problems 12 | 13 | - This lint is noisy! The `at_least_one_field` and `ignore` options (see below) can be used 14 | to make the lint less noisy. 15 | - Currently does not support traits with type or constant parameters (e.g., `PartialEq`), or 16 | traits with supertraits with type or constant parameters (e.g., `Eq`). 17 | 18 | ### Example 19 | 20 | ```rust 21 | #[derive(Default)] 22 | struct S; 23 | 24 | struct T(S); 25 | ``` 26 | 27 | Use instead: 28 | 29 | ```rust 30 | #[derive(Default)] 31 | struct S; 32 | 33 | #[derive(Default)] 34 | struct T(S); 35 | ``` 36 | 37 | ### Configuration 38 | 39 | - `at_least_one_field: bool` (default `false`): If set to `true`, the lint suggests to 40 | derive a trait only when there is at least one field that implements (or could derive) the 41 | trait. 42 | - `ignore: Vec` (default `[]`): A list of macro paths the lint should not suggest to 43 | derive. 44 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | #[derive(Clone, Copy, Hash)] 8 | struct Derived; 9 | 10 | #[derive(Default, serde::Deserialize)] 11 | #[derive(Clone, Copy, Hash)] 12 | struct DerivedWithParam { 13 | foo: T, 14 | } 15 | 16 | #[derive(Clone, Copy, Default, Hash, serde_derive::Deserialize)] 17 | struct Empty; 18 | 19 | #[derive(Clone, Copy, Default, Hash, serde_derive::Deserialize)] 20 | struct SimpleStruct { 21 | foo: Derived, 22 | } 23 | 24 | #[derive(Clone, Copy, Hash, serde_derive::Deserialize)] 25 | enum SimpleEnum { 26 | Foo(Derived), 27 | } 28 | 29 | #[derive(Clone, Copy, Default, Hash, serde_derive::Deserialize)] 30 | struct StructWithParam { 31 | foo: Derived, 32 | bar: T, 33 | } 34 | 35 | #[derive(Clone, Copy, Hash, serde_derive::Deserialize)] 36 | enum EnumWithParam { 37 | Foo(Derived), 38 | Bar(T), 39 | } 40 | 41 | #[derive(Clone, Copy, Default, Hash, serde_derive::Deserialize)] 42 | struct TransitiveStruct { 43 | foo: SimpleStruct, 44 | } 45 | 46 | #[derive(Clone, Copy, Hash, serde_derive::Deserialize)] 47 | enum TransitiveEnum { 48 | Foo(SimpleStruct), 49 | } 50 | 51 | #[derive(Default)] 52 | #[derive(Clone, Copy, Hash, serde_derive::Deserialize)] 53 | struct PartiallyDerivedStruct { 54 | foo: Derived, 55 | } 56 | 57 | #[derive(serde::Deserialize)] 58 | #[derive(Clone, Copy, Hash)] 59 | enum PartiallyDerivedEnum { 60 | Foo(Derived), 61 | } 62 | 63 | bitflags::bitflags! { 64 | struct Flags: u8 { 65 | const X = 1 << 0; 66 | const Y = 1 << 1; 67 | const Z = 1 << 2; 68 | } 69 | } 70 | 71 | struct StructWithFlags { 72 | flags: Flags, 73 | } 74 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | struct Derived; 8 | 9 | #[derive(Default, serde::Deserialize)] 10 | struct DerivedWithParam { 11 | foo: T, 12 | } 13 | 14 | struct Empty; 15 | 16 | struct SimpleStruct { 17 | foo: Derived, 18 | } 19 | 20 | enum SimpleEnum { 21 | Foo(Derived), 22 | } 23 | 24 | struct StructWithParam { 25 | foo: Derived, 26 | bar: T, 27 | } 28 | 29 | enum EnumWithParam { 30 | Foo(Derived), 31 | Bar(T), 32 | } 33 | 34 | struct TransitiveStruct { 35 | foo: SimpleStruct, 36 | } 37 | 38 | enum TransitiveEnum { 39 | Foo(SimpleStruct), 40 | } 41 | 42 | #[derive(Default)] 43 | struct PartiallyDerivedStruct { 44 | foo: Derived, 45 | } 46 | 47 | #[derive(serde::Deserialize)] 48 | enum PartiallyDerivedEnum { 49 | Foo(Derived), 50 | } 51 | 52 | bitflags::bitflags! { 53 | struct Flags: u8 { 54 | const X = 1 << 0; 55 | const Y = 1 << 1; 56 | const Z = 1 << 2; 57 | } 58 | } 59 | 60 | struct StructWithFlags { 61 | flags: Flags, 62 | } 63 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui_at_least_one_field/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | struct Derived; 8 | 9 | #[derive(Default, serde::Deserialize)] 10 | struct DerivedWithParam { 11 | foo: T, 12 | } 13 | 14 | struct Empty; 15 | 16 | #[derive(Default, serde_derive::Deserialize)] 17 | struct SimpleStruct { 18 | foo: Derived, 19 | } 20 | 21 | #[derive(serde_derive::Deserialize)] 22 | enum SimpleEnum { 23 | Foo(Derived), 24 | } 25 | 26 | #[derive(Default, serde_derive::Deserialize)] 27 | struct StructWithParam { 28 | foo: Derived, 29 | bar: T, 30 | } 31 | 32 | #[derive(serde_derive::Deserialize)] 33 | enum EnumWithParam { 34 | Foo(Derived), 35 | Bar(T), 36 | } 37 | 38 | #[derive(Default, serde_derive::Deserialize)] 39 | struct TransitiveStruct { 40 | foo: SimpleStruct, 41 | } 42 | 43 | #[derive(serde_derive::Deserialize)] 44 | enum TransitiveEnum { 45 | Foo(SimpleStruct), 46 | } 47 | 48 | #[derive(Default)] 49 | #[derive(serde_derive::Deserialize)] 50 | struct PartiallyDerivedStruct { 51 | foo: Derived, 52 | } 53 | 54 | #[derive(serde::Deserialize)] 55 | enum PartiallyDerivedEnum { 56 | Foo(Derived), 57 | } 58 | 59 | bitflags::bitflags! { 60 | struct Flags: u8 { 61 | const X = 1 << 0; 62 | const Y = 1 << 1; 63 | const Z = 1 << 2; 64 | } 65 | } 66 | 67 | struct StructWithFlags { 68 | flags: Flags, 69 | } 70 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui_at_least_one_field/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | struct Derived; 8 | 9 | #[derive(Default, serde::Deserialize)] 10 | struct DerivedWithParam { 11 | foo: T, 12 | } 13 | 14 | struct Empty; 15 | 16 | struct SimpleStruct { 17 | foo: Derived, 18 | } 19 | 20 | enum SimpleEnum { 21 | Foo(Derived), 22 | } 23 | 24 | struct StructWithParam { 25 | foo: Derived, 26 | bar: T, 27 | } 28 | 29 | enum EnumWithParam { 30 | Foo(Derived), 31 | Bar(T), 32 | } 33 | 34 | struct TransitiveStruct { 35 | foo: SimpleStruct, 36 | } 37 | 38 | enum TransitiveEnum { 39 | Foo(SimpleStruct), 40 | } 41 | 42 | #[derive(Default)] 43 | struct PartiallyDerivedStruct { 44 | foo: Derived, 45 | } 46 | 47 | #[derive(serde::Deserialize)] 48 | enum PartiallyDerivedEnum { 49 | Foo(Derived), 50 | } 51 | 52 | bitflags::bitflags! { 53 | struct Flags: u8 { 54 | const X = 1 << 0; 55 | const Y = 1 << 1; 56 | const Z = 1 << 2; 57 | } 58 | } 59 | 60 | struct StructWithFlags { 61 | flags: Flags, 62 | } 63 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui_ignore/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | #[derive(Clone, Copy, Hash)] 8 | struct Derived; 9 | 10 | #[derive(Default, serde::Deserialize)] 11 | #[derive(Clone, Copy, Hash)] 12 | struct DerivedWithParam { 13 | foo: T, 14 | } 15 | 16 | #[derive(Clone, Copy, Default, Hash)] 17 | struct Empty; 18 | 19 | #[derive(Clone, Copy, Default, Hash)] 20 | struct SimpleStruct { 21 | foo: Derived, 22 | } 23 | 24 | #[derive(Clone, Copy, Hash)] 25 | enum SimpleEnum { 26 | Foo(Derived), 27 | } 28 | 29 | #[derive(Clone, Copy, Default, Hash)] 30 | struct StructWithParam { 31 | foo: Derived, 32 | bar: T, 33 | } 34 | 35 | #[derive(Clone, Copy, Hash)] 36 | enum EnumWithParam { 37 | Foo(Derived), 38 | Bar(T), 39 | } 40 | 41 | #[derive(Clone, Copy, Default, Hash)] 42 | struct TransitiveStruct { 43 | foo: SimpleStruct, 44 | } 45 | 46 | #[derive(Clone, Copy, Hash)] 47 | enum TransitiveEnum { 48 | Foo(SimpleStruct), 49 | } 50 | 51 | #[derive(Default)] 52 | #[derive(Clone, Copy, Hash)] 53 | struct PartiallyDerivedStruct { 54 | foo: Derived, 55 | } 56 | 57 | #[derive(serde::Deserialize)] 58 | #[derive(Clone, Copy, Hash)] 59 | enum PartiallyDerivedEnum { 60 | Foo(Derived), 61 | } 62 | 63 | bitflags::bitflags! { 64 | struct Flags: u8 { 65 | const X = 1 << 0; 66 | const Y = 1 << 1; 67 | const Z = 1 << 2; 68 | } 69 | } 70 | 71 | struct StructWithFlags { 72 | flags: Flags, 73 | } 74 | -------------------------------------------------------------------------------- /examples/experimental/derive_opportunity/ui_ignore/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | #[derive(Default, serde::Deserialize)] 7 | struct Derived; 8 | 9 | #[derive(Default, serde::Deserialize)] 10 | struct DerivedWithParam { 11 | foo: T, 12 | } 13 | 14 | struct Empty; 15 | 16 | struct SimpleStruct { 17 | foo: Derived, 18 | } 19 | 20 | enum SimpleEnum { 21 | Foo(Derived), 22 | } 23 | 24 | struct StructWithParam { 25 | foo: Derived, 26 | bar: T, 27 | } 28 | 29 | enum EnumWithParam { 30 | Foo(Derived), 31 | Bar(T), 32 | } 33 | 34 | struct TransitiveStruct { 35 | foo: SimpleStruct, 36 | } 37 | 38 | enum TransitiveEnum { 39 | Foo(SimpleStruct), 40 | } 41 | 42 | #[derive(Default)] 43 | struct PartiallyDerivedStruct { 44 | foo: Derived, 45 | } 46 | 47 | #[derive(serde::Deserialize)] 48 | enum PartiallyDerivedEnum { 49 | Foo(Derived), 50 | } 51 | 52 | bitflags::bitflags! { 53 | struct Flags: u8 { 54 | const X = 1 << 0; 55 | const Y = 1 << 1; 56 | const Z = 1 << 2; 57 | } 58 | } 59 | 60 | struct StructWithFlags { 61 | flags: Flags, 62 | } 63 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "missing_doc_comment_openai" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint that suggests doc comments using OpenAI" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 14 | curl = "0.4" 15 | serde = "1.0" 16 | serde_json = "1.0" 17 | 18 | dylint_linting = { path = "../../../utils/linting" } 19 | 20 | [dev-dependencies] 21 | dylint_testing = { path = "../../../utils/testing" } 22 | 23 | [lints.rust.unexpected_cfgs] 24 | level = "deny" 25 | check-cfg = ["cfg(dylint_lib, values(any()))"] 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | 30 | [workspace] 31 | 32 | [workspace.metadata.dylint] 33 | libraries = [ 34 | { path = "../../general" }, 35 | { path = "../../supplementary" }, 36 | { path = "../../testing/clippy" }, 37 | { path = "../../restriction/*" }, 38 | ] 39 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/src/openai.rs: -------------------------------------------------------------------------------- 1 | #![expect(dead_code)] 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize)] 6 | pub struct Request { 7 | pub prompt: String, 8 | 9 | pub model: String, 10 | 11 | pub max_tokens: u32, 12 | 13 | pub temperature: f32, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub top_p: Option, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub frequency_penalty: Option, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub presence_penalty: Option, 23 | 24 | pub stop: &'static [&'static str], 25 | } 26 | 27 | #[derive(Debug, Deserialize)] 28 | pub struct Choice { 29 | pub text: String, 30 | pub index: u64, 31 | pub finish_reason: String, 32 | } 33 | 34 | #[derive(Debug, Deserialize)] 35 | pub struct Usage { 36 | pub prompt_tokens: u64, 37 | pub completion_tokens: u64, 38 | pub total_tokens: u64, 39 | } 40 | 41 | #[derive(Debug, Default, Deserialize)] 42 | pub struct Response { 43 | pub id: Option, 44 | pub object: Option, 45 | pub created: Option, 46 | pub model: Option, 47 | pub choices: Vec, 48 | pub usage: Option, 49 | } 50 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | fn main() {} 4 | 5 | /// A doc comment generated by OpenAI. 6 | // A comment. 7 | #[expect(clippy::disallowed_names)] 8 | pub fn foo() {} 9 | 10 | /// Negative test. 11 | pub fn bar() {} 12 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/ui/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | fn main() {} 4 | 5 | // A comment. 6 | #[expect(clippy::disallowed_names)] 7 | pub fn foo() {} 8 | 9 | /// Negative test. 10 | pub fn bar() {} 11 | -------------------------------------------------------------------------------- /examples/experimental/missing_doc_comment_openai/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: exported function lacks a doc comment 2 | --> $DIR/main.rs:7:1 3 | | 4 | LL | pub fn foo() {} 5 | | ^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(missing_doc_comment_openai)]` on by default 8 | help: use the following suggestion from OpenAI 9 | | 10 | LL + /// A doc comment generated by OpenAI. 11 | | 12 | 13 | warning: 1 warning emitted 14 | 15 | -------------------------------------------------------------------------------- /examples/experimental/overscoped_allow/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/experimental/overscoped_allow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "overscoped_allow" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for `allow` attributes whose scope could be reduced" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui_general" 14 | path = "ui_general/main.rs" 15 | 16 | [[bin]] 17 | name = "ui_test" 18 | path = "ui_test/main.rs" 19 | 20 | [dependencies] 21 | anyhow = "1.0" 22 | cargo_metadata = "0.19" 23 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 24 | once_cell = "1.21" 25 | serde = "1.0" 26 | serde_json = "1.0" 27 | 28 | dylint_internal = { path = "../../../internal" } 29 | dylint_linting = { path = "../../../utils/linting" } 30 | 31 | [dev-dependencies] 32 | assert_cmd = "2.0" 33 | tempfile = "3.20" 34 | 35 | dylint_testing = { path = "../../../utils/testing" } 36 | 37 | [lints.rust.unexpected_cfgs] 38 | level = "deny" 39 | check-cfg = ["cfg(dylint_lib, values(any()))"] 40 | 41 | [package.metadata.rust-analyzer] 42 | rustc_private = true 43 | 44 | [workspace] 45 | 46 | [workspace.metadata.dylint] 47 | libraries = [ 48 | { path = "../../general" }, 49 | { path = "../../supplementary" }, 50 | { path = "../../testing/clippy" }, 51 | { path = "../../restriction/*" }, 52 | ] 53 | -------------------------------------------------------------------------------- /examples/experimental/overscoped_allow/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/experimental/overscoped_allow/ui_test/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_const_for_fn, clippy::manual_assert)] 2 | #![cfg_attr(dylint_lib = "general", allow(crate_wide_allow))] 3 | #![warn(clippy::panic)] 4 | 5 | fn main() {} 6 | 7 | #[allow(clippy::panic)] 8 | mod outside { 9 | #[test] 10 | fn panic() { 11 | if false { 12 | panic!(); 13 | } 14 | } 15 | } 16 | 17 | mod inside { 18 | #[test] 19 | fn panic() { 20 | #[allow(clippy::panic)] 21 | if false { 22 | panic!(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/experimental/overscoped_allow/ui_test/main.stderr: -------------------------------------------------------------------------------- 1 | warning: `allow` could be moved closer to diagnostic source 2 | --> $DIR/main.rs:7:9 3 | | 4 | LL | #[allow(clippy::panic)] 5 | | ^^^^^^^^^^^^^ 6 | | 7 | help: `allow` could be moved here 8 | --> $DIR/main.rs:10:5 9 | | 10 | LL | #[test] 11 | | ------- in this procedural macro expansion 12 | LL | fn panic() { 13 | | ^ 14 | = note: `#[warn(overscoped_allow)]` on by default 15 | 16 | warning: 1 warning emitted 17 | 18 | -------------------------------------------------------------------------------- /examples/general/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/general/README.md: -------------------------------------------------------------------------------- 1 | # General-purpose lints 2 | 3 | ## Use of `dylint_linting`'s `constituent` feature 4 | 5 | The general-purpose lints use `dylint_linting`'s [`constituent` feature]. This allows the lints to be built in either of the following two configurations: 6 | 7 | - as individual libraries 8 | - as a single, combined `general` library 9 | 10 | However, some additional organization of both the `general` directory and its subdirectories is required. 11 | 12 | For the general directory itself: 13 | 14 | - Each lint is listed under `[dependencies]` in general/Cargo.toml 15 | - Each lint is listed under `[workspace.members]` in general/Cargo.toml 16 | - Each lint's `register_lints` function is called from the `register_lints` function in src/lib.rs. 17 | 18 | For each lint subdirectory: 19 | 20 | - The following files do not appear, even though they would be created by `cargo dylint new`: 21 | - .cargo/config.toml 22 | - .gitignore 23 | - rust-toolchain 24 | - The lint's Cargo.toml has the following `[lib]` section (note: `crate-type` is not just `["cdylib"]`): 25 | ```toml 26 | [lib] 27 | crate-type = ["cdylib", "rlib"] 28 | ``` 29 | - The lint gets its `clippy_utils` dependency from the workspace, i.e.: 30 | ```toml 31 | [dependencies] 32 | clippy_utils = { workspace = true } 33 | ``` 34 | - The lint's Cargo.toml has the following `[features]` section: 35 | ```toml 36 | [features] 37 | rlib = ["dylint_linting/constituent"] 38 | ``` 39 | 40 | [`constituent` feature]: ../../utils/linting/README.md#constituent-feature 41 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "abs_home_path" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for string literals that are absolute paths into the user's home directory" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | once_cell = { workspace = true } 15 | 16 | dylint_internal = { path = "../../../internal", features = ["home", "testing"] } 17 | dylint_linting = { path = "../../../utils/linting" } 18 | 19 | [dev-dependencies] 20 | dylint_testing = { path = "../../../utils/testing" } 21 | 22 | [features] 23 | rlib = ["dylint_linting/constituent"] 24 | 25 | [lints] 26 | workspace = true 27 | 28 | [package.metadata.rust-analyzer] 29 | rustc_private = true 30 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/README.md: -------------------------------------------------------------------------------- 1 | # abs_home_path 2 | 3 | ### What it does 4 | 5 | Checks for string literals that are absolute paths into the user's home directory, e.g., 6 | `env!("CARGO_MANIFEST_DIR")`. 7 | 8 | ### Why is this bad? 9 | 10 | The path might not exist when the code is used in production. 11 | 12 | ### Known problems 13 | 14 | The lint does not apply inside macro arguments. So false negatives could result. 15 | 16 | ### Note 17 | 18 | This lint doesn't warn in build scripts (`build.rs`) or test contexts, as they often need to reference absolute paths. 19 | 20 | ### Example 21 | 22 | ```rust 23 | fn main() { 24 | let path = option_env!("CARGO"); 25 | println!("{:?}", path); 26 | } 27 | ``` 28 | 29 | Use instead: 30 | 31 | ```rust 32 | fn main() { 33 | let path = std::env::var("CARGO"); 34 | println!("{:?}", path); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _ = env!("CARGO"); 3 | let _ = env!("CARGO_MANIFEST_DIR"); 4 | let _ = option_env!("CARGO"); 5 | let _ = option_env!("CARGO_MANIFEST_DIR"); 6 | } 7 | 8 | #[test] 9 | fn test() { 10 | let _ = env!("CARGO"); 11 | } 12 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: this path might not exist in production 2 | --> $DIR/main.rs:2:13 3 | | 4 | LL | let _ = env!("CARGO"); 5 | | ^^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(abs_home_path)]` on by default 8 | 9 | warning: this path might not exist in production 10 | --> $DIR/main.rs:3:13 11 | | 12 | LL | let _ = env!("CARGO_MANIFEST_DIR"); 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 14 | 15 | warning: this path might not exist in production 16 | --> $DIR/main.rs:4:13 17 | | 18 | LL | let _ = option_env!("CARGO"); 19 | | ^^^^^^^^^^^^^^^^^^^^ 20 | 21 | warning: this path might not exist in production 22 | --> $DIR/main.rs:5:13 23 | | 24 | LL | let _ = option_env!("CARGO_MANIFEST_DIR"); 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 26 | 27 | warning: 4 warnings emitted 28 | 29 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_build_script/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ui_build_script" 7 | version = "4.1.0" 8 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_build_script/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ui_build_script" 3 | version = "4.1.0" 4 | edition = "2024" 5 | 6 | [workspace] 7 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_build_script/build.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | fn main() { 4 | // This should NOT trigger the abs_home_path lint because we're in a build script, 5 | // which is explicitly allowed to use absolute paths 6 | let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); 7 | println!( 8 | "cargo:warning=Using manifest dir: {}", 9 | manifest_dir.display() 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_build_script/src/main.rs: -------------------------------------------------------------------------------- 1 | // This is a test program for verifying that the abs_home_path lint 2 | // correctly allows absolute paths in build scripts, while still catching them in regular code. 3 | fn main() { 4 | println!("Testing abs_home_path lint's build script allowance"); 5 | 6 | // The actual test is in build.rs, which references an absolute path 7 | // that would normally trigger the lint, but should be allowed in build scripts 8 | } 9 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_test/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ui_test" 7 | version = "4.1.0" 8 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ui_test" 3 | version = "4.1.0" 4 | edition = "2024" 5 | 6 | [workspace] 7 | -------------------------------------------------------------------------------- /examples/general/abs_home_path/ui_test/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Testing abs_home_path lint's test context allowance"); 3 | non_test_function_with_home_path(); 4 | } 5 | 6 | fn non_test_function_with_home_path() { 7 | // This should trigger the lint 8 | let _ = env!("CARGO_MANIFEST_DIR"); 9 | let _ = option_env!("CARGO"); 10 | } 11 | 12 | #[test] 13 | fn test_function_with_home_path() { 14 | // This should NOT trigger the lint 15 | let _ = env!("CARGO_MANIFEST_DIR"); 16 | let _ = option_env!("CARGO"); 17 | } 18 | -------------------------------------------------------------------------------- /examples/general/await_holding_span_guard/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "await_holding_span_guard" 3 | version = "4.1.0" 4 | authors = ["David Barsky"] 5 | description = "A lint to check for Span guards held while calling await inside an async function" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_linting = { path = "../../../utils/linting" } 20 | 21 | [dev-dependencies] 22 | tracing = { workspace = true } 23 | 24 | dylint_testing = { path = "../../../utils/testing" } 25 | 26 | [features] 27 | rlib = ["dylint_linting/constituent"] 28 | 29 | [lints] 30 | workspace = true 31 | 32 | [package.metadata.rust-analyzer] 33 | rustc_private = true 34 | -------------------------------------------------------------------------------- /examples/general/await_holding_span_guard/ui/main.rs: -------------------------------------------------------------------------------- 1 | use tracing::{Instrument, Level, span}; 2 | 3 | async fn good_in_scope() { 4 | let span = span!(Level::INFO, "good"); 5 | 6 | let some_value = span.in_scope(|| 32); 7 | 8 | baz(some_value).await; 9 | } 10 | 11 | async fn good_instrument() { 12 | let span = span!(Level::INFO, "good"); 13 | 14 | baz(32).instrument(span).await; 15 | } 16 | 17 | async fn bad_borrowed() { 18 | let span = span!(Level::INFO, "bad_borrowed"); 19 | 20 | let _guard = span.enter(); 21 | bar().await; 22 | } 23 | 24 | async fn bad_owned() { 25 | let span = span!(Level::INFO, "bad_owned"); 26 | 27 | let _guard = span.entered(); 28 | bar().await; 29 | } 30 | 31 | #[expect(clippy::manual_async_fn)] 32 | fn bad_async_block_borrowed() -> impl std::future::Future + 'static { 33 | async move { 34 | let span = span!(Level::INFO, "async_block_borrowed"); 35 | let _guard = span.enter(); 36 | bar().await 37 | } 38 | } 39 | 40 | async fn bar() {} 41 | 42 | async fn baz(value: usize) { 43 | let _ = value; 44 | } 45 | 46 | #[expect(unused_must_use)] 47 | fn main() { 48 | good_in_scope(); 49 | good_instrument(); 50 | bad_borrowed(); 51 | bad_owned(); 52 | bad_async_block_borrowed(); 53 | } 54 | -------------------------------------------------------------------------------- /examples/general/await_holding_span_guard/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: this Span guard is held across an 'await' point. Consider using the `.instrument()` combinator or the `.in_scope()` method instead 2 | --> $DIR/main.rs:20:9 3 | | 4 | LL | let _guard = span.enter(); 5 | | ^^^^^^ 6 | | 7 | note: these are all the await points this ref is held through 8 | --> $DIR/main.rs:21:11 9 | | 10 | LL | bar().await; 11 | | ^^^^^ 12 | = note: `#[warn(await_holding_span_guard)]` on by default 13 | 14 | warning: this Span guard is held across an 'await' point. Consider using the `.instrument()` combinator or the `.in_scope()` method instead 15 | --> $DIR/main.rs:27:9 16 | | 17 | LL | let _guard = span.entered(); 18 | | ^^^^^^ 19 | | 20 | note: these are all the await points this ref is held through 21 | --> $DIR/main.rs:28:11 22 | | 23 | LL | bar().await; 24 | | ^^^^^ 25 | 26 | warning: this Span guard is held across an 'await' point. Consider using the `.instrument()` combinator or the `.in_scope()` method instead 27 | --> $DIR/main.rs:35:13 28 | | 29 | LL | let _guard = span.enter(); 30 | | ^^^^^^ 31 | | 32 | note: these are all the await points this ref is held through 33 | --> $DIR/main.rs:36:15 34 | | 35 | LL | bar().await 36 | | ^^^^^ 37 | 38 | warning: 3 warnings emitted 39 | 40 | -------------------------------------------------------------------------------- /examples/general/basic_dead_store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_dead_store" 3 | version = "4.1.0" 4 | authors = ["Filipe Casal "] 5 | description = "A lint to find simple instances of dead stores in arrays" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | dylint_linting = { path = "../../../utils/linting" } 15 | 16 | [dev-dependencies] 17 | dylint_testing = { path = "../../../utils/testing" } 18 | 19 | [features] 20 | rlib = ["dylint_linting/constituent"] 21 | 22 | [lints] 23 | workspace = true 24 | 25 | [package.metadata.rust-analyzer] 26 | rustc_private = true 27 | -------------------------------------------------------------------------------- /examples/general/basic_dead_store/README.md: -------------------------------------------------------------------------------- 1 | # basic_dead_store 2 | 3 | ### What it does 4 | 5 | Finds instances of dead stores in arrays: array positions that are assigned twice without a 6 | use or read in between. 7 | 8 | ### Why is this bad? 9 | 10 | A dead store might indicate a logic error in the program or an unnecessary assignment. 11 | 12 | ### Known problems 13 | 14 | This lint only checks for literal indices and will not try to find instances where an array 15 | is indexed by a variable. 16 | 17 | ### Example 18 | 19 | ```rust 20 | let mut arr = [0u64; 2]; 21 | arr[0] = 1; 22 | arr[0] = 2; 23 | ``` 24 | 25 | Use instead: 26 | 27 | ```rust 28 | let mut arr = [0u64; 2]; 29 | arr[0] = 2; 30 | arr[1] = 1; 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/general/basic_dead_store/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn dead_store() { 2 | let sum = |a: u64, b: u64| a + b; 3 | 4 | let mut arr = [0u64; 4]; 5 | 6 | let v = sum(1, 2); 7 | // dead store here 8 | arr[0] = v; 9 | 10 | let v = sum(3, 4); 11 | // rewrites the previous store 12 | arr[0] = v; 13 | } 14 | 15 | fn no_dead_store() { 16 | let sum = |a: u64, b: u64| a + b; 17 | 18 | let mut arr = [0u64; 4]; 19 | 20 | let v = sum(1, 2); 21 | arr[0] = v; 22 | 23 | let v = sum(3, 4); 24 | arr[1] = v; 25 | } 26 | 27 | fn dead_store_vec() { 28 | let sum = |a: u64, b: u64| a + b; 29 | 30 | let mut arr = vec![0u64; 4]; 31 | 32 | let v = sum(1, 2); 33 | arr[0] = v; 34 | 35 | let v = sum(3, 4); 36 | // rewriting the previous store 37 | arr[0] = v; 38 | } 39 | 40 | fn dead_store_variant() { 41 | let mut arr = [0u64; 4]; 42 | arr[0] = 1; 43 | arr[1] = 2; 44 | // rewriting the previous store 45 | arr[0] = 3; 46 | } 47 | 48 | fn no_dead_store_read() { 49 | let mut arr = [0u64; 4]; 50 | arr[0] = 1; 51 | arr[1] = arr[0]; 52 | arr[0] = 3; 53 | } 54 | 55 | fn no_dead_store_mutated() { 56 | let mut arr = [0u64; 4]; 57 | arr[0] = 1; 58 | // we can't make any assumptions about arr after this call 59 | f(arr); 60 | arr[0] = 3; 61 | } 62 | 63 | fn f(mut arr: [u64; 4]) { 64 | unimplemented!(); 65 | } 66 | 67 | fn main() {} 68 | -------------------------------------------------------------------------------- /examples/general/basic_dead_store/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: reassigning the same array position without using it 2 | --> $DIR/main.rs:12:12 3 | | 4 | LL | arr[0] = v; 5 | | ^ 6 | | 7 | help: original assignment was here 8 | --> $DIR/main.rs:8:12 9 | | 10 | LL | arr[0] = v; 11 | | ^ 12 | = note: `#[warn(basic_dead_store)]` on by default 13 | 14 | warning: reassigning the same array position without using it 15 | --> $DIR/main.rs:37:12 16 | | 17 | LL | arr[0] = v; 18 | | ^ 19 | | 20 | help: original assignment was here 21 | --> $DIR/main.rs:33:12 22 | | 23 | LL | arr[0] = v; 24 | | ^ 25 | 26 | warning: reassigning the same array position without using it 27 | --> $DIR/main.rs:45:12 28 | | 29 | LL | arr[0] = 3; 30 | | ^ 31 | | 32 | help: original assignment was here 33 | --> $DIR/main.rs:42:12 34 | | 35 | LL | arr[0] = 1; 36 | | ^ 37 | 38 | warning: 3 warnings emitted 39 | 40 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crate_wide_allow" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for `#![allow(...)]` used at the crate level" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_linting = { path = "../../../utils/linting" } 20 | 21 | [target.'cfg(not(no_dev_dependencies))'.dev-dependencies] 22 | assert_cmd = "2.0" 23 | predicates = "3.1" 24 | 25 | dylint_internal = { path = "../../../internal", features = ["testing"] } 26 | dylint_testing = { path = "../../../utils/testing" } 27 | 28 | [features] 29 | rlib = ["dylint_linting/constituent"] 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [package.metadata.rust-analyzer] 35 | rustc_private = true 36 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/README.md: -------------------------------------------------------------------------------- 1 | # crate_wide_allow 2 | 3 | ### What it does 4 | 5 | Checks for use of `#![allow(...)]` at the crate level. 6 | 7 | ### Why is this bad? 8 | 9 | Such uses cannot be overridden with `--warn` or `--deny` from the command line. They _can_ 10 | be overridden with `--force-warn` or `--forbid`, but one must know the `#![allow(...)]` 11 | are present to use these unconventional options. 12 | 13 | ### Example 14 | 15 | ```rust 16 | #![allow(clippy::assertions_on_constants)] // in code 17 | ``` 18 | 19 | Use instead: 20 | 21 | ```rust 22 | // Allow `clippy::assertions-on-constants` in Cargo.toml. See: 23 | // - https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section 24 | // - https://doc.rust-lang.org/clippy/configuration.html#lints-section-in-cargotoml 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::assertions_on_constants)] 2 | 3 | fn main() { 4 | assert!(true); 5 | } 6 | 7 | mod inner_attribute { 8 | #![expect(clippy::bool_assert_comparison)] 9 | #![expect(dead_code)] 10 | fn foo() {} 11 | fn bar() { 12 | assert_eq!("a".is_empty(), false); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: silently overrides `--warn clippy::assertions-on-constants` and `--deny clippy::assertions-on-constants` 2 | --> $DIR/main.rs:1:1 3 | | 4 | LL | #![allow(clippy::assertions_on_constants)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: allow `clippy::assertions-on-constants` in Cargo.toml 8 | = note: `#[warn(crate_wide_allow)]` on by default 9 | 10 | warning: 1 warning emitted 11 | 12 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/ui_manifest/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ui_manifest" 7 | version = "4.1.0" 8 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/ui_manifest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ui_manifest" 3 | version = "4.1.0" 4 | edition = "2024" 5 | 6 | [lints.clippy] 7 | assertions_on_constants = "allow" 8 | 9 | [workspace] 10 | -------------------------------------------------------------------------------- /examples/general/crate_wide_allow/ui_manifest/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | assert!(true); 3 | } 4 | -------------------------------------------------------------------------------- /examples/general/incorrect_matches_operation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "incorrect_matches_operation" 3 | version = "4.1.0" 4 | authors = ["Dominik Czarnota "] 5 | description = "A lint to check for incorrect operators used with matches! macros" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | dylint_linting = { path = "../../../utils/linting" } 15 | 16 | [dev-dependencies] 17 | dylint_testing = { path = "../../../utils/testing" } 18 | 19 | [features] 20 | rlib = ["dylint_linting/constituent"] 21 | 22 | [lints] 23 | workspace = true 24 | 25 | [package.metadata.rust-analyzer] 26 | rustc_private = true 27 | -------------------------------------------------------------------------------- /examples/general/incorrect_matches_operation/README.md: -------------------------------------------------------------------------------- 1 | # incorrect_matches_operation 2 | 3 | ### What it does 4 | 5 | Checks for inefficient or incorrect use of the `matches!` macro. 6 | Examples of inefficient or boiler plate uses: 7 | 8 | - `matches!(obj, case1) | matches!(obj, case2)` 9 | - `matches!(obj, case1) || matches!(obj, case2)` 10 | 11 | Examples of incorrect uses (the condition is probably always false): 12 | 13 | - `matches!(obj, case1) & matches!(obj, case2)` 14 | - `matches!(obj, case1) && matches!(obj, case2)` 15 | 16 | ### Why is this bad? 17 | 18 | One should use `matches!(obj, case1 | case2)` instead. 19 | 20 | ### Known problems 21 | 22 | Since we use a pre-expansion-lint, we match the `matches!` argument tokens. 23 | This is not ideal since we don't know if the argument is a variable name or, e.g., 24 | a call. If it is a call, this lint may result in a false positive, though I bet there won't 25 | be many of those. 26 | 27 | ### Example 28 | 29 | ```rust 30 | fn main() { 31 | let x = 1; 32 | if matches!(x, 123) | matches!(x, 256) { 33 | println!("Matches"); 34 | } 35 | } 36 | ``` 37 | 38 | Use instead: 39 | 40 | ```rust 41 | fn main() { 42 | let x = 1; 43 | if matches!(x, 123 | 256) { 44 | println!("Matches"); 45 | } 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/general/incorrect_matches_operation/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let x = 1; 3 | 4 | if matches!(x, 123) | matches!(x, 256) { 5 | println!("Matches"); 6 | } 7 | 8 | if matches!(x, 123) || matches!(x, 256) { 9 | println!("Matches"); 10 | } 11 | 12 | if matches!(x, 123) && matches!(x, 256) { 13 | println!("That is an unreachable state!"); 14 | } 15 | 16 | if matches!(x, 123) & matches!(x, 256) { 17 | println!("That is an unreachable state!"); 18 | } 19 | 20 | let _a = matches!(x, 1); 21 | let _b = matches!(x, 1) | matches!(x, 2); 22 | 23 | // This one will not error out 24 | let _c = matches!(x, 2) | matches!(_b, true); 25 | 26 | false_negative(); 27 | false_positive(); 28 | } 29 | 30 | fn false_negative() { 31 | let x = 2; 32 | assert!(matches!(x, 1) | matches!(x, 2)); 33 | } 34 | 35 | fn false_positive() { 36 | let x = 2; 37 | let a = matches!(x, 1) | matches!(x, 2 if false); 38 | assert!(!a); 39 | } 40 | -------------------------------------------------------------------------------- /examples/general/incorrect_matches_operation/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: This matches! macro use can be rewritten to matches!(obj, A | B) 2 | --> $DIR/main.rs:4:8 3 | | 4 | LL | if matches!(x, 123) | matches!(x, 256) { 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(incorrect_matches_operation)]` on by default 8 | 9 | warning: This matches! macro use can be rewritten to matches!(obj, A | B) 10 | --> $DIR/main.rs:8:8 11 | | 12 | LL | if matches!(x, 123) || matches!(x, 256) { 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 14 | 15 | warning: Is this a bug? matches!(obj, A) && matches!(obj, B) is (almost) always false 16 | --> $DIR/main.rs:12:8 17 | | 18 | LL | if matches!(x, 123) && matches!(x, 256) { 19 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20 | 21 | warning: Is this a bug? matches!(obj, A) & matches!(obj, B) is (almost) always false 22 | --> $DIR/main.rs:16:8 23 | | 24 | LL | if matches!(x, 123) & matches!(x, 256) { 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 26 | 27 | warning: This matches! macro use can be rewritten to matches!(obj, A | B) 28 | --> $DIR/main.rs:21:14 29 | | 30 | LL | let _b = matches!(x, 1) | matches!(x, 2); 31 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 32 | 33 | warning: This matches! macro use can be rewritten to matches!(obj, A | B) 34 | --> $DIR/main.rs:37:13 35 | | 36 | LL | let a = matches!(x, 1) | matches!(x, 2 if false); 37 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 38 | 39 | warning: 6 warnings emitted 40 | 41 | -------------------------------------------------------------------------------- /examples/general/non_local_effect_before_error_return/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "non_local_effect_before_error_return" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for non-local effects before return of an error" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [[example]] 17 | name = "ui_public_only" 18 | path = "ui_public_only/main.rs" 19 | 20 | [dependencies] 21 | clippy_utils = { workspace = true } 22 | serde = { workspace = true, features = ["derive"] } 23 | 24 | dylint_linting = { path = "../../../utils/linting" } 25 | 26 | [dev-dependencies] 27 | bitflags = { workspace = true } 28 | derivative = { workspace = true } 29 | 30 | dylint_testing = { path = "../../../utils/testing" } 31 | 32 | [features] 33 | rlib = ["dylint_linting/constituent"] 34 | 35 | [lints] 36 | workspace = true 37 | 38 | [package.metadata.rust-analyzer] 39 | rustc_private = true 40 | -------------------------------------------------------------------------------- /examples/general/non_local_effect_before_error_return/README.md: -------------------------------------------------------------------------------- 1 | # non_local_effect_before_error_return 2 | 3 | ### What it does 4 | 5 | Checks for non-local effects (e.g., assignments to mutable references) before return of an 6 | error. 7 | 8 | ### Why is this bad? 9 | 10 | Functions that make changes to the program state before returning an error are difficult to 11 | reason about. Generally speaking, if a function returns an error, it should be as though the 12 | function was never called. 13 | 14 | ### Known problems 15 | 16 | - The search strategy is exponential in the number of blocks in a function body. To help 17 | deal with complex bodies, the lint includes a "work limit" (see "Configuration" below). 18 | - Errors in loops are not handled properly. 19 | 20 | ### Example 21 | 22 | ```rust 23 | impl Account { 24 | fn withdraw(&mut self, amount: i64) -> Result { 25 | self.balance -= amount; 26 | if self.balance < 0 { 27 | return Err(InsufficientBalance); 28 | } 29 | Ok(self.balance) 30 | } 31 | } 32 | ``` 33 | 34 | Use instead: 35 | 36 | ```rust 37 | impl Account { 38 | fn withdraw(&mut self, amount: i64) -> Result { 39 | let new_balance = self.balance - amount; 40 | if new_balance < 0 { 41 | return Err(InsufficientBalance); 42 | } 43 | self.balance = new_balance; 44 | Ok(self.balance) 45 | } 46 | } 47 | ``` 48 | 49 | ### Configuration 50 | 51 | - `public_only: bool` (default `true`): Whether to check only publicly accessible functions. 52 | - `work_limit: u64` (default 500000): When exploring a function body, the maximum number of 53 | times the search path is extended. Setting this to a higher number allows more bodies to 54 | be explored exhaustively, but at the expense of greater runtime. 55 | -------------------------------------------------------------------------------- /examples/general/non_local_effect_before_error_return/src/rvalue_places.rs: -------------------------------------------------------------------------------- 1 | use rustc_middle::mir::{ 2 | Location, Place, Rvalue, 3 | visit::{PlaceContext, Visitor}, 4 | }; 5 | 6 | pub fn rvalue_places<'tcx>(rvalue: &Rvalue<'tcx>, location: Location) -> Vec> { 7 | let mut visitor = RvaluePlacesVisitor { places: Vec::new() }; 8 | visitor.visit_rvalue(rvalue, location); 9 | visitor.places 10 | } 11 | 12 | struct RvaluePlacesVisitor<'tcx> { 13 | places: Vec>, 14 | } 15 | 16 | impl<'tcx> Visitor<'tcx> for RvaluePlacesVisitor<'tcx> { 17 | fn visit_place(&mut self, place: &Place<'tcx>, _context: PlaceContext, _location: Location) { 18 | self.places.push(*place); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "non_thread_safe_call_in_test" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for non-thread-safe function calls in tests" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "interprocedural" 14 | path = "ui/interprocedural.rs" 15 | 16 | [[example]] 17 | name = "one_test" 18 | path = "ui/one_test.rs" 19 | 20 | [[example]] 21 | name = "set_current_dir" 22 | path = "ui/set_current_dir.rs" 23 | 24 | [dependencies] 25 | clippy_utils = { workspace = true } 26 | 27 | dylint_internal = { path = "../../../internal" } 28 | dylint_linting = { path = "../../../utils/linting" } 29 | 30 | [dev-dependencies] 31 | dylint_testing = { path = "../../../utils/testing" } 32 | 33 | [features] 34 | rlib = ["dylint_linting/constituent"] 35 | 36 | [lints] 37 | workspace = true 38 | 39 | [package.metadata.rust-analyzer] 40 | rustc_private = true 41 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/README.md: -------------------------------------------------------------------------------- 1 | # non_thread_safe_call_in_test 2 | 3 | ### What it does 4 | 5 | Checks for calls to non-thread-safe functions in code attributed with 6 | `#[test]`. For this lint to be effective, `--tests` must be passed to `cargo check`. 7 | 8 | ### Why is this bad? 9 | 10 | "When you run multiple tests, by default they run in parallel using 11 | threads" ([reference]). Calling a non-thread-safe function in one test could affect the 12 | outcome of another. 13 | 14 | ### Known problems 15 | 16 | - Synchronization is not considered, so false positives could result. 17 | - Tries to flag uses of `std::process::Command::new("cargo").arg("run")`, but does not track 18 | values. So false negatives will result if the `Command::new("cargo")` is not 19 | `Command::arg("run")`'s receiver. 20 | 21 | ### Example 22 | 23 | ```rust 24 | #[test] 25 | fn set_var() { 26 | std::env::set_var("KEY", "SOME_VALUE"); 27 | std::process::Command::new("env").status().unwrap(); 28 | } 29 | ``` 30 | 31 | Use instead: 32 | 33 | ```rust 34 | #[test] 35 | fn set_var() { 36 | std::process::Command::new("env") 37 | .env("KEY", "SOME_VALUE") 38 | .status() 39 | .unwrap(); 40 | } 41 | ``` 42 | 43 | [reference]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#running-tests-in-parallel-or-consecutively 44 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/src/blacklist.rs: -------------------------------------------------------------------------------- 1 | use dylint_internal::paths; 2 | 3 | pub const BLACKLIST: &[&[&str]] = &[ 4 | &paths::ENV_REMOVE_VAR, 5 | &paths::ENV_SET_CURRENT_DIR, 6 | &paths::ENV_SET_VAR, 7 | &paths::FS_COPY, 8 | &paths::FS_CREATE_DIR, 9 | &paths::FS_CREATE_DIR_ALL, 10 | &paths::FS_HARD_LINK, 11 | &paths::FS_REMOVE_DIR, 12 | &paths::FS_REMOVE_DIR_ALL, 13 | &paths::FS_REMOVE_FILE, 14 | &paths::FS_RENAME, 15 | &paths::FS_SET_PERMISSIONS, 16 | &paths::FS_SOFT_LINK, 17 | &paths::FS_WRITE, 18 | &paths::COMMAND_NEW, 19 | ]; 20 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![feature(let_chains)] 3 | #![warn(unused_extern_crates)] 4 | 5 | #[cfg(not(feature = "rlib"))] 6 | dylint_linting::dylint_library!(); 7 | 8 | extern crate rustc_ast; 9 | extern crate rustc_hir; 10 | extern crate rustc_lint; 11 | extern crate rustc_middle; 12 | extern crate rustc_session; 13 | 14 | mod blacklist; 15 | mod late; 16 | 17 | #[cfg_attr(not(feature = "rlib"), unsafe(no_mangle))] 18 | pub fn register_lints(_sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { 19 | lint_store.register_lints(&[late::NON_THREAD_SAFE_CALL_IN_TEST]); 20 | lint_store.register_late_pass(|_| Box::::default()); 21 | } 22 | 23 | #[test] 24 | fn ui() { 25 | dylint_testing::ui::Test::examples(env!("CARGO_PKG_NAME")) 26 | .rustc_flags(["--test"]) 27 | .run(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/interprocedural.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod test { 5 | #[test] 6 | fn foo() { 7 | set_var(); 8 | } 9 | 10 | #[test] 11 | fn bar() { 12 | set_var(); 13 | } 14 | 15 | fn set_var() { 16 | unsafe { 17 | std::env::set_var("KEY", "VALUE"); 18 | } 19 | std::process::Command::new("env").status().unwrap(); 20 | } 21 | 22 | #[test] 23 | fn baz() { 24 | cargo_arg_run(); 25 | cargo_args_run(); 26 | } 27 | 28 | fn cargo_arg_run() { 29 | std::process::Command::new("cargo") 30 | .arg("run") 31 | .status() 32 | .unwrap(); 33 | } 34 | 35 | fn cargo_args_run() { 36 | std::process::Command::new("cargo") 37 | .args(["run"]) 38 | .status() 39 | .unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/interprocedural.stderr: -------------------------------------------------------------------------------- 1 | warning: calling `std::env::set_var` in a test could affect the outcome of other tests 2 | --> $DIR/interprocedural.rs:17:13 3 | | 4 | LL | std::env::set_var("KEY", "VALUE"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: the call is reachable from at least this test 8 | --> $DIR/interprocedural.rs:6:8 9 | | 10 | LL | fn foo() { 11 | | ^^^ 12 | = note: `#[warn(non_thread_safe_call_in_test)]` on by default 13 | 14 | warning: calling `std::process::Command::new` in a test could affect the outcome of other tests 15 | --> $DIR/interprocedural.rs:29:9 16 | | 17 | LL | std::process::Command::new("cargo") 18 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 19 | | 20 | note: the call is reachable from at least this test 21 | --> $DIR/interprocedural.rs:23:8 22 | | 23 | LL | fn baz() { 24 | | ^^^ 25 | 26 | warning: calling `std::process::Command::new` in a test could affect the outcome of other tests 27 | --> $DIR/interprocedural.rs:36:9 28 | | 29 | LL | std::process::Command::new("cargo") 30 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 31 | | 32 | note: the call is reachable from at least this test 33 | --> $DIR/interprocedural.rs:23:8 34 | | 35 | LL | fn baz() { 36 | | ^^^ 37 | 38 | warning: 3 warnings emitted 39 | 40 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/one_test.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[test] 4 | fn set_var() { 5 | unsafe { 6 | std::env::set_var("KEY", "VALUE"); 7 | } 8 | std::process::Command::new("env").status().unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/one_test.stderr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/dylint/f176ee4b8bf6c05ccd69b61742f18cac1bc7711d/examples/general/non_thread_safe_call_in_test/ui/one_test.stderr -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/set_current_dir.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | #[cfg(test)] 4 | mod test { 5 | use std::sync::Mutex; 6 | 7 | #[rustfmt::skip] 8 | enum Dir { North, East, South, West } 9 | 10 | static CURRENT_DIR: Mutex = Mutex::new(Dir::North); 11 | 12 | fn set_current_dir(dir: Dir) { 13 | *CURRENT_DIR.lock().unwrap() = dir; 14 | } 15 | 16 | #[test] 17 | fn test_set_current_dir() { 18 | for dir in [Dir::North, Dir::East, Dir::South, Dir::West] { 19 | set_current_dir(dir); 20 | } 21 | } 22 | 23 | #[test] 24 | fn env_set_current_dir() { 25 | std::env::set_current_dir("/").unwrap(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/general/non_thread_safe_call_in_test/ui/set_current_dir.stderr: -------------------------------------------------------------------------------- 1 | warning: calling `std::env::set_current_dir` in a test could affect the outcome of other tests 2 | --> $DIR/set_current_dir.rs:25:9 3 | | 4 | LL | std::env::set_current_dir("/").unwrap(); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: the call is reachable from at least this test 8 | --> $DIR/set_current_dir.rs:24:8 9 | | 10 | LL | fn env_set_current_dir() { 11 | | ^^^^^^^^^^^^^^^^^^^ 12 | = note: `#[warn(non_thread_safe_call_in_test)]` on by default 13 | 14 | warning: 1 warning emitted 15 | 16 | -------------------------------------------------------------------------------- /examples/general/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/general/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![warn(unused_extern_crates)] 3 | 4 | dylint_linting::dylint_library!(); 5 | 6 | extern crate rustc_lint; 7 | extern crate rustc_session; 8 | 9 | #[expect(clippy::no_mangle_with_rust_abi)] 10 | #[unsafe(no_mangle)] 11 | pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { 12 | // smoelius: Please keep the following `register_lints` calls sorted by crate name. 13 | abs_home_path::register_lints(sess, lint_store); 14 | await_holding_span_guard::register_lints(sess, lint_store); 15 | basic_dead_store::register_lints(sess, lint_store); 16 | crate_wide_allow::register_lints(sess, lint_store); 17 | incorrect_matches_operation::register_lints(sess, lint_store); 18 | non_local_effect_before_error_return::register_lints(sess, lint_store); 19 | non_thread_safe_call_in_test::register_lints(sess, lint_store); 20 | wrong_serialize_struct_arg::register_lints(sess, lint_store); 21 | } 22 | -------------------------------------------------------------------------------- /examples/general/wrong_serialize_struct_arg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wrong_serialize_struct_arg" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for calls to serialization methods with incorrect `len` arguments" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_internal = { path = "../../../internal" } 20 | dylint_linting = { path = "../../../utils/linting" } 21 | 22 | [dev-dependencies] 23 | serde = { workspace = true } 24 | serde_json = { workspace = true } 25 | 26 | dylint_testing = { path = "../../../utils/testing" } 27 | 28 | [features] 29 | rlib = ["dylint_linting/constituent"] 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [package.metadata.rust-analyzer] 35 | rustc_private = true 36 | -------------------------------------------------------------------------------- /examples/restriction/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/restriction/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["*"] 3 | exclude = [".cargo"] 4 | resolver = "2" 5 | 6 | [workspace.dependencies] 7 | anyhow = "1.0" 8 | assert_cmd = "2.0" 9 | camino = "1.1" 10 | cargo_metadata = "0.18" 11 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 12 | diesel = "2.2" 13 | git2 = "0.18" 14 | heck = "0.5" 15 | serde = "1.0" 16 | serde_json = "1.0" 17 | tempfile = "3.20" 18 | thiserror = "2.0" 19 | toml = "0.8" 20 | 21 | [workspace.lints.clippy] 22 | nursery = { level = "warn", priority = -1 } 23 | pedantic = { level = "warn", priority = -1 } 24 | option-if-let-else = "allow" 25 | 26 | [workspace.lints.rust.unexpected_cfgs] 27 | level = "deny" 28 | check-cfg = ["cfg(dylint_lib, values(any()))"] 29 | 30 | [workspace.metadata.dylint] 31 | libraries = [ 32 | { path = "../general" }, 33 | { path = "../supplementary" }, 34 | { path = "../testing/clippy" }, 35 | { path = "../restriction/*" }, 36 | ] 37 | -------------------------------------------------------------------------------- /examples/restriction/README.md: -------------------------------------------------------------------------------- 1 | # Restriction lints 2 | -------------------------------------------------------------------------------- /examples/restriction/assert_eq_arg_misordering/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "assert_eq_arg_misordering" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for `assert_eq!(actual, expected)`" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_linting = { path = "../../../utils/linting" } 16 | 17 | [dev-dependencies] 18 | dylint_testing = { path = "../../../utils/testing" } 19 | 20 | [lints] 21 | workspace = true 22 | 23 | [package.metadata.rust-analyzer] 24 | rustc_private = true 25 | -------------------------------------------------------------------------------- /examples/restriction/assert_eq_arg_misordering/README.md: -------------------------------------------------------------------------------- 1 | # assert_eq_arg_misordering 2 | 3 | ### What it does 4 | 5 | Checks for invocations of `assert_eq!` whose arguments are "non-const, const", which 6 | suggests they could be "actual, expected". 7 | 8 | ### Why is this bad? 9 | 10 | In a long list of output, one's eyes naturally go to the last line. Hence, it should be what 11 | is unusual, i.e., the "actual" value. 12 | 13 | ### Known problems 14 | 15 | A common source of false positives is "sorted, unsorted" where the check is of the 16 | sortedness of a collection that is const. 17 | 18 | ### Example 19 | 20 | ```rust 21 | assert_eq!(x, 0); 22 | ``` 23 | 24 | Use instead: 25 | 26 | ```rust 27 | assert_eq!(0, x); 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/restriction/assert_eq_arg_misordering/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | const CONST_WITH_A_REALLY_LONG_NAME: u32 = 0; 7 | 8 | fn non_const_const(x: u32) { 9 | assert_eq!(0, x); 10 | } 11 | 12 | fn non_const_const_multiline(variable_with_a_really_long_name: u32) { 13 | assert_eq!( 14 | CONST_WITH_A_REALLY_LONG_NAME, 15 | variable_with_a_really_long_name 16 | ); 17 | } 18 | 19 | fn non_const_const_with_message(x: u32) { 20 | assert_eq!(0, x, "this is a message (with parens)"); 21 | } 22 | 23 | fn const_const() { 24 | assert_eq!(0, 0); 25 | } 26 | 27 | fn non_const_non_const(x: u32, y: u32) { 28 | assert_eq!(x, y); 29 | } 30 | -------------------------------------------------------------------------------- /examples/restriction/assert_eq_arg_misordering/ui/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![expect(dead_code)] 3 | 4 | fn main() {} 5 | 6 | const CONST_WITH_A_REALLY_LONG_NAME: u32 = 0; 7 | 8 | fn non_const_const(x: u32) { 9 | assert_eq!(x, 0); 10 | } 11 | 12 | fn non_const_const_multiline(variable_with_a_really_long_name: u32) { 13 | assert_eq!( 14 | variable_with_a_really_long_name, 15 | CONST_WITH_A_REALLY_LONG_NAME 16 | ); 17 | } 18 | 19 | fn non_const_const_with_message(x: u32) { 20 | assert_eq!(x, 0, "this is a message (with parens)"); 21 | } 22 | 23 | fn const_const() { 24 | assert_eq!(0, 0); 25 | } 26 | 27 | fn non_const_non_const(x: u32, y: u32) { 28 | assert_eq!(x, y); 29 | } 30 | -------------------------------------------------------------------------------- /examples/restriction/assert_eq_arg_misordering/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: arguments are "non-const, const", which looks like "actual, expected" 2 | --> $DIR/main.rs:9:16 3 | | 4 | LL | assert_eq!(x, 0); 5 | | ^^^^ help: prefer "expected, actual": `0, x` 6 | | 7 | = note: `#[warn(assert_eq_arg_misordering)]` on by default 8 | 9 | warning: arguments are "non-const, const", which looks like "actual, expected" 10 | --> $DIR/main.rs:14:9 11 | | 12 | LL | / variable_with_a_really_long_name, 13 | LL | | CONST_WITH_A_REALLY_LONG_NAME 14 | | |_____________________________________^ 15 | | 16 | help: prefer "expected, actual" 17 | | 18 | LL ~ CONST_WITH_A_REALLY_LONG_NAME, 19 | LL + variable_with_a_really_long_name 20 | | 21 | 22 | warning: arguments are "non-const, const", which looks like "actual, expected" 23 | --> $DIR/main.rs:20:16 24 | | 25 | LL | assert_eq!(x, 0, "this is a message (with parens)"); 26 | | ^^^^ help: prefer "expected, actual": `0, x` 27 | 28 | warning: 3 warnings emitted 29 | 30 | -------------------------------------------------------------------------------- /examples/restriction/collapsible_unwrap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "collapsible_unwrap" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for an `unwrap` that could be combined with an `expect` or `unwrap` using `and_then`" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | heck = { workspace = true } 19 | 20 | dylint_linting = { path = "../../../utils/linting" } 21 | 22 | [dev-dependencies] 23 | toml = { workspace = true } 24 | 25 | dylint_testing = { path = "../../../utils/testing" } 26 | 27 | [lints] 28 | workspace = true 29 | 30 | [package.metadata.rust-analyzer] 31 | rustc_private = true 32 | -------------------------------------------------------------------------------- /examples/restriction/collapsible_unwrap/README.md: -------------------------------------------------------------------------------- 1 | # collapsible_unwrap 2 | 3 | ### What it does 4 | 5 | Checks for an `unwrap` that could be combined with an `expect` or `unwrap` using `and_then`. 6 | 7 | ### Why is this bad? 8 | 9 | Using `and_then`s tends to produce shorter method call chains, which are easier to read and 10 | reason about. 11 | 12 | ### Known problems 13 | 14 | The lint considers only `unwrap`s in method call chains. It does not consider unwrapped 15 | values that are assigned to local variables, or assignments to local variables that are 16 | later unwrapped, for example. 17 | 18 | ### Example 19 | 20 | ```rust,no_run 21 | let package = toml.as_table().unwrap().get("package").unwrap(); 22 | ``` 23 | 24 | Use instead: 25 | 26 | ```rust,no_run 27 | let package = toml.as_table().and_then(|map| map.get("package")).unwrap(); 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/restriction/collapsible_unwrap/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | use std::fs; 4 | 5 | const PATH: &str = "Cargo.toml"; 6 | 7 | #[expect(unused_variables)] 8 | fn main() { 9 | assert!( 10 | std::path::Path::new(PATH) 11 | .canonicalize() 12 | .and_then(|path_buf| path_buf.try_exists()) 13 | .unwrap() 14 | ); 15 | 16 | assert!( 17 | std::path::Path::new(PATH) 18 | .canonicalize() 19 | .and_then(|path_buf| path_buf.try_exists()) 20 | .expect("could not determine whether path exists") 21 | ); 22 | 23 | // Should not lint, i.e., should not try to collapse an `expect` 24 | assert!( 25 | std::path::Path::new(PATH) 26 | .canonicalize() 27 | .expect("could not canonicalize path") 28 | .try_exists() 29 | .unwrap() 30 | ); 31 | 32 | // Should not lint, error types differ 33 | let toml = fs::read_to_string(PATH) 34 | .unwrap() 35 | .parse::() 36 | .unwrap(); 37 | 38 | let name = toml 39 | .as_table() 40 | .and_then(|map| map.get("package")).and_then(|value| value.as_table()).and_then(|map| map.get("name")) 41 | .unwrap(); 42 | 43 | let name = toml 44 | .as_table() 45 | .and_then(|map| map.get("package")).and_then(|value| value.as_table()) 46 | .and_then(|map| map.get("name")) 47 | .unwrap(); 48 | 49 | let name = toml 50 | .as_table() 51 | .and_then(|map| map.get("package")).and_then(|value| value.as_table()).and_then(|map| map.get("name")) 52 | .unwrap(); 53 | 54 | let name = toml 55 | .as_table() 56 | .and_then(|map| map.get("package")) 57 | .and_then(|value| value.as_table()).and_then(|map| map.get("name")) 58 | .unwrap(); 59 | 60 | println!("{:?}", name); 61 | } 62 | 63 | #[expect(dead_code)] 64 | fn mut_test() { 65 | let _ = fs::read_dir(".").ok().and_then(|mut read_dir| read_dir.next()).unwrap(); 66 | } 67 | -------------------------------------------------------------------------------- /examples/restriction/const_path_join/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const_path_join" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for joining of constant path components" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_internal = { path = "../../../internal" } 20 | dylint_linting = { path = "../../../utils/linting" } 21 | 22 | [dev-dependencies] 23 | camino = { workspace = true } 24 | 25 | dylint_testing = { path = "../../../utils/testing" } 26 | 27 | [lints] 28 | workspace = true 29 | 30 | [package.metadata.rust-analyzer] 31 | rustc_private = true 32 | -------------------------------------------------------------------------------- /examples/restriction/const_path_join/README.md: -------------------------------------------------------------------------------- 1 | # const_path_join 2 | 3 | ### What it does 4 | 5 | Checks for joining of constant path components. 6 | 7 | ### Why is this bad? 8 | 9 | Such paths can be constructed from string literals using `/`, since `/` works as a path 10 | separator on both Unix and Windows (see [`std::path::Path`]). 11 | 12 | ### Example 13 | 14 | ```rust 15 | PathBuf::from("..").join("target") 16 | ``` 17 | 18 | Use instead: 19 | 20 | ```rust 21 | PathBuf::from("../target") 22 | ``` 23 | 24 | [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html 25 | -------------------------------------------------------------------------------- /examples/restriction/const_path_join/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | fn main() { 4 | let _ = std::path::PathBuf::from("../target"); 5 | let _ = std::path::PathBuf::from("../target"); 6 | let _ = std::path::PathBuf::from("../target").as_path(); 7 | let _ = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../target"); 8 | 9 | let _ = camino::Utf8PathBuf::from("../target"); 10 | let _ = camino::Utf8PathBuf::from("../target"); 11 | let _ = camino::Utf8PathBuf::from("../target").as_path(); 12 | let _ = camino::Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../target"); 13 | } 14 | -------------------------------------------------------------------------------- /examples/restriction/const_path_join/ui/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | fn main() { 4 | let _ = std::path::Path::new("..").join("target"); 5 | let _ = std::path::PathBuf::from("..").join("target"); 6 | let _ = std::path::PathBuf::from("..").join("target").as_path(); 7 | let _ = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) 8 | .join("..") 9 | .join("target"); 10 | 11 | let _ = camino::Utf8Path::new("..").join("target"); 12 | let _ = camino::Utf8PathBuf::from("..").join("target"); 13 | let _ = camino::Utf8PathBuf::from("..").join("target").as_path(); 14 | let _ = camino::Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")) 15 | .join("..") 16 | .join("target"); 17 | } 18 | -------------------------------------------------------------------------------- /examples/restriction/env_literal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "env_literal" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for environment variables referred to with string literals" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_internal = { path = "../../../internal" } 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [lints] 22 | workspace = true 23 | 24 | [package.metadata.rust-analyzer] 25 | rustc_private = true 26 | -------------------------------------------------------------------------------- /examples/restriction/env_literal/README.md: -------------------------------------------------------------------------------- 1 | # env_literal 2 | 3 | ### What it does 4 | 5 | Checks for environment variables referred to with string literals. 6 | 7 | ### Why is this bad? 8 | 9 | A typo in the string literal will result in a runtime error, not a compile time error. 10 | 11 | ### Example 12 | 13 | ```rust 14 | let _ = std::env::var("RUSTFLAGS"); 15 | unsafe { 16 | std::env::remove_var("RUSTFALGS"); // Oops 17 | } 18 | ``` 19 | 20 | Use instead: 21 | 22 | ```rust 23 | const RUSTFLAGS: &str = "RUSTFLAGS"; 24 | let _ = std::env::var(RUSTFLAGS); 25 | unsafe { 26 | std::env::remove_var(RUSTFLAGS); 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/restriction/env_literal/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _ = std::env::var("RUSTFLAGS"); 3 | std::env::remove_var("RUSTFALGS"); 4 | } 5 | -------------------------------------------------------------------------------- /examples/restriction/env_literal/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: referring to an environment variable with a string literal is error prone 2 | --> $DIR/main.rs:2:27 3 | | 4 | LL | let _ = std::env::var("RUSTFLAGS"); 5 | | ^^^^^^^^^^^ 6 | | 7 | = help: define a constant `RUSTFLAGS` and use that instead 8 | = note: `#[warn(env_literal)]` on by default 9 | 10 | warning: referring to an environment variable with a string literal is error prone 11 | --> $DIR/main.rs:3:26 12 | | 13 | LL | std::env::remove_var("RUSTFALGS"); 14 | | ^^^^^^^^^^^ 15 | | 16 | = help: define a constant `RUSTFALGS` and use that instead 17 | 18 | warning: 2 warnings emitted 19 | 20 | -------------------------------------------------------------------------------- /examples/restriction/inconsistent_qualification/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inconsistent_qualification" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for inconsistent qualification of module items" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_linting = { path = "../../../utils/linting" } 20 | 21 | [dev-dependencies] 22 | diesel = { workspace = true } 23 | 24 | dylint_testing = { path = "../../../utils/testing" } 25 | 26 | [lints] 27 | workspace = true 28 | 29 | [package.metadata.rust-analyzer] 30 | rustc_private = true 31 | -------------------------------------------------------------------------------- /examples/restriction/inconsistent_qualification/README.md: -------------------------------------------------------------------------------- 1 | # inconsistent_qualification 2 | 3 | ### What it does 4 | 5 | Checks that a module's items are either imported or qualified with the module's path, but 6 | not both. 7 | 8 | ### Why is this bad? 9 | 10 | Mixing the two styles can lead to confusing code. 11 | 12 | ### Known problems 13 | 14 | - No exception is made for for qualifications required for disambiguation. 15 | - Re-exports may not be handled correctly. 16 | 17 | ### Example 18 | 19 | ```rust 20 | use std::env::var; 21 | fn main() { 22 | assert_eq!(var("LD_PRELOAD"), Err(std::env::VarError::NotPresent)); 23 | } 24 | ``` 25 | 26 | Instead, either use: 27 | 28 | ```rust 29 | use std::env::{var, VarError}; 30 | fn main() { 31 | assert_eq!(var("LD_PRELOAD"), Err(VarError::NotPresent)); 32 | } 33 | ``` 34 | 35 | Or use: 36 | 37 | ```rust 38 | fn main() { 39 | assert_eq!( 40 | std::env::var("LD_PRELOAD"), 41 | Err(std::env::VarError::NotPresent) 42 | ); 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /examples/restriction/misleading_variable_name/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "misleading_variable_name" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for variables whose names suggest they have types other than the ones they have" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | heck = { workspace = true } 19 | 20 | dylint_linting = { path = "../../../utils/linting" } 21 | 22 | [dev-dependencies] 23 | anyhow = { workspace = true } 24 | cargo_metadata = { workspace = true } 25 | 26 | dylint_testing = { path = "../../../utils/testing" } 27 | 28 | [lints] 29 | workspace = true 30 | 31 | [package.metadata.rust-analyzer] 32 | rustc_private = true 33 | -------------------------------------------------------------------------------- /examples/restriction/misleading_variable_name/README.md: -------------------------------------------------------------------------------- 1 | # misleading_variable_name 2 | 3 | ### What it does 4 | 5 | Checks for variables satisfying the following three conditions: 6 | 7 | - The variable is initialized with the result of a function call. 8 | - The variable's name matches the name of a type defined within the module in which the 9 | function is defined. 10 | - The variable's type is not the matched type. 11 | 12 | ### Why is this bad? 13 | 14 | A reader could mistakenly believe the variable has a type other than the one it actually 15 | has. 16 | 17 | ### Example 18 | 19 | ```rust,no_run 20 | let file = read_to_string(path).unwrap(); 21 | ``` 22 | 23 | Use instead: 24 | 25 | ```rust,no_run 26 | let contents = read_to_string(path).unwrap(); 27 | ``` 28 | -------------------------------------------------------------------------------- /examples/restriction/misleading_variable_name/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![expect(unused)] 2 | 3 | use anyhow::{Context, Result}; 4 | use std::{ 5 | fs::read_to_string, 6 | io::{BufRead, Cursor}, 7 | path::Path, 8 | }; 9 | 10 | mod one_type { 11 | pub struct Bar; 12 | 13 | pub fn foo() -> bool { 14 | false 15 | } 16 | } 17 | 18 | mod two_types { 19 | pub struct Bar; 20 | pub struct Baz; 21 | 22 | pub fn foo() -> bool { 23 | false 24 | } 25 | } 26 | 27 | mod private { 28 | struct Bar; 29 | 30 | pub fn foo() -> bool { 31 | false 32 | } 33 | } 34 | 35 | mod rename { 36 | pub use std::process::Command as Bar; 37 | 38 | pub fn foo() -> Bar { 39 | Bar::new("true") 40 | } 41 | } 42 | 43 | mod rc { 44 | pub struct Bar; 45 | 46 | pub fn foo() -> std::rc::Rc { 47 | std::rc::Rc::new(Bar) 48 | } 49 | } 50 | 51 | fn main() -> Result<()> { 52 | let path = Path::new("x"); 53 | 54 | let file = read_to_string(path)?; 55 | let file = read_to_string(path).with_context(|| "read")?; 56 | let file = read_to_string(path).unwrap(); 57 | 58 | let buf_reader = Cursor::new([]).lines(); 59 | 60 | let bar = one_type::foo(); 61 | 62 | let bar = two_types::foo(); 63 | 64 | // negative tests 65 | let contents = read_to_string(path).unwrap(); 66 | 67 | let lines = Cursor::new([]).lines(); 68 | 69 | let bar = private::foo(); 70 | 71 | // private in extern crate 72 | let command = cargo_metadata::MetadataCommand::new(); 73 | 74 | let bar = rename::foo(); 75 | 76 | let bar = rc::foo(); 77 | 78 | let rc = std::rc::Rc::new(String::new()); 79 | 80 | Ok(()) 81 | } 82 | 83 | // smoelius: Don't flag functions/types defined in the same module as the call. 84 | mod same_mod { 85 | pub struct Bar; 86 | 87 | pub fn foo() -> bool { 88 | false 89 | } 90 | 91 | fn baz() { 92 | let bar = foo(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/restriction/misleading_variable_name/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: `std::fs` exports a type `File`, which is not the type of `file` 2 | --> $DIR/main.rs:54:9 3 | | 4 | LL | let file = read_to_string(path)?; 5 | | ^^^^ 6 | | 7 | = help: use a name that is not `dir_builder`, `dir_entry`, `file`, `file_times`, `file_type`, `metadata`, `open_options`, `permissions`, or `read_dir` 8 | = note: `#[warn(misleading_variable_name)]` on by default 9 | 10 | warning: `std::fs` exports a type `File`, which is not the type of `file` 11 | --> $DIR/main.rs:55:9 12 | | 13 | LL | let file = read_to_string(path).with_context(|| "read")?; 14 | | ^^^^ 15 | | 16 | = help: use a name that is not `dir_builder`, `dir_entry`, `file`, `file_times`, `file_type`, `metadata`, `open_options`, `permissions`, or `read_dir` 17 | 18 | warning: `std::fs` exports a type `File`, which is not the type of `file` 19 | --> $DIR/main.rs:56:9 20 | | 21 | LL | let file = read_to_string(path).unwrap(); 22 | | ^^^^ 23 | | 24 | = help: use a name that is not `dir_builder`, `dir_entry`, `file`, `file_times`, `file_type`, `metadata`, `open_options`, `permissions`, or `read_dir` 25 | 26 | warning: `std::io` exports a type `BufReader`, which is not the type of `buf_reader` 27 | --> $DIR/main.rs:58:9 28 | | 29 | LL | let buf_reader = Cursor::new([]).lines(); 30 | | ^^^^^^^^^^ 31 | | 32 | = help: use `lines` or something similar 33 | 34 | warning: `one_type` exports a type `Bar`, which is not the type of `bar` 35 | --> $DIR/main.rs:60:9 36 | | 37 | LL | let bar = one_type::foo(); 38 | | ^^^ 39 | | 40 | = help: use a name other than `bar` 41 | 42 | warning: `two_types` exports a type `Bar`, which is not the type of `bar` 43 | --> $DIR/main.rs:62:9 44 | | 45 | LL | let bar = two_types::foo(); 46 | | ^^^ 47 | | 48 | = help: use a name that is not `bar` or `baz` 49 | 50 | warning: 6 warnings emitted 51 | 52 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "question_mark_in_expression" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for the `?` operator in expressions" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "assign" 14 | path = "ui/assign.rs" 15 | 16 | [[example]] 17 | name = "clone" 18 | path = "ui/clone.rs" 19 | 20 | [[example]] 21 | name = "ls" 22 | path = "ui/ls.rs" 23 | 24 | [[example]] 25 | name = "non-empty" 26 | path = "ui/non-empty.rs" 27 | 28 | [dependencies] 29 | clippy_utils = { workspace = true } 30 | 31 | dylint_linting = { path = "../../../utils/linting" } 32 | 33 | [dev-dependencies] 34 | git2 = { workspace = true } 35 | tempfile = { workspace = true } 36 | 37 | dylint_testing = { path = "../../../utils/testing" } 38 | 39 | [lints] 40 | workspace = true 41 | 42 | [package.metadata.rust-analyzer] 43 | rustc_private = true 44 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/README.md: -------------------------------------------------------------------------------- 1 | # question_mark_in_expression 2 | 3 | ### What it does 4 | 5 | Checks for `?` operators embedded within a larger expression. 6 | 7 | ### Why is this bad? 8 | 9 | It can be easy to overlook the `?`. Code is more readable when a `?` is the outermost 10 | operator in an expression. 11 | 12 | ### Example 13 | 14 | ```rust 15 | Ok(PathBuf::from(&var("PWD")?)) 16 | ``` 17 | 18 | Use instead: 19 | 20 | ```rust 21 | let val = var("PWD")?; 22 | Ok(PathBuf::from(&val)) 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/assign.rs: -------------------------------------------------------------------------------- 1 | #![expect(unused)] 2 | 3 | fn main() -> Result<(), ()> { 4 | let mut x = 0; 5 | x = foo()?; 6 | x += foo()?; 7 | Ok(()) 8 | } 9 | 10 | fn foo() -> Result { 11 | Ok(1) 12 | } 13 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/assign.stderr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/dylint/f176ee4b8bf6c05ccd69b61742f18cac1bc7711d/examples/restriction/question_mark_in_expression/ui/assign.stderr -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/clone.rs: -------------------------------------------------------------------------------- 1 | const DYLINT_URL: &str = "https://github.com/trailofbits/dylint"; 2 | 3 | fn main() { 4 | clone().unwrap(); 5 | } 6 | 7 | #[derive(Debug)] 8 | struct Error; 9 | 10 | impl From for Error { 11 | fn from(_: git2::Error) -> Self { 12 | Self 13 | } 14 | } 15 | 16 | impl From for Error { 17 | fn from(_: std::io::Error) -> Self { 18 | Self 19 | } 20 | } 21 | 22 | fn clone() -> Result<(), Error> { 23 | let _ = git2::Repository::clone(DYLINT_URL, tempfile::tempdir()?.path())?; 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/clone.stderr: -------------------------------------------------------------------------------- 1 | warning: using the `?` operator within an expression 2 | --> $DIR/clone.rs:23:49 3 | | 4 | LL | let _ = git2::Repository::clone(DYLINT_URL, tempfile::tempdir()?.path())?; 5 | | ^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: consider breaking this up into multiple expressions 8 | = note: `#[warn(question_mark_in_expression)]` on by default 9 | 10 | warning: 1 warning emitted 11 | 12 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/ls.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | ls().unwrap(); 3 | } 4 | 5 | fn ls() -> Result<(), std::io::Error> { 6 | let path = pwd().unwrap(); 7 | for entry in std::fs::read_dir(path)? { 8 | let entry = entry?; 9 | println!("{}", entry.path().to_string_lossy()); 10 | } 11 | Ok(()) 12 | } 13 | 14 | fn pwd() -> Result { 15 | Ok(std::path::PathBuf::from(&std::env::var("PWD")?)) 16 | } 17 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/ls.stderr: -------------------------------------------------------------------------------- 1 | warning: using the `?` operator within an expression 2 | --> $DIR/ls.rs:15:34 3 | | 4 | LL | Ok(std::path::PathBuf::from(&std::env::var("PWD")?)) 5 | | ^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: consider breaking this up into multiple expressions 8 | = note: `#[warn(question_mark_in_expression)]` on by default 9 | 10 | warning: 1 warning emitted 11 | 12 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/non-empty.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), std::io::Error> { 2 | if !std::fs::read_to_string("Cargo.toml")?.is_empty() { 3 | println!("Cargo.toml is non-empty."); 4 | } 5 | 6 | if String::new() != std::fs::read_to_string("Cargo.lock")? { 7 | println!("Cargo.lock is non-empty."); 8 | } 9 | 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /examples/restriction/question_mark_in_expression/ui/non-empty.stderr: -------------------------------------------------------------------------------- 1 | warning: using the `?` operator within an expression 2 | --> $DIR/non-empty.rs:2:9 3 | | 4 | LL | if !std::fs::read_to_string("Cargo.toml")?.is_empty() { 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: consider breaking this up into multiple expressions 8 | = note: `#[warn(question_mark_in_expression)]` on by default 9 | 10 | warning: 1 warning emitted 11 | 12 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ref_aware_redundant_closure_for_method_calls" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A ref-aware fork of `redundant_closure_for_method_calls`" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "eta" 14 | path = "ui/eta.rs" 15 | 16 | [[example]] 17 | name = "ref_aware" 18 | path = "ui/ref_aware.rs" 19 | 20 | [dependencies] 21 | clippy_utils = { workspace = true } 22 | 23 | dylint_internal = { path = "../../../internal" } 24 | dylint_linting = { path = "../../../utils/linting" } 25 | 26 | [dev-dependencies] 27 | dylint_testing = { path = "../../../utils/testing" } 28 | 29 | [lints] 30 | workspace = true 31 | 32 | [package.metadata.rust-analyzer] 33 | rustc_private = true 34 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/README.md: -------------------------------------------------------------------------------- 1 | # ref_aware_redundant_closure_for_method_calls 2 | 3 | ### What it does 4 | 5 | This is essentially a ref-aware fork of Clippy's [`redundant_closure_for_method_calls`] 6 | lint. It suggests to remove a closure when made possible by a use of `as_ref`, `as_mut`, 7 | `as_deref`, or `as_deref_mut`. 8 | 9 | ### Known problems 10 | 11 | Currently works only for [`Option`]s. 12 | 13 | ### Example 14 | 15 | ```rust 16 | Some(String::from("a")).map(|s| s.is_empty()); 17 | Some(String::from("a")).map(|s| s.to_uppercase()); 18 | ``` 19 | 20 | Use instead: 21 | 22 | ```rust 23 | Some(String::from("a")).as_ref().map(String::is_empty); 24 | Some(String::from("a")).as_deref().map(str::to_uppercase); 25 | ``` 26 | 27 | [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html 28 | [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/#redundant_closure_for_method_calls 29 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/ui/eta.stderr: -------------------------------------------------------------------------------- 1 | warning: redundant closure 2 | --> $DIR/eta.rs:89:46 3 | | 4 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.as_ref().map(TestTrait::trait_foo_ref)` 6 | | 7 | = note: `#[warn(ref_aware_redundant_closure_for_method_calls)]` on by default 8 | 9 | warning: 1 warning emitted 10 | 11 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/ui/ref_aware.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | use std::{ 4 | ops::{Deref, DerefMut}, 5 | path::Path, 6 | }; 7 | 8 | struct DerefMutExample { 9 | value: T, 10 | } 11 | 12 | impl Deref for DerefMutExample { 13 | type Target = T; 14 | 15 | fn deref(&self) -> &Self::Target { 16 | &self.value 17 | } 18 | } 19 | 20 | impl DerefMut for DerefMutExample { 21 | fn deref_mut(&mut self) -> &mut Self::Target { 22 | &mut self.value 23 | } 24 | } 25 | 26 | fn main() { 27 | let _ = std::fs::read_dir(Path::new(".")) 28 | .ok() 29 | .as_mut().and_then(std::iter::Iterator::next) 30 | .unwrap() 31 | .unwrap(); 32 | let _ = Some(String::from("a")).as_ref().map(std::string::String::is_empty); 33 | let _ = Some(String::from("a")).as_deref().map(str::to_uppercase); 34 | let _ = Some(DerefMutExample { value: 'a' }).as_deref_mut().map(char::make_ascii_uppercase); 35 | 36 | let _ = "a".chars().peekable().peek().copied().map(char::is_uppercase); 37 | 38 | // negative test: `Iterator` 39 | let _ = [String::from("a")].into_iter().map(|s| s.is_empty()); 40 | 41 | // negative test: `Result` 42 | let _ = Path::new(".") 43 | .metadata() 44 | .and_then(|metadata| metadata.modified()); 45 | } 46 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/ui/ref_aware.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | 3 | use std::{ 4 | ops::{Deref, DerefMut}, 5 | path::Path, 6 | }; 7 | 8 | struct DerefMutExample { 9 | value: T, 10 | } 11 | 12 | impl Deref for DerefMutExample { 13 | type Target = T; 14 | 15 | fn deref(&self) -> &Self::Target { 16 | &self.value 17 | } 18 | } 19 | 20 | impl DerefMut for DerefMutExample { 21 | fn deref_mut(&mut self) -> &mut Self::Target { 22 | &mut self.value 23 | } 24 | } 25 | 26 | fn main() { 27 | let _ = std::fs::read_dir(Path::new(".")) 28 | .ok() 29 | .and_then(|mut entries| entries.next()) 30 | .unwrap() 31 | .unwrap(); 32 | let _ = Some(String::from("a")).map(|s| s.is_empty()); 33 | let _ = Some(String::from("a")).map(|s| s.to_uppercase()); 34 | let _ = Some(DerefMutExample { value: 'a' }).map(|mut x| x.make_ascii_uppercase()); 35 | 36 | let _ = "a".chars().peekable().peek().map(|c| c.is_uppercase()); 37 | 38 | // negative test: `Iterator` 39 | let _ = [String::from("a")].into_iter().map(|s| s.is_empty()); 40 | 41 | // negative test: `Result` 42 | let _ = Path::new(".") 43 | .metadata() 44 | .and_then(|metadata| metadata.modified()); 45 | } 46 | -------------------------------------------------------------------------------- /examples/restriction/ref_aware_redundant_closure_for_method_calls/ui/ref_aware.stderr: -------------------------------------------------------------------------------- 1 | warning: redundant closure 2 | --> $DIR/ref_aware.rs:29:9 3 | | 4 | LL | .and_then(|mut entries| entries.next()) 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.as_mut().and_then(std::iter::Iterator::next)` 6 | | 7 | = note: `#[warn(ref_aware_redundant_closure_for_method_calls)]` on by default 8 | 9 | warning: redundant closure 10 | --> $DIR/ref_aware.rs:32:36 11 | | 12 | LL | let _ = Some(String::from("a")).map(|s| s.is_empty()); 13 | | ^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.as_ref().map(std::string::String::is_empty)` 14 | 15 | warning: redundant closure 16 | --> $DIR/ref_aware.rs:33:36 17 | | 18 | LL | let _ = Some(String::from("a")).map(|s| s.to_uppercase()); 19 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.as_deref().map(str::to_uppercase)` 20 | 21 | warning: redundant closure 22 | --> $DIR/ref_aware.rs:34:49 23 | | 24 | LL | let _ = Some(DerefMutExample { value: 'a' }).map(|mut x| x.make_ascii_uppercase()); 25 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.as_deref_mut().map(char::make_ascii_uppercase)` 26 | 27 | warning: redundant closure 28 | --> $DIR/ref_aware.rs:36:42 29 | | 30 | LL | let _ = "a".chars().peekable().peek().map(|c| c.is_uppercase()); 31 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `.copied().map(char::is_uppercase)` 32 | 33 | warning: 5 warnings emitted 34 | 35 | -------------------------------------------------------------------------------- /examples/restriction/register_lints_warn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "register_lints_warn" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for calls to `rustc_errors::DiagCtxtHandle::warn` from within a `register_lints` function" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_linting = { path = "../../../utils/linting" } 16 | 17 | [dev-dependencies] 18 | dylint_testing = { path = "../../../utils/testing" } 19 | 20 | [lints] 21 | workspace = true 22 | 23 | [package.metadata.rust-analyzer] 24 | rustc_private = true 25 | -------------------------------------------------------------------------------- /examples/restriction/register_lints_warn/README.md: -------------------------------------------------------------------------------- 1 | # register_lints_warn 2 | 3 | ### What it does 4 | 5 | Checks for calls to `rustc_errors::DiagCtxtHandle::warn` from within a `register_lints` 6 | function. 7 | 8 | ### Why is this bad? 9 | 10 | Dylint lists a library's lints by calling the library's `register_lints` function and 11 | comparing the lints that are registered before and after the call. If the library's 12 | `register_lints` functions emits warnings, they will be emitted when a user tries to list 13 | the library's lints. 14 | 15 | ### Example 16 | 17 | ```rust 18 | pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { 19 | if condition { 20 | sess.dcx().warn("something bad happened"); 21 | } 22 | } 23 | ``` 24 | 25 | Use instead: 26 | 27 | ```rust 28 | impl<'tcx> rustc_lint::LateLintPass<'tcx> for LintPass { 29 | fn check_crate(&mut self, cx: &rustc_lint::LateContext<'tcx>) { 30 | if condition { 31 | cx.sess().dcx().warn("something bad happened"); 32 | } 33 | } 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/restriction/register_lints_warn/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | 3 | extern crate rustc_lint; 4 | extern crate rustc_session; 5 | 6 | pub fn register_lints(sess: &rustc_session::Session, _lint_store: &mut rustc_lint::LintStore) { 7 | sess.dcx().warn("something bad happened"); 8 | } 9 | 10 | use rustc_lint::LintContext; 11 | 12 | struct LintPass; 13 | 14 | impl rustc_lint::LintPass for LintPass { 15 | fn name(&self) -> &'static str { 16 | "lint_pass" 17 | } 18 | fn get_lints(&self) -> Vec<&'static rustc_lint::Lint> { 19 | Vec::new() 20 | } 21 | } 22 | 23 | impl<'tcx> rustc_lint::LateLintPass<'tcx> for LintPass { 24 | fn check_crate(&mut self, cx: &rustc_lint::LateContext<'tcx>) { 25 | cx.sess().dcx().warn("something bad happened"); 26 | } 27 | } 28 | 29 | fn main() {} 30 | -------------------------------------------------------------------------------- /examples/restriction/register_lints_warn/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: call to `rustc_errors::DiagCtxtHandle::warn` from within a `register_lints` function 2 | --> $DIR/main.rs:7:5 3 | | 4 | LL | sess.dcx().warn("something bad happened"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(register_lints_warn)]` on by default 8 | 9 | warning: 1 warning emitted 10 | 11 | -------------------------------------------------------------------------------- /examples/restriction/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/restriction/suboptimal_pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "suboptimal_pattern" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for patterns that could perform additional destructuring" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | serde = { workspace = true, features = ["derive"] } 15 | 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [lints] 22 | workspace = true 23 | 24 | [package.metadata.rust-analyzer] 25 | rustc_private = true 26 | -------------------------------------------------------------------------------- /examples/restriction/suboptimal_pattern/README.md: -------------------------------------------------------------------------------- 1 | # suboptimal_pattern 2 | 3 | ### What it does 4 | 5 | Checks for patterns that could perform additional destructuring. 6 | 7 | ### Why is this bad? 8 | 9 | The use of destructuring patterns in closure parameters (for example) often leads to more 10 | concise closure bodies. Beyond that, the benefits of this lint are similar to those of 11 | [pattern-type-mismatch]. 12 | 13 | ### Known problems 14 | 15 | - Currently only checks closure parameters (not, e.g., match patterns). 16 | - Currently only suggests destructuring references and tuples (not, e.g., arrays or 17 | structs). 18 | - For the lint to suggest destructuring a reference, the idents involved must not use `ref` 19 | annotations. 20 | 21 | ### Example 22 | 23 | ```rust 24 | let xs = [0, 1, 2]; 25 | let ys = xs.iter().map(|x| *x == 0).collect::>(); 26 | ``` 27 | 28 | Use instead: 29 | 30 | ```rust 31 | let xs = [0, 1, 2]; 32 | let ys = xs.iter().map(|&x| x == 0).collect::>(); 33 | ``` 34 | 35 | ### Configuration 36 | 37 | - `explicit_deref_check: bool` (default `true`): By default, `suboptimal_pattern` will not 38 | suggest to destructure a reference unless it would eliminate at least one explicit 39 | dereference. Setting `explicit_deref_check` to `false` disables this check. 40 | 41 | [pattern-type-mismatch]: https://rust-lang.github.io/rust-clippy/master/#pattern_type_mismatch 42 | -------------------------------------------------------------------------------- /examples/restriction/try_io_result/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "try_io_result" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for the `?` operator applied to `std::io::Result`" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [[example]] 13 | name = "ui" 14 | path = "ui/main.rs" 15 | 16 | [dependencies] 17 | clippy_utils = { workspace = true } 18 | 19 | dylint_internal = { path = "../../../internal" } 20 | dylint_linting = { path = "../../../utils/linting" } 21 | 22 | [dev-dependencies] 23 | anyhow = { workspace = true } 24 | thiserror = { workspace = true } 25 | 26 | dylint_testing = { path = "../../../utils/testing" } 27 | 28 | [lints] 29 | workspace = true 30 | 31 | [package.metadata.rust-analyzer] 32 | rustc_private = true 33 | -------------------------------------------------------------------------------- /examples/restriction/try_io_result/README.md: -------------------------------------------------------------------------------- 1 | # try_io_result 2 | 3 | ### What it does 4 | 5 | Checks for `?` operators applied to values of type `std::io::Result`. 6 | 7 | ### Why is this bad? 8 | 9 | Returning a `std::io::Result` could mean relevant context (e.g., files or paths involved) is 10 | lost. The problem is discussed under "Verbose IO errors" in Yoshua Wuyts' [Error Handling 11 | Survey]. 12 | 13 | ### Known problems 14 | 15 | No interprocedural analysis is done. So if context is added by the caller, it will go 16 | unnoticed. 17 | 18 | ### Example 19 | 20 | ```rust 21 | fn foo() -> anyhow::Result<()> { 22 | let _ = File::open("/nonexistent")?; 23 | Ok(()) 24 | } 25 | ``` 26 | 27 | Use instead: 28 | 29 | ```rust 30 | use anyhow::Context; 31 | fn foo() -> anyhow::Result<()> { 32 | let _ = File::open("/nonexistent").with_context(|| "could not open `/nonexistent`")?; 33 | Ok(()) 34 | } 35 | ``` 36 | 37 | [Error Handling Survey]: https://blog.yoshuawuyts.com/error-handling-survey/ 38 | -------------------------------------------------------------------------------- /examples/restriction/try_io_result/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use anyhow::Context; 4 | use std::{fs::File, io, path::PathBuf}; 5 | use thiserror::Error as ThisError; 6 | 7 | #[derive(Debug, ThisError)] 8 | pub enum Error { 9 | #[error("io error")] 10 | Io(#[from] io::Error), 11 | #[error("failed to open {0:?}")] 12 | OpenFailed(PathBuf, #[source] io::Error), 13 | } 14 | 15 | fn main() {} 16 | 17 | fn foo() -> anyhow::Result<()> { 18 | let _ = File::open("/nonexistent")?; 19 | Ok(()) 20 | } 21 | 22 | fn foo_with_context() -> anyhow::Result<()> { 23 | let _ = File::open("/nonexistent").with_context(|| "could not open `/nonexistent`")?; 24 | Ok(()) 25 | } 26 | 27 | fn bar() -> Result<(), Error> { 28 | let _ = File::open("/nonexistent")?; 29 | Ok(()) 30 | } 31 | 32 | fn bar_with_context() -> Result<(), Error> { 33 | let _ = File::open("/nonexistent") 34 | .map_err(|error| Error::OpenFailed(PathBuf::from("/nonexistent"), error))?; 35 | Ok(()) 36 | } 37 | 38 | fn baz() -> io::Result<()> { 39 | let _ = File::open("/nonexistent")?; 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /examples/restriction/try_io_result/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: returning a `std::io::Result` could discard relevant context (e.g., files or paths involved) 2 | --> $DIR/main.rs:18:13 3 | | 4 | LL | let _ = File::open("/nonexistent")?; 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: return a type that includes relevant context 8 | = note: `#[warn(try_io_result)]` on by default 9 | 10 | warning: returning a `std::io::Result` could discard relevant context (e.g., files or paths involved) 11 | --> $DIR/main.rs:28:13 12 | | 13 | LL | let _ = File::open("/nonexistent")?; 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | | 16 | = help: return a type that includes relevant context 17 | 18 | warning: 2 warnings emitted 19 | 20 | -------------------------------------------------------------------------------- /examples/supplementary/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/supplementary/README.md: -------------------------------------------------------------------------------- 1 | # Supplementary lints 2 | 3 | Like the [general-purpose lints], the supplementary lints use `dylint_linting`'s [`constituent` feature]. 4 | 5 | See the general-purpose lints' [documentation] for an explanation. 6 | 7 | [`constituent` feature]: ../../utils/linting/README.md#constituent-feature 8 | [documentation]: ../general/README.md 9 | [general-purpose lints]: ../general 10 | -------------------------------------------------------------------------------- /examples/supplementary/arg_iter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arg_iter" 3 | version = "4.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | clippy_utils = { workspace = true } 12 | dylint_linting = { path = "../../../utils/linting" } 13 | 14 | [dev-dependencies] 15 | dylint_testing = { path = "../../../utils/testing" } 16 | 17 | [features] 18 | rlib = ["dylint_linting/constituent"] 19 | 20 | [package.metadata.rust-analyzer] 21 | rustc_private = true 22 | -------------------------------------------------------------------------------- /examples/supplementary/arg_iter/README.md: -------------------------------------------------------------------------------- 1 | # arg_iter 2 | 3 | ### What it does 4 | 5 | Checks for functions that take `Iterator` trait bounds when they could use 6 | `IntoIterator` instead. 7 | 8 | ### Why is this bad? 9 | 10 | Using `IntoIterator` makes functions more flexible by allowing them to 11 | accept more types like arrays, slices, and `Vec` without requiring explicit 12 | `.iter()` calls. This often makes the API easier to use. 13 | 14 | ### Example 15 | 16 | ```rust 17 | // Bad: Requires caller to call .iter() on Vec, slice, etc. 18 | fn process_bad>(iter: I) { 19 | for item in iter { 20 | // ... 21 | } 22 | } 23 | ``` 24 | 25 | Good: Accepts Vec, slice, etc. directly. 26 | 27 | ```rust 28 | fn process_good>(iterable: I) { 29 | for item in iterable { // .into_iter() is implicitly called 30 | // ... 31 | } 32 | } 33 | ``` 34 | 35 | This lint ignores cases where the parameter is also bounded by other traits 36 | (besides the implicit `Sized`), as `IntoIterator` might not be suitable. 37 | 38 | ```rust 39 | fn complex_bound(iter: I) { // Ok 40 | // ... 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /examples/supplementary/arg_iter/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn good>(iter: I) { 2 | // This is fine - using IntoIterator 3 | for item in iter { 4 | println!("{}", item); 5 | } 6 | } 7 | 8 | fn bad>(iter: I) { 9 | // This should trigger the lint - could use IntoIterator instead 10 | for item in iter { 11 | println!("{}", item); 12 | } 13 | } 14 | 15 | fn bad_with_type_parameter>(iter: I) { 16 | // This should also trigger the lint 17 | for item in iter { 18 | println!("{:?}", item); 19 | } 20 | } 21 | 22 | // This should NOT trigger the lint because Iterator is used in another trait bound 23 | fn with_other_bound + Clone>(iter: I) { 24 | let cloned = iter.clone(); 25 | for item in cloned { 26 | println!("{}", item); 27 | } 28 | } 29 | 30 | fn main() { 31 | let v = vec![1, 2, 3]; 32 | 33 | // With IntoIterator we can pass the vec directly 34 | good(v.clone()); 35 | 36 | // With Iterator we need to call into_iter() 37 | bad(v.into_iter()); 38 | } 39 | -------------------------------------------------------------------------------- /examples/supplementary/arg_iter/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: parameter type has `Iterator` bound 2 | --> $DIR/main.rs:8:39 3 | | 4 | LL | fn bad>(iter: I) { 5 | | ^ 6 | | 7 | = help: consider using `IntoIterator` instead of `Iterator` for parameter `I` 8 | = note: `#[warn(arg_iter)]` on by default 9 | 10 | warning: parameter type has `Iterator` bound 11 | --> $DIR/main.rs:15:77 12 | | 13 | LL | fn bad_with_type_parameter>(iter: I) { 14 | | ^ 15 | | 16 | = help: consider using `IntoIterator` instead of `Iterator` for parameter `I` 17 | 18 | warning: 2 warnings emitted 19 | 20 | -------------------------------------------------------------------------------- /examples/supplementary/commented_out_code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "commented_out_code" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for code that has been commented out" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | regex = { workspace = true } 15 | syn = { workspace = true, features = ["full"] } 16 | 17 | dylint_linting = { path = "../../../utils/linting" } 18 | 19 | [dev-dependencies] 20 | dylint_testing = { path = "../../../utils/testing" } 21 | 22 | [features] 23 | rlib = ["dylint_linting/constituent"] 24 | 25 | [lints] 26 | workspace = true 27 | 28 | [package.metadata.rust-analyzer] 29 | rustc_private = true 30 | -------------------------------------------------------------------------------- /examples/supplementary/commented_out_code/README.md: -------------------------------------------------------------------------------- 1 | # commented_out_code 2 | 3 | ### What it does 4 | 5 | Checks for code that has been commented out. 6 | 7 | ### Why is this bad? 8 | 9 | Commented-out code is often meant to be removed, but kept by mistake. 10 | 11 | ### Known problems 12 | 13 | - Currently only checks for commented-out statements in blocks. 14 | - Does not handle statements spanning multiple line comments, e.g.: 15 | 16 | ```rust 17 | // dbg!( 18 | // x 19 | // ); 20 | ``` 21 | 22 | ### Example 23 | 24 | ```rust 25 | // dbg!(x); 26 | f(x); 27 | ``` 28 | 29 | Use instead: 30 | 31 | ```rust 32 | f(x); 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/supplementary/commented_out_code/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | 3 | fn foo(_: u32) {} 4 | 5 | fn empty_with_line_comment() { 6 | // dbg!(x); 7 | } 8 | 9 | fn empty_with_block_comment() { 10 | /* dbg!(x); */ 11 | } 12 | 13 | fn interspersed(x: u32) { 14 | // dbg!(x); 15 | 16 | let y = 0; 17 | 18 | /* dbg!(y); */ 19 | 20 | foo(y); 21 | 22 | /* 23 | dbg!(x); 24 | dbg!(y); 25 | */ 26 | } 27 | 28 | fn negative_tests() { 29 | // a line comment 30 | 31 | /* a block comment */ 32 | 33 | /* 34 | a multiline 35 | block comment 36 | */ 37 | 38 | /// doc_comment(); 39 | foo(0); 40 | } 41 | 42 | fn single_identifier() { 43 | // smoelius: This is a "false positive." Ideally, the lint would not fire on the next line. 44 | // Identifier 45 | } 46 | 47 | // smoelius: @fcasal noticed that the lint produced multiple warnings in async functions. 48 | async fn async_fn() { 49 | // dbg!(x); 50 | } 51 | -------------------------------------------------------------------------------- /examples/supplementary/commented_out_code/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: commented-out code 2 | --> $DIR/main.rs:6:5 3 | | 4 | LL | // dbg!(x); 5 | | ^^^^^^^^^^^ 6 | | 7 | = help: uncomment or remove 8 | = note: `#[warn(commented_out_code)]` on by default 9 | 10 | warning: commented-out code 11 | --> $DIR/main.rs:10:5 12 | | 13 | LL | /* dbg!(x); */ 14 | | ^^^^^^^^^^^^^^ 15 | | 16 | = help: uncomment or remove 17 | 18 | warning: commented-out code 19 | --> $DIR/main.rs:14:5 20 | | 21 | LL | // dbg!(x); 22 | | ^^^^^^^^^^^ 23 | | 24 | = help: uncomment or remove 25 | 26 | warning: commented-out code 27 | --> $DIR/main.rs:18:5 28 | | 29 | LL | /* dbg!(y); */ 30 | | ^^^^^^^^^^^^^^ 31 | | 32 | = help: uncomment or remove 33 | 34 | warning: commented-out code 35 | --> $DIR/main.rs:22:5 36 | | 37 | LL | / /* 38 | LL | | dbg!(x); 39 | LL | | dbg!(y); 40 | LL | | */ 41 | | |______^ 42 | | 43 | = help: uncomment or remove 44 | 45 | warning: commented-out code 46 | --> $DIR/main.rs:49:5 47 | | 48 | LL | // dbg!(x); 49 | | ^^^^^^^^^^^ 50 | | 51 | = help: uncomment or remove 52 | 53 | warning: 6 warnings emitted 54 | 55 | -------------------------------------------------------------------------------- /examples/supplementary/escaping_doc_link/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "escaping_doc_link" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for doc comment links that escape their packages" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | cargo-util = { workspace = true } 14 | cargo_metadata = { workspace = true } 15 | clippy_utils = { workspace = true } 16 | once_cell = { workspace = true } 17 | pulldown-cmark = { workspace = true } 18 | 19 | dylint_linting = { path = "../../../utils/linting" } 20 | 21 | [dev-dependencies] 22 | dylint_testing = { path = "../../../utils/testing" } 23 | 24 | [features] 25 | rlib = ["dylint_linting/constituent"] 26 | 27 | [lints] 28 | workspace = true 29 | 30 | [package.metadata.rust-analyzer] 31 | rustc_private = true 32 | -------------------------------------------------------------------------------- /examples/supplementary/escaping_doc_link/README.md: -------------------------------------------------------------------------------- 1 | # escaping_doc_link 2 | 3 | ### What it does 4 | 5 | Checks for doc comment links that refer to files outside of their source file's package. 6 | 7 | ### Why is this bad? 8 | 9 | Such links will be broken on [docs.rs], for example. 10 | 11 | ### Example 12 | 13 | ```rust 14 | //! [general-purpose lints]: ../../general 15 | ``` 16 | 17 | Use instead: 18 | 19 | ```rust 20 | //! [general-purpose lints]: https://github.com/trailofbits/dylint/tree/master/examples/general 21 | ``` 22 | 23 | [docs.rs]: https://docs.rs 24 | -------------------------------------------------------------------------------- /examples/supplementary/escaping_doc_link/ui/main.rs: -------------------------------------------------------------------------------- 1 | //! [parent]: .. 2 | 3 | //! [sibling]: ../redundant_reference 4 | 5 | //! [self]: ../escaping_doc_link 6 | 7 | //! [self with section]: ../escaping_doc_link#what-it-does 8 | 9 | //! [url]: https://github.com/Manishearth/compiletest-rs 10 | 11 | //! [intra-doc link]: crate::register_lints 12 | 13 | //! [broken link]: ../nonexistent_library 14 | 15 | fn main() {} 16 | -------------------------------------------------------------------------------- /examples/supplementary/escaping_doc_link/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: link refers to files outside of the package directory 2 | --> $DIR/main.rs:1:1 3 | | 4 | LL | //! [parent]: .. 5 | | ^^^^^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(escaping_doc_link)]` on by default 8 | 9 | warning: link refers to files outside of the package directory 10 | --> $DIR/main.rs:3:1 11 | | 12 | LL | //! [sibling]: ../redundant_reference 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 14 | 15 | warning: broken link 16 | --> $DIR/main.rs:13:1 17 | | 18 | LL | //! [broken link]: ../nonexistent_library 19 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20 | 21 | warning: 3 warnings emitted 22 | 23 | -------------------------------------------------------------------------------- /examples/supplementary/inconsistent_struct_pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inconsistent_struct_pattern" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for struct patterns whose fields do not match their declared order" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_linting = { path = "../../../utils/linting" } 16 | 17 | [dev-dependencies] 18 | dylint_testing = { path = "../../../utils/testing" } 19 | 20 | [features] 21 | rlib = ["dylint_linting/constituent"] 22 | 23 | [lints] 24 | workspace = true 25 | 26 | [package.metadata.rust-analyzer] 27 | rustc_private = true 28 | -------------------------------------------------------------------------------- /examples/supplementary/inconsistent_struct_pattern/README.md: -------------------------------------------------------------------------------- 1 | # inconsistent_struct_pattern 2 | 3 | ### What it does 4 | 5 | Checks for struct patterns whose fields whose fields do not match their declared order. 6 | 7 | ### Why is this bad? 8 | 9 | It can be harder to spot mistakes in inconsistent code. 10 | 11 | ### Example 12 | 13 | ```rust 14 | struct Struct { 15 | a: bool, 16 | b: bool, 17 | }; 18 | let strukt = Struct { a: false, b: true }; 19 | let Struct { b, a } = strukt; 20 | ``` 21 | 22 | Use instead: 23 | 24 | ```rust 25 | struct Struct { 26 | a: bool, 27 | b: bool, 28 | }; 29 | let strukt = Struct { a: false, b: true }; 30 | let Struct { a, b } = strukt; 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/supplementary/inconsistent_struct_pattern/ui/main.fixed: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![allow(unused)] 3 | 4 | #[derive(Default)] 5 | struct Struct { 6 | a: bool, 7 | b: bool, 8 | c: bool, 9 | } 10 | 11 | fn main() { 12 | let strukt = Struct::default(); 13 | 14 | // should not lint 15 | let Struct { a, b, c } = strukt; 16 | let Struct { a, b, .. } = strukt; 17 | let Struct { a, c, .. } = strukt; 18 | let Struct { b, c, .. } = strukt; 19 | 20 | // should lint 21 | let Struct { a, b, c } = strukt; 22 | let Struct { a, b, c } = strukt; 23 | let Struct { a, b, c } = strukt; 24 | } 25 | -------------------------------------------------------------------------------- /examples/supplementary/inconsistent_struct_pattern/ui/main.rs: -------------------------------------------------------------------------------- 1 | // run-rustfix 2 | #![allow(unused)] 3 | 4 | #[derive(Default)] 5 | struct Struct { 6 | a: bool, 7 | b: bool, 8 | c: bool, 9 | } 10 | 11 | fn main() { 12 | let strukt = Struct::default(); 13 | 14 | // should not lint 15 | let Struct { a, b, c } = strukt; 16 | let Struct { a, b, .. } = strukt; 17 | let Struct { a, c, .. } = strukt; 18 | let Struct { b, c, .. } = strukt; 19 | 20 | // should lint 21 | let Struct { a, c, b } = strukt; 22 | let Struct { b, a, c } = strukt; 23 | let Struct { c, b, a } = strukt; 24 | } 25 | -------------------------------------------------------------------------------- /examples/supplementary/inconsistent_struct_pattern/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: struct pattern field order is inconsistent with struct definition field order 2 | --> $DIR/main.rs:21:18 3 | | 4 | LL | let Struct { a, c, b } = strukt; 5 | | ^^^^^^^ help: use: `a, b, c` 6 | | 7 | = note: `#[warn(inconsistent_struct_pattern)]` on by default 8 | 9 | warning: struct pattern field order is inconsistent with struct definition field order 10 | --> $DIR/main.rs:22:18 11 | | 12 | LL | let Struct { b, a, c } = strukt; 13 | | ^^^^^^^ help: use: `a, b, c` 14 | 15 | warning: struct pattern field order is inconsistent with struct definition field order 16 | --> $DIR/main.rs:23:18 17 | | 18 | LL | let Struct { c, b, a } = strukt; 19 | | ^^^^^^^ help: use: `a, b, c` 20 | 21 | warning: 3 warnings emitted 22 | 23 | -------------------------------------------------------------------------------- /examples/supplementary/local_ref_cell/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "local_ref_cell" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for `RefCell` local variables" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_internal = { path = "../../../internal" } 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [features] 22 | rlib = ["dylint_linting/constituent"] 23 | 24 | [lints] 25 | workspace = true 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | -------------------------------------------------------------------------------- /examples/supplementary/local_ref_cell/README.md: -------------------------------------------------------------------------------- 1 | # local_ref_cell 2 | 3 | ### What it does 4 | 5 | Checks for local variables that are [`RefCell`]s. 6 | 7 | ### Why is this bad? 8 | 9 | There is rarely a need for a locally declared `RefCell`. 10 | 11 | ### Example 12 | 13 | ```rust 14 | let x = RefCell::::new(0); 15 | ``` 16 | 17 | Use instead: 18 | 19 | ```rust 20 | let mut x: usize = 0; 21 | ``` 22 | 23 | [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html 24 | -------------------------------------------------------------------------------- /examples/supplementary/local_ref_cell/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![expect(unused_variables)] 2 | 3 | use std::cell::RefCell; 4 | 5 | fn main() { 6 | let x: RefCell; 7 | let y = RefCell::new(0); 8 | } 9 | -------------------------------------------------------------------------------- /examples/supplementary/local_ref_cell/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: locally declared `RefCell` 2 | --> $DIR/main.rs:6:5 3 | | 4 | LL | let x: RefCell; 5 | | ^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(local_ref_cell)]` on by default 8 | 9 | warning: locally declared `RefCell` 10 | --> $DIR/main.rs:7:5 11 | | 12 | LL | let y = RefCell::new(0); 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^ 14 | 15 | warning: 2 warnings emitted 16 | 17 | -------------------------------------------------------------------------------- /examples/supplementary/nonexistent_path_in_comment/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nonexistent_path_in_comment" 3 | version = "4.1.0" 4 | authors = ["Augustin Villetard"] 5 | description = "Lint for nonexistent paths in comments" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | cargo_metadata = { workspace = true } 14 | clippy_utils = { workspace = true } 15 | dylint_linting = { path = "../../../utils/linting" } 16 | regex = { workspace = true } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [features] 22 | rlib = ["dylint_linting/constituent"] 23 | 24 | [lints] 25 | workspace = true 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | -------------------------------------------------------------------------------- /examples/supplementary/nonexistent_path_in_comment/README.md: -------------------------------------------------------------------------------- 1 | # nonexistent_path_in_comment 2 | 3 | ### What it does 4 | 5 | This lint checks code comments, including both line comments (using `//`) and block comments 6 | (`/*...*/`) for file path references. It then validates that the referenced files exist either 7 | relative to the source file's directory or relative to the workspace root. When a file path 8 | reference does not point to an existing file, the lint emits a warning. 9 | 10 | ### Why is this bad? 11 | 12 | References to nonexistent files in comments can be misleading: 13 | 14 | - They clutter the code with outdated or inaccurate references. 15 | - They may cause confusion among developers who are trying to trace implementation details 16 | or documentation. 17 | 18 | ### Known problems 19 | 20 | Currently, this lint must be allowed at the crate level. 21 | 22 | - This example: 23 | 24 | ```rust 25 | // dylint/dylint/build.rs (it exists) 26 | ``` 27 | 28 | would get flagged here because the workspace root is `supplementary` 29 | it did exist, as this lint doesn't check for project root. 30 | 31 | ### Example 32 | 33 | ``` 34 | // See ../nonexistent/path/file.rs for implementation details 35 | fn main() {} 36 | ``` 37 | 38 | Use instead: 39 | 40 | ``` 41 | // See ../actual/path/file.rs for implementation details 42 | fn main() {} 43 | ``` 44 | -------------------------------------------------------------------------------- /examples/supplementary/nonexistent_path_in_comment/ui/main.rs: -------------------------------------------------------------------------------- 1 | // This path does not exist 2 | // See ../nonexistent/path/file.rs 3 | 4 | // This path should exist 5 | // See ../src/lib.rs 6 | 7 | /* This is a block comment with a nonexistent path 8 | See ../another/nonexistent/path/file.go 9 | */ 10 | 11 | // Single dot path that does exist 12 | // ./main.rs 13 | 14 | // Single dot path that does not exist 15 | // The span ./ido/not/exist.rs only points to the path 16 | 17 | // Workspace root path that does exist 18 | // nonexistent_path_in_comment/Cargo.toml 19 | 20 | // Negative test: urls 21 | // This is a url: https://github.com/trailofbits/dylint 22 | 23 | // Nonexistent path with line and column reference 24 | // See ../nonexistent/path/file.rs:1:1 25 | 26 | // Don't strip back to a colon before the last slash 27 | // See ../nonexistent:directory:with:colons/file.rs:1:1 28 | 29 | // Negative test: existing path with line and column reference 30 | // See ../src/lib.rs:1:1 31 | 32 | fn main() {} 33 | -------------------------------------------------------------------------------- /examples/supplementary/nonexistent_path_in_comment/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: referenced path does not exist 2 | --> $DIR/main.rs:2:8 3 | | 4 | LL | // See ../nonexistent/path/file.rs 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = help: verify the path is correct or remove the reference 8 | = note: `#[warn(nonexistent_path_in_comment)]` on by default 9 | 10 | warning: referenced path does not exist 11 | --> $DIR/main.rs:15:13 12 | | 13 | LL | // The span ./ido/not/exist.rs only points to the path 14 | | ^^^^^^^^^^^^^^^^^^ 15 | | 16 | = help: verify the path is correct or remove the reference 17 | 18 | warning: referenced path does not exist 19 | --> $DIR/main.rs:24:8 20 | | 21 | LL | // See ../nonexistent/path/file.rs:1:1 22 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 23 | | 24 | = help: verify the path is correct or remove the reference 25 | 26 | warning: referenced path does not exist 27 | --> $DIR/main.rs:27:8 28 | | 29 | LL | // See ../nonexistent:directory:with:colons/file.rs:1:1 30 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 31 | | 32 | = help: verify the path is correct or remove the reference 33 | 34 | warning: referenced path does not exist 35 | --> $DIR/main.rs:8:8 36 | | 37 | LL | See ../another/nonexistent/path/file.go 38 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 39 | | 40 | = help: verify the path is correct or remove the reference 41 | 42 | warning: 5 warnings emitted 43 | 44 | -------------------------------------------------------------------------------- /examples/supplementary/redundant_reference/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "redundant_reference" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for reference fields used only to read one copyable subfield" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | serde = { workspace = true, features = ["derive"] } 15 | 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [features] 22 | rlib = ["dylint_linting/constituent"] 23 | 24 | [lints] 25 | workspace = true 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | -------------------------------------------------------------------------------- /examples/supplementary/redundant_reference/README.md: -------------------------------------------------------------------------------- 1 | # redundant_reference 2 | 3 | ### What it does 4 | 5 | Checks for fields that are references used only to read one copyable subfield, and whose 6 | lifetimes are not used elsewhere. 7 | 8 | ### Why is this bad? 9 | 10 | Storing the reference instead of a copy of the subfield adds an unnecessary lifetime 11 | parameter to the struct. It also creates an unnecessary pointer dereference at runtime. 12 | 13 | ### Example 14 | 15 | ```rust 16 | struct V<'cx, 'tcx> { 17 | cx: &'cx LateContext<'tcx>, 18 | } 19 | 20 | impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> { 21 | type MaybeTyCtxt = rustc_middle::ty::TyCtxt<'tcx>; 22 | type NestedFilter = rustc_middle::hir::nested_filter::All; 23 | 24 | fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { 25 | self.cx.tcx 26 | } 27 | } 28 | ``` 29 | 30 | Use instead: 31 | 32 | ```rust 33 | struct V<'tcx> { 34 | tcx: TyCtxt<'tcx>, 35 | } 36 | 37 | impl<'tcx> Visitor<'tcx> for V<'tcx> { 38 | type MaybeTyCtxt = rustc_middle::ty::TyCtxt<'tcx>; 39 | type NestedFilter = rustc_middle::hir::nested_filter::All; 40 | 41 | fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { 42 | self.tcx 43 | } 44 | } 45 | ``` 46 | 47 | ### Configuration 48 | 49 | - `lifetime_check: bool` (default `true`): Setting this to `false` disables the check that 50 | the lifetime use is unique. That is, the lint becomes a check for: fields that are 51 | references used only to read one copyable subfield. 52 | -------------------------------------------------------------------------------- /examples/supplementary/redundant_reference/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: `.cx` is the only field of `V` that uses lifetime `'cx`, and is used only to read `.cx.tcx`, whose type `rustc_middle::ty::TyCtxt<'_>` implements `Copy` 2 | --> $DIR/main.rs:38:9 3 | | 4 | LL | cx: &'cx LateContext<'tcx>, 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: read here 8 | --> $DIR/main.rs:46:17 9 | | 10 | LL | self.cx.tcx 11 | | ^^^^^^^ 12 | = help: consider storing a copy of `.cx.tcx` to eliminate the need for `'cx` 13 | = note: `#[warn(redundant_reference)]` on by default 14 | 15 | warning: 1 warning emitted 16 | 17 | -------------------------------------------------------------------------------- /examples/supplementary/redundant_reference/ui_no_lifetime_check/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | 3 | extern crate rustc_hir; 4 | extern crate rustc_lint; 5 | extern crate rustc_middle; 6 | 7 | pub struct Bar { 8 | baz: String, 9 | qux: bool, 10 | quux: bool, 11 | } 12 | 13 | fn main() {} 14 | 15 | // smoelius: For some reason, the order in which the diagnostic messages are printed varies when 16 | // `REDUNDANT_REFERENCE_NO_LIFETIME_CHECK` is enabled. The next module is the only one that produces 17 | // an additional warning when the feature is enabled. So, the work around is to ensure that this 18 | // module appears first (to facilitate comparing files), and that its warning is the only one 19 | // produced when `REDUNDANT_REFERENCE_NO_LIFETIME_CHECK` is enabled. 20 | mod multiple_lifetime_uses { 21 | struct S<'a> { 22 | bar: &'a super::Bar, 23 | baz: &'a str, 24 | } 25 | 26 | impl<'a> S<'a> { 27 | fn foo(&self) -> bool { 28 | self.bar.qux 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/supplementary/redundant_reference/ui_no_lifetime_check/main.stderr: -------------------------------------------------------------------------------- 1 | warning: `.bar` is used only to read `.bar.qux`, whose type `bool` implements `Copy` 2 | --> $DIR/main.rs:22:9 3 | | 4 | LL | bar: &'a super::Bar, 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: read here 8 | --> $DIR/main.rs:28:17 9 | | 10 | LL | self.bar.qux 11 | | ^^^^^^^^ 12 | = help: consider storing a copy of `.bar.qux` instead 13 | = note: `#[warn(redundant_reference)]` on by default 14 | 15 | warning: 1 warning emitted 16 | 17 | -------------------------------------------------------------------------------- /examples/supplementary/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /examples/supplementary/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![warn(unused_extern_crates)] 3 | 4 | dylint_linting::dylint_library!(); 5 | 6 | extern crate rustc_lint; 7 | extern crate rustc_session; 8 | 9 | #[expect(clippy::no_mangle_with_rust_abi)] 10 | #[unsafe(no_mangle)] 11 | pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { 12 | // smoelius: Please keep the following `register_lints` calls sorted by crate name. 13 | arg_iter::register_lints(sess, lint_store); 14 | commented_out_code::register_lints(sess, lint_store); 15 | escaping_doc_link::register_lints(sess, lint_store); 16 | inconsistent_struct_pattern::register_lints(sess, lint_store); 17 | local_ref_cell::register_lints(sess, lint_store); 18 | nonexistent_path_in_comment::register_lints(sess, lint_store); 19 | redundant_reference::register_lints(sess, lint_store); 20 | unnamed_constant::register_lints(sess, lint_store); 21 | unnecessary_borrow_mut::register_lints(sess, lint_store); 22 | unnecessary_conversion_for_trait::register_lints(sess, lint_store); 23 | } 24 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unnamed_constant" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for unnamed constants, aka magic numbers" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | serde = { workspace = true } 15 | 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [features] 22 | rlib = ["dylint_linting/constituent"] 23 | 24 | [lints] 25 | workspace = true 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/README.md: -------------------------------------------------------------------------------- 1 | # unnamed_constant 2 | 3 | ### What it does 4 | 5 | Checks for unnamed constants, aka magic numbers. 6 | 7 | ### Why is this bad? 8 | 9 | "Magic numbers are considered bad practice in programming, because they can make the code 10 | more difficult to understand and harder to maintain." ([pandaquests]) 11 | 12 | ### Example 13 | 14 | ```rust 15 | x *= 1000; 16 | ``` 17 | 18 | Use instead: 19 | 20 | ```rust 21 | const MILLIS: u64 = 1000; 22 | x *= MILLIS; 23 | ``` 24 | 25 | ### Configuration 26 | 27 | - `threshold: u64` (default `10`): Minimum value a constant must exceed to be flagged. 28 | 29 | [pandaquests]: https://levelup.gitconnected.com/whats-so-bad-about-magic-numbers-4c0a0c524b7d 30 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut x: i64 = 1; 3 | 4 | x *= -11; 5 | x *= 11; 6 | 7 | // negative tests (with default threshold) 8 | 9 | x *= -10; 10 | x *= 10; 11 | 12 | // negative tests (with default threshold or otherwise) 13 | 14 | const MILLIS: i64 = 1000; 15 | 16 | const GIGABYTE: u64 = 1024 * 1024 * 1024; 17 | 18 | let a: [&str; 2] = ["x", "y"]; 19 | 20 | x *= -1; 21 | x *= 1; 22 | } 23 | 24 | fn revised_heuristic() { 25 | let mut x: i64 = 1; 26 | 27 | x *= -48; 28 | x *= 48; 29 | 30 | x *= -80; 31 | x *= 80; 32 | 33 | // negative tests: one flip 34 | 35 | x *= -15; 36 | x *= 15; 37 | 38 | // negative tests: single bit 39 | 40 | x *= -16; 41 | x *= 16; 42 | } 43 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: unnamed constant 2 | --> $DIR/main.rs:4:11 3 | | 4 | LL | x *= -11; 5 | | ^^ 6 | | 7 | = help: give the constant a name and use that instead 8 | = note: `#[warn(unnamed_constant)]` on by default 9 | 10 | warning: unnamed constant 11 | --> $DIR/main.rs:5:10 12 | | 13 | LL | x *= 11; 14 | | ^^ 15 | | 16 | = help: give the constant a name and use that instead 17 | 18 | warning: unnamed constant 19 | --> $DIR/main.rs:27:11 20 | | 21 | LL | x *= -48; 22 | | ^^ 23 | | 24 | = help: give the constant a name and use that instead 25 | 26 | warning: unnamed constant 27 | --> $DIR/main.rs:28:10 28 | | 29 | LL | x *= 48; 30 | | ^^ 31 | | 32 | = help: give the constant a name and use that instead 33 | 34 | warning: unnamed constant 35 | --> $DIR/main.rs:30:11 36 | | 37 | LL | x *= -80; 38 | | ^^ 39 | | 40 | = help: give the constant a name and use that instead 41 | 42 | warning: unnamed constant 43 | --> $DIR/main.rs:31:10 44 | | 45 | LL | x *= 80; 46 | | ^^ 47 | | 48 | = help: give the constant a name and use that instead 49 | 50 | warning: 6 warnings emitted 51 | 52 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/ui_threshold/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut x: i64 = 1; 3 | 4 | x *= -11; 5 | x *= 11; 6 | 7 | // negative tests (with default threshold) 8 | 9 | x *= -10; 10 | x *= 10; 11 | 12 | // negative tests (with default threshold or otherwise) 13 | 14 | const MILLIS: i64 = 1000; 15 | 16 | const GIGABYTE: u64 = 1024 * 1024 * 1024; 17 | 18 | let a: [&str; 2] = ["x", "y"]; 19 | 20 | x *= -1; 21 | x *= 1; 22 | } 23 | 24 | fn revised_heuristic() { 25 | let mut x: i64 = 1; 26 | 27 | x *= -48; 28 | x *= 48; 29 | 30 | x *= -80; 31 | x *= 80; 32 | 33 | // negative tests: one flip 34 | 35 | x *= -15; 36 | x *= 15; 37 | 38 | // negative tests: single bit 39 | 40 | x *= -16; 41 | x *= 16; 42 | } 43 | -------------------------------------------------------------------------------- /examples/supplementary/unnamed_constant/ui_threshold/main.stderr: -------------------------------------------------------------------------------- 1 | warning: unnamed constant 2 | --> $DIR/main.rs:4:11 3 | | 4 | LL | x *= -11; 5 | | ^^ 6 | | 7 | = help: give the constant a name and use that instead 8 | = note: `#[warn(unnamed_constant)]` on by default 9 | 10 | warning: unnamed constant 11 | --> $DIR/main.rs:5:10 12 | | 13 | LL | x *= 11; 14 | | ^^ 15 | | 16 | = help: give the constant a name and use that instead 17 | 18 | warning: unnamed constant 19 | --> $DIR/main.rs:9:11 20 | | 21 | LL | x *= -10; 22 | | ^^ 23 | | 24 | = help: give the constant a name and use that instead 25 | 26 | warning: unnamed constant 27 | --> $DIR/main.rs:10:10 28 | | 29 | LL | x *= 10; 30 | | ^^ 31 | | 32 | = help: give the constant a name and use that instead 33 | 34 | warning: unnamed constant 35 | --> $DIR/main.rs:27:11 36 | | 37 | LL | x *= -48; 38 | | ^^ 39 | | 40 | = help: give the constant a name and use that instead 41 | 42 | warning: unnamed constant 43 | --> $DIR/main.rs:28:10 44 | | 45 | LL | x *= 48; 46 | | ^^ 47 | | 48 | = help: give the constant a name and use that instead 49 | 50 | warning: unnamed constant 51 | --> $DIR/main.rs:30:11 52 | | 53 | LL | x *= -80; 54 | | ^^ 55 | | 56 | = help: give the constant a name and use that instead 57 | 58 | warning: unnamed constant 59 | --> $DIR/main.rs:31:10 60 | | 61 | LL | x *= 80; 62 | | ^^ 63 | | 64 | = help: give the constant a name and use that instead 65 | 66 | warning: 8 warnings emitted 67 | 68 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_borrow_mut/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unnecessary_borrow_mut" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for calls to `RefCell::borrow_mut` that could be `RefCell::borrow`" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [dependencies] 13 | clippy_utils = { workspace = true } 14 | 15 | dylint_internal = { path = "../../../internal" } 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [features] 22 | rlib = ["dylint_linting/constituent"] 23 | 24 | [lints] 25 | workspace = true 26 | 27 | [package.metadata.rust-analyzer] 28 | rustc_private = true 29 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_borrow_mut/README.md: -------------------------------------------------------------------------------- 1 | # unnecessary_borrow_mut 2 | 3 | ### What it does 4 | 5 | Checks for calls to [`RefCell::borrow_mut`] that could be calls to [`RefCell::borrow`]. 6 | 7 | ### Why is this bad? 8 | 9 | A call to [`RefCell::borrow_mut`] "panics if the value is currently borrowed." Thus, a call 10 | to [`RefCell::borrow_mut`] can panic in situations where a call to [`RefCell::borrow`] would 11 | not. 12 | 13 | ### Example 14 | 15 | ```rust 16 | x = *cell.borrow_mut(); 17 | ``` 18 | 19 | Use instead: 20 | 21 | ```rust 22 | x = *cell.borrow(); 23 | ``` 24 | 25 | [`RefCell::borrow_mut`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.borrow_mut 26 | [`RefCell::borrow`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.borrow 27 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_borrow_mut/ui/main.rs: -------------------------------------------------------------------------------- 1 | #![expect(unused_assignments, unused_variables)] 2 | 3 | use std::cell::RefCell; 4 | 5 | fn main() { 6 | let mut x = 0; 7 | let cell = RefCell::new(1); 8 | 9 | x = *cell.borrow_mut(); 10 | 11 | let ref_mut = cell.borrow_mut(); 12 | x = *ref_mut; 13 | 14 | // negative tests 15 | 16 | x = *cell.borrow(); 17 | 18 | *cell.borrow_mut() = 2; 19 | 20 | require_mut_ref(&mut cell.borrow_mut()); 21 | 22 | let mut ref_mut = cell.borrow_mut(); 23 | require_mut_ref(&mut ref_mut); 24 | 25 | let _: &mut u32 = &mut cell.borrow_mut(); 26 | } 27 | 28 | fn require_mut_ref(_: &mut u32) {} 29 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_borrow_mut/ui/main.stderr: -------------------------------------------------------------------------------- 1 | warning: borrowed reference is used only immutably 2 | --> $DIR/main.rs:9:15 3 | | 4 | LL | x = *cell.borrow_mut(); 5 | | ^^^^^^^^^^^^ help: use: `borrow()` 6 | | 7 | = note: `#[warn(unnecessary_borrow_mut)]` on by default 8 | 9 | warning: borrowed reference is used only immutably 10 | --> $DIR/main.rs:11:24 11 | | 12 | LL | let ref_mut = cell.borrow_mut(); 13 | | ^^^^^^^^^^^^ help: use: `borrow()` 14 | 15 | warning: 2 warnings emitted 16 | 17 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_conversion_for_trait/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unnecessary_conversion_for_trait" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint to check for unnecessary trait-behavior-preserving calls" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[example]] 13 | name = "general" 14 | path = "ui/general.rs" 15 | 16 | [[example]] 17 | name = "unnecessary_to_owned" 18 | path = "ui/unnecessary_to_owned.rs" 19 | 20 | [[example]] 21 | name = "vec" 22 | path = "ui/vec.rs" 23 | 24 | [dependencies] 25 | clippy_utils = { workspace = true } 26 | 27 | dylint_internal = { path = "../../../internal", features = ["cargo"] } 28 | dylint_linting = { path = "../../../utils/linting" } 29 | 30 | [dev-dependencies] 31 | tempfile = { workspace = true } 32 | 33 | dylint_testing = { path = "../../../utils/testing" } 34 | 35 | [features] 36 | rlib = ["dylint_linting/constituent"] 37 | 38 | [lints] 39 | workspace = true 40 | 41 | [package.metadata.rust-analyzer] 42 | rustc_private = true 43 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_conversion_for_trait/README.md: -------------------------------------------------------------------------------- 1 | # unnecessary_conversion_for_trait 2 | 3 | ### What it does 4 | 5 | Checks for trait-behavior-preserving calls in positions where a trait implementation is 6 | expected. 7 | 8 | ### Why is this bad? 9 | 10 | Such unnecessary calls make the code more verbose and could impact performance. 11 | 12 | ### Example 13 | 14 | ```rust 15 | let _ = Command::new("ls").args(["-a", "-l"].iter()); 16 | let _ = Path::new("/").join(Path::new(".")); 17 | ``` 18 | 19 | Use instead: 20 | 21 | ```rust 22 | let _ = Command::new("ls").args(["-a", "-l"]); 23 | let _ = Path::new("/").join("."); 24 | ``` 25 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_conversion_for_trait/ui/vec.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _ = std::fs::write("x", vec![0]); 3 | } 4 | -------------------------------------------------------------------------------- /examples/supplementary/unnecessary_conversion_for_trait/ui/vec.stderr: -------------------------------------------------------------------------------- 1 | warning: the inner argument implements the required traits 2 | --> $DIR/vec.rs:2:33 3 | | 4 | LL | let _ = std::fs::write("x", vec![0]); 5 | | ^^^^^^^ 6 | | 7 | = help: use the macro arguments directly 8 | = note: `#[warn(unnecessary_conversion_for_trait)]` on by default 9 | 10 | warning: 1 warning emitted 11 | 12 | -------------------------------------------------------------------------------- /examples/testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing lints 2 | -------------------------------------------------------------------------------- /examples/testing/clippy/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../../target/examples" 3 | 4 | [target.'cfg(all())'] 5 | linker = "dylint-link" 6 | -------------------------------------------------------------------------------- /examples/testing/clippy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clippy" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "All of the Clippy lints as a Dylint library" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_config = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 14 | clippy_lints = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 15 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 16 | serde_json = "1.0" 17 | 18 | dylint_internal = { path = "../../../internal" } 19 | dylint_linting = { path = "../../../utils/linting" } 20 | 21 | [dev-dependencies] 22 | anyhow = "1.0" 23 | cargo_metadata = "0.19" 24 | env_logger = "0.11" 25 | log = "0.4" 26 | tempfile = "3.20" 27 | 28 | dylint = { path = "../../../dylint" } 29 | dylint_internal = { path = "../../../internal", features = ["git"] } 30 | dylint_testing = { path = "../../../utils/testing" } 31 | 32 | [lints.rust.unexpected_cfgs] 33 | level = "deny" 34 | check-cfg = ["cfg(dylint_lib, values(any()))"] 35 | 36 | [package.metadata.rust-analyzer] 37 | rustc_private = true 38 | 39 | [workspace] 40 | 41 | [workspace.metadata.dylint] 42 | libraries = [ 43 | { path = "../../general" }, 44 | { path = "../../supplementary" }, 45 | { path = "../../testing/clippy" }, 46 | { path = "../../restriction/*" }, 47 | ] 48 | -------------------------------------------------------------------------------- /examples/testing/clippy/README.md: -------------------------------------------------------------------------------- 1 | # clippy 2 | -------------------------------------------------------------------------------- /examples/testing/clippy/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | # smoelius: The `or_fun_call` test passes only if `rust-src` is installed. 4 | # smoelius: `rust-src` no longer seems to be necessary. 5 | components = ["llvm-tools-preview", "rustc-dev"] 6 | -------------------------------------------------------------------------------- /examples/testing/clippy/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![warn(unused_extern_crates)] 3 | 4 | dylint_linting::dylint_library!(); 5 | 6 | extern crate rustc_lint; 7 | extern crate rustc_session; 8 | 9 | use dylint_internal::env; 10 | use std::env::{remove_var, set_var}; 11 | 12 | /// All of the Clippy lints as a Dylint library 13 | #[expect(clippy::no_mangle_with_rust_abi)] 14 | #[unsafe(no_mangle)] 15 | pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { 16 | if let Ok(clippy_disable_docs_links) = env::var(env::CLIPPY_DISABLE_DOCS_LINKS) { 17 | if let Ok(val) = serde_json::from_str::>(&clippy_disable_docs_links) { 18 | if let Some(val) = val { 19 | unsafe { 20 | set_var(env::CLIPPY_DISABLE_DOCS_LINKS, val); 21 | } 22 | } else { 23 | unsafe { 24 | remove_var(env::CLIPPY_DISABLE_DOCS_LINKS); 25 | } 26 | } 27 | } 28 | } 29 | 30 | let conf_path = clippy_config::lookup_conf_file(); 31 | let conf = clippy_config::Conf::read(sess, &conf_path); 32 | clippy_lints::register_lints(lint_store, conf); 33 | clippy_lints::register_pre_expansion_lints(lint_store, conf); 34 | } 35 | -------------------------------------------------------------------------------- /examples/testing/straggler/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # smoelius: `straggler` uses a different compiler version than the other example libraries. 3 | target-dir = "../../../target/straggler" 4 | 5 | [target.'cfg(all())'] 6 | linker = "dylint-link" 7 | -------------------------------------------------------------------------------- /examples/testing/straggler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "straggler" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "A lint that uses an old toolchain for testing purposes" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | # smoelius: `straggler` is intentionally held back for testing purposes. 14 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "238edf273d195c8e472851ebd60571f77f978ac8" } 15 | 16 | dylint_linting = { path = "../../../utils/linting" } 17 | 18 | [dev-dependencies] 19 | dylint_testing = { path = "../../../utils/testing" } 20 | 21 | [lints.rust.unexpected_cfgs] 22 | level = "deny" 23 | check-cfg = ["cfg(dylint_lib, values(any()))"] 24 | 25 | [package.metadata.rust-analyzer] 26 | rustc_private = true 27 | 28 | [workspace] 29 | 30 | [workspace.metadata.dylint] 31 | libraries = [] 32 | -------------------------------------------------------------------------------- /examples/testing/straggler/README.md: -------------------------------------------------------------------------------- 1 | # straggler 2 | 3 | ### What it does 4 | 5 | This lint does nothing. Its Rust toolchain is intentionally held back for testing purposes. 6 | 7 | ### Why is this bad? 8 | 9 | It's not. 10 | 11 | ### Known problems 12 | 13 | This lint does nothing. 14 | -------------------------------------------------------------------------------- /examples/testing/straggler/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # smoelius: `straggler` is intentionally held back for testing purposes. 3 | channel = "nightly-2025-02-20" 4 | components = ["llvm-tools-preview", "rustc-dev"] 5 | -------------------------------------------------------------------------------- /examples/testing/straggler/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![warn(unused_extern_crates)] 3 | 4 | use rustc_lint::LateLintPass; 5 | 6 | dylint_linting::declare_late_lint! { 7 | /// ### What it does 8 | /// 9 | /// This lint does nothing. Its Rust toolchain is intentionally held back for testing purposes. 10 | /// 11 | /// ### Why is this bad? 12 | /// 13 | /// It's not. 14 | /// 15 | /// ### Known problems 16 | /// 17 | /// This lint does nothing. 18 | pub STRAGGLER, 19 | Allow, 20 | "this lint does nothing" 21 | } 22 | 23 | impl<'tcx> LateLintPass<'tcx> for Straggler {} 24 | -------------------------------------------------------------------------------- /expensive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expensive" 3 | description = "Expensive tests" 4 | version = "4.1.0" 5 | edition = "2024" 6 | publish = false 7 | 8 | [dev-dependencies] 9 | anyhow = { workspace = true } 10 | tempfile = { workspace = true } 11 | 12 | dylint_internal = { version = "=4.1.0", path = "../internal", features = [ 13 | "clippy_utils", 14 | "rustup", 15 | "sed", 16 | "testing", 17 | ] } 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /expensive/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /expensive/tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | COPY . dylint 4 | 5 | WORKDIR dylint 6 | 7 | RUN apk --no-cache add g++ pkgconfig openssl-dev openssl-libs-static 8 | 9 | # smoelius: Based on: https://www.rust-lang.org/learn/get-started 10 | RUN wget -O - https://sh.rustup.rs | sh -s -- -y 11 | 12 | ENV PATH="/root/.cargo/bin:$PATH" 13 | 14 | # smoelius: The nightly toolchain is needed by the `can_install_while_driver_is_running` test. 15 | RUN rustup install nightly 16 | RUN rustup component add llvm-tools-preview rustc-dev --toolchain nightly 17 | 18 | RUN cargo install --path dylint-link 19 | 20 | RUN cargo build --all-targets 21 | 22 | ENV RUSTFLAGS="-C target-feature=-crt-static" 23 | 24 | RUN cargo test -p dylint --lib 25 | -------------------------------------------------------------------------------- /expensive/tests/alpine.rs: -------------------------------------------------------------------------------- 1 | #![cfg(all(not(coverage), unix))] 2 | 3 | use std::{ 4 | io::{Write, stderr}, 5 | process::Command, 6 | }; 7 | 8 | #[test] 9 | fn alpine() { 10 | let status = Command::new("which").arg("docker").status().unwrap(); 11 | if !status.success() { 12 | #[allow(clippy::explicit_write)] 13 | writeln!( 14 | stderr(), 15 | "Skipping `alpine` test as `docker` could not be found", 16 | ) 17 | .unwrap(); 18 | return; 19 | } 20 | 21 | // smoelius: Don't use `assert_cmd::Command` here because it would hide the output. 22 | let status = Command::new("docker") 23 | .args([ 24 | "build", 25 | "--progress=plain", 26 | "-f", 27 | "expensive/tests/Dockerfile", 28 | ".", 29 | ]) 30 | .current_dir("..") 31 | .status() 32 | .unwrap(); 33 | assert!(status.success()); 34 | 35 | Command::new("docker") 36 | .args(["system", "prune"]) 37 | .current_dir("..") 38 | .status() 39 | .unwrap(); 40 | assert!(status.success()); 41 | } 42 | -------------------------------------------------------------------------------- /fixtures/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | -------------------------------------------------------------------------------- /fixtures/array_pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "array_pattern" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [workspace.metadata.dylint] 8 | libraries = [ 9 | { path = "../../examples", pattern = [ 10 | "testing/clippy", 11 | "restriction/question_mark_in_expression", 12 | ] }, 13 | ] 14 | -------------------------------------------------------------------------------- /fixtures/array_pattern/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixtures/depinfo_dylint_libs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "depinfo_dylint_libs" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1.0" 9 | 10 | [workspace] 11 | 12 | [workspace.metadata.dylint] 13 | libraries = [ 14 | { path = "../../examples/restriction/question_mark_in_expression" }, 15 | { path = "../../examples/restriction/try_io_result" }, 16 | ] 17 | -------------------------------------------------------------------------------- /fixtures/depinfo_dylint_libs/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::{ 3 | io::{Write, stdout}, 4 | str::from_utf8, 5 | }; 6 | 7 | fn main() -> Result<()> { 8 | write!(stdout(), "{}", from_utf8(b"Hello, world!")?)?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/library_packages_in_dylint_toml/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "library_packages_in_dylint_toml" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [workspace] 8 | -------------------------------------------------------------------------------- /fixtures/library_packages_in_dylint_toml/dylint.toml: -------------------------------------------------------------------------------- 1 | [workspace.metadata.dylint] 2 | libraries = [{ path = "../../examples/restriction/collapsible_unwrap" }] 3 | -------------------------------------------------------------------------------- /fixtures/library_packages_in_dylint_toml/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let result: Result<_, _> = std::fs::read_dir(".").unwrap().next().unwrap(); 3 | println!("{:?}", result.unwrap().path()); 4 | } 5 | -------------------------------------------------------------------------------- /fixtures/library_packages_with_rust_toolchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "library_packages_with_rust_toolchain" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [workspace.metadata.dylint] 8 | libraries = [ 9 | { git = "https://github.com/trailofbits/dylint", pattern = "examples/general" }, 10 | ] 11 | -------------------------------------------------------------------------------- /fixtures/library_packages_with_rust_toolchain/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /fixtures/library_packages_with_rust_toolchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixtures/name_toolchain_map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "name_toolchain_map" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [workspace.metadata.dylint] 8 | libraries = [ 9 | { path = "../../examples/restriction/question_mark_in_expression" }, 10 | { path = "../../examples/testing/clippy" }, 11 | { path = "../../examples/testing/straggler" }, 12 | ] 13 | -------------------------------------------------------------------------------- /fixtures/name_toolchain_map/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixtures/no_deps/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["a", "b"] 3 | resolver = "2" 4 | 5 | [workspace.metadata.dylint] 6 | libraries = [ 7 | { path = "../../examples/restriction/question_mark_in_expression" }, 8 | ] 9 | -------------------------------------------------------------------------------- /fixtures/no_deps/a/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "a" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | b = { path = "../b" } 9 | -------------------------------------------------------------------------------- /fixtures/no_deps/a/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | b::greet().unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /fixtures/no_deps/b/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "b" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | -------------------------------------------------------------------------------- /fixtures/no_deps/b/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn greet() -> Result<(), std::str::Utf8Error> { 2 | println!("{}", std::str::from_utf8(b"Hello, world!")?); 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /internal/.gitignore: -------------------------------------------------------------------------------- 1 | template/Cargo.lock 2 | -------------------------------------------------------------------------------- /internal/README.md: -------------------------------------------------------------------------------- 1 | # dylint_internal 2 | 3 | The package is used by [Dylint]. Users should not need to refer to this package directly. 4 | 5 | [Dylint]: .. 6 | -------------------------------------------------------------------------------- /internal/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "packaging"))] 2 | fn main() {} 3 | 4 | #[cfg(feature = "packaging")] 5 | fn main() { 6 | use std::{ 7 | env::var, 8 | ffi::OsStr, 9 | fs::File, 10 | path::{Path, PathBuf}, 11 | }; 12 | use tar::Builder; 13 | use walkdir::WalkDir; 14 | 15 | #[cfg_attr(dylint_lib = "env_literal", allow(env_literal))] 16 | let outdir = var("OUT_DIR").map(PathBuf::from).unwrap(); 17 | let path_buf = outdir.join("template.tar"); 18 | let file = File::create(path_buf).unwrap(); 19 | let mut archive = Builder::new(file); 20 | let root = Path::new("template"); 21 | for result in WalkDir::new(root).into_iter().filter_entry(|entry| { 22 | let filename = entry.file_name(); 23 | filename != OsStr::new("Cargo.lock") && filename != OsStr::new("target") 24 | }) { 25 | let entry = result.unwrap(); 26 | let path = entry.path(); 27 | if !path.is_file() { 28 | continue; 29 | } 30 | let mut file = File::open(path).unwrap(); 31 | let path_stripped = path.strip_prefix(root).unwrap(); 32 | archive.append_file(path_stripped, &mut file).unwrap(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/src/home.rs: -------------------------------------------------------------------------------- 1 | use crate::env; 2 | use std::path::PathBuf; 3 | 4 | #[must_use] 5 | pub fn cargo_home() -> Option { 6 | if let Ok(cargo_home) = env::var(env::CARGO_HOME) { 7 | Some(PathBuf::from(cargo_home)) 8 | } else { 9 | home_dir().map(|path| path.join(".cargo")) 10 | } 11 | } 12 | 13 | // smoelius: https://github.com/rust-lang/cargo/commit/014378f8c07bec557b6c6608e5baf0761a12d504 14 | #[rustversion::before(1.86)] 15 | pub use home::home_dir; 16 | 17 | #[rustversion::since(1.86)] 18 | #[must_use] 19 | pub fn home_dir() -> Option { 20 | // smoelius: The `deprecated` attribute hasn't been removed yet: 21 | // https://github.com/rust-lang/rust/pull/132515#discussion_r1829715262 22 | // smoelius: The attribute is removed in Rust's master branch: 23 | // https://github.com/rust-lang/rust/commit/2c752bcf559975995eb8086a7fa6a7f9b5ba0de8 24 | #[allow(deprecated)] 25 | std::env::home_dir() 26 | } 27 | -------------------------------------------------------------------------------- /internal/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(dylint_lib = "general", allow(crate_wide_allow))] 2 | #![cfg_attr(dylint_lib = "supplementary", allow(nonexistent_path_in_comment))] 3 | 4 | #[cfg(feature = "cargo")] 5 | pub mod cargo; 6 | 7 | #[cfg(feature = "clippy_utils")] 8 | pub mod clippy_utils; 9 | 10 | #[cfg(feature = "config")] 11 | pub mod config; 12 | 13 | #[cfg(feature = "command")] 14 | mod command; 15 | #[cfg(feature = "command")] 16 | pub use command::*; 17 | 18 | pub mod env; 19 | 20 | #[cfg(feature = "examples")] 21 | pub mod examples; 22 | 23 | mod filename; 24 | pub use filename::{library_filename, parse_path_filename}; 25 | 26 | #[cfg(feature = "git")] 27 | mod git; 28 | #[cfg(feature = "git")] 29 | pub use git::*; 30 | 31 | #[cfg(feature = "git")] 32 | pub use git2; 33 | 34 | #[cfg(feature = "home")] 35 | pub mod home; 36 | 37 | #[cfg(feature = "packaging")] 38 | pub mod packaging; 39 | 40 | pub mod paths; 41 | 42 | #[cfg(feature = "rustup")] 43 | pub mod rustup; 44 | 45 | #[cfg(feature = "sed")] 46 | mod sed; 47 | #[cfg(feature = "sed")] 48 | pub use sed::find_and_replace; 49 | 50 | #[cfg(feature = "testing")] 51 | pub mod testing; 52 | -------------------------------------------------------------------------------- /internal/src/sed.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use regex::Regex; 3 | use std::{ 4 | fs::{read_to_string, write}, 5 | path::Path, 6 | }; 7 | 8 | pub fn find_and_replace(path: &Path, re: &str, replacement: R) -> Result<()> 9 | where 10 | R: AsRef, 11 | { 12 | let before = read_to_string(path) 13 | .with_context(|| format!("`read_to_string` failed for `{}`", path.to_string_lossy()))?; 14 | let re = Regex::new(re)?; 15 | let after = re.replace_all(&before, replacement.as_ref()); 16 | write(path, after.as_bytes()).map_err(Into::into) 17 | } 18 | -------------------------------------------------------------------------------- /internal/src/testing.rs: -------------------------------------------------------------------------------- 1 | use crate::CommandExt; 2 | use anyhow::Result; 3 | use cargo_metadata::MetadataCommand; 4 | use std::{ 5 | env::consts, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | #[ctor::ctor] 10 | fn init() { 11 | env_logger::init(); 12 | } 13 | 14 | pub fn new_template(path: &Path) -> Result<()> { 15 | crate::packaging::new_template(path)?; 16 | crate::packaging::use_local_packages(path)?; 17 | Ok(()) 18 | } 19 | 20 | /// Debug-builds `cargo-dylint` and returns a path to the resulting executable 21 | /// 22 | /// Arguments 23 | /// 24 | /// - `workspace_root`: path to workspace root 25 | /// 26 | /// To run the executable from a test, the likely easiest way is to pass 27 | /// `--path ` or `--lib-path `. 28 | pub fn cargo_dylint(workspace_root: impl AsRef) -> Result { 29 | let mut command = crate::cargo::build("`cargo-dylint`").build(); 30 | command 31 | .current_dir(&workspace_root) 32 | .args(["--bin", "cargo-dylint"]) 33 | .success()?; 34 | 35 | let metadata = MetadataCommand::new() 36 | .current_dir(workspace_root.as_ref()) 37 | .no_deps() 38 | .exec() 39 | .unwrap(); 40 | let cargo_dylint = metadata 41 | .target_directory 42 | .as_std_path() 43 | .join("debug") 44 | .join(format!("cargo-dylint{}", consts::EXE_SUFFIX)); 45 | 46 | Ok(cargo_dylint) 47 | } 48 | -------------------------------------------------------------------------------- /internal/template/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all())'] 2 | rustflags = ["-C", "linker=dylint-link"] 3 | 4 | # For Rust versions 1.74.0 and onward, the following alternative can be used 5 | # (see https://github.com/rust-lang/cargo/pull/12535): 6 | # linker = "dylint-link" 7 | -------------------------------------------------------------------------------- /internal/template/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /internal/template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fill_me_in" 3 | version = "0.1.0" 4 | authors = ["authors go here"] 5 | description = "description goes here" 6 | edition = "2024" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "7bb54d91be1af212faaa078786c1d2271a67d4f9" } 14 | dylint_linting = "4.1.0" 15 | 16 | [dev-dependencies] 17 | dylint_testing = "4.1.0" 18 | 19 | [workspace] 20 | 21 | [package.metadata.rust-analyzer] 22 | rustc_private = true 23 | -------------------------------------------------------------------------------- /internal/template/README.md: -------------------------------------------------------------------------------- 1 | # template 2 | 3 | ### What it does 4 | 5 | ### Why is this bad? 6 | 7 | ### Known problems 8 | 9 | Remove if none. 10 | 11 | ### Example 12 | 13 | ```rust 14 | // example code where a warning is issued 15 | ``` 16 | 17 | Use instead: 18 | 19 | ```rust 20 | // example code that does not raise a warning 21 | ``` 22 | -------------------------------------------------------------------------------- /internal/template/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-04-03" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /internal/template/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![feature(let_chains)] 3 | // #![warn(unused_extern_crates)] 4 | 5 | extern crate rustc_arena; 6 | extern crate rustc_ast; 7 | extern crate rustc_ast_pretty; 8 | extern crate rustc_data_structures; 9 | extern crate rustc_errors; 10 | extern crate rustc_hir; 11 | extern crate rustc_hir_pretty; 12 | extern crate rustc_index; 13 | extern crate rustc_infer; 14 | extern crate rustc_lexer; 15 | extern crate rustc_middle; 16 | extern crate rustc_mir_dataflow; 17 | extern crate rustc_parse; 18 | extern crate rustc_span; 19 | extern crate rustc_target; 20 | extern crate rustc_trait_selection; 21 | 22 | use rustc_lint::LateLintPass; 23 | 24 | dylint_linting::declare_late_lint! { 25 | /// ### What it does 26 | /// 27 | /// ### Why is this bad? 28 | /// 29 | /// ### Known problems 30 | /// 31 | /// Remove if none. 32 | /// 33 | /// ### Example 34 | /// 35 | /// ```rust 36 | /// // example code where a warning is issued 37 | /// ``` 38 | /// 39 | /// Use instead: 40 | /// 41 | /// ```rust 42 | /// // example code that does not raise a warning 43 | /// ``` 44 | pub FILL_ME_IN, 45 | Warn, 46 | "description goes here" 47 | } 48 | 49 | impl<'tcx> LateLintPass<'tcx> for FillMeIn { 50 | // A list of things you might check can be found here: 51 | // https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html 52 | } 53 | 54 | #[test] 55 | fn ui() { 56 | dylint_testing::ui_test(env!("CARGO_PKG_NAME"), "ui"); 57 | } 58 | -------------------------------------------------------------------------------- /internal/template/ui/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /internal/template/ui/main.stderr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/dylint/f176ee4b8bf6c05ccd69b61742f18cac1bc7711d/internal/template/ui/main.stderr -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 100 2 | format_code_in_doc_comments = true 3 | style_edition = "2024" 4 | wrap_comments = true 5 | -------------------------------------------------------------------------------- /scheduled/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scheduled" 3 | description = "Scheduled tests" 4 | version = "4.1.0" 5 | edition = "2024" 6 | publish = false 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | anyhow = { workspace = true } 12 | assert_cmd = { workspace = true } 13 | predicates = { workspace = true } 14 | regex = { workspace = true } 15 | serde_json = { workspace = true } 16 | similar-asserts = { workspace = true } 17 | 18 | dylint_internal = { version = "=4.1.0", path = "../internal" } 19 | 20 | [lints] 21 | workspace = true 22 | -------------------------------------------------------------------------------- /scheduled/duplicate_dependencies/aarch64-apple-darwin.txt: -------------------------------------------------------------------------------- 1 | bitflags v1.3.2 2 | bitflags v2.9.1 3 | thiserror v1.0.69 4 | thiserror v2.0.12 5 | thiserror-impl v1.0.69 6 | thiserror-impl v2.0.12 7 | -------------------------------------------------------------------------------- /scheduled/duplicate_dependencies/x86_64-apple-darwin.txt: -------------------------------------------------------------------------------- 1 | bitflags v1.3.2 2 | bitflags v2.9.1 3 | thiserror v1.0.69 4 | thiserror v2.0.12 5 | thiserror-impl v1.0.69 6 | thiserror-impl v2.0.12 7 | -------------------------------------------------------------------------------- /scheduled/duplicate_dependencies/x86_64-pc-windows-msvc.txt: -------------------------------------------------------------------------------- 1 | bitflags v1.3.2 2 | bitflags v2.9.1 3 | thiserror v1.0.69 4 | thiserror v2.0.12 5 | thiserror-impl v1.0.69 6 | thiserror-impl v2.0.12 7 | windows-sys v0.48.0 8 | windows-sys v0.52.0 9 | windows-sys v0.59.0 10 | windows-targets v0.48.5 11 | windows-targets v0.52.6 12 | windows_x86_64_msvc v0.48.5 13 | windows_x86_64_msvc v0.52.6 14 | -------------------------------------------------------------------------------- /scheduled/duplicate_dependencies/x86_64-unknown-linux-gnu.txt: -------------------------------------------------------------------------------- 1 | bitflags v1.3.2 2 | bitflags v2.9.1 3 | thiserror v1.0.69 4 | thiserror v2.0.12 5 | thiserror-impl v1.0.69 6 | thiserror-impl v2.0.12 7 | -------------------------------------------------------------------------------- /scheduled/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/check_CHANGELOG.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 1 ]]; then 7 | echo "$0: expect one argument: \`github.ref\`" >&2 8 | exit 1 9 | fi 10 | 11 | REF="$1" 12 | 13 | if [[ ${REF::11} != 'refs/tags/v' ]]; then 14 | echo "$0: expect \`github.ref\` to start with \`refs/tags/v\`: $REF" >&2 15 | exit 1 16 | fi 17 | 18 | VERSION="${REF:11}" 19 | 20 | SCRIPTS="$(dirname "$(realpath "$0")")" 21 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 22 | 23 | cd "$WORKSPACE" 24 | 25 | grep "^## $VERSION$" CHANGELOG.md 26 | 27 | scripts/lint_CHANGELOG.sh 28 | -------------------------------------------------------------------------------- /scripts/check_examples.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # smoelius: This script is no longer used in CI. 4 | 5 | # set -x 6 | set -euo pipefail 7 | 8 | if [[ $# -ne 0 ]]; then 9 | echo "$0: expect no arguments" >&2 10 | exit 1 11 | fi 12 | 13 | SCRIPTS="$(dirname "$(realpath "$0")")" 14 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 15 | 16 | cd "$WORKSPACE"/examples 17 | 18 | for EXAMPLE in */*; do 19 | if [[ ! -d "$EXAMPLE" ]]; then 20 | continue 21 | fi 22 | 23 | pushd "$EXAMPLE" 24 | cargo check --all-targets 25 | popd 26 | done 27 | -------------------------------------------------------------------------------- /scripts/lint_CHANGELOG.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # shellcheck disable=SC2001 4 | 5 | # set -x 6 | set -euo pipefail 7 | 8 | if [[ $# -ne 0 ]]; then 9 | echo "$0: expect no arguments" >&2 10 | exit 1 11 | fi 12 | 13 | SCRIPTS="$(dirname "$(realpath "$0")")" 14 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 15 | 16 | cd "$WORKSPACE" 17 | 18 | grep -o '\[[0-9A-Fa-f]*\]([^)]*)' CHANGELOG.md | 19 | while read -r LINK; do 20 | TEXT="$(echo "$LINK" | sed 's/^\[\([^]]*\)\](.*)$/\1/')" 21 | URL="$(echo "$LINK" | sed 's/^\[[^]]*\](\(.*\))$/\1/')" 22 | if [[ 23 | ( ${#TEXT} -eq 7 || "$TEXT" = 'c28639ee' ) && 24 | $(expr "$URL" : "https://.*/commit/${TEXT}[0-9a-z]*") -eq ${#URL} 25 | ]]; then 26 | continue 27 | fi 28 | echo "bad link: $LINK" >&2 29 | exit 1 30 | done 31 | 32 | grep -o '\[#[0-9]*\]([^)]*)' CHANGELOG.md | 33 | while read -r LINK; do 34 | N="$(echo "$LINK" | sed 's/^\[#\([^]]*\)\](.*)$/\1/')" 35 | URL="$(echo "$LINK" | sed 's/^\[[^]]*\](\(.*\))$/\1/')" 36 | if [[ 37 | $(expr "$URL" : "https://.*/issues/$N") -eq ${#URL} || 38 | $(expr "$URL" : "https://.*/pull/$N") -eq ${#URL} 39 | ]]; then 40 | continue 41 | fi 42 | echo "bad link: $LINK" >&2 43 | exit 1 44 | done 45 | -------------------------------------------------------------------------------- /scripts/remove_patch_versions.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 0 ]]; then 7 | echo "$0: expect no arguments" >&2 8 | exit 1 9 | fi 10 | 11 | SCRIPTS="$(dirname "$(realpath "$0")")" 12 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 13 | 14 | cd "$WORKSPACE" 15 | 16 | find . -name Cargo.toml -exec sed -i '/^version\>/!s/"\([0-9]\+\.[0-9]\+\)\.[0-9]\+"/"\1"/' {} \; 17 | -------------------------------------------------------------------------------- /scripts/sync_with_workspace_README.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 0 ]]; then 7 | echo "$0: expect no arguments" >&2 8 | exit 1 9 | fi 10 | 11 | SCRIPTS="$(dirname "$(realpath "$0")")" 12 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 13 | 14 | cd "$WORKSPACE" 15 | 16 | sed 's,^\[[^]]*\]: \.,&.,' README.md > cargo-dylint/README.md 17 | 18 | cp cargo-dylint/README.md dylint/README.md 19 | -------------------------------------------------------------------------------- /scripts/toolchain_from_version.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -euo pipefail 4 | 5 | if [[ $# -ne 1 ]]; then 6 | echo "$0: expect one argument: Rust version" >&2 7 | exit 1 8 | fi 9 | 10 | VERSION="$1" 11 | 12 | TMP="$(mktemp -d)" 13 | 14 | git clone --branch rust-"$VERSION" 'https://github.com/rust-lang/rust-clippy' "$TMP" 2>/dev/null 15 | cd "$TMP" 16 | sed -n 's/^channel = "\([^"]*\)"$/\1/;T;p' rust-toolchain 17 | -------------------------------------------------------------------------------- /scripts/update_example_READMEs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 0 ]]; then 7 | echo "$0: expect no arguments" >&2 8 | exit 1 9 | fi 10 | 11 | SCRIPTS="$(dirname "$(realpath "$0")")" 12 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 13 | 14 | cd "$WORKSPACE"/examples 15 | 16 | TMP="$(mktemp)" 17 | 18 | CATEGORIES=(general supplementary restriction experimental testing) 19 | LISTED= 20 | 21 | IFS= 22 | cat README.md | 23 | while read -r X; do 24 | if [[ "$X" =~ ^\| ]]; then 25 | if [[ -z "$LISTED" ]]; then 26 | CATEGORY="${CATEGORIES[0]}" 27 | # shellcheck disable=SC2206 28 | CATEGORIES=(${CATEGORIES[@]:1}) 29 | echo '| Example | Description/check |' 30 | echo '| - | - |' 31 | # shellcheck disable=SC2016 32 | grep '^description = "[^"]*"$' "$CATEGORY"/*/Cargo.toml | 33 | sed 's,^\([^/]*/\([^/]*\)\)/Cargo.toml:description = "\([^"]*\)"$,| [`\2`](./\1) | \3 |,' | 34 | sed 's/| A lint to check for \([^|]*\) |$/| \u\1 |/' 35 | LISTED=1 36 | fi 37 | continue 38 | else 39 | LISTED= 40 | fi 41 | echo "$X" 42 | done | 43 | cat > "$TMP" 44 | 45 | mv "$TMP" README.md 46 | 47 | prettier --write README.md 48 | -------------------------------------------------------------------------------- /scripts/update_lockfiles.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 0 ]]; then 7 | echo "$0: expect no arguments" >&2 8 | exit 1 9 | fi 10 | 11 | SCRIPTS="$(dirname "$(realpath "$0")")" 12 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 13 | 14 | cd "$WORKSPACE" 15 | 16 | find . -name Cargo.toml | 17 | while read -r X; do 18 | cargo update --manifest-path "$X" 19 | done 20 | -------------------------------------------------------------------------------- /scripts/update_toml_rs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 2 ]]; then 7 | echo "$0: expect two arguments: from tag and to tag" >&2 8 | exit 1 9 | fi 10 | 11 | # FROM_TAG="$1" 12 | TO_TAG="$2" 13 | 14 | OUR_TOML_RS="$PWD"/"$(find . -name toml.rs)" 15 | THEIR_TOML_RS='./src/cargo/util/toml/mod.rs' 16 | 17 | TMP="$(mktemp -d)" 18 | 19 | git clone 'https://github.com/rust-lang/cargo' "$TMP" 20 | cd "$TMP" 21 | # git checkout "$FROM_TAG" 22 | # cp "$OUR_TOML_RS" "$THEIR_TOML_RS" 23 | # git stash 24 | git checkout "$TO_TAG" 25 | # git stash pop || true 26 | cp "$THEIR_TOML_RS" "$OUR_TOML_RS" 27 | -------------------------------------------------------------------------------- /scripts/update_versions.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # set -x 4 | set -euo pipefail 5 | 6 | if [[ $# -ne 1 ]]; then 7 | echo "$0: expect one argument: version" >&2 8 | exit 1 9 | fi 10 | 11 | VERSION="version = \"$1\"" 12 | 13 | SCRIPTS="$(dirname "$(realpath "$0")")" 14 | WORKSPACE="$(realpath "$SCRIPTS"/..)" 15 | 16 | cd "$WORKSPACE" 17 | 18 | if ! scripts/check_CHANGELOG.sh refs/tags/v"$1"; then 19 | echo "$0: Please update CHANGELOG.md." >&2 20 | exit 1 21 | fi 22 | 23 | find . -name Cargo.toml | 24 | grep -vw fixtures | 25 | grep -vw template | 26 | xargs -n 1 sed -i "{ 27 | s/^version = \"[^\"]*\"$/$VERSION/ 28 | }" 29 | 30 | REQ="${VERSION/\"/\"=}" 31 | 32 | find . -name Cargo.toml -exec sed -i "/^dylint/{ 33 | s/^\(.*\)\"] 5 | description = "Utilities for writing Dylint libraries" 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/trailofbits/dylint" 9 | 10 | [dependencies] 11 | cargo_metadata = "0.19" 12 | paste = "1.0" 13 | rustversion = "1.0" 14 | serde = "1.0" 15 | thiserror = "2.0" 16 | toml = "0.8" 17 | 18 | dylint_internal = { version = "=4.1.0", path = "../../internal", features = [ 19 | "config", 20 | ] } 21 | 22 | [build-dependencies] 23 | toml = "0.8" 24 | 25 | [dev-dependencies] 26 | assert_cmd = "2.0" 27 | rustc_version = "0.4" 28 | tempfile = "3.20" 29 | 30 | [features] 31 | constituent = [] 32 | 33 | [lints.rust.unexpected_cfgs] 34 | level = "deny" 35 | check-cfg = ["cfg(dylint_lib, values(any()))"] 36 | 37 | [package.metadata.docs.rs] 38 | rustc-args = ["--cfg", "docsrs"] 39 | 40 | [package.metadata.rust-analyzer] 41 | rustc_private = true 42 | 43 | [workspace] 44 | 45 | [workspace.metadata.dylint] 46 | libraries = [ 47 | { path = "../../examples/general" }, 48 | { path = "../../examples/supplementary" }, 49 | { path = "../../examples/testing/clippy" }, 50 | { path = "../../examples/restriction/*" }, 51 | ] 52 | -------------------------------------------------------------------------------- /utils/linting/build.rs: -------------------------------------------------------------------------------- 1 | const COMPONENTS: &[&str] = &["llvm-tools-preview", "rustc-dev"]; 2 | 3 | fn main() { 4 | check_components(); 5 | 6 | #[cfg(docsrs)] 7 | add_components(); 8 | } 9 | 10 | fn check_components() { 11 | use std::{fs::read_to_string, path::Path}; 12 | use toml::{Table, Value}; 13 | 14 | let rust_toolchain = Path::new("rust-toolchain"); 15 | 16 | if !rust_toolchain.try_exists().unwrap() { 17 | return; 18 | } 19 | 20 | let contents = read_to_string(rust_toolchain).unwrap(); 21 | let table = contents.parse::().unwrap(); 22 | let array = table 23 | .get("toolchain") 24 | .and_then(Value::as_table) 25 | .and_then(|table| table.get("components")) 26 | .and_then(Value::as_array) 27 | .unwrap(); 28 | let components = array 29 | .iter() 30 | .map(Value::as_str) 31 | .collect::>>() 32 | .unwrap(); 33 | 34 | assert_eq!(COMPONENTS, components); 35 | } 36 | 37 | #[cfg(docsrs)] 38 | fn add_components() { 39 | for component in COMPONENTS { 40 | assert!( 41 | std::process::Command::new("rustup") 42 | .args(["component", "add", component, "--toolchain", "nightly"]) 43 | .status() 44 | .unwrap() 45 | .success() 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /utils/linting/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["llvm-tools-preview", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /utils/linting/tests/build.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::{assert::AssertResult, prelude::*}; 2 | use dylint_internal::env; 3 | use rustc_version::{Channel, version_meta}; 4 | use std::sync::Mutex; 5 | use tempfile::TempDir; 6 | 7 | #[test] 8 | fn channel_is_nightly() { 9 | assert!(matches!(version_meta().unwrap().channel, Channel::Nightly)); 10 | } 11 | 12 | #[test] 13 | fn builds_with_cfg_docsrs() { 14 | update_nightly().unwrap(); 15 | 16 | let tempdir = TempDir::new().unwrap(); 17 | 18 | std::process::Command::new("cargo") 19 | .env(env::RUSTFLAGS, "--cfg docsrs") 20 | .args(["build", "--target-dir", &tempdir.path().to_string_lossy()]) 21 | .assert() 22 | .success(); 23 | } 24 | 25 | // smoelius: Avoid: https://github.com/rust-lang/rustup/issues/988 26 | static MUTEX: Mutex<()> = Mutex::new(()); 27 | 28 | #[allow(clippy::result_large_err)] 29 | fn update_nightly() -> AssertResult { 30 | let _lock = MUTEX.lock().unwrap(); 31 | 32 | std::process::Command::new("rustup") 33 | .args(["update", "nightly"]) 34 | .assert() 35 | .try_success() 36 | } 37 | -------------------------------------------------------------------------------- /utils/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dylint_testing" 3 | version = "4.1.0" 4 | authors = ["Samuel E. Moelius III "] 5 | description = "Utilities for testing Dylint libraries" 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/trailofbits/dylint" 9 | 10 | [dependencies] 11 | anyhow = { workspace = true } 12 | cargo_metadata = { workspace = true } 13 | compiletest_rs = { workspace = true } 14 | env_logger = { workspace = true } 15 | once_cell = { workspace = true } 16 | regex = { workspace = true } 17 | serde_json = { workspace = true } 18 | tempfile = { workspace = true } 19 | 20 | dylint = { version = "=4.1.0", path = "../../dylint" } 21 | dylint_internal = { version = "=4.1.0", path = "../../internal" } 22 | 23 | [features] 24 | default = [] 25 | deny_warnings = [] 26 | 27 | [lints] 28 | workspace = true 29 | --------------------------------------------------------------------------------