Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add infrastructure for linting and creating new Lints #1140

Merged
merged 101 commits into from
Mar 19, 2024

Conversation

orpuente-MS
Copy link
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
@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
16 checks passed
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