diff --git a/Cargo.lock b/Cargo.lock index 8f495d0171..b2194dd06a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1379,6 +1379,7 @@ dependencies = [ "qsc_hir", "qsc_parse", "qsc_passes", + "rustc-hash", "serde", "serde_json", "thiserror", diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs index 2c736ee701..39270781b4 100644 --- a/compiler/qsc/src/interpret.rs +++ b/compiler/qsc/src/interpret.rs @@ -404,10 +404,12 @@ impl Interpreter { compile_unit, // see https://github.com/microsoft/qsharp/pull/1627 for context // on why we override this config - Some(&[qsc_linter::LintConfig { - kind: LintKind::Hir(HirLint::NeedlessOperation), - level: LintLevel::Warn, - }]), + Some(&[qsc_linter::LintOrGroupConfig::Lint( + qsc_linter::LintConfig { + kind: LintKind::Hir(HirLint::NeedlessOperation), + level: LintLevel::Warn, + }, + )]), ) } else { Vec::new() diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 5847ab4449..1d05e3e7ab 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -58,7 +58,9 @@ pub use qsc_eval::{ }; pub mod linter { - pub use qsc_linter::{run_lints, LintConfig, LintKind, LintLevel}; + pub use qsc_linter::{ + run_lints, GroupConfig, LintConfig, LintKind, LintLevel, LintOrGroupConfig, + }; } pub use qsc_doc_gen::{display, generate_docs}; diff --git a/compiler/qsc_linter/Cargo.toml b/compiler/qsc_linter/Cargo.toml index 833936dbaa..f7d7933fc0 100644 --- a/compiler/qsc_linter/Cargo.toml +++ b/compiler/qsc_linter/Cargo.toml @@ -15,6 +15,7 @@ qsc_hir = { path = "../qsc_hir" } qsc_data_structures = { path = "../qsc_data_structures" } qsc_frontend = { path = "../qsc_frontend" } qsc_doc_gen = { path = "../qsc_doc_gen" } +rustc-hash = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } diff --git a/compiler/qsc_linter/src/lib.rs b/compiler/qsc_linter/src/lib.rs index efe0d90862..856e6e7604 100644 --- a/compiler/qsc_linter/src/lib.rs +++ b/compiler/qsc_linter/src/lib.rs @@ -61,10 +61,12 @@ #![deny(missing_docs)] +mod lint_groups; mod linter; mod lints; #[cfg(test)] mod tests; -pub use linter::{run_lints, Lint, LintConfig, LintKind, LintLevel}; +pub use lint_groups::GroupConfig; +pub use linter::{run_lints, Lint, LintConfig, LintKind, LintLevel, LintOrGroupConfig}; pub use lints::{ast::AstLint, hir::HirLint}; diff --git a/compiler/qsc_linter/src/lint_groups.rs b/compiler/qsc_linter/src/lint_groups.rs new file mode 100644 index 0000000000..1010e07ab2 --- /dev/null +++ b/compiler/qsc_linter/src/lint_groups.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; + +use crate::{LintKind, LintLevel}; + +/// End-user configuration for a lint group. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +pub struct GroupConfig { + #[serde(rename = "group")] + /// The lint group. + pub lint_group: LintGroup, + /// The group level. + pub level: LintLevel, +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum LintGroup { + Pedantic, +} + +impl LintGroup { + pub fn unfold(self) -> Vec { + use crate::AstLint::*; + use crate::HirLint::*; + match self { + LintGroup::Pedantic => { + vec![ + LintKind::Ast(DivisionByZero), + LintKind::Ast(NeedlessParens), + LintKind::Ast(RedundantSemicolons), + LintKind::Ast(DeprecatedNewtype), + LintKind::Ast(DeprecatedSet), + LintKind::Ast(DiscourageChainAssignment), + LintKind::Hir(NeedlessOperation), + LintKind::Hir(DeprecatedFunctionConstructor), + LintKind::Hir(DeprecatedWithOperator), + LintKind::Hir(DeprecatedDoubleColonOperator), + ] + } + } + } +} diff --git a/compiler/qsc_linter/src/linter.rs b/compiler/qsc_linter/src/linter.rs index 1be9eefc38..a1f833b1d4 100644 --- a/compiler/qsc_linter/src/linter.rs +++ b/compiler/qsc_linter/src/linter.rs @@ -5,11 +5,15 @@ 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 crate::{ + lints::{ast::AstLint, hir::HirLint}, + GroupConfig, +}; use miette::{Diagnostic, LabeledSpan}; use qsc_data_structures::span::Span; use qsc_frontend::compile::{CompileUnit, PackageStore}; use qsc_hir::hir::{Item, ItemId}; +use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::fmt::Display; @@ -19,15 +23,25 @@ use std::fmt::Display; pub fn run_lints( package_store: &PackageStore, compile_unit: &CompileUnit, - config: Option<&[LintConfig]>, + config: Option<&[LintOrGroupConfig]>, ) -> Vec { let compilation = Compilation { package_store, compile_unit, }; - let mut ast_lints = run_ast_lints(&compile_unit.ast.package, config, compilation); - let mut hir_lints = run_hir_lints(&compile_unit.package, config, compilation); + let unfolded_config = config.map(unfold_groups); + + let mut ast_lints = run_ast_lints( + &compile_unit.ast.package, + unfolded_config.as_deref(), + compilation, + ); + let mut hir_lints = run_hir_lints( + &compile_unit.package, + unfolded_config.as_deref(), + compilation, + ); let mut lints = Vec::new(); lints.append(&mut ast_lints); @@ -35,6 +49,35 @@ pub fn run_lints( lints } +/// Unfolds groups into lists of lints. Specific lints override group configs. +pub(crate) fn unfold_groups(config: &[LintOrGroupConfig]) -> Vec { + let mut config_map: FxHashMap = FxHashMap::default(); + + // Unfold groups in the order they appear. + for elt in config { + if let LintOrGroupConfig::Group(group) = elt { + for lint in group.lint_group.unfold() { + config_map.insert(lint, group.level); + } + } + } + + // Specific lints override group configs. + for elt in config { + if let LintOrGroupConfig::Lint(lint) = elt { + config_map.insert(lint.kind, lint.level); + } + } + + config_map + .iter() + .map(|(kind, level)| LintConfig { + kind: *kind, + level: *level, + }) + .collect() +} + #[derive(Clone, Copy)] pub(crate) struct Compilation<'a> { pub package_store: &'a PackageStore, @@ -177,18 +220,28 @@ impl Display for LintLevel { } } -/// End-user configuration for each lint level. +/// End-user configuration for a specific lint or a lint group. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(untagged)] +pub enum LintOrGroupConfig { + /// An specific lint configuration. + Lint(LintConfig), + /// A lint group configuration. + Group(GroupConfig), +} + +/// End-user configuration for a specific lint. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] pub struct LintConfig { #[serde(rename = "lint")] - /// Represents the lint name. + /// The lint name. pub kind: LintKind, /// The lint level. pub level: LintLevel, } /// Represents a lint name. -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)] #[serde(untagged)] pub enum LintKind { /// AST lint name. diff --git a/compiler/qsc_linter/src/linter/ast.rs b/compiler/qsc_linter/src/linter/ast.rs index b9a201ba33..7bca591d16 100644 --- a/compiler/qsc_linter/src/linter/ast.rs +++ b/compiler/qsc_linter/src/linter/ast.rs @@ -3,7 +3,7 @@ use crate::{ lints::ast::{AstLint, CombinedAstLints}, - Lint, LintConfig, LintLevel, + Lint, LintLevel, }; use qsc_ast::{ ast::{ @@ -24,9 +24,9 @@ pub fn run_ast_lints( let config: Vec<(AstLint, LintLevel)> = config .unwrap_or(&[]) .iter() - .filter_map(|lint_config| { - if let LintKind::Ast(kind) = lint_config.kind { - Some((kind, lint_config.level)) + .filter_map(|lint| { + if let LintKind::Ast(kind) = lint.kind { + Some((kind, lint.level)) } else { None } @@ -185,7 +185,7 @@ macro_rules! declare_ast_lints { use serde::{Deserialize, Serialize}; /// An enum listing all existing AST lints. - #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] + #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)] #[serde(rename_all = "camelCase")] pub enum AstLint { $( @@ -342,4 +342,4 @@ macro_rules! declare_ast_lints { pub(crate) use declare_ast_lints; -use super::{Compilation, LintKind}; +use super::{Compilation, LintConfig, LintKind}; diff --git a/compiler/qsc_linter/src/linter/hir.rs b/compiler/qsc_linter/src/linter/hir.rs index 4c5e76edbd..dfa1c32371 100644 --- a/compiler/qsc_linter/src/linter/hir.rs +++ b/compiler/qsc_linter/src/linter/hir.rs @@ -3,7 +3,7 @@ use crate::{ lints::hir::{CombinedHirLints, HirLint}, - Lint, LintConfig, LintLevel, + Lint, LintLevel, }; use qsc_hir::{ hir::{Block, CallableDecl, Expr, Ident, Item, Package, Pat, QubitInit, SpecDecl, Stmt}, @@ -21,9 +21,9 @@ pub fn run_hir_lints( let config: Vec<(HirLint, LintLevel)> = config .unwrap_or(&[]) .iter() - .filter_map(|lint_config| { - if let LintKind::Hir(kind) = lint_config.kind { - Some((kind, lint_config.level)) + .filter_map(|lint| { + if let LintKind::Hir(kind) = lint.kind { + Some((kind, lint.level)) } else { None } @@ -149,7 +149,7 @@ macro_rules! declare_hir_lints { use serde::{Deserialize, Serialize}; /// An enum listing all existing HIR lints. - #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] + #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)] #[serde(rename_all = "camelCase")] pub enum HirLint { $( @@ -264,4 +264,4 @@ macro_rules! declare_hir_lints { pub(crate) use declare_hir_lints; -use super::{Compilation, LintKind}; +use super::{Compilation, LintConfig, LintKind}; diff --git a/compiler/qsc_linter/src/tests.rs b/compiler/qsc_linter/src/tests.rs index b9a014bf34..2af5af10da 100644 --- a/compiler/qsc_linter/src/tests.rs +++ b/compiler/qsc_linter/src/tests.rs @@ -2,8 +2,9 @@ // Licensed under the MIT License. use crate::{ - linter::{ast::run_ast_lints, hir::run_hir_lints, Compilation}, - Lint, LintConfig, LintLevel, + lint_groups::LintGroup, + linter::{ast::run_ast_lints, hir::run_hir_lints, unfold_groups, Compilation}, + Lint, LintLevel, LintOrGroupConfig, }; use expect_test::{expect, Expect}; use indoc::indoc; @@ -101,6 +102,72 @@ fn set_keyword_lint() { ); } +#[test] +fn lint_group() { + check_with_config( + &wrap_in_callable("let x = ((1 + 2)) / 0;;;;", CallableKind::Operation), + Some(&[LintOrGroupConfig::Group(crate::GroupConfig { + lint_group: LintGroup::Pedantic, + level: LintLevel::Error, + })]), + &expect![[r#" + [ + SrcLint { + source: ";;;", + level: Error, + message: "redundant semicolons", + help: "remove the redundant semicolons", + code_action_edits: [ + ( + "", + Span { + lo: 94, + hi: 97, + }, + ), + ], + }, + SrcLint { + source: "((1 + 2)) / 0", + level: Error, + message: "attempt to divide by zero", + help: "division by zero will fail at runtime", + code_action_edits: [], + }, + SrcLint { + source: "((1 + 2))", + level: Error, + message: "unnecessary parentheses", + help: "remove the extra parentheses for clarity", + code_action_edits: [ + ( + "", + Span { + lo: 80, + hi: 81, + }, + ), + ( + "", + Span { + lo: 88, + hi: 89, + }, + ), + ], + }, + SrcLint { + source: "RunProgram", + level: Error, + message: "operation does not contain any quantum operations", + help: "this callable can be declared as a function instead", + code_action_edits: [], + }, + ] + "#]], + ); +} + #[test] fn multiple_lints() { check( @@ -685,6 +752,10 @@ fn needless_operation_inside_function_call() { } fn check(source: &str, expected: &Expect) { + check_with_config(source, None, expected); +} + +fn check_with_config(source: &str, config: Option<&[LintOrGroupConfig]>, expected: &Expect) { let source = wrap_in_namespace(source); let mut store = PackageStore::new(compile::core()); let std = store.insert(compile::std(&store, TargetCapabilityFlags::all())); @@ -701,7 +772,7 @@ fn check(source: &str, expected: &Expect) { let id = store.insert(unit); let unit = store.get(id).expect("user package should exist"); - let actual: Vec = run_lints(&store, unit, None) + let actual: Vec = run_lints(&store, unit, config) .into_iter() .map(|lint| SrcLint::from(&lint, &source)) .collect(); @@ -757,15 +828,17 @@ impl SrcLint { fn run_lints( package_store: &PackageStore, compile_unit: &CompileUnit, - config: Option<&[LintConfig]>, + config: Option<&[LintOrGroupConfig]>, ) -> Vec { let compilation = Compilation { package_store, compile_unit, }; - let mut ast_lints = run_ast_lints(&compile_unit.ast.package, config, compilation); - let mut hir_lints = run_hir_lints(&compile_unit.package, config, compilation); + let config = config.map(unfold_groups); + + let mut ast_lints = run_ast_lints(&compile_unit.ast.package, config.as_deref(), compilation); + let mut hir_lints = run_hir_lints(&compile_unit.package, config.as_deref(), compilation); let mut lints = Vec::new(); lints.append(&mut ast_lints); lints.append(&mut hir_lints); diff --git a/compiler/qsc_project/src/manifest.rs b/compiler/qsc_project/src/manifest.rs index 8f3a20cf33..8200d0b7ee 100644 --- a/compiler/qsc_project/src/manifest.rs +++ b/compiler/qsc_project/src/manifest.rs @@ -9,7 +9,7 @@ use std::{ fs::{self, DirEntry, FileType}, }; -pub use qsc_linter::LintConfig; +pub use qsc_linter::LintOrGroupConfig; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -25,7 +25,7 @@ pub struct Manifest { #[serde(default)] pub language_features: Vec, #[serde(default)] - pub lints: Vec, + pub lints: Vec, #[serde(default)] pub dependencies: FxHashMap, #[serde(default)] diff --git a/compiler/qsc_project/src/project.rs b/compiler/qsc_project/src/project.rs index 10d516aee2..423f8d045a 100644 --- a/compiler/qsc_project/src/project.rs +++ b/compiler/qsc_project/src/project.rs @@ -9,7 +9,7 @@ use async_trait::async_trait; use futures::FutureExt; use miette::Diagnostic; use qsc_data_structures::language_features::LanguageFeatures; -use qsc_linter::LintConfig; +use qsc_linter::LintOrGroupConfig; use rustc_hash::FxHashMap; use std::{ cell::RefCell, @@ -33,7 +33,7 @@ pub struct Project { /// configuration settings. pub package_graph_sources: PackageGraphSources, /// Lint configuration for the project, typically comes from the root `qsharp.json`. - pub lints: Vec, + pub lints: Vec, /// Any errors encountered while loading the project. pub errors: Vec, } diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index 253eab560e..c5487e5d66 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -14,7 +14,7 @@ use qsc::{ target::Profile, CompileUnit, LanguageFeatures, PackageStore, PackageType, PassContext, SourceMap, Span, }; -use qsc_linter::{LintConfig, LintLevel}; +use qsc_linter::{LintLevel, LintOrGroupConfig}; use qsc_project::{PackageGraphSources, Project}; use rustc_hash::FxHashMap; use std::sync::Arc; @@ -59,7 +59,7 @@ impl Compilation { package_type: PackageType, target_profile: Profile, language_features: LanguageFeatures, - lints_config: &[LintConfig], + lints_config: &[LintOrGroupConfig], package_graph_sources: PackageGraphSources, project_errors: Vec, ) -> Self { @@ -119,7 +119,7 @@ impl Compilation { cells: I, target_profile: Profile, language_features: LanguageFeatures, - lints_config: &[LintConfig], + lints_config: &[LintOrGroupConfig], project: Option, ) -> Self where @@ -310,7 +310,7 @@ impl Compilation { package_type: PackageType, target_profile: Profile, language_features: LanguageFeatures, - lints_config: &[LintConfig], + lints_config: &[LintOrGroupConfig], ) { let sources = self .user_unit() @@ -386,7 +386,7 @@ fn run_linter_passes( errors: &mut Vec>, package_store: &PackageStore, unit: &CompileUnit, - config: &[LintConfig], + config: &[LintOrGroupConfig], ) { if errors.is_empty() { let lints = qsc::linter::run_lints(package_store, unit, Some(config)); diff --git a/language_service/src/protocol.rs b/language_service/src/protocol.rs index 4a75b701ca..a4348722f2 100644 --- a/language_service/src/protocol.rs +++ b/language_service/src/protocol.rs @@ -4,7 +4,8 @@ use miette::Diagnostic; use qsc::line_column::Range; use qsc::{compile, project}; -use qsc::{linter::LintConfig, project::Manifest, target::Profile, LanguageFeatures, PackageType}; +use qsc::{project::Manifest, target::Profile, LanguageFeatures, PackageType}; +use qsc_linter::LintOrGroupConfig; use thiserror::Error; /// A change to the workspace configuration @@ -13,7 +14,7 @@ pub struct WorkspaceConfigurationUpdate { pub target_profile: Option, pub package_type: Option, pub language_features: Option, - pub lints_config: Option>, + pub lints_config: Option>, } #[derive(Clone, Debug, Diagnostic, Error)] diff --git a/language_service/src/state.rs b/language_service/src/state.rs index 3eaed8c13c..e91d9b5e73 100644 --- a/language_service/src/state.rs +++ b/language_service/src/state.rs @@ -11,7 +11,7 @@ use log::{debug, trace}; use miette::Diagnostic; use qsc::{compile, project}; use qsc::{target::Profile, LanguageFeatures, PackageType}; -use qsc_linter::LintConfig; +use qsc_linter::LintOrGroupConfig; use qsc_project::{FileSystemAsync, JSProjectHost, PackageCache, Project}; use rustc_hash::{FxHashMap, FxHashSet}; use std::path::PathBuf; @@ -62,7 +62,7 @@ struct Configuration { pub target_profile: Profile, pub package_type: PackageType, pub language_features: LanguageFeatures, - pub lints_config: Vec, + pub lints_config: Vec, } impl Default for Configuration { @@ -81,7 +81,7 @@ pub struct PartialConfiguration { pub target_profile: Option, pub package_type: Option, pub language_features: Option, - pub lints_config: Vec, + pub lints_config: Vec, } pub(super) struct CompilationStateUpdater<'a> { @@ -597,9 +597,26 @@ fn merge_configurations( let mut override_lints = compilation_overrides.lints_config.clone(); override_lints.retain(|override_lint| { for merged_lint in &mut merged_lints { - if merged_lint.kind == override_lint.kind { - merged_lint.level = override_lint.level; - return false; + match (merged_lint, override_lint) { + ( + LintOrGroupConfig::Lint(lint_config), + LintOrGroupConfig::Lint(lint_config_override), + ) => { + if lint_config.kind == lint_config_override.kind { + lint_config.level = lint_config_override.level; + return false; + } + } + ( + LintOrGroupConfig::Group(group_config), + LintOrGroupConfig::Group(group_config_override), + ) => { + if group_config.lint_group == group_config_override.lint_group { + group_config.level = group_config_override.level; + return false; + } + } + _ => continue, } } true diff --git a/language_service/src/state/tests.rs b/language_service/src/state/tests.rs index 2cf949e2c0..f81a51db4d 100644 --- a/language_service/src/state/tests.rs +++ b/language_service/src/state/tests.rs @@ -11,7 +11,7 @@ use crate::{ }; use expect_test::{expect, Expect}; use qsc::{compile, project, target::Profile, LanguageFeatures, PackageType}; -use qsc_linter::{AstLint, LintConfig, LintKind, LintLevel}; +use qsc_linter::{AstLint, LintConfig, LintKind, LintLevel, LintOrGroupConfig}; use std::{cell::RefCell, fmt::Write, rc::Rc}; #[tokio::test] @@ -1778,18 +1778,22 @@ async fn loading_lints_config_from_manifest() { &updater, &expect![[r#" [ - LintConfig { - kind: Ast( - DivisionByZero, - ), - level: Error, - }, - LintConfig { - kind: Ast( - NeedlessParens, - ), - level: Error, - }, + Lint( + LintConfig { + kind: Ast( + DivisionByZero, + ), + level: Error, + }, + ), + Lint( + LintConfig { + kind: Ast( + NeedlessParens, + ), + level: Error, + }, + ), ]"#]], ) .await; @@ -1958,10 +1962,10 @@ async fn lints_prefer_workspace_over_defaults() { let received_errors = RefCell::new(Vec::new()); let mut updater = new_updater(&received_errors); updater.update_configuration(WorkspaceConfigurationUpdate { - lints_config: Some(vec![LintConfig { + lints_config: Some(vec![LintOrGroupConfig::Lint(LintConfig { kind: LintKind::Ast(AstLint::DivisionByZero), level: LintLevel::Warn, - }]), + })]), ..WorkspaceConfigurationUpdate::default() }); @@ -2018,10 +2022,10 @@ async fn lints_prefer_manifest_over_workspace() { let received_errors = RefCell::new(Vec::new()); let mut updater = new_updater_with_file_system(&received_errors, &fs); updater.update_configuration(WorkspaceConfigurationUpdate { - lints_config: Some(vec![LintConfig { + lints_config: Some(vec![LintOrGroupConfig::Lint(LintConfig { kind: LintKind::Ast(AstLint::DivisionByZero), level: LintLevel::Warn, - }]), + })]), ..WorkspaceConfigurationUpdate::default() }); diff --git a/wasm/src/language_service.rs b/wasm/src/language_service.rs index 5e60a3f9fb..fbabdc8738 100644 --- a/wasm/src/language_service.rs +++ b/wasm/src/language_service.rs @@ -8,7 +8,8 @@ use crate::{ serializable_type, }; use qsc::{ - self, line_column::Encoding, linter::LintConfig, target::Profile, LanguageFeatures, PackageType, + self, line_column::Encoding, linter::LintOrGroupConfig, target::Profile, LanguageFeatures, + PackageType, }; use qsc_project::Manifest; use qsls::protocol::DiagnosticUpdate; @@ -330,13 +331,13 @@ serializable_type! { pub targetProfile: Option, pub packageType: Option, pub languageFeatures: Option>, - pub lints: Option> + pub lints: Option> }, r#"export interface IWorkspaceConfiguration { targetProfile?: TargetProfile; packageType?: "exe" | "lib"; languageFeatures?: LanguageFeatures[]; - lints?: { lint: string; level: string }[]; + lints?: ({ lint: string; level: string } | { group: string; level: string })[]; }"#, IWorkspaceConfiguration } diff --git a/wasm/src/project_system.rs b/wasm/src/project_system.rs index f1b7b62613..ee4575ee65 100644 --- a/wasm/src/project_system.rs +++ b/wasm/src/project_system.rs @@ -4,7 +4,7 @@ use crate::{diagnostic::project_errors_into_qsharp_errors, serializable_type}; use async_trait::async_trait; use miette::Report; -use qsc::{linter::LintConfig, packages::BuildableProgram, LanguageFeatures}; +use qsc::{linter::LintOrGroupConfig, packages::BuildableProgram, LanguageFeatures}; use qsc_project::{EntryType, FileSystemAsync, JSFileEntry, JSProjectHost, PackageCache}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -276,7 +276,7 @@ serializable_type! { pub package_type: Option, }, r#" - + export interface IPackageInfo { sources: [string, string][]; languageFeatures: string[]; @@ -311,7 +311,7 @@ serializable_type! { pub project_name: String, pub project_uri: String, pub package_graph_sources: PackageGraphSources, - pub lints: Vec, + pub lints: Vec, }, r#"export interface IProjectConfig { /** @@ -323,10 +323,7 @@ serializable_type! { */ projectUri: string; packageGraphSources: IPackageGraphSources; - lints: { - lint: string; - level: string; - }[]; + lints: ({ lint: string; level: string } | { group: string; level: string })[]; errors: string[]; }"#, IProjectConfig