Clippy

repository·master·Indexed 11 days ago

https://github.com/rust-lang/rust-clippy

A collection of over 800 lints for Rust designed to catch common mistakes, improve idiomaticity, and optimize performance. It extends the Rust compiler's linting capabilities and is primarily used as a cargo subcommand via `cargo clippy`. Lints are organized into categories such as correctness, suspicious, style, complexity, and perf.

Tokens
52.3K
Snippets
192
Records
285
Agent score
95%

What's inside Clippy

  1. Overview of Clippy lints and categories

    master

    Clippy is a collection of over 800 lints designed to catch common mistakes and improve Rust code quality. Lints are organized into categories, each with a default lint level (allow, warn, deny). You can control the strictness of Clippy by adjusting these levels.

    Lint Categories

    CategoryDescriptionDefault level
    clippy::allAll lints on by default (correctness, suspicious, style, complexity, perf)warn/deny
    clippy::correctnessCode that is outright wrong or uselessdeny
    clippy::suspiciousCode that is most likely wrong or uselesswarn
    clippy::styleCode that should be written in a more idiomatic waywarn
    clippy::complexityCode that does something simple but in a complex waywarn
    clippy::perfCode that can be written to run fasterwarn
    clippy::pedanticStrict lints that may have occasional false positivesallow
    clippy::restrictionLints that prevent the use of specific language or library featuresallow
    clippy::nurseryNew lints still under developmentallow
    clippy::cargoLints for the Cargo manifestallow
  2. Understand the PatternTree data structure

    master

    The PatternTree is the core internal representation of a syntax tree pattern after it has been parsed and lowered. It is similar to a Rust AST or HIR but optimized for pattern matching:

    • No Spans: It does not contain parsing information like Spans.
    • Structural Abstractions: It uses specific types to represent logic:
      • Alt<T>: Represents alternatives (choices).
      • Seq<T>: Represents sequences and repetitions.
      • Opt<T>: Represents optional elements.

    Note that names used in the pattern! macro correspond to the variants of the PatternTree enums (e.g., Lit in a pattern refers to Expr::Lit).

  3. How to mark lint triggers in UI tests

    master

    When writing UI tests in .rs files, you can specify exactly where a lint should trigger using the //~^ comment syntax followed by the lint name. This tells the test runner that a diagnostic is expected at that specific location.

    Example syntax:

    //~^ clippy::lint_name
    // Example of marking a lint trigger
    fn foo() {}
    //~^ clippy::foo_functions
  4. Test Rustfix suggestions

    master

    If your lint uses structured suggestions (via span_lint_and_sugg), rustfix is used during testing to verify that the suggested fixes result in valid code. rustfix applies the suggestions to the test file and compares the result against a .fixed file.

    • Generate fixes: Use cargo bless to automatically generate the .fixed file.
    • Disable rustfix: If a lint provides a suggestion that is not intended to be a valid code fix, add //@no-rustfix to the top of the test file to prevent rustfix from running on it.
  5. Choose between EarlyLintPass and LateLintPass

    master

    When creating a new Clippy lint, you must decide which pass type to implement based on the required analysis depth:

    • EarlyLintPass: Runs before type checking and HIR lowering. Use this for AST-level analysis (e.g., checking function names or syntax patterns) where type information is not required. This is less complex to implement.
    • LateLintPass: Runs after type checking and HIR lowering. Use this when your lint requires access to type information.

    When using the cargo dev new_lint automation, you can specify --pass=early to default to an AST-level lint.

    # Example command to create an early lint
    cargo dev new_lint --pass=early
  6. Configure Clippy lint levels in code

    master

    You can control lint behavior directly within your Rust source code using attributes. This allows you to suppress, warn, or error on specific lints or groups of lints.

    Available Actions

    • allow: Suppress the lint.
    • warn: Emit a warning.
    • deny: Emit an error (causes Clippy to exit with an error code, useful for CI).

    Usage Patterns

    • Deny all Clippy lints: #![deny(clippy::all)]
    • Deny specific lints: #![deny(clippy::single_match, clippy::box_vec)]
    • Limit to a module or function: Use #[allow(...)] or #[deny(...)] (without the ! for item-level attributes).
    • Enable pedantic lints: #![deny(clippy::pedantic)]
    #![deny(clippy::all)]
    
    #[allow(clippy::too_many_arguments)]
    fn my_function(a: i32, b: i32, c: i32, d: i32, e: i32) {}
  7. Choosing between EarlyLintPass and LateLintPass

    master

    When developing a new Clippy lint, you must decide whether to implement EarlyLintPass or LateLintPass. This decision is based on whether your lint requires access to type and symbol information.

    EarlyLintPass

    Use EarlyLintPass if your lint only deals with syntax-related issues. It operates on the Abstract Syntax Tree (AST) level after expansion but before lowering to HIR.

    • Pros: Faster execution.
    • Limitations: No access to type information or symbol meanings; nodes are only identified by their position in the AST.
    • Use Case: Checking function names (e.g., detecting functions named foo) or other purely syntactic patterns.

    LateLintPass

    Use LateLintPass if your lint needs to inspect types or symbols. It operates at the HIR level and has access to type-checking information.

    • Pros: Access to LateContext methods like maybe_typeck_results and typeck_results to inspect types.
    • Use Case: Most Clippy lints fall into this category because they often need to verify if a method exists on a specific type or if a variable's type matches a certain pattern.
    • Limitations: Slower than EarlyLintPass.
  8. Future capabilities of syntax-tree-patterns

    master

    The proposal outlines several advanced features that could be implemented to enhance the pattern matching system:

    • Early Filtering: Evaluating conditions (e.g., where !in_macro(#then.span)) as soon as a specific part of the pattern is matched to improve performance.
    • Backreferences: Using syntax like =#target to ensure multiple parts of a pattern match the exact same value (e.g., for detecting a = a + b patterns).
    • Negation Operators: Using syntax like Lit(!Bool(_)) to match a node that is not a specific type.
    • Functional Composition: Allowing the definition of reusable sub-patterns using a function-like syntax to reduce repetition in complex lints.
    • Descendant Matching: Extending the syntax to match subtrees that are not direct descendants of the current node.
    • Language Agnosticism: The system is designed such that by implementing a new PatternTree and IsMatch trait, it could be used to lint other programming languages or even the pattern syntax itself.
  9. How syntax-tree-patterns work

    master

    Syntax tree patterns are inspired by regular expressions and use sequences, repetitions, and alternatives to match against a PatternTree.

    Core Mechanics:

    • PatternTree: A structure representing the hierarchy of the syntax tree (similar to AST/HIR).
    • IsMatch Trait: The mechanism that connects the PatternTree to the actual syntax tree implementation.
    • Matching Behavior: When a pattern can match a node in multiple ways (e.g., due to ambiguity in repetitions), the current implementation returns the first match it finds (it does not currently support greedy/non-greedy selection).

    Comparison to Rust-like syntax: Unlike a quote! macro approach which uses actual Rust syntax, syntax-tree-patterns require knowledge of the PatternTree structure. This avoids the complexity of operator precedence and the ambiguity of naming submatches in a language with implicit structure.

  10. Extend Clippy coverage to 3rd party libraries using attributes

    master
    Crate authors can use specific Clippy attributes to extend linting coverage to their libraries. These attributes allow Clippy to understand the semantics of custom macros or types that would otherwise be opaque to the linter, ensuring that users of the library receive appropriate lint warnings.
  11. Handle macro expansions in lints

    master

    Clippy works on code that has already been expanded and desugared. This can lead to false positives in macro-generated code. To avoid linting code the user cannot modify, use these tools:

    • span.from_expansion(): Returns true if the span is from a macro expansion or desugaring. Use this to skip macro-generated code immediately.
    • span.ctxt(): Returns the SpanContext. You can use this to check if two spans belong to the same macro expansion context. If left.span.ctxt() != right.span.ctxt(), the expressions likely originate from different parts of a macro expansion.
    • span.in_external_macro(sm): Returns true if the span is inside a macro defined in a foreign crate. Use this to avoid linting code generated by external dependencies.
    // Detect if a span is from macro expansion and skip
    if expr.span.from_expansion() {
        return;
    }
    
    // Check if two spans are in the same macro context
    if left.span.ctxt() != right.span.ctxt() {
        // The coder most likely cannot modify this expression
        return;
    }
    
    // Detect if a span is from a macro in a foreign crate
    if match_span.in_external_macro(cx.sess().source_map()) {
        // Skip lints for macros from other crates
    }
  12. Use repetitions and optionality in patterns

    master

    You can use regex-style quantifiers to match repeated elements or optional nodes within a syntax tree.

    • Optionality: Use _? to match an optional element (like the else block in an if expression) or () to match an empty/None variant.
    • Quantifiers:
      • *: Zero or more
      • +: One or more
      • ?: Zero or one
      • {n}: Exactly n times
      • {n,m}: Between n and m times
      • {n,}: n or more times

    Example: Matching an array that contains exactly two 'x' characters as its last or second-to-last elements:

    pattern!{
        my_pattern: Expr = 
            Array( _* Lit(Char('x')){2} _? )
    }
    pattern!{
        // matches arrays that contain 2 'x's as their last or second-last elements
        my_pattern: Expr = 
            Array( _* Lit(Char('x')){2} _? )
    }