-
Notifications
You must be signed in to change notification settings - Fork 171
Add infrastructure for linting and creating new Lints #1140
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
Merged
Merged
Changes from all commits
Commits
Show all changes
101 commits
Select commit
Hold shift + click to select a range
181b5e2
linter barebones
orpuente-MS 737068c
basic demo
orpuente-MS 477e38c
colored output
orpuente-MS 5480cd3
added division by zero lint
orpuente-MS 9ae026a
make LINT_BUFFER static and add lint declaration macros
orpuente-MS 484eb80
add linter public api
orpuente-MS 04eef46
add docs for the usage of the newtype idiom & remove unnecessary life…
orpuente-MS 632b4ea
fix typo in docstring
orpuente-MS f793e90
reduce visibility
orpuente-MS fe8830b
combined lint pass for performance
orpuente-MS bd0365a
moved some things around
orpuente-MS 189e20f
fix typos
orpuente-MS 20ba97c
refactored tests
orpuente-MS 0d7379c
improved docs
orpuente-MS fe28721
reduce visibility of drain()
orpuente-MS 3f87d79
moved things arond
orpuente-MS 1cad2f3
remove linter ui demo
orpuente-MS e9c7296
removed mut static buffer
orpuente-MS 9063cc2
Merge branch 'main' of https://github.com/orpuente-MS/qsharp
orpuente-MS 5dd58b6
integrated linter into language_server
orpuente-MS 022c392
changed lint levels names
orpuente-MS 3b449da
disable division by zero lint
orpuente-MS 15f2483
filter out LintLevel::Allow lints
orpuente-MS d7fa0ba
Merge branch 'microsoft:main' into main
orpuente-MS e9cdcf6
Update compiler/qsc_linter/Cargo.toml
orpuente-MS b9c7db9
Add docstrings to LintPass traits
orpuente-MS 7890971
Removed allow atributes across the crate
orpuente-MS a11d842
Removed empty hir lints file. Can be added again later.
orpuente-MS 99f6d40
Merge branch 'main' of https://github.com/orpuente-MS/qsharp
orpuente-MS fd2eacc
removed #warn
orpuente-MS 8825bdf
add #derive[(Default)] to CombinedLints
orpuente-MS ab613f0
Added better explanation for "speed" in the docstring for the Combine…
orpuente-MS 2744fbe
added back warn and allow
orpuente-MS eed48db
Merge branch 'microsoft:main' into main
orpuente-MS af0958c
add default lang_features
orpuente-MS 989a508
clean Cargo.toml
orpuente-MS 781dd43
add unit tests
orpuente-MS 852fb82
reverse changes in qsc_frontend
orpuente-MS 7465ea9
remove unnecesary wrapper
orpuente-MS d2e6af2
delay computation of lints until necesary
orpuente-MS 02734aa
remove unnecesary trait impl
orpuente-MS c4a47d9
prefix underscore unused fn params in default empty impl
orpuente-MS a941168
prefix underscore unused fn params in default empty impl
orpuente-MS ef3eee0
remove fir dependency
orpuente-MS 0ce6413
added division by zero unit test
orpuente-MS 33d0e07
small stylistic change
orpuente-MS c7feaeb
remove unnecesary nesting
orpuente-MS 24457b3
fixed typo
orpuente-MS 74744a8
added MIT License to new src files
orpuente-MS 7c8e90d
Merge branch 'microsoft:main' into main
orpuente-MS 75a8d0b
fixed docstring
orpuente-MS 08761dc
changed push_lint macro to allow more control over the pushed span
orpuente-MS fde9b69
fixed typo in unit test
orpuente-MS 5d09dca
Added redundant semicolons lint
orpuente-MS 5c5d36b
added unit test for redundant semicolons
orpuente-MS 1452400
add needless parens lint
orpuente-MS ea6d61e
add needless parens lint
orpuente-MS 2095eff
fixed fmt
orpuente-MS 76fb38e
fixed needless parens lint
orpuente-MS 24918f2
less boilerplate
orpuente-MS 17ffa4d
fixed crate docstring
orpuente-MS 1e153d9
revert unnecesary name changes
orpuente-MS 9b2e109
f1e4ed1
Added infrastructure for user-personalized lint levels
orpuente-MS 4273e0d
updated docstring to reflect API change
orpuente-MS 8048624
Merge branch 'microsoft:main' into main
orpuente-MS 6eb74ac
loading user-preferences from qsharp.json
orpuente-MS 68bc4fb
Merge branch 'main' of https://github.com/orpuente-MS/qsharp
orpuente-MS 3ecab31
reverted sample change
orpuente-MS bd86459
integrated lints_config into the qsharp.json file
orpuente-MS 2fd7ad3
changed level name "warning" to "warn"
orpuente-MS 36fe71a
Update compiler/qsc_linter/Cargo.toml
orpuente-MS eaa8efe
removed project clippy config since now it is set to in the workspace.
orpuente-MS 9d24bad
removed the linter dependency in qsc_frontend
orpuente-MS e35e893
updated crate docstring
orpuente-MS e10764f
changed hir placeholder to LintLevel::Allow
orpuente-MS 3de391d
added variant comment
orpuente-MS 66f681b
added help messages to the lints
orpuente-MS 81adad7
check for empty help
orpuente-MS 2d41af2
.
orpuente-MS 14bf1bd
4a239db
Fixed precedence and set NeedlessParens to Allow
orpuente-MS 24cf6a0
Merge branch 'microsoft:main' into main
orpuente-MS ae38f47
fixed typo in docstring
orpuente-MS de92e60
updated unit tests
orpuente-MS 40156cc
Update samples/language/MultiFileProject/src/Main.qs
orpuente-MS 927a22b
added unit-test to check that lints-config is being loaded
orpuente-MS ce603af
Merge branch 'main' of https://github.com/orpuente-MS/qsharp
orpuente-MS bc71993
simplified push_lint macro to lint
orpuente-MS 6997f6d
Update wasm/src/project_system.rs
orpuente-MS a1776fb
Merge branch 'main' of https://github.com/orpuente-MS/qsharp
orpuente-MS 1641373
moved Lint related structs back to linter crate (from qsc_data_structs)
orpuente-MS f833a4d
fixed unit test
orpuente-MS e7ba5f7
updated json schema
orpuente-MS d208971
Merge remote-tracking branch 'upstream/main'
orpuente-MS a7e6119
add lints to qsharp.schema.json
orpuente-MS 6e1f6d1
remove unused dependecy to thiserror
orpuente-MS daf9614
remove serde Serialize from lints, we only need Desrialize
orpuente-MS 1ef0c7a
348553f
d69ac28
fix test case in language_service
orpuente-MS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| [package] | ||
| name = "qsc_linter" | ||
|
|
||
| version.workspace = true | ||
| authors.workspace = true | ||
| edition.workspace = true | ||
| homepage.workspace = true | ||
| license.workspace = true | ||
| repository.workspace = true | ||
|
|
||
| [dependencies] | ||
| miette = { workspace = true } | ||
| qsc_ast = { path = "../qsc_ast" } | ||
| qsc_hir = { path = "../qsc_hir" } | ||
| qsc_data_structures = { path = "../qsc_data_structures" } | ||
| qsc_frontend = { path = "../qsc_frontend" } | ||
| serde = { workspace = true } | ||
| thiserror = { workspace = true } | ||
|
|
||
| [dev-dependencies] | ||
| expect-test = { workspace = true } | ||
| qsc_parse = { path = "../qsc_parse" } | ||
| serde_json = { workspace = true } | ||
| qsc = { path = "../qsc" } | ||
| qsc_passes = { path = "../qsc_passes" } | ||
|
|
||
| [lints] | ||
| workspace = true | ||
|
|
||
| [lib] | ||
| doctest = false | ||
|
orpuente-MS marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| //! This crate contains the linter for the Q# language. | ||
| //! | ||
| //! It includes lints for the following stages of the compilation process: | ||
| //! - AST | ||
| //! - HIR | ||
| //! | ||
| //! # Usage | ||
| //! | ||
| //! The entry points to the linter is the `run_lints` function, which takes | ||
| //! a [`qsc_frontend::compile::CompileUnit`] as input and outputs a [`Vec<Lint>`](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 you set the lint name, the default [`LintLevel`], and the message the user will see. | ||
| //! 2. Implementing the lint: here you write the pattern matching logic of the new lint. | ||
| //! | ||
| //! Below is a full example of how to a new AST lint. | ||
| //! | ||
| //! ## Example | ||
| //! | ||
| //! First, we add our lint to `src/lints/ast.rs`. | ||
| //! ``` | ||
| //! declare_ast_lints!{ | ||
| //! ... | ||
| //! (DoubleParens, LintLevel::Warn, "unnecesary double parentheses"), | ||
| //! } | ||
| //! ``` | ||
| //! | ||
| //! 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(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); | ||
| //! } | ||
| //! } | ||
| //! } | ||
| //! } | ||
| //! ``` | ||
|
|
||
| #![deny(missing_docs)] | ||
|
|
||
| mod linter; | ||
| mod lints; | ||
| #[cfg(test)] | ||
| mod tests; | ||
|
|
||
| pub use linter::{run_lints, Lint, LintConfig, LintKind, LintLevel}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| pub(crate) mod ast; | ||
| pub(crate) mod hir; | ||
|
|
||
| use self::{ast::run_ast_lints, hir::run_hir_lints}; | ||
| use crate::lints::{ast::AstLint, hir::HirLint}; | ||
| use miette::{Diagnostic, LabeledSpan}; | ||
| use qsc_data_structures::span::Span; | ||
| use qsc_frontend::compile::CompileUnit; | ||
| use serde::Deserialize; | ||
| use std::fmt::Display; | ||
|
|
||
| /// The entry point to the linter. It takes a [`qsc_frontend::compile::CompileUnit`] | ||
| /// as input and outputs a [`Vec<Lint>`](Lint). | ||
| #[must_use] | ||
| pub fn run_lints(compile_unit: &CompileUnit, config: Option<&[LintConfig]>) -> Vec<Lint> { | ||
| let mut ast_lints = run_ast_lints(&compile_unit.ast.package, config); | ||
| let mut hir_lints = run_hir_lints(&compile_unit.package, config); | ||
|
|
||
| let mut lints = Vec::new(); | ||
| lints.append(&mut ast_lints); | ||
| lints.append(&mut hir_lints); | ||
| lints | ||
| .into_iter() | ||
| .filter(|lint| !matches!(lint.level, LintLevel::Allow)) | ||
| .collect() | ||
| } | ||
|
|
||
| /// A lint emited by the linter. | ||
| #[derive(Debug, Clone, thiserror::Error)] | ||
| pub struct Lint { | ||
| /// A span indicating where the diagnostic is in the source code. | ||
| pub span: Span, | ||
| /// The lint level: allow, warning, error. | ||
| pub level: LintLevel, | ||
| /// The message the user will see in the code editor. | ||
| pub message: &'static str, | ||
| /// The help text the user will see in the code editor. | ||
| pub help: &'static str, | ||
| } | ||
|
|
||
| impl std::fmt::Display for Lint { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "{}", self.message) | ||
| } | ||
| } | ||
|
|
||
| impl Diagnostic for Lint { | ||
| fn severity(&self) -> Option<miette::Severity> { | ||
| match self.level { | ||
| LintLevel::Allow => None, | ||
| LintLevel::Warn | LintLevel::ForceWarn => Some(miette::Severity::Warning), | ||
| LintLevel::Error | LintLevel::ForceError => Some(miette::Severity::Error), | ||
| } | ||
| } | ||
|
|
||
| fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> { | ||
| let source_span = miette::SourceSpan::from(self.span); | ||
| let labeled_span = LabeledSpan::new_with_span(Some(self.to_string()), source_span); | ||
| Some(Box::new(vec![labeled_span].into_iter())) | ||
| } | ||
|
|
||
| fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { | ||
| if self.help.is_empty() { | ||
| None | ||
| } else { | ||
| Some(Box::new(self.help)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A lint level. This defines if a lint will be treated as a warning or an error, | ||
| /// and if the lint level can be overriden by the user. | ||
| #[derive(Debug, Clone, Copy, Deserialize)] | ||
| #[serde(rename_all = "camelCase")] | ||
| pub enum LintLevel { | ||
|
ScottCarda-MS marked this conversation as resolved.
|
||
| /// The lint is effectively disabled. | ||
| Allow, | ||
| /// The lint will be treated as a warning. | ||
| Warn, | ||
| /// The lint will be treated as a warning and cannot be overriden by the user. | ||
| ForceWarn, | ||
| /// The lint will be treated as an error. | ||
| Error, | ||
| /// The lint will be treated as an error and cannot be overriden by the user. | ||
| ForceError, | ||
| } | ||
|
|
||
| impl Display for LintLevel { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| let level = match self { | ||
| LintLevel::Allow => "", | ||
| LintLevel::Warn | LintLevel::ForceWarn => "warning", | ||
| LintLevel::Error | LintLevel::ForceError => "error", | ||
| }; | ||
|
|
||
| write!(f, "{level}") | ||
| } | ||
| } | ||
|
|
||
| /// End-user configuration for each lint level. | ||
| #[derive(Debug, Clone, Deserialize)] | ||
| pub struct LintConfig { | ||
| #[serde(rename = "lint")] | ||
| /// Represents the lint name. | ||
| pub kind: LintKind, | ||
| /// The lint level. | ||
| pub level: LintLevel, | ||
| } | ||
|
|
||
| /// Represents a lint name. | ||
| #[derive(Debug, Clone, Copy, Deserialize)] | ||
| #[serde(untagged)] | ||
| pub enum LintKind { | ||
| /// AST lint name. | ||
| Ast(AstLint), | ||
| /// HIR lint name. | ||
| Hir(HirLint), | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.