Skip to content

Add infrastructure for linting and creating new Lints#1140

Merged
orpuente-MS merged 101 commits into
microsoft:mainfrom
orpuente-MS:main
Mar 19, 2024
Merged

Add infrastructure for linting and creating new Lints#1140
orpuente-MS merged 101 commits into
microsoft:mainfrom
orpuente-MS:main

Conversation

@orpuente-MS
Copy link
Copy Markdown
Contributor

@orpuente-MS orpuente-MS commented Feb 9, 2024

This PR adds infrastructure for linting and creating new Lints. It also adds integration of the linter to the language service.

End user experience

The users can set the lint level from the qsharp.json file in a project as follows,

{
  "lints": [
    {
      "lint": "needlessParens",
      "level": "allow",
    },
    {
      "lint": "redundantSemicolons",
      "level": "warn",
    },
    {
      "lint": "divisionByZero",
      "level": "error",
    }
  ]
}

Contributor experience

The entry points to the linter is the run_lints function, which takes a qsc_frontend::CompileUnit as input and outputs a Vec<Lint>.

Example

use linter::run_lints;;
use qsc::compile::compile;

let unit: CompileUnit = compile(...);

// The second argument is an optional user configuration.
let lints: Vec<Lint> = run_ast_lints(&package, None);

How to add a new Lint

We can add a new lint in two steps:

  1. Declaring the lint: here we set the lint name, the default LintLevel, the message, and the help text the user will see.
  2. Implementing the lint: here we write the pattern matching logic of the new lint.

Below is a full example of how to add a new AST lint.

Example

First, we add our lint to src/lints/ast.rs.

declare_ast_lints! {
  ...
  (DoubleParens, LintLevel::Warn, "unnecesary double parentheses", "remove extra parentheses for clarity"),
}

Then we implement the right LintPass for our new lint, in this case linter::ast::AstLintPass

impl linter::ast::AstLintPass for DoubleParens {
    // we only need to impl the relevant check_* method, all the other ones
    // will default to an empty method that will get optmized by rust
    fn check_expr(&self, expr: &qsc_ast::ast::Expr, buffer: &mut Vec<Lint>) {
        // we match the relevant pattern
        if let ExprKind::Paren(ref inner_expr) = *expr.kind {
            if matches!(*inner_expr.kind, ExprKind::Paren(_)) {
                // we push the lint to the buffer
                push_lint!(self, expr.span, buffer);
            }
        }
    }
}

@orpuente-MS orpuente-MS marked this pull request as ready for review February 21, 2024 20:45
Comment thread compiler/qsc_linter/src/tests.rs Outdated
Comment thread compiler/qsc_linter/src/lints/hir.rs
Comment thread samples/language/MultiFileProject/src/Main.qs Outdated
Comment thread wasm/src/project_system.rs
Comment thread language_service/src/compilation.rs
Comment thread compiler/qsc_project/tests/tests.rs
Co-authored-by: Mine Starks <16928427+minestarks@users.noreply.github.com>
Comment thread wasm/src/project_system.rs Outdated
Comment thread compiler/qsc_linter/src/lints.rs Outdated
Comment thread language_service/src/state/tests.rs Outdated
Comment thread language_service/src/state/tests.rs
@orpuente-MS orpuente-MS added this pull request to the merge queue Mar 19, 2024
Merged via the queue into microsoft:main with commit c172d60 Mar 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants