diff --git a/.cargo/config.toml b/.cargo/config.toml index 7e4e7a0f90..eb89fa1e55 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,8 @@ [alias] xtask = "run --package xtask --" -# @fb-only -# @fb-only +# @fb-only: [build] +# @fb-only: target-dir = "../../../buck-out/elp" [profile.release] codegen-units = 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 471b9d2418..e9980283f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - platform-arch: [ubuntu-22.04-x64, ubuntu-22.04-arm, macos-13-x64, macos-latest-arm, windows-2022-x64] + platform-arch: [ubuntu-22.04-x64, ubuntu-22.04-arm, macos-15-x64, macos-latest-arm, windows-2022-x64] otp-version: [26.2, 27.3, 28.0] include: - otp-version: 26.2 @@ -55,8 +55,8 @@ jobs: os: linux target: aarch64-unknown-linux-gnu vscode-target: linux-arm64 - - platform-arch: macos-13-x64 - platform: macos-13 + - platform-arch: macos-15-x64 + platform: macos-15-intel os: macos target: x86_64-apple-darwin vscode-target: darwin-x64 @@ -97,6 +97,8 @@ jobs: run: | sudo apt-get update sudo apt-get install -y crossbuild-essential-arm64 + - name: Install Buck2 + uses: dtolnay/install-buck2@latest - id: setup-erlang uses: ./.github/actions/setup-erlang with: @@ -135,7 +137,7 @@ jobs: - name: Test elp # Do not run the tests in case of cross-compilation or on Windows if: matrix.platform-arch != 'macos-latest-arm' && matrix.os != 'windows' - run: 'cargo test --no-default-features --workspace --target ${{ matrix.target }}' + run: 'cargo test --workspace --target ${{ matrix.target }}' - name: Build elp (No Windows) if: matrix.os != 'windows' run: 'cargo build --release --target ${{ matrix.target }} --config target.aarch64-unknown-linux-gnu.linker=\"aarch64-linux-gnu-gcc\"' @@ -200,6 +202,8 @@ jobs: node-version: 20 - name: Install VSCE run: npm install -g vsce + - name: Install OVSX + run: npm install -g ovsx - name: Prepare VS Code Extension to host binaries (No Windows) if: matrix.os != 'windows' run: mkdir -p editors/code/bin @@ -285,3 +289,7 @@ jobs: working-directory: editors/code if: ${{ github.event_name == 'release' && matrix.vscode-publish && matrix.os != 'windows' }} run: vsce publish -p ${{ secrets.VSCE_PAT }} --packagePath erlang-language-platform.vsix + - name: Publish extension to OpenVSX marketplace + working-directory: editors/code + if: ${{ github.event_name == 'release' && matrix.vscode-publish && matrix.os != 'windows' }} + run: ovsx publish -p ${{ secrets.OVSX_PAT }} --packagePath erlang-language-platform.vsix diff --git a/.llms/rules/elp_development.md b/.llms/rules/elp_development.md index 579764599a..efd132ac4b 100644 --- a/.llms/rules/elp_development.md +++ b/.llms/rules/elp_development.md @@ -3,13 +3,34 @@ llms-gk: 'devmate_elp_development_md' apply_to_regex: '^(.*\.rs|.*\.md)$' oncalls: ['vscode_erlang'] --- - -# ELP Development Rules for LLMs - +# ELP Development Rules for LLMs (OSS) ## Project Overview -ELP (Erlang Language Platform) is a language server and development tools suite for Erlang, built in Rust. This project provides IDE features, diagnostics, and code analysis for Erlang codebases. +ELP (Erlang Language Platform) is a language server and development tools suite +for Erlang, built in Rust. This project provides IDE features, diagnostics, and +code analysis for Erlang codebases. + +## Build System + +Use standard Cargo commands: + +```bash +# Build +cargo build --release + +# Run tests +cargo test --workspace + +# Run clippy +cargo clippy --tests + +# Format code +cargo fmt + +# Code generation +cargo xtask codegen +``` ## Diagnostic Code Management @@ -17,13 +38,13 @@ ELP (Erlang Language Platform) is a language server and development tools suite When adding new diagnostic codes to `DiagnosticCode` enum: -1. **Naming Convention**: Use descriptive PascalCase names that clearly indicate the issue +1. **Naming Convention**: Use descriptive PascalCase names that clearly indicate + the issue - Good: `UnusedFunctionArg`, `MissingCompileWarnMissingSpec` - Bad: `Error1`, `BadCode` 2. **Code Assignment**: Follow the established numbering scheme - `W0000-W9999`: Native ELP diagnostics, visible in the OSS version - - `WA000-WA999`: WhatsApp-specific warnings, only visible in Meta builds - Use the next available number in the appropriate range - Never change the number of an existing diagnostic code - Never change the label of an existing diagnostic code @@ -37,7 +58,8 @@ When adding new diagnostic codes to `DiagnosticCode` enum: 4. **Documentation**: Add comments explaining complex diagnostic codes -5. **Documentation File**: Create a corresponding documentation file in the website +5. **Documentation File**: Create a corresponding documentation file in the + website - Location: `website/docs/erlang-error-index/{namespace}/{code}.md` - Example: `W0051` → `website/docs/erlang-error-index/w/W0051.md` - Include frontmatter with `sidebar_position` matching the code number @@ -51,16 +73,19 @@ When adding new diagnostic codes to `DiagnosticCode` enum: ### Creating DiagnosticDescriptor -Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines when and how the diagnostic runs: +Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines +when and how the diagnostic runs: -1. **Static Descriptor Declaration**: Create a public static descriptor in your diagnostic module +1. **Static Descriptor Declaration**: Create a public static descriptor in your + diagnostic module - Use `pub(crate) static DESCRIPTOR: DiagnosticDescriptor` pattern - Define `DiagnosticConditions` with appropriate flags - Provide a checker function that implements the diagnostic logic 2. **Diagnostic Conditions**: Configure when the diagnostic should run - `experimental`: Mark as true for experimental/unstable diagnostics - - `include_generated`: Set to false if diagnostic shouldn't run on generated code + - `include_generated`: Set to false if diagnostic shouldn't run on generated + code - `include_tests`: Set to false if diagnostic shouldn't run on test files - `default_disabled`: Set to true if diagnostic requires explicit enabling @@ -69,7 +94,8 @@ Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines w - Push diagnostics to the `diags` vector using `Diagnostic::new()` - Use helper functions to keep the checker clean and focused -4. **Registration**: Add the descriptor to `diagnostics_descriptors()` function in `diagnostics.rs` +4. **Registration**: Add the descriptor to `diagnostics_descriptors()` function + in `diagnostics.rs` - Include your module's `DESCRIPTOR` in the returned vector 5. **Module Structure**: Follow the established pattern @@ -78,12 +104,6 @@ Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines w - Include comprehensive tests with `#[cfg(test)]` - Use SSR patterns when appropriate for complex matching -### Meta-Only vs OSS Code - -- Use `@fb-only` and `@oss-only` comments to mark platform-specific code -- Meta-only diagnostics should use `MetaOnlyDiagnosticCode` wrapper -- Ensure OSS builds work by providing fallbacks for Meta-only features - ## Rust Code Style ### Error Handling @@ -120,24 +140,40 @@ Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines w ### Declarative Test Fixtures -ELP uses a declarative test fixture system that allows you to write tests with inline annotations and markers directly in test strings. This system is defined in `/data/sandcastle/boxes/fbsource/fbcode/whatsapp/elp/crates/project_model/src/test_fixture.rs`. +ELP uses a declarative test fixture system that allows you to write tests with +inline annotations and markers directly in test strings. This system is defined +in `crates/project_model/src/test_fixture.rs`. #### Key Features -1. **File Organization**: Use `//- /path/to/file.erl` to define multiple files in a single test -2. **Metadata Markers**: Specify app names, include paths, OTP apps, etc. using metadata after the path +1. **File Organization**: Use `//- /path/to/file.erl` to define multiple files + in a single test +2. **Metadata Markers**: Specify app names, include paths, OTP apps, etc. using + metadata after the path 3. **Annotations**: Mark expected diagnostics or ranges using `%% ^^^` syntax -4. **Cursors and Ranges**: Use `~` markers to indicate positions or ranges in test code +4. **Cursors and Ranges**: Use `~` markers to indicate positions or ranges in + test code #### Annotation Syntax -Annotations allow you to mark expected diagnostics, types, or other information directly in test code: +Annotations allow you to mark expected diagnostics, types, or other information +directly in test code: -- **Basic annotation**: `%% ^^^ some text` - Points to the range above matching the caret length -- **Top-of-file marker**: `%% <<< text` (at file start) - Creates annotation at position 0..0 -- **File-wide annotation**: `%% ^^^file text` - Annotation spans the entire file contents -- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position instead of first `^` +- **Basic annotation**: `%% ^^^ some text` - Points to the range above matching + the caret length +- **Top-of-file marker**: `%% <<< text` (at file start) - Creates annotation at + position 0..0 +- **File-wide annotation**: `%% ^^^file text` - Annotation spans the entire file + contents +- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position + instead of first `^` - **Multiline annotations**: Use continuation lines with `%% | next line` + - Continuation lines are particularly useful for diagnostics with related information: + ```erlang + foo() -> syntax error oops. + %% ^^^^^ error: P1711: syntax error before: error + %% | Related info: 0:45-50 function foo/0 undefined + ``` #### Example Test Fixture @@ -146,9 +182,8 @@ let fixture = r#" //- /src/main.erl -module(main). -foo(X) -> - X + undefined. - %% ^^^^^^^^^ error: type mismatch +foo( -> ok. %% +%% ^ error: W0004: Missing ')'~ "#; ``` @@ -160,34 +195,37 @@ foo(X) -> ### Running Tests for Specific Crates -When running tests for a specific crate, you need to specify the crate name, not the directory name. The mapping is: +When running tests for a specific crate, you need to specify the crate name, not +the directory name. The mapping is: -| Crate Name | Directory Name | -|------------|----------------| -| `elp_base_db` | `crates/base_db` | -| `elp_eqwalizer` | `crates/eqwalizer` | +| Crate Name | Directory Name | +| -------------------- | ----------------------- | +| `elp` | `crates/elp` | +| `elp_base_db` | `crates/base_db` | +| `elp_eqwalizer` | `crates/eqwalizer` | | `elp_erlang_service` | `crates/erlang_service` | -| `elp_ide` | `crates/ide` | -| `elp_ide_assists` | `crates/ide_assists` | +| `elp_ide` | `crates/ide` | +| `elp_ide_assists` | `crates/ide_assists` | | `elp_ide_completion` | `crates/ide_completion` | -| `elp_ide_db` | `crates/ide_db` | -| `elp_ide_ssr` | `crates/ide_ssr` | -| `elp_log` | `crates/elp_log` | -| `elp_project_model` | `crates/project_model` | -| `elp_syntax` | `crates/syntax` | -| `elp_text_edit` | `crates/text_edit` | -| `elp_types_db` | `crates/types_db` | -| `hir` | `crates/hir` | -| `erl_ast` | `crates/erl_ast` | +| `elp_ide_db` | `crates/ide_db` | +| `elp_ide_ssr` | `crates/ide_ssr` | +| `elp_log` | `crates/elp_log` | +| `elp_project_model` | `crates/project_model` | +| `elp_syntax` | `crates/syntax` | +| `elp_text_edit` | `crates/text_edit` | +| `elp_types_db` | `crates/types_db` | +| `hir` | `crates/hir` | Example: To run tests for the `elp_ide` crate: + ```bash -./meta/cargo.sh test -p elp_ide +cargo test -p elp_ide ``` Or to run tests in a specific directory: + ```bash -./meta/cargo.sh test --manifest-path crates/ide/Cargo.toml +cargo test --manifest-path crates/ide/Cargo.toml ``` ### Existing tests @@ -277,14 +315,8 @@ Or to run tests in a specific directory: - Collect multiple errors rather than failing on the first one - Provide partial results when full analysis isn't possible -### Tools - -- ELP uses a cargo workspace. -- Inside Meta, use `./meta/cargo.sh` instead of `cargo` -- Inside Meta, use `./meta/clippy.sh` to run clippy -- Use `arc lint --apply-patches` for formatting. - ### Process -- Always run tests before finishing. -- Always run `./meta/cargo.sh clippy --tests` before submitting a diff +- Always run tests before finishing +- Always run `cargo clippy --tests` before submitting PRs +- Use `cargo fmt` for code formatting diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 51f0340659..7572a84e98 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ { "label": "ELP: build (debug)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build", "command": "cargo build", // @oss-only "group": { "kind": "build", @@ -19,7 +19,7 @@ { "label": "ELP: build (release)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build --release", "command": "cargo build --release", // @oss-only "group": { "kind": "build", @@ -34,7 +34,7 @@ { "label": "ELP: build (release-thin)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build --profile release-thin --bins", "command": "cargo build --profile release-thin --bins", // @oss-only "group": { "kind": "build", @@ -49,7 +49,7 @@ { "label": "ELP: run clippy on workspace", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/clippy.sh --workspace --tests", "command": "cargo clippy --workspace --tests", // @oss-only "group": { "kind": "build", @@ -64,7 +64,7 @@ { "label": "ELP: run clippy on workspace, apply fixes", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/clippy.sh --workspace --tests --fix", "command": "cargo clippy --workspace --tests --fix", // @oss-only "group": { "kind": "build", @@ -79,7 +79,7 @@ { "label": "ELP: run tests on workspace", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh test --workspace", "command": "cargo test --workspace", // @oss-only "group": { "kind": "build", diff --git a/Cargo.lock b/Cargo.lock index 03f23763eb..2da9907eca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -446,10 +446,10 @@ dependencies = [ "crossbeam-channel", "elp_eqwalizer", "elp_ide", + "elp_ide_db", "elp_log", "elp_project_model", "elp_syntax", - "elp_text_edit", "env_logger", "expect-test", "fs_extra", @@ -572,7 +572,6 @@ dependencies = [ "elp_ide_ssr", "elp_project_model", "elp_syntax", - "elp_text_edit", "elp_types_db", "env_logger", "expect-test", @@ -604,7 +603,6 @@ dependencies = [ "cov-mark", "elp_ide_db", "elp_syntax", - "elp_text_edit", "expect-test", "fxhash", "hir", @@ -637,6 +635,7 @@ name = "elp_ide_db" version = "1.1.0" dependencies = [ "anyhow", + "cov-mark", "eetf", "either", "elp_base_db", @@ -644,12 +643,12 @@ dependencies = [ "elp_erlang_service", "elp_project_model", "elp_syntax", - "elp_text_edit", "elp_types_db", "expect-test", "fxhash", "hir", "indexmap 2.9.0", + "itertools 0.10.5", "lazy_static", "log", "memchr", @@ -664,6 +663,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", + "text-size", "toml", "tracing", ] @@ -734,10 +734,8 @@ dependencies = [ name = "elp_syntax" version = "1.1.0" dependencies = [ - "cov-mark", "eetf", "elp_ide_db", - "elp_text_edit", "expect-test", "fxhash", "indexmap 2.9.0", @@ -757,14 +755,6 @@ dependencies = [ "tree-sitter-erlang", ] -[[package]] -name = "elp_text_edit" -version = "1.1.0" -dependencies = [ - "itertools 0.10.5", - "text-size", -] - [[package]] name = "elp_types_db" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 5814517745..825530fe4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,13 +30,9 @@ elp_ide_ssr = { path = "./crates/ide_ssr" } elp_log = { path = "./crates/elp_log" } elp_project_model = { path = "./crates/project_model" } elp_syntax = { path = "./crates/syntax" } -elp_text_edit = { path = "./crates/text_edit" } elp_types_db = { path = "./crates/types_db" } hir = { path = "./crates/hir" } -# Forks -erl_ast = { path = "./crates/erl_ast" } - # External crates trie-rs = "0.4.2" always-assert = "0.1.3" diff --git a/bench_runner/example_bench/benches/main.rs b/bench_runner/example_bench/benches/main.rs deleted file mode 100644 index 6b2733b5b9..0000000000 --- a/bench_runner/example_bench/benches/main.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is dual-licensed under either the MIT license found in the - * LICENSE-MIT file in the root directory of this source tree or the Apache - * License, Version 2.0 found in the LICENSE-APACHE file in the root directory - * of this source tree. You may select, at your option, one of the - * above-listed licenses. - */ - -use std::thread; -use std::time; - -use criterion::BenchmarkId; -use criterion::Criterion; -use criterion::criterion_group; -use criterion::criterion_main; - -fn fibonacci_slow(n: u64) -> u64 { - match n { - 0 => 1, - 1 => 1, - n => fibonacci_slow(n - 1) + fibonacci_slow(n - 2), - } -} - -fn fibonacci_fast(n: u64) -> u64 { - let mut a = 0; - let mut b = 1; - let millis = time::Duration::from_millis(12); - thread::sleep(millis); - - match n { - 0 => b, - _ => { - for _ in 0..n { - let c = a + b; - a = b; - b = c; - } - b - } - } -} - -fn bench_fibs(c: &mut Criterion) { - let mut group = c.benchmark_group("Fibonacci"); - for i in [20u64, 21u64].iter() { - group.bench_with_input(BenchmarkId::new("Recursive", i), i, |b, i| { - b.iter(|| fibonacci_slow(*i)) - }); - group.bench_with_input(BenchmarkId::new("Iterative", i), i, |b, i| { - b.iter(|| fibonacci_fast(*i)) - }); - } - group.finish(); -} - -criterion_group!(benches, bench_fibs); -criterion_main!(benches); diff --git a/bench_runner/runner/main.rs b/bench_runner/runner/main.rs deleted file mode 100644 index f895c08c52..0000000000 --- a/bench_runner/runner/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is dual-licensed under either the MIT license found in the - * LICENSE-MIT file in the root directory of this source tree or the Apache - * License, Version 2.0 found in the LICENSE-APACHE file in the root directory - * of this source tree. You may select, at your option, one of the - * above-listed licenses. - */ - -use std::env; - -fn main() { - let args: Vec = env::args().collect(); - println!("ARGS: {:?}", args); -} diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index d6d2205ce9..cd1328d14d 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -87,6 +87,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { let (fixture, change) = ChangeFixture::parse(fixture_str); let mut db = Self::default(); change.apply(&mut db, &|path| fixture.resolve_file_id(path)); + fixture.validate(&db); (db, fixture) } } @@ -101,6 +102,7 @@ pub struct ChangeFixture { pub diagnostics_enabled: DiagnosticsEnabled, pub tags: FxHashMap)>>, pub annotations: FxHashMap>, + pub expect_parse_errors: bool, } struct Builder { @@ -172,6 +174,7 @@ impl ChangeFixture { let FixtureWithProjectMeta { fixture, mut diagnostics_enabled, + expect_parse_errors, } = fixture_with_meta.clone(); let builder = Builder::new(diagnostics_enabled.clone()); @@ -344,6 +347,7 @@ impl ChangeFixture { diagnostics_enabled, tags, annotations, + expect_parse_errors, }, change, project, @@ -405,6 +409,64 @@ impl ChangeFixture { .get(&VfsPath::from(path.clone())) .cloned() } + + /// Validate all files in the fixture for syntax errors. + /// Panics with context if any syntax errors are found. + /// Skips validation if `expect_parse_errors` is set to true. + #[track_caller] + pub fn validate(&self, db: &DB) { + if self.expect_parse_errors { + return; + } + + let mut errors_found = Vec::new(); + + for file_id in &self.files { + let parse = db.parse(*file_id); + let errors = parse.errors(); + + if !errors.is_empty() { + let path = self + .files_by_path + .iter() + .find_map(|(vfs_path, id)| { + if id == file_id { + Some( + vfs_path + .as_path() + .map(|p| p.to_string()) + .unwrap_or_else(|| format!("{:?}", vfs_path)), + ) + } else { + None + } + }) + .unwrap_or_else(|| format!("FileId({:?})", file_id)); + + let file_text = SourceDatabaseExt::file_text(db, *file_id); + let tree = parse.tree(); + errors_found.push((path, file_text.to_string(), errors.to_vec(), tree)); + } + } + + if !errors_found.is_empty() { + let mut message = + String::from("Fixture validation failed: syntax errors found in test fixture\n\n"); + + for (path, text, errors, tree) in errors_found { + message.push_str(&format!("File: {}\n", path)); + message.push_str(&format!("Errors: {:?}\n", errors)); + message.push_str(&format!("Content:\n{}\n", text)); + message.push_str(&format!("Parse Tree:\n{:#?}\n", tree)); + message.push_str("---\n"); + } + message.push_str( + "If this is expected, add `//- expect_parse_errors` to the start of the fixture\n", + ); + + panic!("{}", message); + } + } } fn inc_file_id(file_id: &mut FileId) { diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 6b3757ff43..0cd8df74c9 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -32,7 +32,7 @@ mod module_index; // Public API pub mod fixture; -// @fb-only +// @fb-only: mod meta_only; pub mod test_utils; pub use change::Change; pub use elp_project_model::AppType; @@ -476,7 +476,7 @@ static ref IGNORED_SOURCES: Vec = { let regexes: Vec> = vec![ vec![Regex::new(r"^.*_SUITE_data/.+$").unwrap()], //ignore sources goes here - // @fb-only + // @fb-only: meta_only::ignored_sources_regexes() ]; regexes.into_iter().flatten().collect::>() }; diff --git a/crates/elp/Cargo.toml b/crates/elp/Cargo.toml index 66552ada1d..2a1b6e8b65 100644 --- a/crates/elp/Cargo.toml +++ b/crates/elp/Cargo.toml @@ -18,10 +18,10 @@ workspace = true [dependencies] elp_eqwalizer.workspace = true elp_ide.workspace = true +elp_ide_db.workspace = true elp_log.workspace = true elp_project_model.workspace = true elp_syntax.workspace = true -elp_text_edit.workspace = true hir.workspace = true always-assert.workspace = true diff --git a/crates/elp/src/arc_types.rs b/crates/elp/src/arc_types.rs index c113311661..374dcdba2f 100644 --- a/crates/elp/src/arc_types.rs +++ b/crates/elp/src/arc_types.rs @@ -8,8 +8,8 @@ * above-listed licenses. */ -// @fb-only -// @fb-only +// @fb-only: /// Types as defined in https://www.internalfb.com/intern/wiki/Linting/adding-linters/#flow-type +// @fb-only: /// and https://www.internalfb.com/code/fbsource/[1238f73dac0efd4009443fee6a345a680dc9401b]/whatsapp/server/erl/tools/lint/arcanist.py?lines=17 use std::path::Path; use serde::Serialize; diff --git a/crates/elp/src/bin/args.rs b/crates/elp/src/bin/args.rs index 343f6bd820..c9790e9314 100644 --- a/crates/elp/src/bin/args.rs +++ b/crates/elp/src/bin/args.rs @@ -11,6 +11,7 @@ use std::cmp::Ordering; use std::env; use std::fs; +use std::io::IsTerminal; use std::path::PathBuf; use anyhow::Result; @@ -71,6 +72,17 @@ pub struct ParseAllElp { /// Report system memory usage and other statistics #[bpaf(long("report-system-stats"))] pub report_system_stats: bool, + /// Minimum severity level to report. Valid values: error, warning, weak_warning, information + #[bpaf( + argument("SEVERITY"), + complete(severity_completer), + fallback(None), + guard( + severity_guard, + "Please use error, warning, weak_warning, or information" + ) + )] + pub severity: Option, } #[derive(Clone, Debug, Bpaf)] @@ -143,8 +155,6 @@ pub struct EqwalizeAll { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Exit with a non-zero status code if any errors are found pub bail_on_error: bool, /// Print statistics when done @@ -161,8 +171,6 @@ pub struct EqwalizeTarget { /// Also eqwalize opted-in generated modules from application (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Exit with a non-zero status code if any errors are found pub bail_on_error: bool, /// target, like //erl/chatd/... @@ -181,8 +189,6 @@ pub struct EqwalizeApp { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Run with rebar pub rebar: bool, /// Exit with a non-zero status code if any errors are found @@ -205,8 +211,6 @@ pub struct EqwalizeStats { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// If specified, use the provided CLI severity mapping instead of the default one pub use_cli_severity: bool, } @@ -274,8 +278,6 @@ pub struct Lint { guard(format_guard, "Please use json") )] pub format: Option, - /// Optional prefix to prepend to each diagnostic file path. Only used when --format=json is set - pub prefix: Option, /// Include diagnostics produced by erlc pub include_erlc_diagnostics: bool, @@ -332,6 +334,9 @@ pub struct Lint { #[bpaf(long("report-system-stats"))] pub report_system_stats: bool, + /// Disable streaming of diagnostics when applying fixes (collect all before printing) + pub no_stream: bool, + /// Rest of args are space separated list of apps to ignore #[bpaf(positional("IGNORED_APPS"))] pub ignore_apps: Vec, @@ -386,6 +391,33 @@ pub struct Ssr { #[bpaf(long("parens"))] pub paren_strategy: bool, + /// Dump a configuration snippet that can be put in .elp_lint.toml to match the given SSR patterns + pub dump_config: bool, + + /// Show source code context for matches + #[bpaf(long("show-source"))] + pub show_source: bool, + + /// Print NUM lines of leading context, enables --show-source + #[bpaf(short('B'), long("before-context"), argument("NUM"))] + pub before_context: Option, + + /// Print NUM lines of trailing context, enables --show-source + #[bpaf(short('A'), long("after-context"), argument("NUM"))] + pub after_context: Option, + + /// Print NUM lines of output context, enables --show-source + #[bpaf(short('C'), long("context"), argument("NUM"))] + pub context: Option, + + /// Print SEP on line between matches with context, enables --show-source + #[bpaf(long("group-separator"), argument("SEP"))] + pub group_separator: Option, + + /// Do not print separator for matches with context, enables --show-source + #[bpaf(long("no-group-separator"))] + pub no_group_separator: bool, + /// Report system memory usage and other statistics #[bpaf(long("report-system-stats"))] pub report_system_stats: bool, @@ -442,8 +474,6 @@ pub struct Glean { pub pretty: bool, /// Output each fact separately pub multi: bool, - /// Optional prefix to prepend to each fact - pub prefix: Option, } #[derive(Clone, Debug, Bpaf)] @@ -490,6 +520,16 @@ pub struct Args { /// Use buck2 targets for first stage project loading pub buck_quick_start: bool, + /// Use color in output; WHEN is 'always', 'never', or 'auto' + #[bpaf( + long("color"), + long("colour"), + argument("WHEN"), + fallback(Some("always".to_string())), + guard(color_guard, "Please use always, never, or auto") + )] + pub color: Option, + #[bpaf(external(command))] pub command: Command, } @@ -504,6 +544,20 @@ impl Args { BuckQueryConfig::BuildGeneratedCode } } + + /// Determine if color should be used based on the --color argument + pub fn should_use_color(&self) -> bool { + match self.color.as_deref() { + Some("always") => true, + Some("never") => false, + Some("auto") | None => { + // Check NO_COLOR environment variable - if set (regardless of value), disable color + // Also check if stdout is connected to a TTY + env::var("NO_COLOR").is_err() && std::io::stdout().is_terminal() + } + _ => false, // Should be caught by the guard, but handle anyway + } + } } pub fn command() -> impl Parser { @@ -575,6 +629,12 @@ pub fn command() -> impl Parser { .command("lint") .help("Parse files in project and emit diagnostics, optionally apply fixes."); + let search = ssr() + .map(Command::Ssr) + .to_options() + .command("search") + .help("Alias for 'ssr': Run SSR (Structural Search and Replace) pattern matching on project files."); + let ssr = ssr() .map(Command::Ssr) .to_options() @@ -636,6 +696,7 @@ pub fn command() -> impl Parser { dialyze_all, lint, ssr, + search, parse_all, parse_elp, explain, @@ -725,6 +786,25 @@ fn format_guard(format: &Option) -> bool { } } +fn severity_completer(_: &Option) -> Vec<(String, Option)> { + vec![ + ("error".to_string(), None), + ("warning".to_string(), None), + ("weak_warning".to_string(), None), + ("information".to_string(), None), + ] +} + +fn severity_guard(severity: &Option) -> bool { + match severity { + None => true, + Some(s) if s == "error" || s == "warning" || s == "weak_warning" || s == "information" => { + true + } + _ => false, + } +} + fn macros_completer(_: &Option) -> Vec<(String, Option)> { vec![ ("expand".to_string(), None), @@ -740,6 +820,14 @@ fn macros_guard(format: &Option) -> bool { } } +fn color_guard(color: &Option) -> bool { + match color { + None => true, + Some(c) if c == "always" || c == "never" || c == "auto" => true, + _ => false, + } +} + #[allow(clippy::ptr_arg)] // This is needed in the BPAF macros fn at_least_1(data: &Vec) -> bool { !data.is_empty() @@ -820,6 +908,11 @@ impl Lint { pub fn is_format_json(&self) -> bool { self.format == Some("json".to_string()) } + + /// To prevent flaky test results we allow disabling streaming when applying fixes + pub fn skip_stream_print(&self) -> bool { + self.apply_fix || self.no_stream + } } fn parse_macro_strategy(macro_strategy: &Option) -> Result { diff --git a/crates/elp/src/bin/elp_parse_cli.rs b/crates/elp/src/bin/elp_parse_cli.rs index fcc8652ec2..770ab60456 100644 --- a/crates/elp/src/bin/elp_parse_cli.rs +++ b/crates/elp/src/bin/elp_parse_cli.rs @@ -24,7 +24,6 @@ use elp::cli::Cli; use elp::convert; use elp::memory_usage::MemoryUsage; use elp::otp_file_to_ignore; -use elp::server::file_id_to_url; use elp_eqwalizer::Mode; use elp_ide::Analysis; use elp_ide::diagnostics; @@ -58,6 +57,35 @@ use crate::args::ParseAllElp; use crate::reporting; use crate::reporting::print_memory_usage; +fn parse_severity(severity: &str) -> Option { + match severity { + "error" => Some(diagnostics::Severity::Error), + "warning" => Some(diagnostics::Severity::Warning), + "weak_warning" => Some(diagnostics::Severity::WeakWarning), + "information" => Some(diagnostics::Severity::Information), + _ => None, + } +} + +fn severity_rank(severity: diagnostics::Severity) -> u8 { + match severity { + diagnostics::Severity::Error => 1, + diagnostics::Severity::Warning => 2, + diagnostics::Severity::WeakWarning => 3, + diagnostics::Severity::Information => 4, + } +} + +fn meets_severity_threshold( + diag_severity: diagnostics::Severity, + min_severity: Option, +) -> bool { + match min_severity { + None => true, + Some(min) => severity_rank(diag_severity) <= severity_rank(min), + } +} + #[derive(Debug)] struct ParseResult { name: String, @@ -132,8 +160,7 @@ pub fn parse_all( (None, _, true) => do_parse_all_seq(cli, &loaded, &cfg, &args.to)?, (None, _, false) => do_parse_all_par(cli, &loaded, &cfg, &args.to)?, (Some(file_id), Some(name), _) => { - do_parse_one(&analysis, &loaded.vfs, &cfg, &args.to, file_id, &name)? - .map_or(vec![], |x| vec![x]) + do_parse_one(&analysis, &cfg, &args.to, file_id, &name)?.map_or(vec![], |x| vec![x]) } (Some(file_id), _, _) => panic!("Could not get name from file_id for {file_id:?}"), }; @@ -144,15 +171,24 @@ pub fn parse_all( let db = loaded.analysis_host.raw_database(); - // We need a `Url` for converting to the lsp_types::Diagnostic for - // printing, but do not print it out. So just create a dummy value - let url = lsp_types::Url::parse("file:///unused_url").ok().unwrap(); - telemetry::report_elapsed_time("parse-elp operational", start_time); let memory_end = MemoryUsage::now(); let memory_used = memory_end - memory_start; + let min_severity = args + .severity + .as_ref() + .and_then(|s| parse_severity(s.as_str())); + + res.retain(|parse_result| { + parse_result + .diagnostics + .diagnostics_for(parse_result.file_id) + .iter() + .any(|diag| meets_severity_threshold(diag.severity, min_severity)) + }); + if res.is_empty() { if args.is_format_normal() { writeln!(cli, "No errors reported")?; @@ -171,6 +207,7 @@ pub fn parse_all( for diags in res { let mut combined: Vec = diags.diagnostics.diagnostics_for(diags.file_id); + combined.retain(|diag| meets_severity_threshold(diag.severity, min_severity)); if args.is_format_normal() { writeln!(cli, " {}: {}", diags.name, combined.len())?; } @@ -197,7 +234,7 @@ pub fn parse_all( cli, )?; } else { - print_diagnostic(&diag, &line_index, &url, &mut err_in_diag, cli)?; + print_diagnostic(&diag, &line_index, &mut err_in_diag, cli)?; } } } @@ -242,11 +279,10 @@ fn print_diagnostic_json( fn print_diagnostic( diag: &diagnostics::Diagnostic, line_index: &LineIndex, - url: &lsp_types::Url, err_in_diag: &mut bool, cli: &mut dyn Cli, ) -> Result<(), anyhow::Error> { - let diag = convert::ide_to_lsp_diagnostic(line_index, url, diag); + let diag = convert::ide_to_lsp_diagnostic(line_index, diag, |_file_id| None); let severity = match diag.severity { None => DiagnosticSeverity::ERROR, Some(sev) => { @@ -289,7 +325,6 @@ fn do_parse_all_par( let pb = cli.progress(module_iter.len() as u64, "Parsing modules"); - let vfs = &loaded.vfs; Ok(module_iter .par_bridge() .progress_with(pb) @@ -300,7 +335,7 @@ fn do_parse_all_par( && file_source == FileSource::Src && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) { - do_parse_one(db, vfs, config, to, file_id, module_name.as_str()).unwrap() + do_parse_one(db, config, to, file_id, module_name.as_str()).unwrap() } else { None } @@ -321,7 +356,6 @@ fn do_parse_all_seq( let pb = cli.progress(module_iter.len() as u64, "Parsing modules (sequential)"); - let vfs = &loaded.vfs; let db = loaded.analysis(); Ok(module_iter .progress_with(pb) @@ -330,7 +364,7 @@ fn do_parse_all_seq( && file_source == FileSource::Src && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) { - do_parse_one(&db, vfs, config, to, file_id, module_name.as_str()).unwrap() + do_parse_one(&db, config, to, file_id, module_name.as_str()).unwrap() } else { None } @@ -340,13 +374,11 @@ fn do_parse_all_seq( fn do_parse_one( db: &Analysis, - vfs: &Vfs, config: &DiagnosticsConfig, to: &Option, file_id: FileId, name: &str, ) -> Result> { - let url = file_id_to_url(vfs, file_id); let native = db.native_diagnostics(config, &vec![], file_id)?; let erlang_service_diagnostics = db.erlang_service_diagnostics(file_id, config, RemoveElpReported::Yes)?; @@ -364,11 +396,13 @@ fn do_parse_one( let mut output = File::create(to_path)?; for diagnostic in native.iter() { - let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); + let diagnostic = + convert::ide_to_lsp_diagnostic(&line_index, diagnostic, |_file_id| None); writeln!(output, "{diagnostic:?}")?; } for diagnostic in erlang_service.iter() { - let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); + let diagnostic = + convert::ide_to_lsp_diagnostic(&line_index, diagnostic, |_file_id| None); writeln!(output, "{diagnostic:?}")?; } } diff --git a/crates/elp/src/bin/eqwalizer_cli.rs b/crates/elp/src/bin/eqwalizer_cli.rs index c946babe42..141b2157d0 100644 --- a/crates/elp/src/bin/eqwalizer_cli.rs +++ b/crates/elp/src/bin/eqwalizer_cli.rs @@ -186,10 +186,7 @@ pub fn do_eqwalize_all( .par_bridge() .progress_with(pb.clone()) .map_with(analysis.clone(), |analysis, (name, _source, file_id)| { - if analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() - && !otp_file_to_ignore(analysis, file_id) + if analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { if args.stats { add_stat(name.to_string()); @@ -269,9 +266,7 @@ pub fn do_eqwalize_app( .iter_own() .filter_map(|(_name, _source, file_id)| { if analysis.file_app_name(file_id).ok()? == Some(AppName(args.app.clone())) - && analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() + && analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { Some(file_id) @@ -339,9 +334,7 @@ pub fn eqwalize_target( let vfs_path = VfsPath::from(src.clone()); if let Some((file_id, _)) = loaded.vfs.file_id(&vfs_path) { at_least_one_found = true; - if analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() + if analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { file_ids.push(file_id); @@ -408,9 +401,7 @@ pub fn eqwalize_stats( .par_bridge() .progress_with(pb.clone()) .map_with(analysis.clone(), |analysis, (name, _source, file_id)| { - if analysis - .should_eqwalize(file_id, args.include_tests) - .expect("cancelled") + if analysis.should_eqwalize(file_id).expect("cancelled") && !otp_file_to_ignore(analysis, file_id) { analysis @@ -482,8 +473,6 @@ fn eqwalize( bail!("No files to eqWAlize detected") } - pre_parse_for_speed(reporter, analysis.clone(), &file_ids); - let files_count = file_ids.len(); let pb = reporter.progress(files_count as u64, "EqWAlizing"); let output = loaded.with_eqwalizer_progress_bar(pb.clone(), move |analysis| { @@ -602,17 +591,6 @@ fn eqwalize( } } -fn pre_parse_for_speed(reporter: &dyn Reporter, analysis: Analysis, file_ids: &[FileId]) { - let pb = reporter.progress(file_ids.len() as u64, "Parsing modules"); - file_ids - .par_iter() - .progress_with(pb.clone()) - .for_each_with(analysis, |analysis, &file_id| { - let _ = analysis.module_ast(file_id); - }); - pb.finish(); -} - fn set_eqwalizer_config(loaded: &mut LoadResult) { let config = EqwalizerConfig::default(); let db = loaded.analysis_host.raw_database_mut(); diff --git a/crates/elp/src/bin/erlang_service_cli.rs b/crates/elp/src/bin/erlang_service_cli.rs index 6611bdffae..5b39a4aa06 100644 --- a/crates/elp/src/bin/erlang_service_cli.rs +++ b/crates/elp/src/bin/erlang_service_cli.rs @@ -150,14 +150,15 @@ pub fn do_parse_one( .chain(result.warnings.iter()) .map(|err| { let relative_path: &Path = err.path.strip_prefix(root_dir).unwrap_or(&err.path); - let (range, line_num) = match err.location { + let (range, line_num) = match &err.location { None => (None, convert::position(&line_index, 0.into()).line + 1), Some(DiagnosticLocation::Normal(range)) => ( Some(range), convert::position(&line_index, range.start()).line + 1, ), Some(DiagnosticLocation::Included { - directive_location, + file_attribute_location: directive_location, + error_path: _, error_location: _, }) => ( Some(directive_location), @@ -169,7 +170,7 @@ pub fn do_parse_one( relative_path: relative_path.to_owned(), line_num, msg: err.msg.to_owned(), - range, + range: range.copied(), } }) .collect(); diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index dad833e0c1..cb420261d2 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -11,7 +11,6 @@ use core::option::Option::None; use std::io::Write; use std::mem; -use std::path::Path; use anyhow::Result; use elp::build::load; @@ -85,7 +84,7 @@ const REC_ARITY: u32 = 99; const HEADER_ARITY: u32 = 100; const FACTS_FILE: &str = "facts.json"; -// @fb-only +// @fb-only: mod meta_only; #[derive(Serialize, Debug, Eq, Hash, PartialEq, Clone)] struct GleanFileId(u32); @@ -93,7 +92,6 @@ struct GleanFileId(u32); #[derive(Clone, Debug, Default)] struct IndexConfig { pub multi: bool, - pub prefix: Option, } impl From for FileId { @@ -769,10 +767,7 @@ pub struct GleanIndexer { pub fn index(args: &Glean, cli: &mut dyn Cli, query_config: &BuckQueryConfig) -> Result<()> { let (indexer, _loaded) = GleanIndexer::new(args, cli, query_config)?; - let config = IndexConfig { - multi: args.multi, - prefix: args.prefix.clone(), - }; + let config = IndexConfig { multi: args.multi }; let (facts, module_index) = indexer.index(config)?; write_results(facts, module_index, cli, args) } @@ -861,14 +856,7 @@ impl GleanIndexer { let source_root_id = db.file_source_root(file_id); let source_root = db.source_root(source_root_id); let path = source_root.path_for_file(&file_id).unwrap(); - match Self::index_file( - db, - file_id, - path, - project_id, - &module_index, - config.prefix.as_ref(), - ) { + match Self::index_file(db, file_id, path, project_id, &module_index) { Some((file, line, decl, xref, facts, module_fact)) => { let mut result = FxHashMap::default(); result.insert( @@ -884,14 +872,7 @@ impl GleanIndexer { .into_par_iter() .map_with(self.analysis.clone(), |analysis, (file_id, path)| { analysis.with_db(|db| { - Self::index_file( - db, - file_id, - &path, - project_id, - &module_index, - config.prefix.as_ref(), - ) + Self::index_file(db, file_id, &path, project_id, &module_index) }) }) .flatten() @@ -948,7 +929,6 @@ impl GleanIndexer { path: &VfsPath, project_id: ProjectId, module_index: &FxHashMap, - prefix: Option<&String>, ) -> Option<( FileFact, FileLinesFact, @@ -957,7 +937,7 @@ impl GleanIndexer { Option<(Vec, XRefFact)>, Option, )> { - let file_fact = Self::file_fact(db, file_id, path, project_id, prefix)?; + let file_fact = Self::file_fact(db, file_id, path, project_id)?; let line_fact = Self::line_fact(db, file_id); let mut xref_v2 = Self::xrefs_v2(db, file_id, module_index); let mut file_decl = Self::declarations_v2(db, file_id, path)?; @@ -1014,7 +994,7 @@ impl GleanIndexer { .filter(|text| !text.is_empty()) }); - // @fb-only + // @fb-only: let exdoc_link = elp_ide::meta_only::exdoc_links::module_exdoc_link(&module, &sema); let exdoc_link: Option = None; // @oss-only ModuleFact::new( @@ -1173,16 +1153,12 @@ impl GleanIndexer { file_id: FileId, path: &VfsPath, project_id: ProjectId, - prefix: Option<&String>, ) -> Option { let project_data = db.project_data(project_id); let root = project_data.root_dir.as_path(); let file_path = path.as_path()?; let file_path = file_path.strip_prefix(root)?; - let file_path = match prefix { - Some(prefix) => Path::new(&prefix).join(file_path).to_str()?.into(), - None => file_path.as_str().to_string(), - }; + let file_path = file_path.as_str().to_string(); Some(FileFact::new(file_id, file_path)) } @@ -1556,7 +1532,7 @@ impl GleanIndexer { }) => { let def = macro_def.as_ref()?; let mut resolved = Self::resolve_macro_v2(sema, def, source_file, ctx)?; - // @fb-only + // @fb-only: meta_only::resolve_macro_expansion(sema, *expansion, ctx, &mut resolved); Some(resolved) } hir::AnyExpr::Pat(Pat::MacroCall { macro_def, .. }) @@ -1584,7 +1560,7 @@ impl GleanIndexer { vars: FxHashMap<&Location, &String>, ) -> Vec { let mut result = vec![]; - if !db.is_eqwalizer_enabled(file_id, false) { + if !db.is_eqwalizer_enabled(file_id) { return result; } let module_diagnostics = db.eqwalizer_diagnostics_by_project(project_id, vec![file_id]); @@ -1899,9 +1875,9 @@ impl GleanIndexer { let source_file = sema.parse(file_id); let range = Self::find_range(sema, ctx, &source_file, &expr_source)?; - // @fb-only - // @fb-only - // @fb-only + // @fb-only: use elp_ide::meta_only::wam_links; + // @fb-only: let wam_ctx = wam_links::WamEventCtx::new(sema.db.upcast()); + // @fb-only: let wam_url = wam_ctx.build_wam_link(name).map(|link| link.url()); let wam_url = None; // @oss-only Some(XRef { @@ -2063,7 +2039,6 @@ mod tests { v2: true, pretty: false, multi: false, - prefix: None, }; let mut module_index = FxHashMap::default(); module_index.insert(file_id.into(), module_name.to_string()); @@ -2090,25 +2065,6 @@ mod tests { ); } - #[test] - fn file_fact_prefix_test() { - let spec = r#" - //- /glean/app_glean/src/glean_module2.erl - -module(glean_module2). - "#; - let config = IndexConfig { - multi: false, - prefix: Some("my/prefix".to_string()), - }; - let result = facts_with_annotations_with_config(spec, config).0; - assert_eq!(result.file_facts.len(), 1); - let file_fact = &result.file_facts[0]; - assert_eq!( - file_fact.file_path.as_str(), - "my/prefix/glean/app_glean/src/glean_module2.erl" - ); - } - #[test] fn line_fact_with_new_line_test() { let spec = r#" @@ -2379,10 +2335,10 @@ mod tests { fn xref_types_test() { let spec = r#" //- /glean/app_glean/src/glean_module81.erl - -type small() :: #{non_neg_integer() | infinity}. + -type small() :: {non_neg_integer() | infinity}. //- /glean/app_glean/src/glean_module8.erl - -type huuuge() :: #{non_neg_integer() | infinity}. + -type huuuge() :: {non_neg_integer() | infinity}. -spec baz( A :: huuuge(), %% ^^^^^^ glean_module8/huuuge/0 @@ -2437,10 +2393,10 @@ mod tests { fn xref_types_v2_test() { let spec = r#" //- /glean/app_glean/src/glean_module81.erl - -type small() :: #{non_neg_integer() | infinity}. + -type small() :: {non_neg_integer() | infinity}. //- /glean/app_glean/src/glean_module8.erl - -type huuuge() :: #{non_neg_integer() | infinity}. + -type huuuge() :: {non_neg_integer() | infinity}. -spec baz( A :: huuuge(), %% ^^^^^^ glean_module8.erl/type/huuuge/0 diff --git a/crates/elp/src/bin/lint_cli.rs b/crates/elp/src/bin/lint_cli.rs index 4ddf80b5c2..241a3d4481 100644 --- a/crates/elp/src/bin/lint_cli.rs +++ b/crates/elp/src/bin/lint_cli.rs @@ -13,13 +13,14 @@ use std::fs; use std::fs::File; use std::io::Write; use std::path::Path; -use std::path::PathBuf; use std::str; use std::sync::Arc; +use std::thread; use std::time::SystemTime; use anyhow::Result; use anyhow::bail; +use crossbeam_channel::unbounded; use elp::build::load; use elp::build::types::LoadResult; use elp::cli::Cli; @@ -52,17 +53,16 @@ use elp_ide::elp_ide_db::elp_base_db::ProjectId; use elp_ide::elp_ide_db::elp_base_db::Vfs; use elp_ide::elp_ide_db::elp_base_db::VfsPath; use elp_ide::elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextSize; use elp_log::telemetry; use elp_project_model::AppName; use elp_project_model::AppType; use elp_project_model::DiscoverConfig; use elp_project_model::buck::BuckQueryConfig; -use elp_text_edit::TextSize; use fxhash::FxHashMap; use fxhash::FxHashSet; use hir::FormIdx; use hir::InFile; -use indicatif::ParallelProgressIterator; use itertools::Itertools; use paths::Utf8PathBuf; use rayon::prelude::ParallelBridge; @@ -132,47 +132,101 @@ pub fn load_project( ) } -fn do_parse_all( - cli: &dyn Cli, +fn do_diagnostics_all( + cli: &mut dyn Cli, analysis: &Analysis, project_id: &ProjectId, config: &DiagnosticsConfig, args: &Lint, -) -> Result> { + loaded: &LoadResult, + module: &Option, +) -> Result<(Vec<(String, FileId, DiagnosticCollection)>, bool, bool)> { let module_index = analysis.module_index(*project_id).unwrap(); - let module_iter = module_index.iter_own(); let ignored_apps: FxHashSet>> = args .ignore_apps .iter() .map(|name| Some(Some(AppName(name.to_string())))) .collect(); - let pb = cli.progress(module_iter.len() as u64, "Parsing modules"); let app_name = args.app.as_ref().map(|name| AppName(name.to_string())); - Ok(module_iter - .par_bridge() - .progress_with(pb) - .map_with( - analysis.clone(), - |db, (module_name, _file_source, file_id)| { - if !otp_file_to_ignore(db, file_id) - && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) - && !ignored_apps.contains(&db.file_app_name(file_id).ok()) - && (app_name.is_none() - || db.file_app_name(file_id).ok().as_ref() == Some(&app_name)) - { - do_parse_one(db, config, file_id, module_name.as_str(), args).unwrap() - } else { - None - } - }, - ) - .flatten() - .collect()) + // Create a channel for streaming results + let (tx, rx) = unbounded(); + + // Collect modules into an owned vector + let modules: Vec<_> = module_index + .iter_own() + .map(|(name, source, file_id)| (name.as_str().to_string(), source, file_id)) + .collect(); + + let analysis_clone = analysis.clone(); + let config_clone = config.clone(); + let args_clone = args.clone(); + + let join_handle = thread::spawn(move || { + modules + .into_iter() + .par_bridge() + .map_with( + (analysis_clone, tx), + |(db, tx), (module_name, _file_source, file_id)| { + if !otp_file_to_ignore(db, file_id) + && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) + && !ignored_apps.contains(&db.file_app_name(file_id).ok()) + && (app_name.is_none() + || db.file_app_name(file_id).ok().as_ref() == Some(&app_name)) + && let Ok(Some(result)) = do_diagnostics_one( + db, + &config_clone, + file_id, + &module_name, + &args_clone, + ) + { + // Send result through channel + let _ = tx.send(result); + } + }, + ) + .for_each(|_| {}); // Consume the iterator + }); + + // Collect results as they arrive from the channel + let mut results = Vec::new(); + let mut err_in_diag = false; + let mut module_count = 0; + let mut any_diagnostics_printed = false; + + for result in rx { + let printed = if args.skip_stream_print() { + false + } else { + print_diagnostic_result( + cli, + analysis, + config, + args, + loaded, + module, + &mut err_in_diag, + &mut module_count, + &result, + )? + }; + any_diagnostics_printed = any_diagnostics_printed || printed; + results.push(result); + } + + // Wait for the thread to complete before returning + // This ensures that analysis_clone is dropped and its read lock is released + join_handle + .join() + .expect("Failed to join diagnostics thread"); + + Ok((results, err_in_diag, any_diagnostics_printed)) } -fn do_parse_one( +fn do_diagnostics_one( db: &Analysis, config: &DiagnosticsConfig, file_id: FileId, @@ -239,6 +293,8 @@ pub fn do_codemod( ) -> Result<()> { // Declare outside the block so it has the right lifetime for filter_diagnostics let res; + let streamed_err_in_diag; + let mut any_diagnostics_printed = false; let mut initial_diags = { // We put this in its own block so that analysis is // freed before we apply lints. To apply lints @@ -279,7 +335,18 @@ pub fn do_codemod( res = match (file_id, name) { (None, _) => { - do_parse_all(cli, &analysis, &loaded.project_id, diagnostics_config, args)? + let (results, err_in_diag, any_printed) = do_diagnostics_all( + cli, + &analysis, + &loaded.project_id, + diagnostics_config, + args, + loaded, + &args.module, + )?; + streamed_err_in_diag = err_in_diag; + any_diagnostics_printed = any_printed; + results } (Some(file_id), Some(name)) => { if let Some(app) = &args.app @@ -288,87 +355,124 @@ pub fn do_codemod( { panic!("Module {} does not belong to app {}", name.as_str(), app) } - do_parse_one(&analysis, diagnostics_config, file_id, &name, args)? - .map_or(vec![], |x| vec![x]) + let result = + do_diagnostics_one(&analysis, diagnostics_config, file_id, &name, args)? + .map_or(vec![], |x| vec![x]); + + // Print diagnostics for the single file + let mut err_in_diag = false; + let mut module_count = 0; + if args.skip_stream_print() { + any_diagnostics_printed = false; + } else { + for r in &result { + let printed = print_diagnostic_result( + cli, + &analysis, + diagnostics_config, + args, + loaded, + &args.module, + &mut err_in_diag, + &mut module_count, + r, + )?; + any_diagnostics_printed = any_diagnostics_printed || printed; + } + } + + streamed_err_in_diag = err_in_diag; + result } (Some(file_id), _) => { panic!("Could not get name from file_id for {file_id:?}") } }; - filter_diagnostics( - &analysis, - &args.module, - Some(&diagnostics_config.enabled), - &res, - &FxHashSet::default(), - )? + res }; - if initial_diags.is_empty() { - if args.is_format_normal() { - writeln!(cli, "No diagnostics reported")?; - } - } else { - initial_diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); - let mut err_in_diag = false; - if args.is_format_json() { - for (_name, file_id, diags) in &initial_diags { - if args.print_diags { - for diag in diags { - // We use JSON output for CI, and want to see warnings too. - // So do not filter on errors only - err_in_diag = true; - let vfs_path = loaded.vfs.file_path(*file_id); - let analysis = loaded.analysis(); - let root_path = &analysis - .project_data(*file_id) - .unwrap_or_else(|_err| panic!("could not find project data")) - .unwrap_or_else(|| panic!("could not find project data")) - .root_dir; - let relative_path = reporting::get_relative_path(root_path, vfs_path); - let prefix = args.prefix.as_ref(); - print_diagnostic_json( - diag, - &analysis, - *file_id, - with_prefix(relative_path, prefix).as_path(), - args.use_cli_severity, - cli, - )?; - } - } - } - } else { - writeln!( - cli, - "Diagnostics reported in {} modules:", - initial_diags.len() - )?; + let mut err_in_diag = streamed_err_in_diag; + // At this point, the analysis variable from above is dropped - for (name, file_id, diags) in &initial_diags { - writeln!(cli, " {}: {}", name, diags.len())?; - if args.print_diags { - for diag in diags { - if let diagnostics::Severity::Error = diag.severity { - err_in_diag = true; - }; - print_diagnostic( - diag, - &loaded.analysis(), - *file_id, - args.use_cli_severity, - cli, - )?; - } - } - } + // When streaming is disabled (--no-stream) and we're not applying fixes, + // we need to print diagnostics now since they weren't printed during streaming + if args.no_stream && !args.apply_fix && !initial_diags.is_empty() { + let analysis = loaded.analysis(); + let mut module_count = 0; + initial_diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); + for result in &initial_diags { + let printed = print_diagnostic_result( + cli, + &analysis, + diagnostics_config, + args, + loaded, + &args.module, + &mut err_in_diag, + &mut module_count, + result, + )?; + any_diagnostics_printed = any_diagnostics_printed || printed; } - if args.apply_fix && diagnostics_config.enabled.all_enabled() { + } + + // Handle apply_fix case separately since it needs to filter diagnostics anyway + if args.apply_fix { + if diagnostics_config.enabled.all_enabled() { bail!( "We cannot apply fixes if all diagnostics enabled. Perhaps provide --diagnostic-filter" ); } - if args.apply_fix && !diagnostics_config.enabled.all_enabled() { + + let mut filtered_diags = { + let analysis = loaded.analysis(); + filter_diagnostics( + &analysis, + &args.module, + Some(&diagnostics_config.enabled), + &initial_diags, + &FxHashSet::default(), + )? + }; + + if filtered_diags.is_empty() { + if args.is_format_normal() { + writeln!(cli, "No diagnostics reported")?; + } + } else { + if args.skip_stream_print() { + filtered_diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); + let module_count: &mut i32 = &mut 0; + let has_diagnostics: &mut bool = &mut false; + if args.is_format_json() { + do_print_diagnostics_json_filtered( + cli, + args, + loaded, + &mut err_in_diag, + module_count, + has_diagnostics, + &filtered_diags, + )?; + } else { + { + // Scope the analysis instance to ensure it's dropped before creating Lints + let analysis = loaded.analysis(); + do_print_diagnostics_filtered( + cli, + &analysis, + args, + loaded, + &mut err_in_diag, + module_count, + has_diagnostics, + &filtered_diags, + )?; + // Analysis is dropped here + } + } + } + let mut changed_files = FxHashSet::default(); let mut lints = Lints::new( &mut loaded.analysis_host, @@ -376,7 +480,7 @@ pub fn do_codemod( &mut loaded.vfs, args, &mut changed_files, - initial_diags, + filtered_diags, ); // We handle the fix application result here, so // the overall status of whether error-severity @@ -388,14 +492,225 @@ pub fn do_codemod( writeln!(cli, "Apply fix failed: {err:#}").ok(); } }; + + if err_in_diag { + bail!("Errors found") + } } - if err_in_diag { + } else { + // Non-apply-fix case: rely on any_diagnostics_printed which is set + // correctly based on filtered diagnostics during streaming/batch printing + if !any_diagnostics_printed { + if args.is_format_normal() { + writeln!(cli, "No diagnostics reported")?; + } + } else if err_in_diag { bail!("Errors found") } } Ok(()) } +#[allow(clippy::too_many_arguments)] +fn print_diagnostic_result( + cli: &mut dyn Cli, + analysis: &Analysis, + config: &DiagnosticsConfig, + args: &Lint, + loaded: &LoadResult, + module: &Option, + err_in_diag: &mut bool, + module_count: &mut i32, + result: &(String, FileId, DiagnosticCollection), +) -> Result { + if args.is_format_json() { + do_print_diagnostic_collection_json( + cli, + analysis, + config, + args, + loaded, + module, + err_in_diag, + module_count, + result, + ) + } else { + do_print_diagnostic_collection( + cli, + analysis, + config, + args, + loaded, + module, + err_in_diag, + module_count, + result, + ) + } +} + +#[allow(clippy::too_many_arguments)] +fn do_print_diagnostic_collection( + cli: &mut dyn Cli, + analysis: &Analysis, + config: &DiagnosticsConfig, + args: &Lint, + loaded: &LoadResult, + module: &Option, + err_in_diag: &mut bool, + module_count: &mut i32, + result: &(String, FileId, DiagnosticCollection), +) -> Result { + let single_result = vec![result.clone()]; + let mut has_diagnostics = false; + if let Ok(filtered) = filter_diagnostics( + analysis, + module, + Some(&config.enabled), + &single_result, + &FxHashSet::default(), + ) { + do_print_diagnostics_filtered( + cli, + analysis, + args, + loaded, + err_in_diag, + module_count, + &mut has_diagnostics, + &filtered, + )?; + } + Ok(has_diagnostics) +} + +#[allow(clippy::too_many_arguments)] +fn do_print_diagnostics_filtered( + cli: &mut dyn Cli, + analysis: &Analysis, + args: &Lint, + loaded: &LoadResult, + err_in_diag: &mut bool, + module_count: &mut i32, + has_diagnostics: &mut bool, + filtered: &[(String, FileId, Vec)], +) -> Result<(), anyhow::Error> { + let _: () = for (name, file_id, diags) in filtered { + if !diags.is_empty() { + *has_diagnostics = true; + if *module_count == 0 { + writeln!(cli, "Diagnostics reported:")?; + } + *module_count += 1; + if !args.print_diags { + writeln!(cli, " {}: {}", name, diags.len())?; + } else { + for diag in diags { + if let diagnostics::Severity::Error = diag.severity { + *err_in_diag = true; + }; + // Get relative path for diagnostic output + let vfs_path = loaded.vfs.file_path(*file_id); + let root_path = &analysis + .project_data(*file_id) + .unwrap_or_else(|_err| panic!("could not find project data")) + .unwrap_or_else(|| panic!("could not find project data")) + .root_dir; + let relative_path = reporting::get_relative_path(root_path, vfs_path); + print_diagnostic( + diag, + analysis, + &loaded.vfs, + *file_id, + Some(relative_path), + args.use_cli_severity, + cli, + )?; + } + } + } + }; + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn do_print_diagnostic_collection_json( + cli: &mut dyn Cli, + analysis: &Analysis, + config: &DiagnosticsConfig, + args: &Lint, + loaded: &LoadResult, + module: &Option, + err_in_diag: &mut bool, + module_count: &mut i32, + result: &(String, FileId, DiagnosticCollection), +) -> Result { + let single_result = vec![result.clone()]; + let mut has_diagnostics = false; + if let Ok(filtered) = filter_diagnostics( + analysis, + module, + Some(&config.enabled), + &single_result, + &FxHashSet::default(), + ) { + do_print_diagnostics_json_filtered( + cli, + args, + loaded, + err_in_diag, + module_count, + &mut has_diagnostics, + &filtered, + )?; + } + Ok(has_diagnostics) +} + +fn do_print_diagnostics_json_filtered( + cli: &mut dyn Cli, + args: &Lint, + loaded: &LoadResult, + err_in_diag: &mut bool, + module_count: &mut i32, + has_diagnostics: &mut bool, + filtered: &[(String, FileId, Vec)], +) -> Result<(), anyhow::Error> { + let _: () = for (name, file_id, diags) in filtered { + if !diags.is_empty() { + *has_diagnostics = true; + *module_count += 1; + if !args.print_diags { + writeln!(cli, " {}: {}", name, diags.len())?; + } else { + for diag in diags { + *err_in_diag = true; + + // Get relative path for diagnostic output + let vfs_path = loaded.vfs.file_path(*file_id); + let analysis = loaded.analysis(); + let root_path = &analysis + .project_data(*file_id) + .unwrap_or_else(|_err| panic!("could not find project data")) + .unwrap_or_else(|| panic!("could not find project data")) + .root_dir; + let relative_path = reporting::get_relative_path(root_path, vfs_path); + print_diagnostic_json( + diag, + &analysis, + *file_id, + relative_path, + args.use_cli_severity, + cli, + )?; + } + } + } + }; + Ok(()) +} + fn get_diagnostics_config(args: &Lint) -> Result { let cfg_from_file = if args.read_config || args.config_file.is_some() { read_lint_config_file(&args.project, &args.config_file)? @@ -421,12 +736,82 @@ fn get_diagnostics_config(args: &Lint) -> Result { fn print_diagnostic( diag: &diagnostics::Diagnostic, analysis: &Analysis, + vfs: &Vfs, file_id: FileId, + path: Option<&Path>, use_cli_severity: bool, cli: &mut dyn Cli, ) -> Result<(), anyhow::Error> { let line_index = analysis.line_index(file_id)?; - writeln!(cli, " {}", diag.print(&line_index, use_cli_severity))?; + let diag_str = diag.print(&line_index, use_cli_severity); + if let Some(path) = path { + writeln!(cli, "{}:{}", path.display(), diag_str)?; + } else { + writeln!(cli, " {}", diag_str)?; + } + + // Print any related information, indented + if let Some(related_info) = &diag.related_info { + for info in related_info { + let info_line_index = analysis.line_index(info.file_id)?; + let start = info_line_index.line_col(info.range.start()); + let end = info_line_index.line_col(info.range.end()); + + // Include file identifier if related info is from a different file + if info.file_id != file_id { + let file_identifier = + if let Ok(Some(module_name)) = analysis.module_name(info.file_id) { + // It's a module (.erl file), use module name + format!("[{}]", module_name.as_str()) + } else { + // Not a module (e.g., include file), use relative path + let vfs_path = vfs.file_path(info.file_id); + if let Ok(Some(project_data)) = analysis.project_data(info.file_id) { + let relative_path = + reporting::get_relative_path(&project_data.root_dir, vfs_path); + format!("[{}]", relative_path.display()) + } else { + // Fallback: just show location without file identifier + String::new() + } + }; + + if file_identifier.is_empty() { + writeln!( + cli, + " {}:{}-{}:{}: {}", + start.line + 1, + start.col_utf16 + 1, + end.line + 1, + end.col_utf16 + 1, + info.message + )?; + } else { + writeln!( + cli, + " {} {}:{}-{}:{}: {}", + file_identifier, + start.line + 1, + start.col_utf16 + 1, + end.line + 1, + end.col_utf16 + 1, + info.message + )?; + } + } else { + writeln!( + cli, + " {}:{}-{}:{}: {}", + start.line + 1, + start.col_utf16 + 1, + end.line + 1, + end.col_utf16 + 1, + info.message + )?; + } + } + } + Ok(()) } @@ -611,13 +996,10 @@ impl<'a> Lints<'a> { if self.args.check_eqwalize_all { writeln!(cli, "Running eqwalize-all to check for knock-on problems.")?; } - let diags = do_parse_one( - &self.analysis_host.analysis(), - self.cfg, - file_id, - &name, - self.args, - )?; + let diags = { + let analysis = self.analysis_host.analysis(); + do_diagnostics_one(&analysis, self.cfg, file_id, &name, self.args)? + }; let err_in_diags = diags.iter().any(|(_, file_id, diags)| { let diags = diags.diagnostics_for(*file_id); diags @@ -628,14 +1010,15 @@ impl<'a> Lints<'a> { bail!("Applying change introduces an error diagnostic"); } else { self.changed_files.insert((file_id, name.clone())); - let changes = changes - .iter() - .filter_map(|d| { - form_from_diff(&self.analysis_host.analysis(), file_id, d) - }) - .collect::>(); + let changed_forms = { + let analysis = self.analysis_host.analysis(); + changes + .iter() + .filter_map(|d| form_from_diff(&analysis, file_id, d)) + .collect::>() + }; - for form_id in &changes { + for form_id in &changed_forms { self.changed_forms.insert(InFile::new(file_id, *form_id)); } @@ -648,24 +1031,24 @@ impl<'a> Lints<'a> { .flatten() .collect::>(); - let new_diagnostics = filter_diagnostics( - &self.analysis_host.analysis(), - &None, - None, - &new_diags, - &self.changed_forms, - )?; + let new_diagnostics = { + let analysis = self.analysis_host.analysis(); + filter_diagnostics(&analysis, &None, None, &new_diags, &self.changed_forms)? + }; self.diags = diagnostics_by_file_id(&new_diagnostics); if !self.diags.is_empty() { writeln!(cli, "---------------------------------------------\n")?; writeln!(cli, "New filtered diagnostics")?; + let analysis = self.analysis_host.analysis(); for (file_id, (name, diags)) in &self.diags { writeln!(cli, " {}: {}", name, diags.len())?; for diag in diags.iter() { print_diagnostic( diag, - &self.analysis_host.analysis(), + &analysis, + self.vfs, *file_id, + None, self.args.use_cli_severity, cli, )?; @@ -737,10 +1120,13 @@ impl<'a> Lints<'a> { if format_normal { writeln!(cli, "---------------------------------------------\n")?; writeln!(cli, "Applying fix in module '{name}' for")?; + let analysis = self.analysis_host.analysis(); print_diagnostic( diagnostic, - &self.analysis_host.analysis(), + &analysis, + self.vfs, file_id, + None, self.args.use_cli_severity, cli, )?; @@ -807,7 +1193,9 @@ impl<'a> Lints<'a> { print_diagnostic( &diagnostic, &self.analysis_host.analysis(), + self.vfs, file_id, + None, self.args.use_cli_severity, cli, )?; @@ -942,13 +1330,6 @@ fn get_form_id_at_offset( Some(form_id) } -fn with_prefix(path: &Path, prefix: Option<&String>) -> PathBuf { - match prefix { - Some(prefix) => Path::new(prefix).join(path), - None => path.into(), - } -} - #[cfg(test)] mod tests { use std::ffi::OsString; @@ -1088,11 +1469,11 @@ mod tests { head_mismatcX(0) -> 0. "#, expect![[r#" - module specified: lints - Diagnostics reported in 1 modules: - lints: 1 - 4:2-4:15::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' - "#]], + module specified: lints + Diagnostics reported: + app_a/src/lints.erl:5:3-5:16::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 4:3-4:16: Mismatched clause name + "#]], expect![""], ); } @@ -1109,9 +1490,8 @@ mod tests { "#, expect![[r#" module specified: lints - Diagnostics reported in 1 modules: - lints: 1 - 2:2-2:5::[Warning] [L1230] function foo/0 is unused + Diagnostics reported: + app_a/src/lints.erl:3:3-3:6::[Warning] [L1230] function foo/0 is unused "#]], expect![""], ); diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 16b7bbdfbe..56535e4492 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -40,7 +40,7 @@ mod erlang_service_cli; mod explain_cli; mod glean; mod lint_cli; -// @fb-only +// @fb-only: mod meta_only; mod reporting; mod shell; mod ssr_cli; @@ -65,9 +65,14 @@ const THREAD_STACK_SIZE: usize = 10_000_000; fn main() { let _timer = timeit!("main"); - let mut cli = cli::Real::default(); let args = args::args().run(); - let res = try_main(&mut cli, args); + let use_color = args.should_use_color(); + let mut cli: Box = if use_color { + Box::new(cli::Real::default()) + } else { + Box::new(cli::NoColor::default()) + }; + let res = try_main(&mut *cli, args); let code = handle_res(res, cli.err()); process::exit(code); } @@ -105,7 +110,7 @@ fn setup_cli_telemetry(args: &Args) { } _ => { // Initialize CLI telemetry, if used - // @fb-only + // @fb-only: meta_only::initialize_telemetry(); } } } @@ -119,6 +124,7 @@ fn try_main(cli: &mut dyn Cli, args: Args) -> Result<()> { setup_thread_pool(); }); let query_config = args.query_config(); + let use_color = args.should_use_color(); match args.command { args::Command::RunServer(_) => run_server(logger)?, args::Command::ParseAll(args) => erlang_service_cli::parse_all(&args, cli, &query_config)?, @@ -136,7 +142,9 @@ fn try_main(cli: &mut dyn Cli, args: Args) -> Result<()> { args::Command::BuildInfo(args) => build_info_cli::save_build_info(args, &query_config)?, args::Command::ProjectInfo(args) => build_info_cli::save_project_info(args, &query_config)?, args::Command::Lint(args) => lint_cli::run_lint_command(&args, cli, &query_config)?, - args::Command::Ssr(args) => ssr_cli::run_ssr_command(&args, cli, &query_config)?, + args::Command::Ssr(ssr_args) => { + ssr_cli::run_ssr_command(&ssr_args, cli, &query_config, use_color)? + } args::Command::GenerateCompletions(args) => { let instructions = args::gen_completions(&args.shell); writeln!(cli, "#Please run this:\n{instructions}")? @@ -280,7 +288,7 @@ mod tests { let (_stdout, stderr, code) = elp(args_vec![ "parse-all", "--project", - "../../test_projects/standard", + "../../test/test_projects/standard", "--to", tmp.path(), ]); @@ -298,7 +306,7 @@ mod tests { fn parse_all_complete(project: &str) -> Result { // Just check the command returns. - let project_path = format!("../../test_projects/{project}"); + let project_path = format!("../../test/test_projects/{project}"); let tmp = Builder::new().prefix("elp_parse_all_").tempdir().unwrap(); let (_stdout, _stderr, code) = elp(args_vec![ "parse-all", @@ -435,33 +443,34 @@ mod tests { }) .unwrap(); + let exp_path = expect_file!(format!( + "../resources/test/{}/{}/{}.pretty", + project, + app, + module.as_str(), + )); + let (stdout, _) = cli.to_strings(); + let otp_version = OTP_VERSION.as_ref().expect("MISSING OTP VERSION"); let otp_version_regex = - regex::bytes::Regex::new(&format!("{}OTPVersionDependent", "@")) - .unwrap(); + regex::bytes::Regex::new(&format!("{}OTP([0-9]+)Only", "@")).unwrap(); let contents = analysis.file_text(file_id).unwrap(); - let otp_version_dependent = otp_version_regex - .is_match(&contents.as_bytes()[0..(2001.min(contents.len()))]); - let exp_path = { - if otp_version_dependent { - expect_file!(format!( - "../resources/test/{}/{}/{}-OTP-{}.pretty", - project, - app, - module.as_str(), - otp_version, - )) - } else { - expect_file!(format!( - "../resources/test/{}/{}/{}.pretty", - project, - app, - module.as_str(), - )) + let otp_version_capture = otp_version_regex + .captures(&contents.as_bytes()[0..(2001.min(contents.len()))]); + if let Some((_, [otp_version_only])) = + otp_version_capture.map(|cap| cap.extract()) + { + if otp_version_only == otp_version.as_bytes() { + assert_normalised_file( + exp_path, + &stdout, + project_path.into(), + false, + ); } - }; - let (stdout, _) = cli.to_strings(); - assert_normalised_file(exp_path, &stdout, project_path.into(), false); + } else { + assert_normalised_file(exp_path, &stdout, project_path.into(), false); + } } } EqwalizerDiagnostics::NoAst { module } => { @@ -596,10 +605,7 @@ mod tests { fn eqwalize_target_diagnostics_match_snapshot_pretty() { if cfg!(feature = "buck") { simple_snapshot( - args_vec![ - "eqwalize-target", - "//whatsapp/elp/test_projects/standard:app_a", - ], + args_vec!["eqwalize-target", "//standard:app_a",], "standard", expect_file!("../resources/test/standard/eqwalize_target_diagnostics.pretty"), true, @@ -663,6 +669,24 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn parse_all_diagnostics_severity(buck: bool) { + simple_snapshot_expect_error( + args_vec![ + "parse-elp", + "--module", + "diagnostics", + "--severity", + "error" + ], + "diagnostics", + expect_file!("../resources/test/diagnostics/parse_all_diagnostics_error.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn parse_elp_file_attribute(buck: bool) { @@ -946,7 +970,9 @@ mod tests { assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); let mut buck_config = BuckConfig::default(); - buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8( + current_dir().unwrap().join(path_str.clone()), + )); let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); let prelude_cell = prelude_cell.strip_prefix("/").unwrap(); let content = content.replace(prelude_cell, "/[prelude]/"); @@ -958,38 +984,13 @@ mod tests { Some(AbsPathBuf::assert(Utf8PathBuf::from_path_buf(abs).unwrap())); let content = normalise_prelude_path(content, buck_config); + let content = sort_json(&content); + expect![[r#" { "apps": [ { - "name": "test_exec", - "dir": "/[prelude]//erlang/common_test/test_exec/src", - "src_dirs": [ - "" - ], - "extra_src_dirs": [], - "include_dirs": [], - "macros": {} - }, - { - "name": "diagnostics_app_a", - "dir": "app_a", - "src_dirs": [ - "src" - ], - "extra_src_dirs": [], - "include_dirs": [ - "include" - ], - "macros": { - "COMMON_TEST": "true", - "TEST": "true" - } - }, - { - "name": "app_a_SUITE", "dir": "app_a/test", - "src_dirs": [], "extra_src_dirs": [ "" ], @@ -997,61 +998,88 @@ mod tests { "macros": { "COMMON_TEST": "true", "TEST": "true" - } + }, + "name": "app_a_SUITE", + "src_dirs": [] }, { - "name": "common", - "dir": "/[prelude]//erlang/common_test/common", + "dir": "/[prelude]//erlang/common_test/test_exec/src", + "extra_src_dirs": [], + "include_dirs": [], + "macros": {}, + "name": "test_exec", "src_dirs": [ - "src" - ], + "" + ] + }, + { + "dir": "/[prelude]//erlang/common_test/common", "extra_src_dirs": [], "include_dirs": [ "include" ], - "macros": {} + "macros": {}, + "name": "common", + "src_dirs": [ + "src" + ] }, { - "name": "cth_hooks", "dir": "/[prelude]//erlang/common_test/cth_hooks/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [ "" ], - "macros": {} + "macros": {}, + "name": "cth_hooks", + "src_dirs": [ + "" + ] }, { - "name": "buck2_shell_utils", "dir": "/[prelude]//erlang/shell/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "buck2_shell_utils", + "src_dirs": [ + "" + ] + }, + { + "dir": "app_a", + "extra_src_dirs": [], + "include_dirs": [ + "include" + ], + "macros": { + "COMMON_TEST": "true", + "TEST": "true" + }, + "name": "diagnostics_app_a", + "src_dirs": [ + "src" + ] }, { - "name": "test_binary", "dir": "/[prelude]//erlang/common_test/test_binary/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_binary", + "src_dirs": [ + "" + ] }, { - "name": "test_cli_lib", "dir": "/[prelude]//erlang/common_test/test_cli_lib/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_cli_lib", + "src_dirs": [ + "" + ] } ], "deps": [] @@ -1066,6 +1094,12 @@ mod tests { content.replace(prelude_cell, "/[prelude]/") } + fn sort_json(content: &str) -> String { + let mut json: serde_json::Value = serde_json::from_str(content).unwrap(); + json.sort_all_objects(); + serde_json::to_string_pretty(&json).unwrap() + } + #[test] #[ignore] fn build_info_json_buck_bxl_generated() { @@ -1079,7 +1113,7 @@ mod tests { "--to", tmp_file.clone(), "--project", - path_str + path_str.clone() ]; let (stdout, stderr, code) = elp(args); assert_eq!( @@ -1094,7 +1128,9 @@ mod tests { assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); let mut buck_config = BuckConfig::default(); - buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8( + current_dir().unwrap().join(path_str.clone()), + )); let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); let prelude_cell = prelude_cell.strip_prefix("/").unwrap(); let content = content.replace(prelude_cell, "/[prelude]/"); @@ -1330,6 +1366,7 @@ mod tests { check_lint_fix( args_vec![ "lint", + "--no-stream", "--diagnostic-filter", "W0010", "--experimental", @@ -1357,6 +1394,7 @@ mod tests { check_lint_fix( args_vec![ "lint", + "--no-stream", "--diagnostic-filter", "W0010", "--experimental", @@ -1382,7 +1420,7 @@ mod tests { fn lint_config_file_used(buck: bool) { let tmp_dir = make_tmp_dir(); let tmp_path = tmp_dir.path(); - check_lint_fix_stderr( + check_lint_fix_stderr_sorted( args_vec![ "lint", "--diagnostic-filter", @@ -1416,7 +1454,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/does_not_exist.toml" + "../../test/test_projects/linter/does_not_exist.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_invalid_output.stdout"), @@ -1428,7 +1466,7 @@ mod tests { &[], false, Some(expect![[r#" - unable to read "../../test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) + unable to read "../../test/test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) "#]]), ) .expect("bad test"); @@ -1439,12 +1477,12 @@ mod tests { fn lint_custom_config_file_used(buck: bool) { let tmp_dir = make_tmp_dir(); let tmp_path = tmp_dir.path(); - check_lint_fix( + check_lint_fix_stderr_sorted( args_vec![ "lint", "--experimental", "--config-file", - "../../test_projects/linter/elp_lint_test1.toml" + "../../test/test_projects/linter/elp_lint_test1.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_output.stdout"), @@ -1455,6 +1493,7 @@ mod tests { Path::new("../resources/test/lint/lint_recursive"), &[], false, + None, ) .expect("bad test"); } @@ -1469,7 +1508,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/elp_lint_adhoc.toml", + "../../test/test_projects/linter/elp_lint_adhoc.toml", "--module", "app_b", "--apply-fix", @@ -1493,14 +1532,14 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_diagnostic_ignore(buck: bool) { - simple_snapshot( + simple_snapshot_sorted( args_vec![ "lint", "--experimental", "--diagnostic-ignore", "W0011", "--config-file", - "../../test_projects/linter/elp_lint_test_ignore.toml" + "../../test/test_projects/linter/elp_lint_test_ignore.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_ignore.stdout"), @@ -1544,7 +1583,7 @@ mod tests { &[], false, Some(expect![[r#" - failed to read "../../test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 + failed to read "../../test/test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 "#]]), ) .expect("bad test"); @@ -1553,8 +1592,8 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_diagnostics_filter_all_enabled(buck: bool) { - simple_snapshot_expect_error( - args_vec!["lint",], + simple_snapshot_expect_error_sorted( + args_vec!["lint"], "linter", expect_file!("../resources/test/linter/parse_elp_no_lint_specified_output.stdout"), buck, @@ -1562,10 +1601,24 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn lint_no_stream_produces_output(buck: bool) { + if otp::supports_eep66_sigils() { + simple_snapshot_expect_error( + args_vec!["lint", "--no-stream"], + "diagnostics", + expect_file!("../resources/test/diagnostics/lint_no_stream.stdout"), + buck, + None, + ); + } + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_diagnostics_filter_all_enabled_json(buck: bool) { - simple_snapshot_expect_error( + simple_snapshot_expect_error_sorted( args_vec!["lint", "--format", "json"], "linter", expect_file!("../resources/test/linter/parse_elp_no_lint_specified_json_output.stdout"), @@ -1592,11 +1645,11 @@ mod tests { fn lint_explicit_enable_diagnostic(buck: bool) { let tmp_dir = make_tmp_dir(); let tmp_path = tmp_dir.path(); - check_lint_fix_stderr( + check_lint_fix_stderr_sorted( args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_test2.toml" + "../../test/test_projects/linter/elp_lint_test2.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_explicit_enable_output.stdout"), @@ -1619,7 +1672,7 @@ mod tests { fn lint_json_output(buck: bool) { let tmp_dir = make_tmp_dir(); let tmp_path = tmp_dir.path(); - check_lint_fix_stderr( + check_lint_fix_stderr_sorted( args_vec![ "lint", "--diagnostic-filter", @@ -1644,38 +1697,6 @@ mod tests { .expect("bad test"); } - #[test_case(false ; "rebar")] - #[test_case(true ; "buck")] - fn lint_json_output_prefix(buck: bool) { - let tmp_dir = make_tmp_dir(); - let tmp_path = tmp_dir.path(); - check_lint_fix_stderr( - args_vec![ - "lint", - "--diagnostic-filter", - "W0010", - "--experimental", - "--format", - "json", - "--prefix", - "my/prefix" - ], - "linter", - expect_file!("../resources/test/linter/parse_elp_lint_json_output_prefix.stdout"), - 101, - buck, - None, - tmp_path, - Path::new("../resources/test/lint/lint_recursive"), - &[], - false, - Some(expect![[r#" - Errors found - "#]]), - ) - .expect("bad test"); - } - #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_applies_fix_using_to_dir(buck: bool) { @@ -1863,7 +1884,7 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_edoc(buck: bool) { - simple_snapshot( + simple_snapshot_sorted( args_vec![ "lint", "--include-edoc-diagnostics", @@ -1897,7 +1918,7 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_ct_include_tests(buck: bool) { - simple_snapshot_expect_error( + simple_snapshot_expect_error_sorted( args_vec![ "lint", "--include-ct-diagnostics", @@ -1915,8 +1936,8 @@ mod tests { #[test] fn lint_resolves_generated_includes() { if cfg!(feature = "buck") { - simple_snapshot_expect_error( - args_vec!["lint"], + simple_snapshot_expect_error_sorted( + args_vec!["lint", "--module", "top_includer",], "buck_tests_2", expect_file!("../resources/test/buck_tests_2/resolves_generated_includes.stdout"), true, @@ -1931,7 +1952,8 @@ mod tests { simple_snapshot_expect_stderror( args_vec!["lint",], "buck_bad_config", - expect_file!("../resources/test/buck_bad_config/bxl_error_message.stdout"), + // @fb-only: expect_file!("../resources/test/buck_bad_config/bxl_error_message.stdout"), + expect_file!("../resources/test/buck_bad_config/bxl_error_message_oss.stdout"), // @oss-only true, None, true, @@ -1941,11 +1963,12 @@ mod tests { #[test] fn lint_warnings_as_errors() { - simple_snapshot_expect_error( + simple_snapshot_expect_error_sorted( args_vec![ "lint", + "--no-stream" "--config-file", - "../../test_projects/linter/elp_lint_warnings_as_errors.toml" + "../../test/test_projects/linter/elp_lint_warnings_as_errors.toml" ], "linter", expect_file!("../resources/test/linter/warnings_as_errors.stdout"), @@ -1960,7 +1983,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_custom_function_matches.toml", + "../../test/test_projects/linter/elp_lint_custom_function_matches.toml", "--module", "custom_function_matches" ], @@ -1972,12 +1995,29 @@ mod tests { } #[test] - fn lint_ssr_from_config() { + fn lint_unavailable_type() { simple_snapshot( args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_ssr_adhoc.toml", + "../../test/test_projects/xref/elp_lint_unavailable_type.toml", + "--module", + "unavailable_type" + ], + "xref", + expect_file!("../resources/test/xref/unavailable_type.stdout"), + true, + None, + ) + } + + #[test] + fn lint_ssr_from_config() { + simple_snapshot_sorted( + args_vec![ + "lint", + "--config-file", + "../../test/test_projects/linter/elp_lint_ssr_adhoc.toml", ], "linter", expect_file!("../resources/test/linter/ssr_ad_hoc.stdout"), @@ -1992,7 +2032,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", + "../../test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", ], "linter", expect_file!("../resources/test/linter/ssr_ad_hoc_parse_fail.stdout"), @@ -2024,6 +2064,46 @@ mod tests { ) } + #[test] + fn lint_ssr_with_context_and_separator() { + simple_snapshot( + args_vec![ + "--colour", + "never", + "ssr", + "--context", + "2", + "--group-separator", + "====", + "{_@A, _@B}", + ], + "linter", + expect_file!("../resources/test/linter/ssr_context_separator.stdout"), + true, + None, + ) + } + + #[test] + fn lint_ssr_with_context_and_separator_color() { + simple_snapshot( + args_vec![ + "--colour", + "always", + "ssr", + "--context", + "2", + "--group-separator", + "====", + "{_@A, _@B}", + ], + "linter", + expect_file!("../resources/test/linter/ssr_context_separator_color.stdout"), + true, + None, + ) + } + #[test] fn lint_ssr_as_cli_arg_multiple_patterns() { simple_snapshot( @@ -2114,6 +2194,87 @@ mod tests { ) } + #[test] + fn lint_ssr_as_cli_dump_config() { + simple_snapshot( + args_vec!["ssr", "--dump-config", "?BAR(_@AA)", "{4}"], + "linter", + expect_file!("../resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout"), + true, + None, + ) + } + + #[test] + fn lint_ssr_as_cli_dump_config_without_info() { + simple_snapshot( + args_vec!["ssr", "--dump-config", "?BAR(_@AA)", "{4}"], + "linter", + expect_file!("../resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout"), + true, + None, + ) + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn ssr_exclude_generated_by_default(buck: bool) { + simple_snapshot( + args_vec!["ssr", "--module", "erlang_diagnostics_errors_gen", "ok"], + "diagnostics", + expect_file!("../resources/test/diagnostics/ssr_exclude_generated.stdout"), + buck, + None, + ); + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn ssr_include_generated_when_requested(buck: bool) { + simple_snapshot( + args_vec![ + "ssr", + "--module", + "erlang_diagnostics_errors_gen", + "--include-generated", + "ok" + ], + "diagnostics", + expect_file!("../resources/test/diagnostics/ssr_include_generated.stdout"), + buck, + None, + ); + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + // We cannot use `should_panic` for this test, since the OSS CI runs with the `buck` feature disabled. + // When this happens the test is translated into a no-op, which does not panic. + // TODO(T248259687): Switch to should_panic once Buck2 is available on GitHub. + // Or remove the ignore once hierarchical support is implemented. + #[ignore] // Support for hierarchical config is not implemented yet + fn lint_hierarchical_config_basic(buck: bool) { + simple_snapshot_sorted( + args_vec!["lint", "--read-config"], + "hierarchical_config", + expect_file!("../resources/test/hierarchical_config/basic.stdout"), + buck, + None, + ); + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn lint_linter_config_basic(buck: bool) { + simple_snapshot_sorted( + args_vec!["lint", "--read-config", "--no-stream"], + "linter_config", + expect_file!("../resources/test/linter_config/basic.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn eqwalizer_tests_check(buck: bool) { @@ -2245,6 +2406,117 @@ mod tests { } } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn eqwalize_with_color_vs_no_color(buck: bool) { + if otp_supported_by_eqwalizer() { + // Test with color (default) + let (mut args_color, _path) = + add_project(args_vec!["eqwalize", "app_a"], "standard", None, None); + if !buck { + args_color.push("--rebar".into()); + } + + // Test without color + let (mut args_no_color, _) = add_project( + args_vec!["--color", "never", "eqwalize", "app_a"], + "standard", + None, + None, + ); + if !buck { + args_no_color.push("--rebar".into()); + } + + let (stdout_color, stderr_color, code_color) = elp(args_color); + let (stdout_no_color, stderr_no_color, code_no_color) = elp(args_no_color); + + // Both should have same exit code + assert_eq!(code_color, code_no_color); + + // Both should have same stderr behavior + if code_color == 0 { + assert!(stderr_color.is_empty()); + assert!(stderr_no_color.is_empty()); + } + + // The content should be similar but no-color version should not contain ANSI escape codes + // ANSI color codes typically start with \x1b[ or \u{1b}[ + let _has_ansi_color = stdout_color.contains('\x1b'); + let has_ansi_no_color = stdout_no_color.contains('\x1b'); + + // With --color never, there should be no ANSI escape sequences + assert!( + !has_ansi_no_color, + "Output with --color never should not contain ANSI escape codes" + ); + + // The outputs should be functionally equivalent when ANSI codes are stripped + let stripped_color = strip_ansi_codes(&stdout_color); + assert_eq!( + stripped_color, stdout_no_color, + "Content should be identical after stripping ANSI codes" + ); + } + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn eqwalize_with_no_color_env_var(buck: bool) { + if otp_supported_by_eqwalizer() { + // Test with NO_COLOR environment variable set + unsafe { + env::set_var("NO_COLOR", "1"); + } + + let (mut args_no_color_env, _) = + add_project(args_vec!["eqwalize", "app_a"], "standard", None, None); + if !buck { + args_no_color_env.push("--rebar".into()); + } + + let (stdout_no_color_env, stderr_no_color_env, code_no_color_env) = + elp(args_no_color_env); + + // Clean up environment variable + unsafe { + env::remove_var("NO_COLOR"); + } + + // Test with normal color (for comparison) + let (mut args_color, _) = + add_project(args_vec!["eqwalize", "app_a"], "standard", None, None); + if !buck { + args_color.push("--rebar".into()); + } + + let (stdout_color, stderr_color, code_color) = elp(args_color); + + // Both should have same exit code + assert_eq!(code_color, code_no_color_env); + + // Both should have same stderr behavior + if code_color == 0 { + assert!(stderr_color.is_empty()); + assert!(stderr_no_color_env.is_empty()); + } + + // The NO_COLOR env var version should not contain ANSI escape codes + let has_ansi_no_color_env = stdout_no_color_env.contains('\x1b'); + assert!( + !has_ansi_no_color_env, + "Output with NO_COLOR env var should not contain ANSI escape codes" + ); + + // The outputs should be functionally equivalent when ANSI codes are stripped + let stripped_color = strip_ansi_codes(&stdout_color); + assert_eq!( + stripped_color, stdout_no_color_env, + "Content should be identical after stripping ANSI codes" + ); + } + } + // ----------------------------------------------------------------- #[test] @@ -2345,6 +2617,16 @@ mod tests { expected.assert_eq(&stdout); } + #[test] + fn search_help() { + let args = args::args() + .run_inner(Args::from(&["search", "--help"])) + .unwrap_err(); + let expected = expect_file!["../resources/test/ssr_help.stdout"]; + let stdout = args.unwrap_stdout(); + expected.assert_eq(&stdout); + } + #[test] fn build_info_help() { let args = args::args() @@ -2563,6 +2845,61 @@ mod tests { } } + fn simple_snapshot_expect_error_sorted( + args: Vec, + project: &str, + expected: ExpectFile, + buck: bool, + file: Option<&str>, + ) { + if !buck || cfg!(feature = "buck") { + let (mut args, path) = add_project(args, project, file, None); + if !buck { + args.push("--rebar".into()); + } + let (stdout, stderr, code) = elp(args); + assert_eq!( + code, 101, + "Expected exit code 101, got: {code}\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + let sorted_stdout = sort_lines(&stdout); + assert_normalised_file(expected, &sorted_stdout, path, false); + } + } + + fn sort_lines(s: &str) -> String { + let mut lines: Vec<&str> = s.lines().collect(); + lines.sort(); + lines.join("\n") + } + + #[track_caller] + fn simple_snapshot_sorted( + args: Vec, + project: &str, + expected: ExpectFile, + buck: bool, + file: Option<&str>, + ) { + if !buck || cfg!(feature = "buck") { + let (mut args, path) = add_project(args, project, file, None); + if !buck { + args.push("--rebar".into()); + } + let (stdout, stderr, code) = elp(args); + assert_eq!( + code, 0, + "failed with unexpected exit code: got {code} not 0\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + let sorted_stdout = sort_lines(&stdout); + assert_normalised_file(expected, &sorted_stdout, path, false); + assert!( + stderr.is_empty(), + "expected stderr to be empty, got:\n{stderr}" + ) + } + } + fn simple_snapshot_expect_stderror( args: Vec, project: &str, @@ -2696,6 +3033,55 @@ mod tests { Ok(()) } + #[allow(clippy::too_many_arguments)] + fn check_lint_fix_stderr_sorted( + args: Vec, + project: &str, + expected: ExpectFile, + expected_code: i32, + buck: bool, + file: Option<&str>, + actual_dir: &Path, + expected_dir: &Path, + files: &[(&str, &str)], + backup_files: bool, + expected_stderr: Option, + ) -> Result<()> { + if !buck || cfg!(feature = "buck") { + let (mut args, path) = add_project(args, project, file, None); + if !buck { + args.push("--rebar".into()); + } + let orig_files = files.iter().map(|x| x.0).collect::>(); + // Take a backup. The Drop instance will restore at the end + let _backup = if backup_files { + BackupFiles::save_files(project, &orig_files) + } else { + BackupFiles::save_files(project, &[]) + }; + let (stdout, stderr, code) = elp(args); + assert_eq!( + code, expected_code, + "Expected exit code {expected_code}, got: {code}\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + if let Some(expected_stderr) = expected_stderr { + expected_stderr.assert_eq(&stderr); + } else { + expect![[""]].assert_eq(&stderr); + } + let sorted_stdout = sort_lines(&stdout); + assert_normalised_file(expected, &sorted_stdout, path, false); + for (expected_file, file) in files { + let expected = expect_file!(expected_dir.join(expected_file)); + let actual = actual_dir.join(file); + assert!(actual.exists()); + let content = fs::read_to_string(actual).unwrap(); + expected.assert_eq(content.as_str()); + } + } + Ok(()) + } + fn assert_normalised_file( expected: ExpectFile, actual: &str, @@ -2750,7 +3136,14 @@ mod tests { } fn project_path(project: &str) -> String { - format!("../../test_projects/{project}") + format!("../../test/test_projects/{project}") + } + + fn strip_ansi_codes(s: &str) -> String { + lazy_static! { + static ref ANSI_RE: Regex = Regex::new(r"\x1b\[[0-9;]*m").unwrap(); + } + ANSI_RE.replace_all(s, "").to_string() } struct BackupFiles { diff --git a/crates/elp/src/bin/reporting.rs b/crates/elp/src/bin/reporting.rs index fe38287629..7ab10459fe 100644 --- a/crates/elp/src/bin/reporting.rs +++ b/crates/elp/src/bin/reporting.rs @@ -227,9 +227,6 @@ impl Reporter for JsonReporter<'_> { diagnostics: &[EqwalizerDiagnostic], ) -> Result<()> { let line_index = self.analysis.line_index(file_id)?; - // Pass include_Tests = false so that errors for tests files that are not opted-in are tagged as - // arc_types::Severity::Disabled and don't break CI. - let eqwalizer_enabled = self.analysis.is_eqwalizer_enabled(file_id, false).unwrap(); let file_path = &self.loaded.vfs.file_path(file_id); let root_path = &self .analysis @@ -238,12 +235,8 @@ impl Reporter for JsonReporter<'_> { .root_dir; let relative_path = get_relative_path(root_path, file_path); for diagnostic in diagnostics { - let diagnostic = convert::eqwalizer_to_arc_diagnostic( - diagnostic, - &line_index, - relative_path, - eqwalizer_enabled, - ); + let diagnostic = + convert::eqwalizer_to_arc_diagnostic(diagnostic, &line_index, relative_path); let diagnostic = serde_json::to_string(&diagnostic)?; writeln!(self.cli, "{diagnostic}")?; } diff --git a/crates/elp/src/bin/shell.rs b/crates/elp/src/bin/shell.rs index 84ab218710..13ff79ed36 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -157,10 +157,9 @@ impl ShellCommand { } "eqwalize-app" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-app".into(), @@ -177,7 +176,6 @@ impl ShellCommand { rebar, app: app.into(), include_generated, - include_tests, bail_on_error: false, }))); } @@ -185,10 +183,9 @@ impl ShellCommand { } "eqwalize-all" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-all".into(), @@ -204,7 +201,6 @@ impl ShellCommand { rebar, format: None, include_generated, - include_tests, bail_on_error: false, stats: false, list_modules: false, @@ -226,10 +222,8 @@ COMMANDS: eqwalize Eqwalize specified modules --clause-coverage Use experimental clause coverage checker eqwalize-all Eqwalize all modules in the current project - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker eqwalize-app Eqwalize all modules in specified application - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker "; diff --git a/crates/elp/src/bin/ssr_cli.rs b/crates/elp/src/bin/ssr_cli.rs index 57c4f17928..36f26d4554 100644 --- a/crates/elp/src/bin/ssr_cli.rs +++ b/crates/elp/src/bin/ssr_cli.rs @@ -11,10 +11,12 @@ use std::fs; use std::path::Path; use std::str; +use std::thread; use std::time::SystemTime; use anyhow::Result; use anyhow::bail; +use crossbeam_channel::unbounded; use elp::build::load; use elp::build::types::LoadResult; use elp::cli::Cli; @@ -28,7 +30,9 @@ use elp_ide::diagnostics; use elp_ide::diagnostics::DiagnosticsConfig; use elp_ide::diagnostics::FallBackToAll; use elp_ide::diagnostics::LintConfig; +use elp_ide::diagnostics::LintsFromConfig; use elp_ide::diagnostics::MatchSsr; +use elp_ide::elp_ide_db::LineCol; use elp_ide::elp_ide_db::elp_base_db::AbsPath; use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; @@ -41,7 +45,6 @@ use elp_project_model::AppType; use elp_project_model::DiscoverConfig; use elp_project_model::buck::BuckQueryConfig; use hir::Semantic; -use indicatif::ParallelProgressIterator; use paths::Utf8PathBuf; use rayon::prelude::ParallelBridge; use rayon::prelude::ParallelIterator; @@ -62,6 +65,7 @@ pub fn run_ssr_command( args: &Ssr, cli: &mut dyn Cli, query_config: &BuckQueryConfig, + use_color: bool, ) -> Result<()> { let start_time = SystemTime::now(); let memory_start = MemoryUsage::now(); @@ -85,10 +89,17 @@ pub fn run_ssr_command( let mut lint_config = LintConfig::default(); for pattern in &args.ssr_specs { let normalized_pattern = normalize_ssr_pattern(pattern); + let severity = if args.dump_config { + // Set the severity so that squiggles are shown in the VS Code UI + Some(diagnostics::Severity::Information) + } else { + None + }; let ssr_lint = diagnostics::Lint::LintMatchSsr(MatchSsr { ssr_pattern: normalized_pattern, message: None, strategy: Some(strategy), + severity, }); lint_config.ad_hoc_lints.lints.push(ssr_lint); } @@ -105,15 +116,20 @@ pub fn run_ssr_command( .set_experimental(false) .set_use_cli_severity(false); - if diagnostics_config.enabled.all_enabled() && args.is_format_normal() { - writeln!(cli, "Reporting all diagnostics codes")?; + if args.dump_config { + let result = toml::to_string::(&diagnostics_config.lints_from_config)?; + // This is a subsection of .elp_lint.toml, add subsection prefix + let result = result.replace("[[lints]]", "[[ad_hoc_lints.lints]]"); + writeln!(cli, "\n# Add this to your .elp_lint.toml")?; + writeln!(cli, "{}", result)?; + return Ok(()); } // Load the project let mut loaded = load_project(args, cli, query_config)?; telemetry::report_elapsed_time("ssr operational", start_time); - let r = run_ssr(cli, &mut loaded, &diagnostics_config, args); + let r = run_ssr(cli, &mut loaded, &diagnostics_config, args, use_color); telemetry::report_elapsed_time("ssr done", start_time); @@ -133,6 +149,7 @@ pub fn run_ssr( loaded: &mut LoadResult, diagnostics_config: &DiagnosticsConfig, args: &Ssr, + use_color: bool, ) -> Result<()> { let analysis = loaded.analysis(); let (file_id, name) = match &args.module { @@ -166,8 +183,23 @@ pub fn run_ssr( }, }; - let mut diags = match (file_id, name) { - (None, _) => do_parse_all(cli, &analysis, &loaded.project_id, diagnostics_config, args)?, + let mut match_count = 0; + + match (file_id, name) { + (None, _) => { + // Streaming case: process all modules + let project_id = loaded.project_id; + do_parse_all_streaming( + cli, + &analysis, + &project_id, + diagnostics_config, + args, + use_color, + loaded, + &mut match_count, + )?; + } (Some(file_id), Some(name)) => { if let Some(app) = &args.app && let Ok(Some(file_app)) = analysis.file_app_name(file_id) @@ -175,42 +207,171 @@ pub fn run_ssr( { panic!("Module {} does not belong to app {}", name.as_str(), app) } - do_parse_one(&analysis, diagnostics_config, file_id, &name, args)? - .map_or(vec![], |x| vec![x]) + if let Some(diag) = do_parse_one(&analysis, diagnostics_config, file_id, &name, args)? { + match_count = 1; + print_single_result(cli, loaded, &diag, args, use_color)?; + } } (Some(file_id), _) => { panic!("Could not get name from file_id for {file_id:?}") } }; - if diags.is_empty() { + + if match_count == 0 { if args.is_format_normal() { writeln!(cli, "No matches found")?; } + } else if args.is_format_normal() { + writeln!(cli, "\nMatches found in {} modules", match_count)?; + } + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn do_parse_all_streaming( + cli: &mut dyn Cli, + analysis: &Analysis, + project_id: &ProjectId, + config: &DiagnosticsConfig, + args: &Ssr, + use_color: bool, + loaded: &mut LoadResult, + match_count: &mut usize, +) -> Result<()> { + let module_index = analysis.module_index(*project_id).unwrap(); + let app_name = args.app.as_ref().map(|name| AppName(name.to_string())); + + // Create a channel for streaming results + let (tx, rx) = unbounded(); + + // Spawn a thread to process modules in parallel and send results + let analysis_clone = analysis.clone(); + let config_clone = config.clone(); + let args_clone = args.clone(); + + // Collect modules into an owned vector + let modules: Vec<_> = module_index + .iter_own() + .map(|(name, source, file_id)| (name.as_str().to_string(), source, file_id)) + .collect(); + + thread::spawn(move || { + modules + .into_iter() + .par_bridge() + .map_with( + (analysis_clone, tx), + |(db, tx), (module_name, _file_source, file_id)| { + if !otp_file_to_ignore(db, file_id) + && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) + && (app_name.is_none() + || db.file_app_name(file_id).ok().as_ref() == Some(&app_name)) + && let Ok(Some(result)) = + do_parse_one(db, &config_clone, file_id, &module_name, &args_clone) + { + // Send result through channel + let _ = tx.send(result); + } + }, + ) + .for_each(|_| {}); // Consume the iterator + // Channel is dropped here, signaling end of results + }); + + // Process and print results as they arrive from the channel + for result in rx { + *match_count += 1; + print_single_result(cli, loaded, &result, args, use_color)?; + } + + Ok(()) +} + +fn print_single_result( + cli: &mut dyn Cli, + loaded: &mut LoadResult, + result: &(String, FileId, Vec), + args: &Ssr, + use_color: bool, +) -> Result<()> { + let (name, file_id, diags) = result; + + if args.is_format_json() { + for diag in diags { + let vfs_path = loaded.vfs.file_path(*file_id); + let analysis = loaded.analysis(); + let root_path = &analysis + .project_data(*file_id) + .unwrap_or_else(|_err| panic!("could not find project data")) + .unwrap_or_else(|| panic!("could not find project data")) + .root_dir; + let relative_path = reporting::get_relative_path(root_path, vfs_path); + print_diagnostic_json(diag, &analysis, *file_id, relative_path, false, cli)?; + } } else { - diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); - if args.is_format_json() { - for (_name, file_id, diags) in &diags { - for diag in diags { - // We use JSON output for CI, and want to see warnings too. - // So do not filter on errors only - let vfs_path = loaded.vfs.file_path(*file_id); - let analysis = loaded.analysis(); - let root_path = &analysis - .project_data(*file_id) - .unwrap_or_else(|_err| panic!("could not find project data")) - .unwrap_or_else(|| panic!("could not find project data")) - .root_dir; - let relative_path = reporting::get_relative_path(root_path, vfs_path); - print_diagnostic_json(diag, &analysis, *file_id, relative_path, false, cli)?; - } + writeln!(cli, " {}: {}", name, diags.len())?; + + // Determine if we should show source context + let show_source = args.show_source + || args.before_context.is_some() + || args.after_context.is_some() + || args.context.is_some() + || args.group_separator.is_some() + || args.no_group_separator; + let (before_lines, after_lines) = calculate_context_lines(args); + let has_context = before_lines > 0 || after_lines > 0; + let group_separator = should_show_group_separator(args, has_context && show_source); + + for (idx, diag) in diags.iter().enumerate() { + // Print group separator before each match (except the first) if showing source with context + if show_source + && idx > 0 + && let Some(ref sep) = group_separator + { + writeln!(cli, "{}", sep)?; } - } else { - writeln!(cli, "Matches found in {} modules:", diags.len())?; - for (name, file_id, diags) in &diags { - writeln!(cli, " {}: {}", name, diags.len())?; - for diag in diags { - print_diagnostic(diag, &loaded.analysis(), *file_id, false, cli)?; + // Get relative path for diagnostic output + let vfs_path = loaded.vfs.file_path(*file_id); + let analysis = loaded.analysis(); + let root_path = &analysis + .project_data(*file_id) + .unwrap_or_else(|_err| panic!("could not find project data")) + .unwrap_or_else(|| panic!("could not find project data")) + .root_dir; + let relative_path = reporting::get_relative_path(root_path, vfs_path); + + // Only show path when showing source context + let path_to_show = if show_source { + Some(relative_path) + } else { + None + }; + print_diagnostic(diag, &loaded.analysis(), *file_id, path_to_show, false, cli)?; + + // Only show source context if --show-source or --show-source-markers is set + if show_source { + if use_color { + print_source_with_context( + diag, + &loaded.analysis(), + *file_id, + before_lines, + after_lines, + true, + cli, + )?; + } else { + print_source_with_context_markers( + diag, + &loaded.analysis(), + *file_id, + before_lines, + after_lines, + cli, + )?; } + writeln!(cli)?; } } } @@ -233,41 +394,6 @@ fn load_project( query_config, ) } - -fn do_parse_all( - cli: &dyn Cli, - analysis: &Analysis, - project_id: &ProjectId, - config: &DiagnosticsConfig, - args: &Ssr, -) -> Result)>> { - let module_index = analysis.module_index(*project_id).unwrap(); - let module_iter = module_index.iter_own(); - - let pb = cli.progress(module_iter.len() as u64, "Scanning modules"); - let app_name = args.app.as_ref().map(|name| AppName(name.to_string())); - - Ok(module_iter - .par_bridge() - .progress_with(pb) - .map_with( - analysis.clone(), - |db, (module_name, _file_source, file_id)| { - if !otp_file_to_ignore(db, file_id) - && db.file_app_type(file_id).ok() != Some(Some(AppType::Dep)) - && (app_name.is_none() - || db.file_app_name(file_id).ok().as_ref() == Some(&app_name)) - { - do_parse_one(db, config, file_id, module_name.as_str(), args).unwrap() - } else { - None - } - }, - ) - .flatten() - .collect()) -} - fn do_parse_one( db: &Analysis, config: &DiagnosticsConfig, @@ -275,6 +401,9 @@ fn do_parse_one( name: &str, args: &Ssr, ) -> Result)>> { + if !args.include_generated && db.is_generated(file_id)? { + return Ok(None); + } if !args.include_tests && db.is_test_suite_or_test_helper(file_id)?.unwrap_or(false) { return Ok(None); } @@ -286,7 +415,6 @@ fn do_parse_one( config .lints_from_config .get_diagnostics(&mut diags, &sema, file_id); - sema.parse(file_id); diags })?; @@ -302,11 +430,17 @@ fn print_diagnostic( diag: &diagnostics::Diagnostic, analysis: &Analysis, file_id: FileId, + path: Option<&Path>, use_cli_severity: bool, cli: &mut dyn Cli, ) -> Result<(), anyhow::Error> { let line_index = analysis.line_index(file_id)?; - writeln!(cli, " {}", diag.print(&line_index, use_cli_severity))?; + let diag_str = diag.print(&line_index, use_cli_severity); + if let Some(path) = path { + writeln!(cli, "{}:{}", path.display(), diag_str)?; + } else { + writeln!(cli, " {}", diag_str)?; + } Ok(()) } @@ -330,3 +464,254 @@ fn print_diagnostic_json( )?; Ok(()) } + +/// Print a line with color highlighting +fn print_line_with_color( + line_num: usize, + line_content: &str, + is_match_line: bool, + start: &LineCol, + end: &LineCol, + current_line: u32, + cli: &mut dyn Cli, +) -> Result<(), anyhow::Error> { + // Line number in gray + write!(cli, "\x1b[90m{:4} |\x1b[0m ", line_num)?; + + if !is_match_line { + // Non-match line: print normally + writeln!(cli, "{}", line_content)?; + } else { + // Match line: highlight the matched portion + if current_line == start.line && current_line == end.line { + // Single-line match + let start_col = start.col_utf16 as usize; + let end_col = end.col_utf16 as usize; + + let before = &line_content[..start_col.min(line_content.len())]; + let matched = + &line_content[start_col.min(line_content.len())..end_col.min(line_content.len())]; + let after = &line_content[end_col.min(line_content.len())..]; + + write!(cli, "{}", before)?; + write!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold + writeln!(cli, "{}", after)?; + } else if current_line == start.line { + // First line of multi-line match + let start_col = start.col_utf16 as usize; + let before = &line_content[..start_col.min(line_content.len())]; + let matched = &line_content[start_col.min(line_content.len())..]; + + write!(cli, "{}", before)?; + writeln!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold + } else if current_line == end.line { + // Last line of multi-line match + let end_col = end.col_utf16 as usize; + let matched = &line_content[..end_col.min(line_content.len())]; + let after = &line_content[end_col.min(line_content.len())..]; + + write!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold + writeln!(cli, "{}", after)?; + } else { + // Middle line of multi-line match + writeln!(cli, "\x1b[91;1m{}\x1b[0m", line_content)?; // Red bold + } + } + + Ok(()) +} + +/// Calculate context lines from the new grep-style arguments +fn calculate_context_lines(args: &Ssr) -> (usize, usize) { + // -C/--context takes precedence and sets both before and after + if let Some(context) = args.context { + return (context, context); + } + + // Otherwise use individual before/after values, defaulting to 0 + let before = args.before_context.unwrap_or(0); + let after = args.after_context.unwrap_or(0); + (before, after) +} + +/// Determine if a group separator should be shown +fn should_show_group_separator(args: &Ssr, has_context: bool) -> Option { + // If --no-group-separator is set, don't show separator + if args.no_group_separator { + return None; + } + + // Only show separators if there's context to separate + if !has_context { + return None; + } + + // Use custom separator if provided, otherwise default to "--" + Some( + args.group_separator + .clone() + .unwrap_or_else(|| "--".to_string()), + ) +} + +/// Print source code context with the specified before/after context lines +fn print_source_with_context( + diag: &diagnostics::Diagnostic, + analysis: &Analysis, + file_id: FileId, + before_lines: usize, + after_lines: usize, + use_color: bool, + cli: &mut dyn Cli, +) -> Result<(), anyhow::Error> { + let line_index = analysis.line_index(file_id)?; + let source = &analysis.file_text(file_id)?; + + let range = diag.range; + let start = line_index.line_col(range.start()); + let end = line_index.line_col(range.end()); + + let lines: Vec<&str> = source.lines().collect(); + let total_lines = lines.len(); + + // Calculate the range of lines to display + let first_line = start.line.saturating_sub(before_lines as u32) as usize; + let last_line = ((end.line + after_lines as u32 + 1) as usize).min(total_lines); + + // Display the source context + for line_idx in first_line..last_line { + let line_num = line_idx + 1; + let line_content = lines.get(line_idx).unwrap_or(&""); + + // Check if this line contains part of the match + let is_match_line = line_idx >= start.line as usize && line_idx <= end.line as usize; + + if use_color { + print_line_with_color( + line_num, + line_content, + is_match_line, + &start, + &end, + line_idx as u32, + cli, + )?; + } else { + // Just print the line without any highlighting + write!(cli, "{:4} | ", line_num)?; + writeln!(cli, "{}", line_content)?; + } + } + + Ok(()) +} + +/// Print source code context with text markers +fn print_source_with_context_markers( + diag: &diagnostics::Diagnostic, + analysis: &Analysis, + file_id: FileId, + before_lines: usize, + after_lines: usize, + cli: &mut dyn Cli, +) -> Result<(), anyhow::Error> { + let line_index = analysis.line_index(file_id)?; + let source = &analysis.file_text(file_id)?; + + let range = diag.range; + let start = line_index.line_col(range.start()); + let end = line_index.line_col(range.end()); + + let lines: Vec<&str> = source.lines().collect(); + let total_lines = lines.len(); + + // Calculate the range of lines to display + let first_line = start.line.saturating_sub(before_lines as u32) as usize; + let last_line = ((end.line + after_lines as u32 + 1) as usize).min(total_lines); + + // Display the source context + for line_idx in first_line..last_line { + let line_num = line_idx + 1; + let line_content = lines.get(line_idx).unwrap_or(&""); + + // Check if this line contains part of the match + let is_match_line = line_idx >= start.line as usize && line_idx <= end.line as usize; + + print_line_with_markers( + line_num, + line_content, + is_match_line, + &start, + &end, + line_idx as u32, + cli, + )?; + } + + Ok(()) +} + +/// Print a line with text markers (like diagnostic carets) +fn print_line_with_markers( + line_num: usize, + line_content: &str, + is_match_line: bool, + start: &LineCol, + end: &LineCol, + current_line: u32, + cli: &mut dyn Cli, +) -> Result<(), anyhow::Error> { + // Line number + write!(cli, "{:4} | ", line_num)?; + writeln!(cli, "{}", line_content)?; + + if is_match_line { + // Print marker line with ^^^ under the match + write!(cli, " | ")?; // Indent to match line content + + if current_line == start.line && current_line == end.line { + // Single-line match + let start_col = start.col_utf16 as usize; + let end_col = end.col_utf16 as usize; + let marker_len = (end_col - start_col).max(1); + + // Spaces before the marker + for _ in 0..start_col { + write!(cli, " ")?; + } + // Marker carets + for _ in 0..marker_len { + write!(cli, "^")?; + } + writeln!(cli)?; + } else if current_line == start.line { + // First line of multi-line match + let start_col = start.col_utf16 as usize; + let marker_len = line_content.len().saturating_sub(start_col).max(1); + + for _ in 0..start_col { + write!(cli, " ")?; + } + for _ in 0..marker_len { + write!(cli, "^")?; + } + writeln!(cli)?; + } else if current_line == end.line { + // Last line of multi-line match + let end_col = end.col_utf16 as usize; + + for _ in 0..end_col { + write!(cli, "^")?; + } + writeln!(cli)?; + } else { + // Middle line of multi-line match + for _ in 0..line_content.len() { + write!(cli, "^")?; + } + writeln!(cli)?; + } + } + + Ok(()) +} diff --git a/crates/elp/src/cli.rs b/crates/elp/src/cli.rs index b3070768d6..0678aaebc1 100644 --- a/crates/elp/src/cli.rs +++ b/crates/elp/src/cli.rs @@ -30,18 +30,13 @@ pub trait Cli: Write + WriteColor { fn err(&mut self) -> &mut dyn Write; } -pub struct Real(StandardStream, Stderr); +pub struct StandardCli(StandardStream, Stderr); -impl Default for Real { - fn default() -> Self { - Self( - StandardStream::stdout(ColorChoice::Always), - std::io::stderr(), - ) +impl StandardCli { + fn new(color_choice: ColorChoice) -> Self { + Self(StandardStream::stdout(color_choice), std::io::stderr()) } -} -impl Real { fn progress_with_style( &self, len: u64, @@ -59,7 +54,7 @@ impl Real { } } -impl Cli for Real { +impl Cli for StandardCli { fn progress(&self, len: u64, prefix: &'static str) -> ProgressBar { self.progress_with_style(len, prefix, " {prefix:25!} {bar} {pos}/{len} {wide_msg}") } @@ -84,6 +79,63 @@ impl Cli for Real { } } +impl Write for StandardCli { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush() + } +} + +impl WriteColor for StandardCli { + fn supports_color(&self) -> bool { + self.0.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> std::io::Result<()> { + self.0.set_color(spec) + } + + fn reset(&mut self) -> std::io::Result<()> { + self.0.reset() + } +} + +pub struct Real(StandardCli); +pub struct NoColor(StandardCli); + +impl Default for Real { + fn default() -> Self { + Real(StandardCli::new(ColorChoice::Always)) + } +} + +impl Default for NoColor { + fn default() -> Self { + NoColor(StandardCli::new(ColorChoice::Never)) + } +} + +impl Cli for Real { + fn progress(&self, len: u64, prefix: &'static str) -> ProgressBar { + self.0.progress(len, prefix) + } + + fn simple_progress(&self, len: u64, prefix: &'static str) -> ProgressBar { + self.0.simple_progress(len, prefix) + } + + fn spinner(&self, prefix: &'static str) -> ProgressBar { + self.0.spinner(prefix) + } + + fn err(&mut self) -> &mut dyn Write { + self.0.err() + } +} + impl Write for Real { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.0.write(buf) @@ -108,6 +160,48 @@ impl WriteColor for Real { } } +impl Cli for NoColor { + fn progress(&self, len: u64, prefix: &'static str) -> ProgressBar { + self.0.progress(len, prefix) + } + + fn simple_progress(&self, len: u64, prefix: &'static str) -> ProgressBar { + self.0.simple_progress(len, prefix) + } + + fn spinner(&self, prefix: &'static str) -> ProgressBar { + self.0.spinner(prefix) + } + + fn err(&mut self) -> &mut dyn Write { + self.0.err() + } +} + +impl Write for NoColor { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush() + } +} + +impl WriteColor for NoColor { + fn supports_color(&self) -> bool { + self.0.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> std::io::Result<()> { + self.0.set_color(spec) + } + + fn reset(&mut self) -> std::io::Result<()> { + self.0.reset() + } +} + pub struct Fake(Buffer, Vec); impl Default for Fake { diff --git a/crates/elp/src/config.rs b/crates/elp/src/config.rs index 2637eae5a2..681a022261 100644 --- a/crates/elp/src/config.rs +++ b/crates/elp/src/config.rs @@ -30,7 +30,7 @@ use serde::de::DeserializeOwned; use serde_json::json; use crate::from_json; -// @fb-only +// @fb-only: use crate::meta_only; // Defines the server-side configuration of ELP. We generate *parts* // of VS Code's `package.json` config from this. @@ -180,7 +180,7 @@ impl Config { return; } self.data = ConfigData::from_json(json); - // @fb-only + // @fb-only: meta_only::harmonise_gks(self); } pub fn update_gks(&mut self, json: serde_json::Value) { diff --git a/crates/elp/src/convert.rs b/crates/elp/src/convert.rs index 3f68488f7d..c8db07a5d0 100644 --- a/crates/elp/src/convert.rs +++ b/crates/elp/src/convert.rs @@ -26,6 +26,7 @@ use elp_ide::elp_ide_db::assists::AssistContextDiagnostic; use elp_ide::elp_ide_db::assists::AssistContextDiagnosticCode; use elp_ide::elp_ide_db::elp_base_db::AbsPath; use elp_ide::elp_ide_db::elp_base_db::AbsPathBuf; +use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::VfsPath; use lsp_types::DiagnosticRelatedInformation; use lsp_types::Location; @@ -67,11 +68,14 @@ pub fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity } } -pub fn ide_to_lsp_diagnostic( +pub fn ide_to_lsp_diagnostic( line_index: &LineIndex, - url: &Url, d: &Diagnostic, -) -> lsp_types::Diagnostic { + get_file_info: F, +) -> lsp_types::Diagnostic +where + F: Fn(FileId) -> Option<(LineIndex, Url)>, +{ let code_description = match &d.code_doc_uri { Some(uri) => match lsp_types::Url::parse(uri) { Ok(href) => Some(lsp_types::CodeDescription { href }), @@ -90,7 +94,7 @@ pub fn ide_to_lsp_diagnostic( code_description, source, message: d.message.clone(), - related_information: from_related(line_index, url, &d.related_info), + related_information: from_related(get_file_info, &d.related_info), tags: d.tag.as_ref().map(lsp_diagnostic_tags), data: None, } @@ -122,18 +126,11 @@ pub fn eqwalizer_to_arc_diagnostic( d: &EqwalizerDiagnostic, line_index: &LineIndex, relative_path: &Path, - eqwalizer_enabled: bool, ) -> arc_types::Diagnostic { let pos = position(line_index, d.range.start()); let line_num = pos.line + 1; let character = Some(pos.character + 1); - let severity = if eqwalizer_enabled { - arc_types::Severity::Error - } else { - // We use Severity::Disabled so that diagnostics are reported in cont lint - // but not in CI. - arc_types::Severity::Disabled - }; + let severity = arc_types::Severity::Error; // formatting: https://fburl.com/max_wiki_link_to_phabricator_rich_text let explanation = match &d.explanation { Some(s) => format!("```\n{s}\n```"), @@ -165,22 +162,26 @@ pub fn eqwalizer_to_arc_diagnostic( ) } -fn from_related( - line_index: &LineIndex, - url: &Url, +fn from_related( + get_file_info: F, r: &Option>, -) -> Option> { +) -> Option> +where + F: Fn(elp_ide::elp_ide_db::elp_base_db::FileId) -> Option<(LineIndex, Url)>, +{ r.as_ref().map(|ri| { ri.iter() - .map(|i| { + .filter_map(|i| { + // Get the line index and URL for the file that contains the related information + let (line_index, uri) = get_file_info(i.file_id)?; let location = Location { - range: range(line_index, i.range), - uri: url.clone(), + range: range(&line_index, i.range), + uri, }; - DiagnosticRelatedInformation { + Some(DiagnosticRelatedInformation { location, message: i.message.clone(), - } + }) }) .collect() }) diff --git a/crates/elp/src/lib.rs b/crates/elp/src/lib.rs index be14930f12..f462141e39 100644 --- a/crates/elp/src/lib.rs +++ b/crates/elp/src/lib.rs @@ -37,7 +37,7 @@ pub mod line_endings; pub mod lsp_ext; mod mem_docs; pub mod memory_usage; -// @fb-only +// @fb-only: mod meta_only; mod op_queue; mod project_loader; pub mod reload; @@ -108,7 +108,7 @@ pub fn otp_file_to_ignore(db: &Analysis, file_id: FileId) -> bool { "redbug_dtop", ] .iter() - // @fb-only + // @fb-only: .chain(meta_only::FILES_TO_IGNORE.iter()) .map(SmolStr::new) .collect(); } @@ -193,6 +193,7 @@ mod tests { ssr_pattern: "ssr: _@A = 10.".to_string(), message: None, strategy: None, + severity: None, }), ], }, diff --git a/crates/elp/src/project_loader.rs b/crates/elp/src/project_loader.rs index d2c76cd270..103353e5db 100644 --- a/crates/elp/src/project_loader.rs +++ b/crates/elp/src/project_loader.rs @@ -192,7 +192,7 @@ impl ReloadManager { self.get_query_config() } - fn get_query_config(&self) -> BuckQueryConfig { + pub fn get_query_config(&self) -> BuckQueryConfig { if self.buck_quick_start { match self.buck_generated { BuckGenerated::NoLoadDone => BuckQueryConfig::BuckTargetsOnly, diff --git a/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout new file mode 100644 index 0000000000..f5225605a5 --- /dev/null +++ b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout @@ -0,0 +1 @@ +Project Initialisation Failed: invalid or missing buck 2 configuration diff --git a/crates/elp/src/resources/test/buck_tests_2/resolves_generated_includes.stdout b/crates/elp/src/resources/test/buck_tests_2/resolves_generated_includes.stdout index 7787c17575..d4954b84ee 100644 --- a/crates/elp/src/resources/test/buck_tests_2/resolves_generated_includes.stdout +++ b/crates/elp/src/resources/test/buck_tests_2/resolves_generated_includes.stdout @@ -1,18 +1,7 @@ + [check_include_separate_1/include/include_with_bug.hrl] 5:14-5:18: E1507: undefined macro 'FOO' + [check_include_separate_1/include/top_includer.hrl] 3:10-3:30: E1516: can't find include file "does_not_exist.hrl" +Diagnostics reported: Reporting all diagnostics codes -Diagnostics reported in 4 modules: - app_a: 3 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 5:9-5:31::[WeakWarning] [W0037] Unspecific include. - 0:8-0:13::[WeakWarning] [W0046] The module is not documented. - auto_gen_a: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:18::[WeakWarning] [W0046] The module is not documented. - eqwalizer: 1 - 1:8-1:17::[WeakWarning] [W0046] The module is not documented. - wa_buck2_module_search: 6 - 19:0-19:18::[WeakWarning] [W0040] The function is non-trivial, exported, but not documented. - 2:8-2:30::[WeakWarning] [W0046] The module is not documented. - 54:18-54:31::[WeakWarning] [W0051] Binary string can be written using sigil syntax. - 56:18-56:28::[WeakWarning] [W0051] Binary string can be written using sigil syntax. - 56:38-56:51::[WeakWarning] [W0051] Binary string can be written using sigil syntax. - 60:30-60:40::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +check_include/src/top_includer.erl:14:5-14:11::[Error] [E1508] undefined macro 'THIRD/2' +check_include/src/top_includer.erl:6:1-6:67::[Error] [L0000] Issue in included file +module specified: top_includer \ No newline at end of file diff --git a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout new file mode 100644 index 0000000000..eef1c92c6d --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout @@ -0,0 +1,138 @@ +Reporting all diagnostics codes +Diagnostics reported: +app_a/src/app_a.erl:52:3-52:23::[Warning] [W0006] this statement has no effect +app_a/src/app_a.erl:3:10-3:21::[WeakWarning] [W0037] Unspecific include. +app_a/src/app_a.erl:27:3-27:9::[Warning] [W0017] Function 'foo:ok/0' is undefined. +app_a/src/app_a.erl:28:4-28:11::[Warning] [W0017] Function 'mod:foo/0' is undefined. +app_a/src/app_a.erl:72:4-72:11::[Warning] [W0017] Function 'foo:bar/2' is undefined. +app_a/src/app_a.erl:37:11-37:28::[Warning] [W0017] Function 'mod_name:fun_name/2' is undefined. +app_a/src/app_a.erl:58:11-58:24::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +app_a/src/app_a.erl:4:1-4:41::[Warning] [W0020] Unused file: inets/include/httpd.hrl +app_a/src/app_a.erl:39:7-39:28::[Error] [L1267] variable 'A' shadowed in 'named fun' +app_a/src/app_a.erl:55:32-55:35::[Error] [L1295] type uri/0 undefined +app_a/src/app_a.erl:56:20-56:26::[Error] [L1295] type binary/1 undefined +app_a/src/app_a.erl:72:3-72:34::[Error] [L1252] record record undefined +app_a/src/app_a.erl:75:5-75:16::[Error] [L1252] record record undefined +app_a/src/app_a.erl:35:1-35:2::[Warning] [L1230] function g/1 is unused +app_a/src/app_a.erl:35:3-35:4::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:36:3-36:4::[Warning] [L1268] variable 'F' is unused +app_a/src/app_a.erl:37:3-37:4::[Warning] [L1268] variable 'G' is unused +app_a/src/app_a.erl:38:3-38:4::[Warning] [L1268] variable 'H' is unused +app_a/src/app_a.erl:39:3-39:4::[Warning] [L1268] variable 'I' is unused +app_a/src/app_a.erl:39:7-39:28::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:41:1-41:2::[Warning] [L1230] function h/0 is unused +app_a/src/app_a.erl:45:1-45:2::[Warning] [L1230] function i/0 is unused +app_a/src/app_a.erl:50:1-50:2::[Warning] [L1230] function j/2 is unused +app_a/src/app_a.erl:50:15-50:16::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:50:23-50:24::[Warning] [L1268] variable 'B' is unused +app_a/src/app_a.erl:55:1-55:46::[Warning] [L1296] type session(_) is unused +app_a/src/app_a.erl:55:1-55:46::[Warning] [L1313] opaque type session(_) is not exported +app_a/src/app_a.erl:56:7-56:13::[Warning] [L1296] type source(_) is unused +app_a/src/app_a.erl:58:1-58:4::[Warning] [L1230] function map/2 is unused +app_a/src/app_a.erl:60:1-60:9::[Warning] [L1230] function with_dot/0 is unused +app_a/src/app_a.erl:62:1-62:9::[Warning] [L1230] function lang_dir/1 is unused +app_a/src/app_a.erl:66:1-66:7::[Warning] [L1230] function escape/1 is unused +app_a/src/app_a.erl:66:13-66:17::[Warning] [L1268] variable 'T' is unused +app_a/src/app_a.erl:67:9-67:25::[Warning] [L1260] record all_configs_file is unused +app_a/src/app_a.erl:71:1-71:2::[Warning] [L1230] function k/0 is unused +app_a/src/app_a.erl:74:1-74:2::[Warning] [L1230] function l/1 is unused +app_a/src/app_a.erl:77:1-77:2::[Warning] [L1230] function m/0 is unused +app_a/src/broken_parse_trans.erl:10:21-10:22::[Error] [L1256] field b undefined in record a +app_a/src/broken_parse_trans.erl:10:32-10:33::[Error] [L1262] variable 'B' is unbound +app_a/src/cascading.erl:9:5-9:6::[Error] [W0004] Missing ')' + 3:10-3:15: function foo/0 undefined + 6:10-6:15: function foo/0 undefined + 8:7-8:10: spec for undefined function foo/0 +app_a/src/diagnostics.erl:3:10-3:27::[WeakWarning] [W0037] Unspecific include. +app_a/src/diagnostics.erl:4:10-4:34::[WeakWarning] [W0037] Unspecific include. +app_a/src/diagnostics.erl:12:8-12:12::[Warning] [W0060] Match on a bound variable +app_a/src/diagnostics.erl:4:1-4:36::[Error] [L0000] Issue in included file + [app_a/include/broken_diagnostics.hrl] 1:8-1:15: P1702: bad attribute + [app_a/include/broken_diagnostics.hrl] 3:6-3:15: P1702: bad attribute +app_a/src/diagnostics.erl:6:31-6:45::[Error] [L1295] type undefined_type/0 undefined +app_a/src/diagnostics.erl:7:1-7:5::[Warning] [L1230] function main/1 is unused +app_a/src/diagnostics.erl:10:1-10:4::[Warning] [L1230] function foo/0 is unused +app_a/src/lint_recursive.erl:23:5-23:14::[Warning] [W0006] this statement has no effect +app_a/src/lint_recursive.erl:6:5-6:7::[Warning] [W0006] this statement has no effect +app_a/src/lint_recursive.erl:14:5-14:12::[Warning] [L1268] variable 'Config1' is unused +app_a/src/lint_recursive.erl:19:5-19:12::[Warning] [L1268] variable 'Config1' is unused +app_a/src/lints.erl:5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 4:1-4:14: Mismatched clause name +app_a/src/lints.erl:4:22-4:23::[Warning] [W0018] Unexpected ';' +app_a/src/lints.erl:2:10-2:25::[Error] [L1227] function head_mismatch/1 undefined +app_a/src/otp27_docstrings.erl:34:9-34:24::[Warning] [W0002] Unused macro (THIS_IS_THE_END) +app_a/src/otp27_docstrings.erl:24:5-24:6::[Warning] [W0060] Match on a bound variable +app_a/src/otp27_docstrings.erl:30:5-30:6::[Warning] [W0060] Match on a bound variable +app_a/src/otp27_sigils.erl:11:6-11:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:12:5-12:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:13:5-13:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:14:5-14:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:15:5-15:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:17:6-17:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:18:5-18:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:19:5-19:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:20:5-20:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:21:5-21:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:23:6-23:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:24:5-24:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:25:5-25:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:26:5-26:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:27:5-27:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:29:6-29:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:30:5-30:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:31:5-31:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:32:5-32:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:33:5-33:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:35:6-35:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:36:5-36:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:37:5-37:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:38:5-38:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:39:5-39:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:41:6-41:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:42:5-42:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:43:5-43:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:44:5-44:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:45:5-45:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:47:6-47:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:48:5-48:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:49:5-49:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:50:5-50:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:51:5-51:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:56:5-56:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:57:5-57:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:59:6-59:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:60:5-60:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:61:5-61:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:62:5-62:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:63:5-63:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:65:6-65:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:66:5-66:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:67:5-67:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:68:5-68:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:69:5-69:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:90:5-94:11::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:95:5-99:12::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:102:5-102:24::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +app_a/src/otp27_sigils.erl:128:9-128:24::[Warning] [W0002] Unused macro (THIS_IS_THE_END) +app_a/src/otp27_sigils.erl:112:4-112:5::[Error] [P1711] syntax error before: X + 4:15-4:18: function g/0 undefined + 74:7-74:8: spec for undefined function g/0 +app_a/src/otp27_sigils.erl:71:5-71:6::[Warning] [L1268] variable 'X' is unused +app_a/src/otp_7655.erl:5:1-5:28::[Error] [L1201] no module definition +app_a/src/parse_error_a_cascade.erl:10:20-11:1::[Error] [W0004] Missing 'atom' + 6:6-6:11: function bar/0 undefined +app_a/src/suppressed.erl:8:5-8:9::[Warning] [L1268] variable 'Life' is unused +app_a/src/syntax.erl:5:46-5:47::[Error] [P1711] syntax error before: ')' +app_a/src/syntax.erl:11:9-11:10::[Error] [W0004] Missing ')' diff --git a/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout b/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout index c8cdedb3ea..523b8d4bc7 100644 --- a/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout +++ b/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout @@ -1,4 +1,3 @@ module specified: suppressed -Diagnostics reported in 1 modules: - suppressed: 1 - 7:4-7:8::[Warning] [W0007] match is redundant +Diagnostics reported: +app_a/src/suppressed.erl:8:5-8:9::[Warning] [W0007] match is redundant diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout index a1db95ccbf..4b0a155055 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout @@ -1,9 +1,10 @@ module specified: diagnostics Diagnostics reported in 1 modules: - diagnostics: 6 + diagnostics: 7 2:9-2:26::[Hint] [W0037] Unspecific include. - 3:0-3:0::[Error] [L0000] Issue in included file + 3:0-3:35::[Error] [L0000] Issue in included file 3:9-3:33::[Hint] [W0037] Unspecific include. 5:30-5:44::[Error] [L1295] type undefined_type/0 undefined 6:0-6:4::[Warning] [L1230] function main/1 is unused 9:0-9:3::[Warning] [L1230] function foo/0 is unused + 11:7-11:11::[Warning] [W0060] Match on a bound variable diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout new file mode 100644 index 0000000000..3af8bfb086 --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout @@ -0,0 +1,5 @@ +module specified: diagnostics +Diagnostics reported in 1 modules: + diagnostics: 2 + 3:0-3:35::[Error] [L0000] Issue in included file + 5:30-5:44::[Error] [L1295] type undefined_type/0 undefined diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout index 0c3e5ae9a3..ede82c7c5f 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout @@ -4,3 +4,4 @@ {"path":"app_a/src/diagnostics.erl","line":6,"char":31,"code":"ELP","severity":"error","name":"L1295 (L1295)","original":null,"replacement":null,"description":"type undefined_type/0 undefined\n\nFor more information see: /erlang-error-index/l/L1295","docPath":null} {"path":"app_a/src/diagnostics.erl","line":7,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function main/1 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} {"path":"app_a/src/diagnostics.erl","line":10,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function foo/0 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} +{"path":"app_a/src/diagnostics.erl","line":12,"char":8,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} diff --git a/crates/elp/src/resources/test/diagnostics/parse_elp_lint_fix.stdout b/crates/elp/src/resources/test/diagnostics/parse_elp_lint_fix.stdout index b3a5f365b7..f040cee98e 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_elp_lint_fix.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_elp_lint_fix.stdout @@ -1,11 +1,12 @@ module specified: lints -Diagnostics reported in 1 modules: - lints: 1 - 4:0-4:13::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' +Diagnostics reported: +app_a/src/lints.erl:5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 4:1-4:14: Mismatched clause name --------------------------------------------- Applying fix in module 'lints' for - 4:0-4:13::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 4:1-4:14: Mismatched clause name @@ -1,6 +1,6 @@ -module(lints). -export([head_mismatch/1]). diff --git a/crates/elp/src/resources/test/diagnostics/parse_elp_lint_recursive.stdout b/crates/elp/src/resources/test/diagnostics/parse_elp_lint_recursive.stdout index 6bcfecb8a6..a88f567370 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_elp_lint_recursive.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_elp_lint_recursive.stdout @@ -1,12 +1,11 @@ module specified: lint_recursive -Diagnostics reported in 1 modules: - lint_recursive: 2 - 18:4-18:11::[Warning] [W0007] match is redundant - 13:4-13:11::[Warning] [W0007] match is redundant +Diagnostics reported: +app_a/src/lint_recursive.erl:19:5-19:12::[Warning] [W0007] match is redundant +app_a/src/lint_recursive.erl:14:5-14:12::[Warning] [W0007] match is redundant --------------------------------------------- Applying fix in module 'lint_recursive' for - 18:4-18:11::[Warning] [W0007] match is redundant + 19:5-19:12::[Warning] [W0007] match is redundant @@ -16,7 +16,7 @@ test_foo2(Config) -> @@ -21,12 +20,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 13:4-13:11::[Warning] [W0007] match is redundant - 18:4-18:10::[Warning] [W0006] this statement has no effect + 14:5-14:12::[Warning] [W0007] match is redundant + 19:5-19:11::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 13:4-13:11::[Warning] [W0007] match is redundant + 14:5-14:12::[Warning] [W0007] match is redundant @@ -11,7 +11,7 @@ %% something/0. test_foo(Config) -> @@ -41,12 +40,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 18:4-18:10::[Warning] [W0006] this statement has no effect - 13:4-13:10::[Warning] [W0006] this statement has no effect + 19:5-19:11::[Warning] [W0006] this statement has no effect + 14:5-14:11::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 18:4-18:10::[Warning] [W0006] this statement has no effect + 19:5-19:11::[Warning] [W0006] this statement has no effect @@ -16,7 +16,6 @@ test_foo2(Config) -> @@ -60,12 +59,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 16:10-16:16::[Warning] [W0010] this variable is unused - 13:4-13:10::[Warning] [W0006] this statement has no effect + 17:11-17:17::[Warning] [W0010] this variable is unused + 14:5-14:11::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 16:10-16:16::[Warning] [W0010] this variable is unused + 17:11-17:17::[Warning] [W0010] this variable is unused @@ -14,7 +14,7 @@ Config, clean_mocks(). @@ -80,11 +79,11 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 1 - 13:4-13:10::[Warning] [W0006] this statement has no effect + 14:5-14:11::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 13:4-13:10::[Warning] [W0006] this statement has no effect + 14:5-14:11::[Warning] [W0006] this statement has no effect @@ -11,7 +11,6 @@ %% something/0. test_foo(Config) -> @@ -98,11 +97,11 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 1 - 11:9-11:15::[Warning] [W0010] this variable is unused + 12:10-12:16::[Warning] [W0010] this variable is unused --------------------------------------------- Applying fix in module 'lint_recursive' for - 11:9-11:15::[Warning] [W0010] this variable is unused + 12:10-12:16::[Warning] [W0010] this variable is unused @@ -9,7 +9,7 @@ %% We want to check that the "no effect" statements in test_foo/1 and %% test_foo2/1 are removed, but not the ones in clean_mocks/0 and diff --git a/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl b/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl index 5e286c673f..c34c9037db 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl +++ b/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl @@ -1,4 +1,6 @@ module specified: otp27_docstrings Diagnostics reported in 1 modules: - otp27_docstrings: 1 + otp27_docstrings: 3 + 23:4-23:5::[Warning] [W0060] Match on a bound variable + 29:4-29:5::[Warning] [W0060] Match on a bound variable 33:8-33:23::[Warning] [W0002] Unused macro (THIS_IS_THE_END) diff --git a/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout new file mode 100644 index 0000000000..1cef26124c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout @@ -0,0 +1,2 @@ +module specified: erlang_diagnostics_errors_gen +No matches found diff --git a/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout new file mode 100644 index 0000000000..2b88b6e10c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout @@ -0,0 +1,5 @@ +module specified: erlang_diagnostics_errors_gen + erlang_diagnostics_errors_gen: 1 + 6:5-6:7::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ok. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/eqwalize_all_help.stdout b/crates/elp/src/resources/test/eqwalize_all_help.stdout index 43f66a0a3a..67497c4000 100644 --- a/crates/elp/src/resources/test/eqwalize_all_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_all_help.stdout @@ -1,11 +1,10 @@ -Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--include-tests] [--bail-on-error] [--stats] [--list-modules] +Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--bail-on-error] [--stats] [--list-modules] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) --format Show diagnostics in JSON format --rebar Run with rebar - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found --stats Print statistics when done --list-modules When printing statistics, include the list of modules parsed diff --git a/crates/elp/src/resources/test/eqwalize_app.stdout b/crates/elp/src/resources/test/eqwalize_app.stdout index eaf1d3126a..bb128f249a 100644 --- a/crates/elp/src/resources/test/eqwalize_app.stdout +++ b/crates/elp/src/resources/test/eqwalize_app.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--as PROFILE] [--include-tests] [--rebar] [--bail-on-error] +Usage: [--project PROJECT] [--as PROFILE] [--rebar] [--bail-on-error] Available positional items: app name @@ -6,7 +6,6 @@ Available positional items: Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) - --include-tests Also eqwalize test modules from project --rebar Run with rebar --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/eqwalize_target_help.stdout b/crates/elp/src/resources/test/eqwalize_target_help.stdout index eec74e4938..e9a01166dd 100644 --- a/crates/elp/src/resources/test/eqwalize_target_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_target_help.stdout @@ -1,10 +1,9 @@ -Usage: [--project PROJECT] [--include-tests] [--bail-on-error] +Usage: [--project PROJECT] [--bail-on-error] Available positional items: target, like //erl/chatd/... Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty index c2a5c42406..69251d1a75 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty @@ -37,12 +37,6 @@ Because in the expression's type: Here the type is: fun((term()) -> term()) Context expects type: fun((term(), term()) -> term()) ------------------------------- Detailed message ------------------------------ - - fun((term()) -> term()) is not compatible with f2() - because - fun((term()) -> term()) is not compatible with fun((term(), term()) -> term()) - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/any_fun_type.erl:64:1 │ @@ -67,16 +61,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) ------------------------------- Detailed message ------------------------------ - - f5('a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with fun((...) -> 'a') - because - 'a' | 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/any_fun_type.erl:98:20 │ @@ -103,14 +87,4 @@ Because in the expression's type: Differs from the expected type: 'a' ) ------------------------------- Detailed message ------------------------------ - - fun((term()) -> 'a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with fun((...) -> 'a') - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - 7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty index 0fe7555553..15655d9cac 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty @@ -60,12 +60,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: 'anything' ------------------------------- Detailed message ------------------------------ - - string() | dynamic() is not compatible with 'anything' - because - string() is not compatible with 'anything' - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/approx.erl:74:1 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty index f1d2ca6927..2c9ecb3a1f 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty @@ -2,15 +2,8 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/auto_imports.erl:22:20 │ 22 │ erlang:error(ok, ok). - │ ^^ - │ │ - │ 'ok'. + │ ^^ 'ok'. Expression has type: 'ok' Context expected type: [term()] | 'none' - │ - - 'ok' is not compatible with [term()] | 'none' - because - 'ok' is not compatible with [term()] 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty index 4407c43d42..7227163b41 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty @@ -22,10 +22,4 @@ Because in the expression's type: However the following candidate: 'false' Differs from the expected type: 'true' ------------------------------- Detailed message ------------------------------ - - 'false' | 'true' is not compatible with 'true' - because - 'false' is not compatible with 'true' - 2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty deleted file mode 100644 index 63be36e638..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty deleted file mode 100644 index 63be36e638..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty deleted file mode 100644 index a24c7a6a48..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), gen_server:action()} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), gen_server:action()} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty new file mode 100644 index 0000000000..6cb2012096 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty @@ -0,0 +1,27 @@ +error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) + ┌─ check/src/callbacks3_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ Incorrect return type for implementation of gen_server:handle_cast/2. +Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} +Got: 'wrong_ret' + +error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) + ┌─ check/src/callbacks3_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ Incorrect return type for implementation of gen_server:handle_info/2. +Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} +Got: {'noreply', 'ok', 'wrong_atom'} + │ + +Because in the expression's type: + { 'noreply', 'ok', + Here the type is: 'wrong_atom' + Context expects type: 'infinity' | number() | 'hibernate' | {'continue', term()} + No candidate matches in the expected union. + } + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty index 9aff530657..a3bc7c96f9 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty @@ -72,17 +72,6 @@ Because in the expression's type: Differs from the expected type: pid() } ------------------------------- Detailed message ------------------------------ - - {'p', pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()} - because - at tuple index 2: - {'p', pid() | reference()} is not compatible with {'p', pid()} - because - pid() | reference() is not compatible with pid() - because - reference() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/case_predicates.erl:144:10 │ @@ -99,14 +88,6 @@ Because in the expression's type: However the following candidate: 'restarting' Differs from the expected type: {'p', pid()} | 'undefined' ------------------------------- Detailed message ------------------------------ - - 'undefined' | 'restarting' is not compatible with {'p', pid()} | 'undefined' - because - 'restarting' is not compatible with {'p', pid()} | 'undefined' - because - 'restarting' is not compatible with {'p', pid()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/case_predicates.erl:174:16 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty index 3143803509..aae5348c54 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty @@ -14,10 +14,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: c. ------------------------------- Detailed message ------------------------------ - -key `c` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:17:21 │ @@ -35,15 +31,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{a => 'b', c => 'd'} is not compatible with #{a => 'b', atom() => number()} - because - #{a => 'b', c => 'd'} is not compatible with #{a => 'b', atom() => number()} - key `c` is declared in the former but not in the latter and key `c` isn't compatible with the default association of the latter map - because - 'd' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:20:19 │ @@ -60,13 +47,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{a => 'b', atom() => atom()} is not compatible with #{a => 'b', c => 'd'} - key c is not present in the former map but is incompatible with its default association - because - atom() is not compatible with 'd' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:23:19 │ @@ -85,13 +65,6 @@ Because in the expression's type: , ... } The context introduces a new association c => 'd' which is incompatible with the expression's default association. ------------------------------- Detailed message ------------------------------ - - #{a => 'b', atom() => number()} is not compatible with #{a => 'b', c => 'd', atom() => number()} - key c is not present in the former map but is incompatible with its default association - because - number() is not compatible with 'd' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:38:21 │ @@ -108,13 +81,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{a => 'b', atom() => atom()} is not compatible with #{a => 'b'} - because - #{a => 'b', atom() => atom()} is not compatible with #{a => 'b'} - the latter map has no default association while the first map has one - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:44:26 │ @@ -131,13 +97,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{a => 'b', dynamic(atom()) => atom()} is not compatible with #{a => 'b'} - because - #{a => 'b', dynamic(atom()) => atom()} is not compatible with #{a => 'b'} - the latter map has no default association while the first map has one - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:47:26 │ @@ -154,13 +113,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{a => 'b', atom() => dynamic(atom())} is not compatible with #{a => 'b'} - because - #{a => 'b', atom() => dynamic(atom())} is not compatible with #{a => 'b'} - the latter map has no default association while the first map has one - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:56:44 │ @@ -218,13 +170,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{a => binary(), atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:83:28 │ @@ -242,13 +187,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := 'b', atom() => binary()} is not compatible with #{a => 'b', {c, d} => atom() | binary(), term() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:86:28 │ @@ -266,13 +204,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := 'b', atom() => binary()} is not compatible with #{a => 'b', {c, d} => atom() | binary(), atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/complex_maps.erl:89:29 │ @@ -290,11 +221,4 @@ Because in the expression's type: Context expects type: binary() , ... } ------------------------------- Detailed message ------------------------------ - - #{dynamic() => atom()} is not compatible with #{dynamic() => binary()} - the default associations are not compatible - because - atom() is not compatible with binary() - 16 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty index 396ce7fc52..2b387ca9b9 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty @@ -15,12 +15,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:40:16 │ @@ -86,12 +80,6 @@ Because in the expression's type: Context expects type: binary() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [binary()] - because - number() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:72:5 │ @@ -270,12 +258,6 @@ Because in the expression's type: Context expects type: binary() ] ------------------------------- Detailed message ------------------------------ - - [atom()] is not compatible with [binary()] - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:226:5 │ @@ -293,12 +275,6 @@ Because in the expression's type: Context expects type: 'false' ] ------------------------------- Detailed message ------------------------------ - - ['true'] is not compatible with ['false'] - because - 'true' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:231:5 │ @@ -316,12 +292,6 @@ Because in the expression's type: Context expects type: 'false' ] ------------------------------- Detailed message ------------------------------ - - ['true'] is not compatible with ['false'] - because - 'true' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:239:5 │ @@ -339,12 +309,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/comprehensions.erl:267:5 │ @@ -363,14 +327,6 @@ Because in the expression's type: Differs from the expected type: binary() ] ------------------------------- Detailed message ------------------------------ - - [binary() | 'undefined'] is not compatible with [binary()] - because - binary() | 'undefined' is not compatible with binary() - because - 'undefined' is not compatible with binary() - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ check/src/comprehensions.erl:386:9 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty index 69c58c0e77..f228f5cd50 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty @@ -16,14 +16,4 @@ Because in the expression's type: Differs from the expected type: 'a' ) -> 'ok') ------------------------------- Detailed message ------------------------------ - - ref_contravariant('a') is not compatible with ref_contravariant_ab() - because - contravariant('a') is not compatible with ref_contravariant_ab() - because - fun(('a') -> 'ok') is not compatible with ref_contravariant_ab() - because - fun(('a') -> 'ok') is not compatible with ref_contravariant('a' | 'b') - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty deleted file mode 100644 index 370f7689e9..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty +++ /dev/null @@ -1,3258 +0,0 @@ -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:26:5 - │ -26 │ element(4, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(4, Tup). -Expression has type: #{dynamic() => dynamic()} -Context expected type: atom() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:30:5 - │ -30 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 3 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:40:5 - │ -40 │ element(2, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(2, Tup). -Expression has type: number() | string() | atom() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:44:16 - │ -44 │ element(2, Tup). - │ ^^^ - │ │ - │ Tup. -Expression has type: {atom(), string()} | [dynamic()] -Context expected type: tuple() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {atom(), string()} - However the following candidate: [dynamic()] - Differs from the expected type: tuple() - ------------------------------- Detailed message ------------------------------ - - {atom(), string()} | [dynamic()] is not compatible with tuple() - because - [dynamic()] is not compatible with tuple() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:48:5 - │ -48 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 2 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:54:5 - │ -54 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(N, Tup). -Expression has type: atom() | number() | string() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:58:5 - │ -58 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Tup). -Expression has type: atom() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - atom() | number() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:68:5 - │ -68 │ element(1, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(1, Tup). -Expression has type: dynamic() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: dynamic() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:86:5 - │ -86 │ element(N, Rec). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Rec). -Expression has type: 'foo' | 'ok' | 'error' | number() | string() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'error' | 'foo' | 'ok' - However the following candidate: string() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - 'foo' | 'ok' | 'error' | number() | string() is not compatible with atom() - because - string() is not compatible with atom() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:91:1 - │ -91 │ ╭ element_2_none_1(Tup) -> -92 │ │ element(42, Tup). - │ ╰────────────────────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:95:1 - │ -95 │ ╭ element_2_none_2(N, Tup) -> -96 │ │ element(N, Tup). - │ ╰───────────────────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:108:5 - │ -108 │ maps:get(K, M). - │ ^^^^^^^^^^^^^^ maps:get(K, M). -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:128:5 - │ -128 │ Res. - │ ^^^ Res. -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:154:12 - │ -154 │ get(a, M). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:160:23 - │ -160 │ Res = maps:get(a, M), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:167:17 - │ -167 │ maps:get(a, M, false). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:173:23 - │ -173 │ Res = maps:get(a, M, false), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:193:5 - │ -193 │ maps:get(K, M, 0). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(K, M, 0). -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:200:5 - │ -200 │ Res. - │ ^^^ - │ │ - │ Res. -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:206:5 - │ -206 │ ╭ ╭ { -207 │ │ │ maps:get(a, M, undefined), -208 │ │ │ maps:get(n, M, undefined) -209 │ │ │ }. - │ ╰─│─────^ {maps:get('a', M, 'undefined'), maps:get('n', M, 'undefined')}. -Expression has type: {atom(), 'undefined' | number()} -Context expected type: {atom(), number()} - │ ╰─────' - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: 'undefined' - Differs from the expected type: number() - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), 'undefined' | number()} is not compatible with {atom(), number()} - because - 'undefined' | number() is not compatible with number() - because - 'undefined' is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:221:27 - │ -221 │ map_get_2_17_neg(V, M) -> maps:get(V, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M). -Expression has type: 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' - ------------------------------- Detailed message ------------------------------ - - 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:233:27 - │ -233 │ map_get_3_19_neg(V, M) -> maps:get(V, M, undefined). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M, 'undefined'). -Expression has type: 'undefined' | 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' | 'undefined' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'undefined' | 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' | 'undefined' - ------------------------------- Detailed message ------------------------------ - - 'undefined' | 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:309:5 - │ -309 │ Res. - │ ^^^ Res. -Expression has type: {'value', #{}} | 'false' -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:363:23 - │ -363 │ fun (_, _) -> self() end, - │ ^^^^^^ erlang:self(). -Expression has type: pid() -Context expected type: boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:382:17 - │ -382 │ maps:filter(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> boolean()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:389:17 - │ -389 │ maps:filter(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((T) -> boolean()), [T]) -> [T]) with 1 type parameter -Context expected type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun()/2 with 1 type parameter - Context expects type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:413:37 - │ -413 │ maps:filter(fun erlang:'=:='/2, X). - │ ^ - │ │ - │ X. -Expression has type: #{K => V} | 'a' -Context expected type: #{term() => term()} | maps:iterator() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: #{K => V} - However the following candidate: 'a' - Differs from the expected type: #{term() => term()} | maps:iterator() - ------------------------------- Detailed message ------------------------------ - - #{K => V} | 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:425:20 - │ -425 │ maps:filter(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:454:5 - │ -454 │ ╭ ╭ maps:map( -455 │ │ │ fun (_, _) -> self() end, -456 │ │ │ M -457 │ │ │ ). - │ ╰─│─────^ maps:map(fun, M). -Expression has type: #{number() => pid()} -Context expected type: #{number() => boolean()} - │ ╰─────' - -Because in the expression's type: - #{ number() => - Here the type is: pid() - Context expects type: boolean() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{number() => pid()} is not compatible with #{number() => boolean()} - the default associations are not compatible - because - pid() is not compatible with boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:474:14 - │ -474 │ maps:map(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> term()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:481:14 - │ -481 │ maps:map(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters -Context expected type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters - Context expects type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:497:5 - │ -497 │ maps:map(F, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:map(F, M). -Expression has type: #{a := boolean(), b := boolean()} -Context expected type: #{a => 'a', b => 'b'} - │ - -Because in the expression's type: - #{ a => - Here the type is: boolean() - Context expects type: 'a' - , ... } - ------------------------------- Detailed message ------------------------------ - - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - at key `a`: - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - boolean() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:503:17 - │ -503 │ maps:map(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:538:9 - │ -538 │ fun (K, V) -> [K, V] end, [], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ fun. -fun with arity 2 used as fun with 3 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:544:5 - │ -544 │ ╭ ╭ maps:fold( -545 │ │ │ fun (_, _, Acc) -> [Acc] end, -546 │ │ │ [], -547 │ │ │ M -548 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [[[]]] -Context expected type: [number() | 'a' | 'b'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: [[]] - Context expects type: number() | 'a' | 'b' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [[[]]] is not compatible with [number() | 'a' | 'b'] - because - [[]] is not compatible with number() | 'a' | 'b' - because - [[]] is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:545:28 - │ -545 │ fun (_, _, Acc) -> [Acc] end, - │ ^^^^^ - │ │ - │ [Acc]. -Expression has type: [[[[]]]] -Context expected type: [[[]]] - │ - -Because in the expression's type: - [ - [ - [ - Here the type is: [] - Context expects type: none() - ] - ] - ] - ------------------------------- Detailed message ------------------------------ - - [[[[]]]] is not compatible with [[[]]] - because - [[[]]] is not compatible with [[]] - because - [[]] is not compatible with [] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:552:5 - │ -552 │ ╭ maps:fold( -553 │ │ fun (_, _, Acc) -> Acc end, -554 │ │ [], -555 │ │ non_kv -556 │ │ ). - │ ╰─────^ maps:fold(fun, [], 'non_kv'). -Expression has type: [] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:555:9 - │ -555 │ non_kv - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:560:1 - │ -560 │ ╭ maps_fold_3_6(None) -> -561 │ │ maps:fold( -562 │ │ None, -563 │ │ #{}, -564 │ │ #{1 => 1} -565 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:568:1 - │ -568 │ ╭ maps_fold_3_7(None) -> -569 │ │ maps:fold( -570 │ │ None, -571 │ │ None, -572 │ │ None -573 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:578:5 - │ -578 │ ╭ ╭ maps:fold( -579 │ │ │ fun (_K, A, _Acc) -> A end, -580 │ │ │ [], -581 │ │ │ M -582 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [] | atom() -Context expected type: atom() - │ ╰─────' - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: [] - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - [] | atom() is not compatible with atom() - because - [] is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', []) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun((number(), 'a', []) -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> [] | dynamic()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:636:5 - │ -636 │ ╭ ╭ maps:fold( -637 │ │ │ fun -638 │ │ │ (_K, {i, I}, Acc) -> -639 │ │ │ [I | Acc]; - · │ │ -646 │ │ │ M -647 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [number() | binary() | atom()] -Context expected type: [binary()] | [number()] | [atom()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: number() - Differs from the expected type: binary() - ] - ------------------------------- Detailed message ------------------------------ - - [number() | binary() | atom()] is not compatible with [binary()] | [number()] | [atom()] - because - [number() | binary() | atom()] is not compatible with [binary()] - because - number() | binary() | atom() is not compatible with binary() - because - number() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:671:41 - │ -671 │ maps_to_list_7_neg(Num) -> maps:to_list(Num). - │ ^^^ - │ │ - │ Num. -Expression has type: number() -Context expected type: #{term() => term()} | maps:iterator() - │ - - number() is not compatible with #{term() => term()} | maps:iterator() - because - number() is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:675:25 - │ -675 │ maps_merge_1(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:679:25 - │ -679 │ maps_merge_2(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number() | boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number() | boolean(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:683:25 - │ -683 │ maps_merge_3(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a := string(), b := boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:697:44 - │ -697 │ maps_merge_7_neg(M1, M2) -> maps:merge(M1, M2). - │ ^^ M2. -Expression has type: number() -Context expected type: #{a => binary()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:701:25 - │ -701 │ maps_merge_8(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'a' | 'b' => atom() | number()} -Context expected type: #{a := atom(), b := number()} | #{a := atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} | #{a := atom()} - because - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} - keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:721:9 - │ -721 │ fun erlang:binary_to_list/1, - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:binary_to_list/1. -Expression has type: fun((binary()) -> [number()]) -Context expected type: fun((number()) -> boolean() | {'true', term()}) - │ - -Because in the expression's type: - fun((binary()) -> - Here the type is: [number()] - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((binary()) -> [number()]) is not compatible with fun((number()) -> boolean() | {'true', term()}) - because - [number()] is not compatible with boolean() | {'true', term()} - because - [number()] is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:739:20 - │ -739 │ (3) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:749:9 - │ -749 │ not_a_list - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:791:17 - │ -791 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: {'true', 'a'} | 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {'true', 'a'} - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | {'true', term()} - ------------------------------- Detailed message ------------------------------ - - {'true', 'a'} | 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:827:9 - │ -827 │ ╭ ╭ fun (a) -> [a]; -828 │ │ │ (b) -> true; -829 │ │ │ (c) -> wrong_ret end, - │ ╰─│────────────────────────────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰────────────────────────────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'true' - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'true' | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:829:20 - │ -829 │ (c) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:9 - │ -837 │ ╭ ╭ fun (1) -> {true, a}; -838 │ │ │ (2) -> true end, - │ ╰─│───────────────────────────^ fun. -Expression has type: fun((dynamic()) -> {'true', 'a'} | 'true') -Context expected type: fun((Item) -> boolean() | [Item]) - │ ╰───────────────────────────' - -Because in the expression's type: - fun((dynamic()) -> - Here the type is a union type with some valid candidates: 'true' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | [Item] - ) - ------------------------------- Detailed message ------------------------------ - - fun((dynamic()) -> {'true', 'a'} | 'true') is not compatible with fun((Item) -> boolean() | [Item]) - because - {'true', 'a'} | 'true' is not compatible with boolean() | [Item] - because - {'true', 'a'} is not compatible with boolean() | [Item] - expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:20 - │ -837 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | [dynamic()] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(Item) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[Item], [Item]} - ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(Item) - because - 'not_a_queue' is not compatible with {[Item], [Item]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(dynamic()) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[dynamic()], [dynamic()]} - ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(dynamic()) - because - 'not_a_queue' is not compatible with {[dynamic()], [dynamic()]} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:846:9 - │ -846 │ ╭ fun (wrong, arity) -> -847 │ │ [a] -848 │ │ end, - │ ╰───────────^ fun. -fun with arity 2 used as fun with 1 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:9 - │ -857 │ ╭ ╭ fun (1) -> {true, a}; -858 │ │ │ (X) -> case X of -859 │ │ │ true -> -860 │ │ │ [a]; - · │ │ -863 │ │ │ end -864 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'false' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'false' | {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - because - {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:20 - │ -857 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:873:9 - │ -873 │ ╭ ╭ fun (a) -> [a]; -874 │ │ │ (X) -> -875 │ │ │ Res = case X of -876 │ │ │ true -> - · │ │ -881 │ │ │ Res -882 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:881:17 - │ -881 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: ['a'] | 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ------------------------------- Detailed message ------------------------------ - - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun((dynamic()) -> boolean() | [dynamic()]) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun((dynamic()) -> boolean() | [dynamic()]) - because - atom() is not compatible with boolean() | [dynamic()] - because - atom() is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - atom() is not compatible with boolean() | ['a' | 'b'] - because - atom() is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:925:1 - │ -925 │ ╭ queue_filter_13_neg(Q) -> -926 │ │ queue:filter( -927 │ │ fun atom_to_list/1, -928 │ │ Q -929 │ │ ), -930 │ │ ok. - │ ╰──────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:955:5 - │ -955 │ ╭ lists:keystore( -956 │ │ a, 1, -957 │ │ [{foo, b}, {c, d}], -958 │ │ non_tuple -959 │ │ ). - │ ╰─────^ lists:keystore('a', 1, [{'foo', 'b'}, {'c', 'd'}], 'non_tuple'). -Expression has type: [{'foo', 'b'} | {'c', 'd'} | dynamic()] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:958:9 - │ -958 │ non_tuple - │ ^^^^^^^^^ 'non_tuple'. -Expression has type: 'non_tuple' -Context expected type: tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:966:5 - │ -966 │ ╭ lists:keystore( -967 │ │ a, 1, -968 │ │ [non_tuple], -969 │ │ {replacement} -970 │ │ ). - │ ╰─────^ lists:keystore('a', 1, ['non_tuple'], {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:968:9 - │ -968 │ [non_tuple], - │ ^^^^^^^^^^^ - │ │ - │ ['non_tuple']. -Expression has type: ['non_tuple'] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: 'non_tuple' - Context expects type: tuple() - ] - ------------------------------- Detailed message ------------------------------ - - ['non_tuple'] is not compatible with [tuple()] - because - 'non_tuple' is not compatible with tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:975:5 - │ -975 │ ╭ lists:keystore( -976 │ │ a, 1, -977 │ │ non_list, -978 │ │ {replacement} -979 │ │ ). - │ ╰─────^ lists:keystore('a', 1, 'non_list', {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:977:9 - │ -977 │ non_list, - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [tuple()] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:983:1 - │ -983 │ ╭ keystore_7(None) -> -984 │ │ lists:keystore( -985 │ │ a, 1, -986 │ │ None, -987 │ │ {replacement} -988 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:991:1 - │ -991 │ ╭ keystore_8(None) -> -992 │ │ lists:keystore( -993 │ │ a, 1, -994 │ │ None, -995 │ │ None -996 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1021:22 - │ -1021 │ lists:keytake(a, non_num, []), - │ ^^^^^^^ 'non_num'. -Expression has type: 'non_num' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1038:15 - │ -1038 │ lists:sum([a, 1]). - │ ^^^^^^ - │ │ - │ ['a', 1]. -Expression has type: ['a' | number()] -Context expected type: [number()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: 'a' - Differs from the expected type: number() - ] - ------------------------------- Detailed message ------------------------------ - - ['a' | number()] is not compatible with [number()] - because - 'a' | number() is not compatible with number() - because - 'a' is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1042:15 - │ -1042 │ lists:sum(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1100:5 - │ -1100 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' - │ - - term() is not compatible with pid() | 'undefined' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1106:5 - │ -1106 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' | 'v' - │ - - term() is not compatible with pid() | 'undefined' | 'v' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1111:5 - │ -1111 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1117:5 - │ -1117 │ proplists:get_value(k, L, 3). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 3). -Expression has type: term() -Context expected type: pid() | number() - │ - - term() is not compatible with pid() | number() - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1123:5 - │ -1123 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v1' | 'v2' | 'my_default' - │ - - term() is not compatible with 'v1' | 'v2' | 'my_default' - because - term() is not compatible with 'v1' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1129:5 - │ -1129 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1134:28 - │ -1134 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1139:5 - │ -1139 │ proplists:get_value(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', []). -Expression has type: term() -Context expected type: 'default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1144:1 - │ -1144 │ proplists:get_value(k, [], my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', [], 'my_default'). -Expression has type: term() -Context expected type: 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1149:28 - │ -1149 │ proplists:get_value(k, b, my_default). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1154:5 - │ -1154 │ proplists:get_bool(b, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_bool('b', L). -Expression has type: boolean() -Context expected type: 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1160:5 - │ -1160 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default'] - because - term() is not compatible with pid() | 'default' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1166:5 - │ -1166 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default' | 'v'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' | 'v' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default' | 'v'] - because - term() is not compatible with pid() | 'default' | 'v' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1171:5 - │ -1171 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid()] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid()] - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1176:5 - │ -1176 │ proplists:get_all_values(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', []). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:5 - │ -1181 │ proplists:get_all_values(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', 'b'). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:33 - │ -1181 │ proplists:get_all_values(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1186:27 - │ -1186 │ proplists:get_bool(b, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1191:5 - │ -1191 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'c' - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['c'] - because - term() is not compatible with 'c' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1197:5 - │ -1197 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['a' | 'b' | 'c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' | 'b' | 'c' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a' | 'b' | 'c'] - because - term() is not compatible with 'a' | 'b' | 'c' - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1202:24 - │ -1202 │ proplists:get_keys(a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1207:5 - │ -1207 │ ╭ ╭ proplists:get_keys( -1208 │ │ │ [{a, b, c}] -1209 │ │ │ ). - │ ╰─│─────^ proplists:get_keys([{'a', 'b', 'c'}]). -Expression has type: [term()] -Context expected type: ['a'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a'] - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1215:5 - │ -1215 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:5 - │ -1220 │ proplists:get_value(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', 'b'). -Expression has type: term() -Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:28 - │ -1220 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1225:1 - │ -1225 │ proplists:lookup(self(), [a, {b, true}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup(erlang:self(), ['a', {'b', 'true'}]). -Expression has type: 'none' | tuple() -Context expected type: {'b', 'true'} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: tuple() - However the following candidate: 'none' - Differs from the expected type: {'b', 'true'} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1229:25 - │ -1229 │ proplists:lookup(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1239:5 - │ -1239 │ proplists:lookup(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup('k', []). -Expression has type: 'none' | tuple() -Context expected type: 'none' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'none' - However the following candidate: tuple() - Differs from the expected type: 'none' - ------------------------------- Detailed message ------------------------------ - - 'none' | tuple() is not compatible with 'none' - because - tuple() is not compatible with 'none' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1252:29 - │ -1252 │ proplists:lookup_all(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1265:5 - │ -1265 │ ╭ ╭ proplists:lookup_all( -1266 │ │ │ self(), -1267 │ │ │ [] -1268 │ │ │ ). - │ ╰─│─────^ proplists:lookup_all(erlang:self(), []). -Expression has type: [tuple()] -Context expected type: [] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: tuple() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1291:34 - │ -1291 │ proplists:is_defined(self(), b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [A] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1311:5 - │ -1311 │ proplists:split(L, Ks). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:split(L, Ks). -Expression has type: {[[term()]], [term()]} -Context expected type: {[plist('a', 'b')], plist('a', 'b')} - │ - -Because in the expression's type: - { - [ - [ - Here the type is: term() - Context expects type: 'a' | {'a', 'b'} - No candidate matches in the expected union. - ] - ] - , [term()]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[[term()]], [term()]} is not compatible with {[plist('a', 'b')], plist('a', 'b')} - because - [[term()]] is not compatible with [plist('a', 'b')] - because - [term()] is not compatible with plist('a', 'b') - because - [term()] is not compatible with ['a' | {'a', 'b'}] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1315:21 - │ -1315 │ proplists:split(b, []). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1319:25 - │ -1319 │ proplists:split([], b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1324:22 - │ -1324 │ proplists:to_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [atom() | {term(), term()} | term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{K => V} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1363:5 - │ -1363 │ proplists:get_value(a, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1368:5 - │ -1368 │ proplists:get_value(X, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value(X, ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1373:5 - │ -1373 │ proplists:get_value(a, [a], b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a'], 'b'). -Expression has type: term() -Context expected type: 'true' | 'b' - │ - - term() is not compatible with 'true' | 'b' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1416:5 - │ -1416 │ file:consult(some_file). - │ ^^^^^^^^^^^^^^^^^^^^^^^ file:consult('some_file'). -Expression has type: {'ok', [dynamic()]} | {'error', {number(), atom(), term()} | 'terminated' | 'badarg' | file:posix() | 'system_limit'} -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1428:5 - │ -1428 │ lists:keysort(2, [{a, c}, {b, d}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:keysort(2, [{'a', 'c'}, {'b', 'd'}]). -Expression has type: [{'a', 'c'} | {'b', 'd'}] -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1432:22 - │ -1432 │ lists:keysort(1, [3]). - │ ^^^ - │ │ - │ [3]. -Expression has type: [number()] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: number() - Context expects type: tuple() - ] - ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [tuple()] - because - number() is not compatible with tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1448:5 - │ -1448 │ ╭ ╭ lists:filtermap( -1449 │ │ │ fun(X) when X div 2 =:= 0 -> -1450 │ │ │ {true, integer_to_list(X)}; -1451 │ │ │ (X) -> - · │ │ -1454 │ │ │ [1, 2, 3, 4] -1455 │ │ │ ). - │ ╰─│─────^ lists:filtermap(fun, [1, 2, 3, 4]). -Expression has type: [string() | number()] -Context expected type: [number()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: string() - Differs from the expected type: number() - ] - ------------------------------- Detailed message ------------------------------ - - [string() | number()] is not compatible with [number()] - because - string() | number() is not compatible with number() - because - string() is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1482:5 - │ -1482 │ erlang:min(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:min(X, Y). -Expression has type: number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1497:5 - │ -1497 │ erlang:max(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:max(X, Y). -Expression has type: atom() | number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1523:9 - │ -1523 │ abs(Atom). - │ ^^^^ Atom. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1536:31 - │ -1536 │ seq3_4_wip_neg() -> lists:seq(a, 2, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1539:30 - │ -1539 │ seq3_5_neg() -> lists:seq(1, a, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1542:33 - │ -1542 │ seq3_6_neg() -> lists:seq(1, 2, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1570:31 - │ -1570 │ seq2_4_wip_neg() -> lists:seq(a, 2). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1573:30 - │ -1573 │ seq2_5_neg() -> lists:seq(1, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1622:5 - │ -1622 │ ╭ timer:tc( -1623 │ │ fun() -> -1624 │ │ err -1625 │ │ end -1626 │ │ ). - │ ╰─────^ timer:tc(fun). -Expression has type: {number(), 'err'} -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1635:5 - │ -1635 │ ets:lookup(tab, Any). - │ ^^^^^^^^^^^^^^^^^^^^ ets:lookup('tab', Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:5 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ets:lookup(string_lit, Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:16 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1663:18 - │ -1663 │ ets:tab2list("not atom"). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1702:23 - │ -1702 │ lists:flatten([], 1). - │ ^ 1. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1711:19 - │ -1711 │ lists:flatten(3). - │ ^ 3. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1724:8 - │ -1724 │ -> lists:flatten(X). - │ ^^^^^^^^^^^^^^^^ - │ │ - │ lists:flatten(X). -Expression has type: [{A, B} | {B, A}] -Context expected type: [{A, B}] - │ - -Because in the expression's type: - [ - { - Here the type is: B - Context expects type: A - , A} - ] - ------------------------------- Detailed message ------------------------------ - - [{A, B} | {B, A}] is not compatible with [{A, B}] - because - {A, B} | {B, A} is not compatible with {A, B} - because - at tuple index 1: - {B, A} is not compatible with {A, B} - because - B is not compatible with A - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1757:5 - │ -1757 │ ╭ ╭ maps:without( -1758 │ │ │ [a, c, DOrE], -1759 │ │ │ #{ -1760 │ │ │ a => ka, - · │ │ -1764 │ │ │ } -1765 │ │ │ ). - │ ╰─│─────^ maps:without(['a', 'c', DOrE], #{..}). -Expression has type: #{a => 'ka', b => atom(), c => pid(), d => 'kd'} -Context expected type: #{b => atom()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared in the former but not in the latter and the latter map has no default association - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1769:18 - │ -1769 │ maps:without(non_list, #{}). - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1773:22 - │ -1773 │ maps:without([], non_map). - │ ^^^^^^^ 'non_map'. -Expression has type: 'non_map' -Context expected type: #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:1808:1 - │ -1808 │ ╭ maps_without_12_neg(None) -> -1809 │ │ maps:without( -1810 │ │ [a, b], -1811 │ │ None -1812 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1818:5 - │ -1818 │ ╭ ╭ maps:without( -1819 │ │ │ Keys, -1820 │ │ │ #{a => self(), b => self()} -1821 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1826:5 - │ -1826 │ ╭ ╭ maps:without( -1827 │ │ │ [a | improper_tail], -1828 │ │ │ #{a => self(), b => self()} -1829 │ │ │ ). - │ ╰─│─────^ maps:without(['a' | 'improper_tail'], #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1827:12 - │ -1827 │ [a | improper_tail], - │ ^^^^^^^^^^^^^^^^ 'improper_tail'. -Expression has type: 'improper_tail' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1837:5 - │ -1837 │ ╭ ╭ maps:without( -1838 │ │ │ Keys, -1839 │ │ │ #{a => ka, b => self()} -1840 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => 'ka', b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1854:5 - │ -1854 │ maps:without([a, b], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:without(['a', 'b'], M). -Expression has type: #{c := 'cv', d := 'dv'} | #{c := 'cv', e => 'ev'} -Context expected type: #{c := atom()} - │ - -Because in the expression's type: - Here the type is: #{d := ...} - Context expects type: #{...} - The expected map has no corresponding key for: d. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1916:23 - │ -1916 │ custom_overloaded(X). - │ ^ - │ │ - │ X. -Expression has type: term() -Context expected type: atom() | binary() - │ - - term() is not compatible with atom() | binary() - because - term() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1939:5 - │ -1939 │ {A, N}. - │ ^^^^^^ - │ │ - │ {A, N}. -Expression has type: {atom(), number() | pid()} -Context expected type: {atom(), number()} - │ - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: pid() - Differs from the expected type: number() - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), number() | pid()} is not compatible with {atom(), number()} - because - number() | pid() is not compatible with number() - because - pid() is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2041:5 - │ -2041 │ filename:join(["server", "erl"]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2046:5 - │ -2046 │ filename:join(["server", <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, <<..>>]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2051:5 - │ -2051 │ filename:join([<<>>, ""]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2066:5 - │ -2066 │ filename:join([<<>>, <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, <<..>>]). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2081:19 - │ -2081 │ filename:join([<<>>, self()]). - │ ^^^^^^^^^^^^^^ - │ │ - │ [<<..>>, erlang:self()]. -Expression has type: [binary() | pid()] -Context expected type: [file:name_all()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: pid() - Differs from the expected type: string() | atom() | file:deep_list() | binary() - ] - ------------------------------- Detailed message ------------------------------ - - [binary() | pid()] is not compatible with [file:name_all()] - because - binary() | pid() is not compatible with file:name_all() - because - binary() | pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2086:5 - │ -2086 │ filename:join("server", "erl"). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2091:5 - │ -2091 │ filename:join("server", <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, <<..>>). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2096:5 - │ -2096 │ filename:join(<<>>, ""). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(<<..>>, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2111:5 - │ -2111 │ filename:join(atom, <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join('atom', <<..>>). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2121:25 - │ -2121 │ filename:join(<<>>, self()). - │ ^^^^^^ - │ │ - │ erlang:self(). -Expression has type: pid() -Context expected type: file:name_all() - │ - -Because in the expression's type: - Here the type is: pid() - Context expects type: string() | atom() | file:deep_list() | binary() - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - pid() is not compatible with file:name_all() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2150:5 - │ -2150 │ ╭ ╭ queue:filter( -2151 │ │ │ fun my_filter1/1, -2152 │ │ │ Q -2153 │ │ │ ). - │ ╰─│─────^ queue:filter(my_filter1/1, Q). -Expression has type: queue:queue(atom() | number()) -Context expected type: queue:queue(number()) - │ ╰─────' - -Because in the expression's type: - { - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: atom() - Differs from the expected type: number() - ] - , [atom() | number()]} - ------------------------------- Detailed message ------------------------------ - - queue:queue(atom() | number()) is not compatible with queue:queue(number()) - because - {[atom() | number()], [atom() | number()]} is not compatible with queue:queue(number()) - because - at tuple index 1: - {[atom() | number()], [atom() | number()]} is not compatible with {[number()], [number()]} - because - [atom() | number()] is not compatible with [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2191:5 - │ -2191 │ M3. - │ ^^ - │ │ - │ M3. -Expression has type: #{count := number(), module := 'foo'} | #{module := 'foo'} -Context expected type: state1() - │ - -Because in the expression's type: - The type is a union type with some valid candidates: #{count := number(), module := 'foo'} - However, the following candidate doesn't match: - Here the type is: #{...} - Context expects type: #{count := ..., ...} - The type of the expression is missing the following required keys: count. - ------------------------------- Detailed message ------------------------------ - - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with state1() - because - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - because - #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - key `count` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2223:13 - │ -2223 │ Atom + Sum - │ ^^^^ Atom. -Expression has type: atom() -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2279:5 - │ -2279 │ maps:remove(A, M). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:remove(A, M). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2320:5 - │ -2320 │ ╭ ╭ maps:filtermap( -2321 │ │ │ fun -2322 │ │ │ (a, V) -> true; -2323 │ │ │ (b, V) -> {true, atom_to_binary(V)}; - · │ │ -2326 │ │ │ M -2327 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{a => atom() | binary(), b => atom() | binary(), c => atom() | binary()} -Context expected type: #{a := atom(), b := binary()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ..., b => ..., ...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2342:5 - │ -2342 │ ╭ ╭ maps:filtermap( -2343 │ │ │ fun (_, V) -> -2344 │ │ │ {true, atom_to_binary(V)} -2345 │ │ │ end, -2346 │ │ │ M -2347 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2354:23 - │ -2354 │ fun (_, _) -> err end, - │ ^^^ - │ │ - │ 'err'. -Expression has type: 'err' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'err' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'err' is not compatible with boolean() | {'true', term()} - because - 'err' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2362:5 - │ -2362 │ ╭ ╭ maps:filtermap( -2363 │ │ │ fun (_, V) -> {true, atom_to_binary(V)} end, -2364 │ │ │ M -2365 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2363:45 - │ -2363 │ fun (_, V) -> {true, atom_to_binary(V)} end, - │ ^ V. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2377:5 - │ -2377 │ re:replace(Subj, "+", "-", [{return, binary}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'binary'}]). -Expression has type: binary() -Context expected type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2381:5 - │ -2381 │ re:replace(Subj, "+", "-", [{return, list}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'list'}]). -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2385:22 - │ -2385 │ Res = re:replace(Subj, "+", "-", [{return, list}]), - │ ^^^^ - │ │ - │ Subj. -Expression has type: atom() -Context expected type: iodata() | unicode:charlist() - │ - -Because in the expression's type: - Here the type is: atom() - Context expects type: iolist() | binary() - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - atom() is not compatible with iodata() | unicode:charlist() - because - atom() is not compatible with iolist() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2386:5 - │ -2386 │ Res. - │ ^^^ Res. -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2390:38 - │ -2390 │ Res = re:replace(Subj, "+", "-", [{return, something}]), - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ [{'return', 'something'}]. -Expression has type: [{'return', 'something'}] -Context expected type: [{'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - │ - -Because in the expression's type: - [ - { 'return', - Here the type is: 'something' - Context expects type: 'iodata' | 'list' | 'binary' - No candidate matches in the expected union. - } - ] - ------------------------------- Detailed message ------------------------------ - - [{'return', 'something'}] is not compatible with [{'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - because - {'return', 'something'} is not compatible with {'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored' - because - at tuple index 2: - {'return', 'something'} is not compatible with {'return', 'iodata' | 'list' | 'binary'} - because - 'something' is not compatible with 'iodata' | 'list' | 'binary' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2506:5 - │ -2506 │ lists:partition(fun is_number/1, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[number()], [atom()]} -Context expected type: {[atom()], [number()]} - │ - -Because in the expression's type: - { - [ - Here the type is: number() - Context expects type: atom() - ] - , [atom()]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[number()], [atom()]} is not compatible with {[atom()], [number()]} - because - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2518:5 - │ -2518 │ lists:partition(fun({_Term, V}) -> is_number(V) end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{term(), number()}], [{term(), atom()}]} -Context expected type: {[{term(), atom()}], [{term(), number()}]} - │ - -Because in the expression's type: - { - [ - { term(), - Here the type is: number() - Context expects type: atom() - } - ] - , [{term(), atom()}]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[{term(), number()}], [{term(), atom()}]} is not compatible with {[{term(), atom()}], [{term(), number()}]} - because - [{term(), number()}] is not compatible with [{term(), atom()}] - because - at tuple index 2: - {term(), number()} is not compatible with {term(), atom()} - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2536:5 - │ -2536 │ lists:partition(fun({ok, _}) -> true; (_) -> false end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} -Context expected type: {[{'ok', atom()}], [{'error', term()}]} - │ - -Because in the expression's type: - { [{'ok', atom()}], - [ - { - Here the type is: 'ok' - Context expects type: 'error' - , atom()} - ] - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} is not compatible with {[{'ok', atom()}], [{'error', term()}]} - because - [{'ok', atom()} | {'error', term()}] is not compatible with [{'error', term()}] - because - {'ok', atom()} | {'error', term()} is not compatible with {'error', term()} - because - at tuple index 1: - {'ok', atom()} is not compatible with {'error', term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2576:33 - │ -2576 │ maps_intersect_2_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2586:33 - │ -2586 │ maps_intersect_4_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a => 'true'} - │ - -Because in the expression's type: - #{ a => - Here the type is: number() - Context expects type: 'true' - , ... } - ------------------------------- Detailed message ------------------------------ - - #{a => number()} is not compatible with #{a => 'true'} - because - at key `a`: - #{a => number()} is not compatible with #{a => 'true'} - because - number() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2596:33 - │ -2596 │ maps_intersect_6_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2709:40 - │ -2709 │ (foo, A) -> binary_to_atom(A); - │ ^ A. -Expression has type: atom() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2710:40 - │ -2710 │ (bar, B) -> atom_to_binary(B); - │ ^ B. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2711:50 - │ -2711 │ ({foo, bar}, I) -> binary_to_integer(I); - │ ^ I. -Expression has type: number() -Context expected type: binary() - -202 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty deleted file mode 100644 index ec107b3c14..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty +++ /dev/null @@ -1,3258 +0,0 @@ -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:26:5 - │ -26 │ element(4, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(4, Tup). -Expression has type: #{dynamic() => dynamic()} -Context expected type: atom() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:30:5 - │ -30 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 3 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:40:5 - │ -40 │ element(2, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(2, Tup). -Expression has type: number() | string() | atom() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:44:16 - │ -44 │ element(2, Tup). - │ ^^^ - │ │ - │ Tup. -Expression has type: {atom(), string()} | [dynamic()] -Context expected type: tuple() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {atom(), string()} - However the following candidate: [dynamic()] - Differs from the expected type: tuple() - ------------------------------- Detailed message ------------------------------ - - {atom(), string()} | [dynamic()] is not compatible with tuple() - because - [dynamic()] is not compatible with tuple() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:48:5 - │ -48 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 2 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:54:5 - │ -54 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(N, Tup). -Expression has type: atom() | number() | string() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:58:5 - │ -58 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Tup). -Expression has type: atom() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - atom() | number() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:68:5 - │ -68 │ element(1, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(1, Tup). -Expression has type: dynamic() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: dynamic() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:86:5 - │ -86 │ element(N, Rec). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Rec). -Expression has type: 'foo' | 'ok' | 'error' | number() | string() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'error' | 'foo' | 'ok' - However the following candidate: string() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - 'foo' | 'ok' | 'error' | number() | string() is not compatible with atom() - because - string() is not compatible with atom() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:91:1 - │ -91 │ ╭ element_2_none_1(Tup) -> -92 │ │ element(42, Tup). - │ ╰────────────────────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:95:1 - │ -95 │ ╭ element_2_none_2(N, Tup) -> -96 │ │ element(N, Tup). - │ ╰───────────────────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:108:5 - │ -108 │ maps:get(K, M). - │ ^^^^^^^^^^^^^^ maps:get(K, M). -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:128:5 - │ -128 │ Res. - │ ^^^ Res. -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:154:12 - │ -154 │ get(a, M). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:160:23 - │ -160 │ Res = maps:get(a, M), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:167:17 - │ -167 │ maps:get(a, M, false). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:173:23 - │ -173 │ Res = maps:get(a, M, false), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:193:5 - │ -193 │ maps:get(K, M, 0). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(K, M, 0). -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:200:5 - │ -200 │ Res. - │ ^^^ - │ │ - │ Res. -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:206:5 - │ -206 │ ╭ ╭ { -207 │ │ │ maps:get(a, M, undefined), -208 │ │ │ maps:get(n, M, undefined) -209 │ │ │ }. - │ ╰─│─────^ {maps:get('a', M, 'undefined'), maps:get('n', M, 'undefined')}. -Expression has type: {atom(), 'undefined' | number()} -Context expected type: {atom(), number()} - │ ╰─────' - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: 'undefined' - Differs from the expected type: number() - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), 'undefined' | number()} is not compatible with {atom(), number()} - because - 'undefined' | number() is not compatible with number() - because - 'undefined' is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:221:27 - │ -221 │ map_get_2_17_neg(V, M) -> maps:get(V, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M). -Expression has type: 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' - ------------------------------- Detailed message ------------------------------ - - 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:233:27 - │ -233 │ map_get_3_19_neg(V, M) -> maps:get(V, M, undefined). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M, 'undefined'). -Expression has type: 'undefined' | 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' | 'undefined' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'undefined' | 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' | 'undefined' - ------------------------------- Detailed message ------------------------------ - - 'undefined' | 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:309:5 - │ -309 │ Res. - │ ^^^ Res. -Expression has type: {'value', #{}} | 'false' -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:363:23 - │ -363 │ fun (_, _) -> self() end, - │ ^^^^^^ erlang:self(). -Expression has type: pid() -Context expected type: boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:382:17 - │ -382 │ maps:filter(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> boolean()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:389:17 - │ -389 │ maps:filter(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((T) -> boolean()), [T]) -> [T]) with 1 type parameter -Context expected type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun()/2 with 1 type parameter - Context expects type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:413:37 - │ -413 │ maps:filter(fun erlang:'=:='/2, X). - │ ^ - │ │ - │ X. -Expression has type: #{K => V} | 'a' -Context expected type: #{term() => term()} | maps:iterator() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: #{K => V} - However the following candidate: 'a' - Differs from the expected type: #{term() => term()} | maps:iterator() - ------------------------------- Detailed message ------------------------------ - - #{K => V} | 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:425:20 - │ -425 │ maps:filter(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:454:5 - │ -454 │ ╭ ╭ maps:map( -455 │ │ │ fun (_, _) -> self() end, -456 │ │ │ M -457 │ │ │ ). - │ ╰─│─────^ maps:map(fun, M). -Expression has type: #{number() => pid()} -Context expected type: #{number() => boolean()} - │ ╰─────' - -Because in the expression's type: - #{ number() => - Here the type is: pid() - Context expects type: boolean() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{number() => pid()} is not compatible with #{number() => boolean()} - the default associations are not compatible - because - pid() is not compatible with boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:474:14 - │ -474 │ maps:map(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> term()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:481:14 - │ -481 │ maps:map(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters -Context expected type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters - Context expects type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:497:5 - │ -497 │ maps:map(F, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:map(F, M). -Expression has type: #{a := boolean(), b := boolean()} -Context expected type: #{a => 'a', b => 'b'} - │ - -Because in the expression's type: - #{ a => - Here the type is: boolean() - Context expects type: 'a' - , ... } - ------------------------------- Detailed message ------------------------------ - - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - at key `a`: - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - boolean() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:503:17 - │ -503 │ maps:map(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:538:9 - │ -538 │ fun (K, V) -> [K, V] end, [], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ fun. -fun with arity 2 used as fun with 3 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:544:5 - │ -544 │ ╭ ╭ maps:fold( -545 │ │ │ fun (_, _, Acc) -> [Acc] end, -546 │ │ │ [], -547 │ │ │ M -548 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [[[]]] -Context expected type: [number() | 'a' | 'b'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: [[]] - Context expects type: number() | 'a' | 'b' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [[[]]] is not compatible with [number() | 'a' | 'b'] - because - [[]] is not compatible with number() | 'a' | 'b' - because - [[]] is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:545:28 - │ -545 │ fun (_, _, Acc) -> [Acc] end, - │ ^^^^^ - │ │ - │ [Acc]. -Expression has type: [[[[]]]] -Context expected type: [[[]]] - │ - -Because in the expression's type: - [ - [ - [ - Here the type is: [] - Context expects type: none() - ] - ] - ] - ------------------------------- Detailed message ------------------------------ - - [[[[]]]] is not compatible with [[[]]] - because - [[[]]] is not compatible with [[]] - because - [[]] is not compatible with [] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:552:5 - │ -552 │ ╭ maps:fold( -553 │ │ fun (_, _, Acc) -> Acc end, -554 │ │ [], -555 │ │ non_kv -556 │ │ ). - │ ╰─────^ maps:fold(fun, [], 'non_kv'). -Expression has type: [] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:555:9 - │ -555 │ non_kv - │ ^^^^^^ - │ │ - │ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:560:1 - │ -560 │ ╭ maps_fold_3_6(None) -> -561 │ │ maps:fold( -562 │ │ None, -563 │ │ #{}, -564 │ │ #{1 => 1} -565 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:568:1 - │ -568 │ ╭ maps_fold_3_7(None) -> -569 │ │ maps:fold( -570 │ │ None, -571 │ │ None, -572 │ │ None -573 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:578:5 - │ -578 │ ╭ ╭ maps:fold( -579 │ │ │ fun (_K, A, _Acc) -> A end, -580 │ │ │ [], -581 │ │ │ M -582 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [] | atom() -Context expected type: atom() - │ ╰─────' - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: [] - Differs from the expected type: atom() - ------------------------------- Detailed message ------------------------------ - - [] | atom() is not compatible with atom() - because - [] is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', []) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun((number(), 'a', []) -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> [] | dynamic()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:636:5 - │ -636 │ ╭ ╭ maps:fold( -637 │ │ │ fun -638 │ │ │ (_K, {i, I}, Acc) -> -639 │ │ │ [I | Acc]; - · │ │ -646 │ │ │ M -647 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [number() | binary() | atom()] -Context expected type: [binary()] | [number()] | [atom()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: number() - Differs from the expected type: binary() - ] - ------------------------------- Detailed message ------------------------------ - - [number() | binary() | atom()] is not compatible with [binary()] | [number()] | [atom()] - because - [number() | binary() | atom()] is not compatible with [binary()] - because - number() | binary() | atom() is not compatible with binary() - because - number() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:671:41 - │ -671 │ maps_to_list_7_neg(Num) -> maps:to_list(Num). - │ ^^^ - │ │ - │ Num. -Expression has type: number() -Context expected type: #{term() => term()} | maps:iterator() - │ - - number() is not compatible with #{term() => term()} | maps:iterator() - because - number() is not compatible with #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:675:25 - │ -675 │ maps_merge_1(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:679:25 - │ -679 │ maps_merge_2(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number() | boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number() | boolean(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:683:25 - │ -683 │ maps_merge_3(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a := string(), b := boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:697:44 - │ -697 │ maps_merge_7_neg(M1, M2) -> maps:merge(M1, M2). - │ ^^ M2. -Expression has type: number() -Context expected type: #{a => binary()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:701:25 - │ -701 │ maps_merge_8(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'a' | 'b' => atom() | number()} -Context expected type: #{a := atom(), b := number()} | #{a := atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} | #{a := atom()} - because - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} - keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:721:9 - │ -721 │ fun erlang:binary_to_list/1, - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:binary_to_list/1. -Expression has type: fun((binary()) -> [number()]) -Context expected type: fun((number()) -> boolean() | {'true', term()}) - │ - -Because in the expression's type: - fun((binary()) -> - Here the type is: [number()] - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((binary()) -> [number()]) is not compatible with fun((number()) -> boolean() | {'true', term()}) - because - [number()] is not compatible with boolean() | {'true', term()} - because - [number()] is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:739:20 - │ -739 │ (3) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:749:9 - │ -749 │ not_a_list - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:791:17 - │ -791 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: {'true', 'a'} | 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {'true', 'a'} - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | {'true', term()} - ------------------------------- Detailed message ------------------------------ - - {'true', 'a'} | 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:827:9 - │ -827 │ ╭ ╭ fun (a) -> [a]; -828 │ │ │ (b) -> true; -829 │ │ │ (c) -> wrong_ret end, - │ ╰─│────────────────────────────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰────────────────────────────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'true' - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'true' | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:829:20 - │ -829 │ (c) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:9 - │ -837 │ ╭ ╭ fun (1) -> {true, a}; -838 │ │ │ (2) -> true end, - │ ╰─│───────────────────────────^ fun. -Expression has type: fun((dynamic()) -> {'true', 'a'} | 'true') -Context expected type: fun((Item) -> boolean() | [Item]) - │ ╰───────────────────────────' - -Because in the expression's type: - fun((dynamic()) -> - Here the type is a union type with some valid candidates: 'true' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | [Item] - ) - ------------------------------- Detailed message ------------------------------ - - fun((dynamic()) -> {'true', 'a'} | 'true') is not compatible with fun((Item) -> boolean() | [Item]) - because - {'true', 'a'} | 'true' is not compatible with boolean() | [Item] - because - {'true', 'a'} is not compatible with boolean() | [Item] - expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:20 - │ -837 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | [dynamic()] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(Item) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[Item], [Item]} - ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(Item) - because - 'not_a_queue' is not compatible with {[Item], [Item]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(dynamic()) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[dynamic()], [dynamic()]} - ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(dynamic()) - because - 'not_a_queue' is not compatible with {[dynamic()], [dynamic()]} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:846:9 - │ -846 │ ╭ fun (wrong, arity) -> -847 │ │ [a] -848 │ │ end, - │ ╰───────────^ fun. -fun with arity 2 used as fun with 1 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:9 - │ -857 │ ╭ ╭ fun (1) -> {true, a}; -858 │ │ │ (X) -> case X of -859 │ │ │ true -> -860 │ │ │ [a]; - · │ │ -863 │ │ │ end -864 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'false' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'false' | {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - because - {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:20 - │ -857 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:873:9 - │ -873 │ ╭ ╭ fun (a) -> [a]; -874 │ │ │ (X) -> -875 │ │ │ Res = case X of -876 │ │ │ true -> - · │ │ -881 │ │ │ Res -882 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:881:17 - │ -881 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: ['a'] | 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ------------------------------- Detailed message ------------------------------ - - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun((dynamic()) -> boolean() | [dynamic()]) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun((dynamic()) -> boolean() | [dynamic()]) - because - atom() is not compatible with boolean() | [dynamic()] - because - atom() is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ) - ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - atom() is not compatible with boolean() | ['a' | 'b'] - because - atom() is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:925:1 - │ -925 │ ╭ queue_filter_13_neg(Q) -> -926 │ │ queue:filter( -927 │ │ fun atom_to_list/1, -928 │ │ Q -929 │ │ ), -930 │ │ ok. - │ ╰──────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:955:5 - │ -955 │ ╭ lists:keystore( -956 │ │ a, 1, -957 │ │ [{foo, b}, {c, d}], -958 │ │ non_tuple -959 │ │ ). - │ ╰─────^ lists:keystore('a', 1, [{'foo', 'b'}, {'c', 'd'}], 'non_tuple'). -Expression has type: [{'foo', 'b'} | {'c', 'd'} | dynamic()] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:958:9 - │ -958 │ non_tuple - │ ^^^^^^^^^ 'non_tuple'. -Expression has type: 'non_tuple' -Context expected type: tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:966:5 - │ -966 │ ╭ lists:keystore( -967 │ │ a, 1, -968 │ │ [non_tuple], -969 │ │ {replacement} -970 │ │ ). - │ ╰─────^ lists:keystore('a', 1, ['non_tuple'], {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:968:9 - │ -968 │ [non_tuple], - │ ^^^^^^^^^^^ - │ │ - │ ['non_tuple']. -Expression has type: ['non_tuple'] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: 'non_tuple' - Context expects type: tuple() - ] - ------------------------------- Detailed message ------------------------------ - - ['non_tuple'] is not compatible with [tuple()] - because - 'non_tuple' is not compatible with tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:975:5 - │ -975 │ ╭ lists:keystore( -976 │ │ a, 1, -977 │ │ non_list, -978 │ │ {replacement} -979 │ │ ). - │ ╰─────^ lists:keystore('a', 1, 'non_list', {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:977:9 - │ -977 │ non_list, - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [tuple()] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:983:1 - │ -983 │ ╭ keystore_7(None) -> -984 │ │ lists:keystore( -985 │ │ a, 1, -986 │ │ None, -987 │ │ {replacement} -988 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:991:1 - │ -991 │ ╭ keystore_8(None) -> -992 │ │ lists:keystore( -993 │ │ a, 1, -994 │ │ None, -995 │ │ None -996 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1021:22 - │ -1021 │ lists:keytake(a, non_num, []), - │ ^^^^^^^ 'non_num'. -Expression has type: 'non_num' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1038:15 - │ -1038 │ lists:sum([a, 1]). - │ ^^^^^^ - │ │ - │ ['a', 1]. -Expression has type: ['a' | number()] -Context expected type: [number()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: 'a' - Differs from the expected type: number() - ] - ------------------------------- Detailed message ------------------------------ - - ['a' | number()] is not compatible with [number()] - because - 'a' | number() is not compatible with number() - because - 'a' is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1042:15 - │ -1042 │ lists:sum(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1100:5 - │ -1100 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' - │ - - term() is not compatible with pid() | 'undefined' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1106:5 - │ -1106 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' | 'v' - │ - - term() is not compatible with pid() | 'undefined' | 'v' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1111:5 - │ -1111 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1117:5 - │ -1117 │ proplists:get_value(k, L, 3). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 3). -Expression has type: term() -Context expected type: pid() | number() - │ - - term() is not compatible with pid() | number() - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1123:5 - │ -1123 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v1' | 'v2' | 'my_default' - │ - - term() is not compatible with 'v1' | 'v2' | 'my_default' - because - term() is not compatible with 'v1' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1129:5 - │ -1129 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1134:28 - │ -1134 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1139:5 - │ -1139 │ proplists:get_value(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', []). -Expression has type: term() -Context expected type: 'default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1144:1 - │ -1144 │ proplists:get_value(k, [], my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', [], 'my_default'). -Expression has type: term() -Context expected type: 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1149:28 - │ -1149 │ proplists:get_value(k, b, my_default). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1154:5 - │ -1154 │ proplists:get_bool(b, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_bool('b', L). -Expression has type: boolean() -Context expected type: 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1160:5 - │ -1160 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default'] - because - term() is not compatible with pid() | 'default' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1166:5 - │ -1166 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default' | 'v'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' | 'v' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default' | 'v'] - because - term() is not compatible with pid() | 'default' | 'v' - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1171:5 - │ -1171 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid()] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid()] - because - term() is not compatible with pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1176:5 - │ -1176 │ proplists:get_all_values(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', []). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:5 - │ -1181 │ proplists:get_all_values(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', 'b'). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:33 - │ -1181 │ proplists:get_all_values(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1186:27 - │ -1186 │ proplists:get_bool(b, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1191:5 - │ -1191 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'c' - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['c'] - because - term() is not compatible with 'c' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1197:5 - │ -1197 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['a' | 'b' | 'c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' | 'b' | 'c' - No candidate matches in the expected union. - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a' | 'b' | 'c'] - because - term() is not compatible with 'a' | 'b' | 'c' - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1202:24 - │ -1202 │ proplists:get_keys(a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1207:5 - │ -1207 │ ╭ ╭ proplists:get_keys( -1208 │ │ │ [{a, b, c}] -1209 │ │ │ ). - │ ╰─│─────^ proplists:get_keys([{'a', 'b', 'c'}]). -Expression has type: [term()] -Context expected type: ['a'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' - ] - ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a'] - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1215:5 - │ -1215 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). -Expression has type: term() -Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:5 - │ -1220 │ proplists:get_value(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', 'b'). -Expression has type: term() -Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:28 - │ -1220 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1225:1 - │ -1225 │ proplists:lookup(self(), [a, {b, true}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup(erlang:self(), ['a', {'b', 'true'}]). -Expression has type: 'none' | tuple() -Context expected type: {'b', 'true'} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: tuple() - However the following candidate: 'none' - Differs from the expected type: {'b', 'true'} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1229:25 - │ -1229 │ proplists:lookup(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1239:5 - │ -1239 │ proplists:lookup(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup('k', []). -Expression has type: 'none' | tuple() -Context expected type: 'none' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'none' - However the following candidate: tuple() - Differs from the expected type: 'none' - ------------------------------- Detailed message ------------------------------ - - 'none' | tuple() is not compatible with 'none' - because - tuple() is not compatible with 'none' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1252:29 - │ -1252 │ proplists:lookup_all(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1265:5 - │ -1265 │ ╭ ╭ proplists:lookup_all( -1266 │ │ │ self(), -1267 │ │ │ [] -1268 │ │ │ ). - │ ╰─│─────^ proplists:lookup_all(erlang:self(), []). -Expression has type: [tuple()] -Context expected type: [] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: tuple() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1291:34 - │ -1291 │ proplists:is_defined(self(), b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [A] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1311:5 - │ -1311 │ proplists:split(L, Ks). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:split(L, Ks). -Expression has type: {[[term()]], [term()]} -Context expected type: {[plist('a', 'b')], plist('a', 'b')} - │ - -Because in the expression's type: - { - [ - [ - Here the type is: term() - Context expects type: 'a' | {'a', 'b'} - No candidate matches in the expected union. - ] - ] - , [term()]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[[term()]], [term()]} is not compatible with {[plist('a', 'b')], plist('a', 'b')} - because - [[term()]] is not compatible with [plist('a', 'b')] - because - [term()] is not compatible with plist('a', 'b') - because - [term()] is not compatible with ['a' | {'a', 'b'}] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1315:21 - │ -1315 │ proplists:split(b, []). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1319:25 - │ -1319 │ proplists:split([], b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1324:22 - │ -1324 │ proplists:to_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [atom() | {term(), term()} | term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{K => V} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1363:5 - │ -1363 │ proplists:get_value(a, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1368:5 - │ -1368 │ proplists:get_value(X, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value(X, ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1373:5 - │ -1373 │ proplists:get_value(a, [a], b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a'], 'b'). -Expression has type: term() -Context expected type: 'true' | 'b' - │ - - term() is not compatible with 'true' | 'b' - because - term() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1416:5 - │ -1416 │ file:consult(some_file). - │ ^^^^^^^^^^^^^^^^^^^^^^^ file:consult('some_file'). -Expression has type: {'ok', [dynamic()]} | {'error', {number(), atom(), term()} | 'terminated' | 'badarg' | file:posix() | 'system_limit'} -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1428:5 - │ -1428 │ lists:keysort(2, [{a, c}, {b, d}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:keysort(2, [{'a', 'c'}, {'b', 'd'}]). -Expression has type: [{'a', 'c'} | {'b', 'd'}] -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1432:22 - │ -1432 │ lists:keysort(1, [3]). - │ ^^^ - │ │ - │ [3]. -Expression has type: [number()] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: number() - Context expects type: tuple() - ] - ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [tuple()] - because - number() is not compatible with tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1448:5 - │ -1448 │ ╭ ╭ lists:filtermap( -1449 │ │ │ fun(X) when X div 2 =:= 0 -> -1450 │ │ │ {true, integer_to_list(X)}; -1451 │ │ │ (X) -> - · │ │ -1454 │ │ │ [1, 2, 3, 4] -1455 │ │ │ ). - │ ╰─│─────^ lists:filtermap(fun, [1, 2, 3, 4]). -Expression has type: [string() | number()] -Context expected type: [number()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: string() - Differs from the expected type: number() - ] - ------------------------------- Detailed message ------------------------------ - - [string() | number()] is not compatible with [number()] - because - string() | number() is not compatible with number() - because - string() is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1482:5 - │ -1482 │ erlang:min(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:min(X, Y). -Expression has type: number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1497:5 - │ -1497 │ erlang:max(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:max(X, Y). -Expression has type: atom() | number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1523:9 - │ -1523 │ abs(Atom). - │ ^^^^ Atom. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1536:31 - │ -1536 │ seq3_4_wip_neg() -> lists:seq(a, 2, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1539:30 - │ -1539 │ seq3_5_neg() -> lists:seq(1, a, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1542:33 - │ -1542 │ seq3_6_neg() -> lists:seq(1, 2, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1570:31 - │ -1570 │ seq2_4_wip_neg() -> lists:seq(a, 2). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1573:30 - │ -1573 │ seq2_5_neg() -> lists:seq(1, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1622:5 - │ -1622 │ ╭ timer:tc( -1623 │ │ fun() -> -1624 │ │ err -1625 │ │ end -1626 │ │ ). - │ ╰─────^ timer:tc(fun). -Expression has type: {number(), 'err'} -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1635:5 - │ -1635 │ ets:lookup(tab, Any). - │ ^^^^^^^^^^^^^^^^^^^^ ets:lookup('tab', Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:5 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ets:lookup(string_lit, Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:16 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1663:18 - │ -1663 │ ets:tab2list("not atom"). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1702:23 - │ -1702 │ lists:flatten([], 1). - │ ^ 1. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1711:19 - │ -1711 │ lists:flatten(3). - │ ^ 3. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1724:8 - │ -1724 │ -> lists:flatten(X). - │ ^^^^^^^^^^^^^^^^ - │ │ - │ lists:flatten(X). -Expression has type: [{A, B} | {B, A}] -Context expected type: [{A, B}] - │ - -Because in the expression's type: - [ - { - Here the type is: B - Context expects type: A - , A} - ] - ------------------------------- Detailed message ------------------------------ - - [{A, B} | {B, A}] is not compatible with [{A, B}] - because - {A, B} | {B, A} is not compatible with {A, B} - because - at tuple index 1: - {B, A} is not compatible with {A, B} - because - B is not compatible with A - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1757:5 - │ -1757 │ ╭ ╭ maps:without( -1758 │ │ │ [a, c, DOrE], -1759 │ │ │ #{ -1760 │ │ │ a => ka, - · │ │ -1764 │ │ │ } -1765 │ │ │ ). - │ ╰─│─────^ maps:without(['a', 'c', DOrE], #{..}). -Expression has type: #{a => 'ka', b => atom(), c => pid(), d => 'kd'} -Context expected type: #{b => atom()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared in the former but not in the latter and the latter map has no default association - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1769:18 - │ -1769 │ maps:without(non_list, #{}). - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1773:22 - │ -1773 │ maps:without([], non_map). - │ ^^^^^^^ 'non_map'. -Expression has type: 'non_map' -Context expected type: #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:1808:1 - │ -1808 │ ╭ maps_without_12_neg(None) -> -1809 │ │ maps:without( -1810 │ │ [a, b], -1811 │ │ None -1812 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1818:5 - │ -1818 │ ╭ ╭ maps:without( -1819 │ │ │ Keys, -1820 │ │ │ #{a => self(), b => self()} -1821 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1826:5 - │ -1826 │ ╭ ╭ maps:without( -1827 │ │ │ [a | improper_tail], -1828 │ │ │ #{a => self(), b => self()} -1829 │ │ │ ). - │ ╰─│─────^ maps:without(['a' | 'improper_tail'], #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1827:12 - │ -1827 │ [a | improper_tail], - │ ^^^^^^^^^^^^^^^^ 'improper_tail'. -Expression has type: 'improper_tail' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1837:5 - │ -1837 │ ╭ ╭ maps:without( -1838 │ │ │ Keys, -1839 │ │ │ #{a => ka, b => self()} -1840 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => 'ka', b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1854:5 - │ -1854 │ maps:without([a, b], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:without(['a', 'b'], M). -Expression has type: #{c := 'cv', d := 'dv'} | #{c := 'cv', e => 'ev'} -Context expected type: #{c := atom()} - │ - -Because in the expression's type: - Here the type is: #{d := ...} - Context expects type: #{...} - The expected map has no corresponding key for: d. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1916:23 - │ -1916 │ custom_overloaded(X). - │ ^ - │ │ - │ X. -Expression has type: term() -Context expected type: atom() | binary() - │ - - term() is not compatible with atom() | binary() - because - term() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1939:5 - │ -1939 │ {A, N}. - │ ^^^^^^ - │ │ - │ {A, N}. -Expression has type: {atom(), number() | pid()} -Context expected type: {atom(), number()} - │ - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: pid() - Differs from the expected type: number() - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), number() | pid()} is not compatible with {atom(), number()} - because - number() | pid() is not compatible with number() - because - pid() is not compatible with number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2041:5 - │ -2041 │ filename:join(["server", "erl"]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2046:5 - │ -2046 │ filename:join(["server", <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, <<..>>]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2051:5 - │ -2051 │ filename:join([<<>>, ""]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2066:5 - │ -2066 │ filename:join([<<>>, <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, <<..>>]). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2081:19 - │ -2081 │ filename:join([<<>>, self()]). - │ ^^^^^^^^^^^^^^ - │ │ - │ [<<..>>, erlang:self()]. -Expression has type: [binary() | pid()] -Context expected type: [file:name_all()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: pid() - Differs from the expected type: string() | atom() | file:deep_list() | binary() - ] - ------------------------------- Detailed message ------------------------------ - - [binary() | pid()] is not compatible with [file:name_all()] - because - binary() | pid() is not compatible with file:name_all() - because - binary() | pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2086:5 - │ -2086 │ filename:join("server", "erl"). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2091:5 - │ -2091 │ filename:join("server", <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, <<..>>). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2096:5 - │ -2096 │ filename:join(<<>>, ""). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(<<..>>, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2111:5 - │ -2111 │ filename:join(atom, <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join('atom', <<..>>). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2121:25 - │ -2121 │ filename:join(<<>>, self()). - │ ^^^^^^ - │ │ - │ erlang:self(). -Expression has type: pid() -Context expected type: file:name_all() - │ - -Because in the expression's type: - Here the type is: pid() - Context expects type: string() | atom() | file:deep_list() | binary() - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - pid() is not compatible with file:name_all() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2150:5 - │ -2150 │ ╭ ╭ queue:filter( -2151 │ │ │ fun my_filter1/1, -2152 │ │ │ Q -2153 │ │ │ ). - │ ╰─│─────^ queue:filter(my_filter1/1, Q). -Expression has type: queue:queue(atom() | number()) -Context expected type: queue:queue(number()) - │ ╰─────' - -Because in the expression's type: - { - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: atom() - Differs from the expected type: number() - ] - , [atom() | number()]} - ------------------------------- Detailed message ------------------------------ - - queue:queue(atom() | number()) is not compatible with queue:queue(number()) - because - {[atom() | number()], [atom() | number()]} is not compatible with queue:queue(number()) - because - at tuple index 1: - {[atom() | number()], [atom() | number()]} is not compatible with {[number()], [number()]} - because - [atom() | number()] is not compatible with [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2191:5 - │ -2191 │ M3. - │ ^^ - │ │ - │ M3. -Expression has type: #{count := number(), module := 'foo'} | #{module := 'foo'} -Context expected type: state1() - │ - -Because in the expression's type: - The type is a union type with some valid candidates: #{count := number(), module := 'foo'} - However, the following candidate doesn't match: - Here the type is: #{...} - Context expects type: #{count := ..., ...} - The type of the expression is missing the following required keys: count. - ------------------------------- Detailed message ------------------------------ - - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with state1() - because - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - because - #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - key `count` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2223:13 - │ -2223 │ Atom + Sum - │ ^^^^ Atom. -Expression has type: atom() -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2279:5 - │ -2279 │ maps:remove(A, M). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:remove(A, M). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2320:5 - │ -2320 │ ╭ ╭ maps:filtermap( -2321 │ │ │ fun -2322 │ │ │ (a, V) -> true; -2323 │ │ │ (b, V) -> {true, atom_to_binary(V)}; - · │ │ -2326 │ │ │ M -2327 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{a => atom() | binary(), b => atom() | binary(), c => atom() | binary()} -Context expected type: #{a := atom(), b := binary()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ..., b => ..., ...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2342:5 - │ -2342 │ ╭ ╭ maps:filtermap( -2343 │ │ │ fun (_, V) -> -2344 │ │ │ {true, atom_to_binary(V)} -2345 │ │ │ end, -2346 │ │ │ M -2347 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2354:23 - │ -2354 │ fun (_, _) -> err end, - │ ^^^ - │ │ - │ 'err'. -Expression has type: 'err' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'err' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - 'err' is not compatible with boolean() | {'true', term()} - because - 'err' is not compatible with 'false' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2362:5 - │ -2362 │ ╭ ╭ maps:filtermap( -2363 │ │ │ fun (_, V) -> {true, atom_to_binary(V)} end, -2364 │ │ │ M -2365 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2363:45 - │ -2363 │ fun (_, V) -> {true, atom_to_binary(V)} end, - │ ^ V. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2377:5 - │ -2377 │ re:replace(Subj, "+", "-", [{return, binary}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'binary'}]). -Expression has type: binary() -Context expected type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2381:5 - │ -2381 │ re:replace(Subj, "+", "-", [{return, list}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'list'}]). -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2385:22 - │ -2385 │ Res = re:replace(Subj, "+", "-", [{return, list}]), - │ ^^^^ - │ │ - │ Subj. -Expression has type: atom() -Context expected type: iodata() | unicode:charlist() - │ - -Because in the expression's type: - Here the type is: atom() - Context expects type: iolist() | binary() - No candidate matches in the expected union. - ------------------------------- Detailed message ------------------------------ - - atom() is not compatible with iodata() | unicode:charlist() - because - atom() is not compatible with iolist() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2386:5 - │ -2386 │ Res. - │ ^^^ Res. -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2390:38 - │ -2390 │ Res = re:replace(Subj, "+", "-", [{return, something}]), - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ [{'return', 'something'}]. -Expression has type: [{'return', 'something'}] -Context expected type: ['notempty_atstart' | 'noteol' | 'notbol' | 'global' | {'match_limit_recursion', number()} | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - │ - -Because in the expression's type: - [ - { 'return', - Here the type is: 'something' - Context expects type: 'iodata' | 'list' | 'binary' - No candidate matches in the expected union. - } - ] - ------------------------------- Detailed message ------------------------------ - - [{'return', 'something'}] is not compatible with ['notempty_atstart' | 'noteol' | 'notbol' | 'global' | {'match_limit_recursion', number()} | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - because - {'return', 'something'} is not compatible with 'notempty_atstart' | 'noteol' | 'notbol' | 'global' | {'match_limit_recursion', number()} | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored' - because - at tuple index 2: - {'return', 'something'} is not compatible with {'return', 'iodata' | 'list' | 'binary'} - because - 'something' is not compatible with 'iodata' | 'list' | 'binary' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2506:5 - │ -2506 │ lists:partition(fun is_number/1, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[number()], [atom()]} -Context expected type: {[atom()], [number()]} - │ - -Because in the expression's type: - { - [ - Here the type is: number() - Context expects type: atom() - ] - , [atom()]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[number()], [atom()]} is not compatible with {[atom()], [number()]} - because - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2518:5 - │ -2518 │ lists:partition(fun({_Term, V}) -> is_number(V) end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{term(), number()}], [{term(), atom()}]} -Context expected type: {[{term(), atom()}], [{term(), number()}]} - │ - -Because in the expression's type: - { - [ - { term(), - Here the type is: number() - Context expects type: atom() - } - ] - , [{term(), atom()}]} - ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[{term(), number()}], [{term(), atom()}]} is not compatible with {[{term(), atom()}], [{term(), number()}]} - because - [{term(), number()}] is not compatible with [{term(), atom()}] - because - at tuple index 2: - {term(), number()} is not compatible with {term(), atom()} - because - number() is not compatible with atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2536:5 - │ -2536 │ lists:partition(fun({ok, _}) -> true; (_) -> false end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} -Context expected type: {[{'ok', atom()}], [{'error', term()}]} - │ - -Because in the expression's type: - { [{'ok', atom()}], - [ - { - Here the type is: 'ok' - Context expects type: 'error' - , atom()} - ] - } - ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} is not compatible with {[{'ok', atom()}], [{'error', term()}]} - because - [{'ok', atom()} | {'error', term()}] is not compatible with [{'error', term()}] - because - {'ok', atom()} | {'error', term()} is not compatible with {'error', term()} - because - at tuple index 1: - {'ok', atom()} is not compatible with {'error', term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2576:33 - │ -2576 │ maps_intersect_2_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2586:33 - │ -2586 │ maps_intersect_4_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a => 'true'} - │ - -Because in the expression's type: - #{ a => - Here the type is: number() - Context expects type: 'true' - , ... } - ------------------------------- Detailed message ------------------------------ - - #{a => number()} is not compatible with #{a => 'true'} - because - at key `a`: - #{a => number()} is not compatible with #{a => 'true'} - because - number() is not compatible with 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2596:33 - │ -2596 │ maps_intersect_6_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2709:40 - │ -2709 │ (foo, A) -> binary_to_atom(A); - │ ^ A. -Expression has type: atom() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2710:40 - │ -2710 │ (bar, B) -> atom_to_binary(B); - │ ^ B. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2711:50 - │ -2711 │ ({foo, bar}, I) -> binary_to_integer(I); - │ ^ I. -Expression has type: number() -Context expected type: binary() - -202 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty similarity index 77% rename from crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty index 370f7689e9..efc8c47aa0 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty @@ -37,12 +37,6 @@ Because in the expression's type: However the following candidate: [dynamic()] Differs from the expected type: tuple() ------------------------------- Detailed message ------------------------------ - - {atom(), string()} | [dynamic()] is not compatible with tuple() - because - [dynamic()] is not compatible with tuple() - error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) ┌─ check/src/custom.erl:48:5 │ @@ -74,12 +68,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - atom() | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:68:5 │ @@ -96,12 +84,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:86:5 │ @@ -118,12 +100,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - 'foo' | 'ok' | 'error' | number() | string() is not compatible with atom() - because - string() is not compatible with atom() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/custom.erl:91:1 │ @@ -202,12 +178,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:200:5 │ @@ -224,12 +194,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:206:5 │ @@ -249,15 +213,6 @@ Because in the expression's type: Differs from the expected type: number() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), 'undefined' | number()} is not compatible with {atom(), number()} - because - 'undefined' | number() is not compatible with number() - because - 'undefined' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:221:27 │ @@ -274,14 +229,6 @@ Because in the expression's type: However the following candidate: 'c_v' Differs from the expected type: 'a_v' | 'b_v' ------------------------------- Detailed message ------------------------------ - - 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' | 'b_v' - because - 'c_v' is not compatible with 'a_v' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:233:27 │ @@ -298,14 +245,6 @@ Because in the expression's type: However the following candidate: 'c_v' Differs from the expected type: 'a_v' | 'b_v' | 'undefined' ------------------------------- Detailed message ------------------------------ - - 'undefined' | 'a_v' | 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' | 'b_v' | 'undefined' - because - 'c_v' is not compatible with 'a_v' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:309:5 │ @@ -362,28 +301,13 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: #{term() => term()} | maps:iterator() ------------------------------- Detailed message ------------------------------ - - #{K => V} | 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} | maps:iterator() - because - 'a' is not compatible with #{term() => term()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:425:20 │ 425 │ maps:filter(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. + │ ^^^^^^ 'non_kv'. Expression has type: 'non_kv' Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:454:5 @@ -403,13 +327,6 @@ Because in the expression's type: Context expects type: boolean() , ... } ------------------------------- Detailed message ------------------------------ - - #{number() => pid()} is not compatible with #{number() => boolean()} - the default associations are not compatible - because - pid() is not compatible with boolean() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:474:14 │ @@ -451,29 +368,13 @@ Because in the expression's type: Context expects type: 'a' , ... } ------------------------------- Detailed message ------------------------------ - - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - at key `a`: - #{a := boolean(), b := boolean()} is not compatible with #{a => 'a', b => 'b'} - because - boolean() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:503:17 │ 503 │ maps:map(F, non_kv), - │ ^^^^^^ - │ │ - │ 'non_kv'. + │ ^^^^^^ 'non_kv'. Expression has type: 'non_kv' Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) ┌─ check/src/custom.erl:538:9 @@ -502,14 +403,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [[[]]] is not compatible with [number() | 'a' | 'b'] - because - [[]] is not compatible with number() | 'a' | 'b' - because - [[]] is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:545:28 │ @@ -531,14 +424,6 @@ Because in the expression's type: ] ] ------------------------------- Detailed message ------------------------------ - - [[[[]]]] is not compatible with [[[]]] - because - [[[]]] is not compatible with [[]] - because - [[]] is not compatible with [] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:552:5 │ @@ -555,16 +440,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/custom.erl:555:9 │ 555 │ non_kv - │ ^^^^^^ - │ │ - │ 'non_kv'. + │ ^^^^^^ 'non_kv'. Expression has type: 'non_kv' Context expected type: #{term() => term()} | maps:iterator() - │ - - 'non_kv' is not compatible with #{term() => term()} | maps:iterator() - because - 'non_kv' is not compatible with #{term() => term()} error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/custom.erl:560:1 @@ -606,12 +484,6 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - [] | atom() is not compatible with atom() - because - [] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:601:9 │ @@ -682,30 +554,13 @@ Because in the expression's type: Differs from the expected type: binary() ] ------------------------------- Detailed message ------------------------------ - - [number() | binary() | atom()] is not compatible with [binary()] | [number()] | [atom()] - because - [number() | binary() | atom()] is not compatible with [binary()] - because - number() | binary() | atom() is not compatible with binary() - because - number() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:671:41 │ 671 │ maps_to_list_7_neg(Num) -> maps:to_list(Num). - │ ^^^ - │ │ - │ Num. + │ ^^^ Num. Expression has type: number() Context expected type: #{term() => term()} | maps:iterator() - │ - - number() is not compatible with #{term() => term()} | maps:iterator() - because - number() is not compatible with #{term() => term()} error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:675:25 @@ -723,13 +578,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:679:25 │ @@ -746,13 +594,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{'b' | 'a' | 'c' => number() | string() | atom()} is not compatible with #{a => string(), b => number() | boolean(), c => atom()} - key a is not present in the former map but is incompatible with its default association - because - number() | string() | atom() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:683:25 │ @@ -769,10 +610,6 @@ Because in the expression's type: Context expects type: #{a := ..., b := ..., ...} The type of the expression is missing the following required keys: a, b. ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:697:44 │ @@ -797,13 +634,6 @@ Because in the expression's type: Context expects type: #{a := ..., b := ..., ...} The type of the expression is missing the following required keys: a, b. ------------------------------- Detailed message ------------------------------ - - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} | #{a := atom()} - because - #{'a' | 'b' => atom() | number()} is not compatible with #{a := atom(), b := number()} - keys `a`, `b` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:721:9 │ @@ -822,14 +652,6 @@ Because in the expression's type: No candidate matches in the expected union. ) ------------------------------- Detailed message ------------------------------ - - fun((binary()) -> [number()]) is not compatible with fun((number()) -> boolean() | {'true', term()}) - because - [number()] is not compatible with boolean() | {'true', term()} - because - [number()] is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:739:20 │ @@ -846,12 +668,6 @@ Because in the expression's type: Context expects type: 'false' | 'true' | {'true', term()} No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:749:9 │ @@ -876,14 +692,6 @@ Because in the expression's type: However the following candidate: 'wrong_ret' Differs from the expected type: 'false' | 'true' | {'true', term()} ------------------------------- Detailed message ------------------------------ - - {'true', 'a'} | 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with boolean() | {'true', term()} - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:827:9 │ @@ -902,16 +710,6 @@ Because in the expression's type: Differs from the expected type: 'false' | 'true' | ['a' | 'b'] ) ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'true' | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:829:20 │ @@ -928,12 +726,6 @@ Because in the expression's type: Context expects type: 'false' | 'true' | ['a' | 'b'] No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:837:9 │ @@ -951,15 +743,6 @@ Because in the expression's type: Differs from the expected type: 'false' | 'true' | [Item] ) ------------------------------- Detailed message ------------------------------ - - fun((dynamic()) -> {'true', 'a'} | 'true') is not compatible with fun((Item) -> boolean() | [Item]) - because - {'true', 'a'} | 'true' is not compatible with boolean() | [Item] - because - {'true', 'a'} is not compatible with boolean() | [Item] - expected union does not contain any tuple type of size 2 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:837:20 │ @@ -976,10 +759,6 @@ Because in the expression's type: Context expects type: 'false' | 'true' | [dynamic()] No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:839:9 │ @@ -995,12 +774,6 @@ Because in the expression's type: Here the type is: 'not_a_queue' Context expects type: {[Item], [Item]} ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(Item) - because - 'not_a_queue' is not compatible with {[Item], [Item]} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:839:9 │ @@ -1016,12 +789,6 @@ Because in the expression's type: Here the type is: 'not_a_queue' Context expects type: {[dynamic()], [dynamic()]} ------------------------------- Detailed message ------------------------------ - - 'not_a_queue' is not compatible with queue:queue(dynamic()) - because - 'not_a_queue' is not compatible with {[dynamic()], [dynamic()]} - error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) ┌─ check/src/custom.erl:846:9 │ @@ -1053,15 +820,6 @@ Because in the expression's type: Differs from the expected type: 'false' | 'true' | ['a' | 'b'] ) ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'false' | {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - because - {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] - expected union does not contain any tuple type of size 2 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:857:20 │ @@ -1078,10 +836,6 @@ Because in the expression's type: Context expects type: 'false' | 'true' | ['a' | 'b'] No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - -expected union does not contain any tuple type of size 2 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:873:9 │ @@ -1104,16 +858,6 @@ Because in the expression's type: Differs from the expected type: 'false' | 'true' | ['a' | 'b'] ) ------------------------------- Detailed message ------------------------------ - - fun(('a' | 'b') -> ['a'] | 'wrong_ret') is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:881:17 │ @@ -1130,14 +874,6 @@ Because in the expression's type: However the following candidate: 'wrong_ret' Differs from the expected type: 'false' | 'true' | ['a' | 'b'] ------------------------------- Detailed message ------------------------------ - - ['a'] | 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] - because - 'wrong_ret' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:891:9 │ @@ -1156,14 +892,6 @@ Because in the expression's type: No candidate matches in the expected union. ) ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun((dynamic()) -> boolean() | [dynamic()]) - because - atom() is not compatible with boolean() | [dynamic()] - because - atom() is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:891:9 │ @@ -1182,14 +910,6 @@ Because in the expression's type: No candidate matches in the expected union. ) ------------------------------- Detailed message ------------------------------ - - fun((string()) -> atom()) is not compatible with fun(('a' | 'b') -> boolean() | ['a' | 'b']) - because - atom() is not compatible with boolean() | ['a' | 'b'] - because - atom() is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:900:9 │ @@ -1207,12 +927,6 @@ Because in the expression's type: Context expects type: boolean() | [atom()] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:900:9 │ @@ -1230,12 +944,6 @@ Because in the expression's type: Context expects type: boolean() | [dynamic(atom())] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:909:9 │ @@ -1253,12 +961,6 @@ Because in the expression's type: Context expects type: boolean() | [atom()] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:909:9 │ @@ -1276,12 +978,6 @@ Because in the expression's type: Context expects type: boolean() | [dynamic(atom())] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:919:9 │ @@ -1299,12 +995,6 @@ Because in the expression's type: Context expects type: boolean() | [atom()] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:919:9 │ @@ -1322,12 +1012,6 @@ Because in the expression's type: Context expects type: boolean() | [dynamic(atom())] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/custom.erl:925:1 │ @@ -1356,12 +1040,6 @@ Because in the expression's type: Context expects type: boolean() | [atom()] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> boolean() | [atom()]) - because - string() is not compatible with boolean() | [atom()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:927:9 │ @@ -1379,12 +1057,6 @@ Because in the expression's type: Context expects type: boolean() | [dynamic(atom())] ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - because - string() is not compatible with boolean() | [dynamic(atom())] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:955:5 │ @@ -1434,12 +1106,6 @@ Because in the expression's type: Context expects type: tuple() ] ------------------------------- Detailed message ------------------------------ - - ['non_tuple'] is not compatible with [tuple()] - because - 'non_tuple' is not compatible with tuple() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:975:5 │ @@ -1540,14 +1206,6 @@ Because in the expression's type: Differs from the expected type: number() ] ------------------------------- Detailed message ------------------------------ - - ['a' | number()] is not compatible with [number()] - because - 'a' | number() is not compatible with number() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1042:15 │ @@ -1592,31 +1250,17 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/custom.erl:1100:5 │ 1100 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). Expression has type: term() Context expected type: pid() | 'undefined' - │ - - term() is not compatible with pid() | 'undefined' - because - term() is not compatible with pid() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1106:5 │ 1106 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). Expression has type: term() Context expected type: pid() | 'undefined' | 'v' - │ - - term() is not compatible with pid() | 'undefined' | 'v' - because - term() is not compatible with pid() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1111:5 @@ -1630,31 +1274,17 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/custom.erl:1117:5 │ 1117 │ proplists:get_value(k, L, 3). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 3). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 3). Expression has type: term() Context expected type: pid() | number() - │ - - term() is not compatible with pid() | number() - because - term() is not compatible with pid() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1123:5 │ 1123 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L, 'my_default'). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). Expression has type: term() Context expected type: 'v1' | 'v2' | 'my_default' - │ - - term() is not compatible with 'v1' | 'v2' | 'my_default' - because - term() is not compatible with 'v1' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1129:5 @@ -1722,14 +1352,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default'] - because - term() is not compatible with pid() | 'default' - because - term() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1166:5 │ @@ -1748,14 +1370,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid() | 'default' | 'v'] - because - term() is not compatible with pid() | 'default' | 'v' - because - term() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1171:5 │ @@ -1773,12 +1387,6 @@ Because in the expression's type: Context expects type: pid() ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [pid()] - because - term() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1176:5 │ @@ -1846,12 +1454,6 @@ Because in the expression's type: Context expects type: 'c' ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['c'] - because - term() is not compatible with 'c' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1197:5 │ @@ -1870,14 +1472,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a' | 'b' | 'c'] - because - term() is not compatible with 'a' | 'b' | 'c' - because - term() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1202:24 │ @@ -1903,41 +1497,21 @@ Because in the expression's type: Context expects type: 'a' ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with ['a'] - because - term() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1215:5 │ 1215 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', L). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). Expression has type: term() Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1220:5 │ 1220 │ proplists:get_value(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('k', 'b'). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', 'b'). Expression has type: term() Context expected type: 'a' | pid() - │ - - term() is not compatible with 'a' | pid() - because - term() is not compatible with 'a' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1220:28 @@ -1987,12 +1561,6 @@ Because in the expression's type: However the following candidate: tuple() Differs from the expected type: 'none' ------------------------------- Detailed message ------------------------------ - - 'none' | tuple() is not compatible with 'none' - because - tuple() is not compatible with 'none' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1252:29 │ @@ -2065,17 +1633,6 @@ Because in the expression's type: ] , [term()]} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[[term()]], [term()]} is not compatible with {[plist('a', 'b')], plist('a', 'b')} - because - [[term()]] is not compatible with [plist('a', 'b')] - because - [term()] is not compatible with plist('a', 'b') - because - [term()] is not compatible with ['a' | {'a', 'b'}] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1315:21 │ @@ -2120,46 +1677,25 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/custom.erl:1363:5 │ 1363 │ proplists:get_value(a, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a']). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a']). Expression has type: term() Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1368:5 │ 1368 │ proplists:get_value(X, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value(X, ['a']). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value(X, ['a']). Expression has type: term() Context expected type: 'true' | 'undefined' - │ - - term() is not compatible with 'true' | 'undefined' - because - term() is not compatible with 'true' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1373:5 │ 1373 │ proplists:get_value(a, [a], b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_value('a', ['a'], 'b'). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a'], 'b'). Expression has type: term() Context expected type: 'true' | 'b' - │ - - term() is not compatible with 'true' | 'b' - because - term() is not compatible with 'true' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1416:5 @@ -2194,12 +1730,6 @@ Because in the expression's type: Context expects type: tuple() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [tuple()] - because - number() is not compatible with tuple() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1448:5 │ @@ -2222,14 +1752,6 @@ Because in the expression's type: Differs from the expected type: number() ] ------------------------------- Detailed message ------------------------------ - - [string() | number()] is not compatible with [number()] - because - string() | number() is not compatible with number() - because - string() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1482:5 │ @@ -2373,17 +1895,6 @@ Because in the expression's type: , A} ] ------------------------------- Detailed message ------------------------------ - - [{A, B} | {B, A}] is not compatible with [{A, B}] - because - {A, B} | {B, A} is not compatible with {A, B} - because - at tuple index 1: - {B, A} is not compatible with {A, B} - because - B is not compatible with A - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1757:5 │ @@ -2404,10 +1915,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1769:18 │ @@ -2451,10 +1958,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1826:5 │ @@ -2472,10 +1975,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1827:12 │ @@ -2501,10 +2000,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1854:5 │ @@ -2525,16 +2020,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/custom.erl:1916:23 │ 1916 │ custom_overloaded(X). - │ ^ - │ │ - │ X. + │ ^ X. Expression has type: term() Context expected type: atom() | binary() - │ - - term() is not compatible with atom() | binary() - because - term() is not compatible with atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:1939:5 @@ -2554,15 +2042,6 @@ Because in the expression's type: Differs from the expected type: number() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {atom(), number() | pid()} is not compatible with {atom(), number()} - because - number() | pid() is not compatible with number() - because - pid() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2041:5 │ @@ -2578,14 +2057,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2046:5 │ @@ -2601,14 +2072,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2051:5 │ @@ -2624,14 +2087,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2066:5 │ @@ -2648,14 +2103,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2081:19 │ @@ -2674,16 +2121,6 @@ Because in the expression's type: Differs from the expected type: string() | atom() | file:deep_list() | binary() ] ------------------------------- Detailed message ------------------------------ - - [binary() | pid()] is not compatible with [file:name_all()] - because - binary() | pid() is not compatible with file:name_all() - because - binary() | pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2086:5 │ @@ -2699,14 +2136,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2091:5 │ @@ -2722,14 +2151,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2096:5 │ @@ -2745,14 +2166,6 @@ Because in the expression's type: Here the type is: string() | binary() Context expects type: string() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with file:filename() - because - string() | binary() is not compatible with file:filename() - because - string() | binary() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2111:5 │ @@ -2769,14 +2182,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with binary() - because - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2121:25 │ @@ -2793,14 +2198,6 @@ Because in the expression's type: Context expects type: string() | atom() | file:deep_list() | binary() No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - pid() is not compatible with file:name_all() - because - pid() is not compatible with string() | atom() | file:deep_list() | binary() - because - pid() is not compatible with string() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2150:5 │ @@ -2822,17 +2219,6 @@ Because in the expression's type: ] , [atom() | number()]} ------------------------------- Detailed message ------------------------------ - - queue:queue(atom() | number()) is not compatible with queue:queue(number()) - because - {[atom() | number()], [atom() | number()]} is not compatible with queue:queue(number()) - because - at tuple index 1: - {[atom() | number()], [atom() | number()]} is not compatible with {[number()], [number()]} - because - [atom() | number()] is not compatible with [number()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2191:5 │ @@ -2851,15 +2237,6 @@ Because in the expression's type: Context expects type: #{count := ..., ...} The type of the expression is missing the following required keys: count. ------------------------------- Detailed message ------------------------------ - - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with state1() - because - #{count := number(), module := 'foo'} | #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - because - #{module := 'foo'} is not compatible with #{count := number(), module := atom()} - key `count` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2223:13 │ @@ -2884,10 +2261,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2320:5 │ @@ -2908,10 +2281,6 @@ Because in the expression's type: Context expects type: #{a := ..., b := ..., ...} The type of the expression is missing the following required keys: a, b. ------------------------------- Detailed message ------------------------------ - -keys `a`, `b` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2342:5 │ @@ -2932,13 +2301,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2354:23 │ @@ -2955,12 +2317,6 @@ Because in the expression's type: Context expects type: 'false' | 'true' | {'true', term()} No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'err' is not compatible with boolean() | {'true', term()} - because - 'err' is not compatible with 'false' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2362:5 │ @@ -2979,13 +2335,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2363:45 │ @@ -3026,12 +2375,6 @@ Because in the expression's type: Context expects type: iolist() | binary() No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - atom() is not compatible with iodata() | unicode:charlist() - because - atom() is not compatible with iolist() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2386:5 │ @@ -3060,17 +2403,6 @@ Because in the expression's type: } ] ------------------------------- Detailed message ------------------------------ - - [{'return', 'something'}] is not compatible with [{'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - because - {'return', 'something'} is not compatible with {'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored' - because - at tuple index 2: - {'return', 'something'} is not compatible with {'return', 'iodata' | 'list' | 'binary'} - because - 'something' is not compatible with 'iodata' | 'list' | 'binary' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2506:5 │ @@ -3090,15 +2422,6 @@ Because in the expression's type: ] , [atom()]} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[number()], [atom()]} is not compatible with {[atom()], [number()]} - because - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2518:5 │ @@ -3120,18 +2443,6 @@ Because in the expression's type: ] , [{term(), atom()}]} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[{term(), number()}], [{term(), atom()}]} is not compatible with {[{term(), atom()}], [{term(), number()}]} - because - [{term(), number()}] is not compatible with [{term(), atom()}] - because - at tuple index 2: - {term(), number()} is not compatible with {term(), atom()} - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2536:5 │ @@ -3153,18 +2464,6 @@ Because in the expression's type: ] } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} is not compatible with {[{'ok', atom()}], [{'error', term()}]} - because - [{'ok', atom()} | {'error', term()}] is not compatible with [{'error', term()}] - because - {'ok', atom()} | {'error', term()} is not compatible with {'error', term()} - because - at tuple index 1: - {'ok', atom()} is not compatible with {'error', term()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2576:33 │ @@ -3181,10 +2480,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2586:33 │ @@ -3202,15 +2497,6 @@ Because in the expression's type: Context expects type: 'true' , ... } ------------------------------- Detailed message ------------------------------ - - #{a => number()} is not compatible with #{a => 'true'} - because - at key `a`: - #{a => number()} is not compatible with #{a => 'true'} - because - number() is not compatible with 'true' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2596:33 │ @@ -3227,10 +2513,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2709:40 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty index a8dc692a9b..bc6265b8c1 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty @@ -66,61 +66,33 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/dynamic_calls.erl:78:14 │ 78 │ (FUnion)(false). - │ ^^^^^ - │ │ - │ 'false'. + │ ^^^^^ 'false'. Expression has type: 'false' Context expected type: 'a1' | 'a2' - │ - - 'false' is not compatible with 'a1' | 'a2' - because - 'false' is not compatible with 'a1' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_calls.erl:78:14 │ 78 │ (FUnion)(false). - │ ^^^^^ - │ │ - │ 'false'. + │ ^^^^^ 'false'. Expression has type: 'false' Context expected type: 'a2' | 'a3' - │ - - 'false' is not compatible with 'a2' | 'a3' - because - 'false' is not compatible with 'a2' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_calls.erl:86:20 │ 86 │ Res = (FUnion)(false), - │ ^^^^^ - │ │ - │ 'false'. + │ ^^^^^ 'false'. Expression has type: 'false' Context expected type: 'a1' | 'a2' - │ - - 'false' is not compatible with 'a1' | 'a2' - because - 'false' is not compatible with 'a1' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_calls.erl:86:20 │ 86 │ Res = (FUnion)(false), - │ ^^^^^ - │ │ - │ 'false'. + │ ^^^^^ 'false'. Expression has type: 'false' Context expected type: 'a2' | 'a3' - │ - - 'false' is not compatible with 'a2' | 'a3' - because - 'false' is not compatible with 'a2' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_calls.erl:87:5 @@ -146,12 +118,6 @@ Because in the expression's type: However the following candidate: 'r2' Differs from the expected type: 'r1' ------------------------------- Detailed message ------------------------------ - - 'r1' | 'r2' is not compatible with 'r1' - because - 'r2' is not compatible with 'r1' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_calls.erl:94:5 │ @@ -176,12 +142,6 @@ Because in the expression's type: However the following candidate: 'r2' Differs from the expected type: 'r1' ------------------------------- Detailed message ------------------------------ - - 'r1' | 'r2' | 'r3' is not compatible with 'r1' - because - 'r2' is not compatible with 'r1' - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ check/src/dynamic_calls.erl:108:5 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty index a8135429d1..78fd9f16f2 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty @@ -14,10 +14,4 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - atom() | dynamic() is not compatible with binary() - because - atom() is not compatible with binary() - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty index 1f55b0f88a..7f7add29a8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty @@ -72,16 +72,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) ------------------------------- Detailed message ------------------------------ - - f5('a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with fun((...) -> 'a') - because - 'a' | 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_fun.erl:155:20 │ @@ -108,16 +98,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) ------------------------------- Detailed message ------------------------------ - - fun((term()) -> 'a' | 'b') is not compatible with f4('a') - because - fun((term()) -> 'a' | 'b') is not compatible with fun((...) -> 'a') - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_fun.erl:176:21 │ @@ -136,16 +116,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) ------------------------------- Detailed message ------------------------------ - - f4('a' | 'b') is not compatible with fun((term()) -> 'a') - because - fun((...) -> 'a' | 'b') is not compatible with fun((term()) -> 'a') - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_fun.erl:208:34 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty index cd54f2d176..78c9a41f3f 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty @@ -38,12 +38,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: none() ------------------------------- Detailed message ------------------------------ - - number() | dynamic() is not compatible with none() - because - number() is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_generics.erl:72:3 │ @@ -68,12 +62,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: 'ok' ------------------------------- Detailed message ------------------------------ - - dynamic() | string() is not compatible with 'ok' - because - string() is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_generics.erl:87:3 │ @@ -112,14 +100,6 @@ Because in the expression's type: Differs from the expected type: number() ] ------------------------------- Detailed message ------------------------------ - - [number() | 'three'] is not compatible with [number()] - because - number() | 'three' is not compatible with number() - because - 'three' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_generics.erl:115:9 │ @@ -136,12 +116,6 @@ Because in the expression's type: However the following candidate: 'three' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | 'three' is not compatible with number() - because - 'three' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_generics.erl:115:22 │ @@ -158,12 +132,6 @@ Because in the expression's type: However the following candidate: 'three' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | 'three' is not compatible with number() - because - 'three' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_generics.erl:127:13 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs.pretty index 3ca5a136e6..6e82aa3702 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs.pretty @@ -14,12 +14,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_local_funs.erl:36:15 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty index 9d7187f610..ce03971816 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty @@ -100,12 +100,6 @@ Because in the expression's type: However the following candidate: 'error' Differs from the expected type: 'ok' ------------------------------- Detailed message ------------------------------ - - dynamic() | 'error' is not compatible with 'ok' - because - 'error' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_refine.erl:236:3 │ @@ -138,12 +132,6 @@ Because in the expression's type: However the following candidate: 'err' Differs from the expected type: 'ok' ------------------------------- Detailed message ------------------------------ - - dyn_alias() | 'err' is not compatible with 'ok' - because - 'err' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/dynamic_refine.erl:260:27 │ @@ -162,15 +150,4 @@ Because in the expression's type: Differs from the expected type: 'ok' } ------------------------------- Detailed message ------------------------------ - - union() is not compatible with {'ok'} - because - at tuple index 1: - {dyn_alias() | 'err'} is not compatible with {'ok'} - because - dyn_alias() | 'err' is not compatible with 'ok' - because - 'err' is not compatible with 'ok' - 16 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty index b9fdc2fbce..e02928cc14 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty @@ -38,12 +38,6 @@ Because in the expression's type: However the following candidate: 'error' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - 'error' | 'exit' | 'throw' | number() is not compatible with number() - because - 'error' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/elab_clause.erl:86:5 │ @@ -60,12 +54,6 @@ Because in the expression's type: However the following candidate: [dynamic()] Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | [dynamic()] is not compatible with number() - because - [dynamic()] is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/elab_clause.erl:93:17 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty index ab48c38337..fab550d5e6 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty @@ -15,17 +15,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{bar := atom(), baz := atom()} is not compatible with foo_map() | #{foo => atom()} - because - #{bar := atom(), baz := atom()} is not compatible with foo_map() - because - #{bar := atom(), baz := atom()} is not compatible with #{bar => atom(), baz => number(), other => atom(), other2 => atom(), other3 => atom(), other4 => atom(), other5 => atom()} - because - at key `baz`: - #{bar := atom(), baz := atom()} is not compatible with #{bar => atom(), baz => number(), other => atom(), other2 => atom(), other3 => atom(), other4 => atom(), other5 => atom()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:19:24 │ @@ -44,12 +33,6 @@ Because in the expression's type: The expected map has no corresponding key for: baz. ] ------------------------------- Detailed message ------------------------------ - - [#{bar => 'a' | 'b'} | #{baz => 'a' | 'b'}] is not compatible with [#{bar => 'a'}] - because - #{bar => 'a' | 'b'} | #{baz => 'a' | 'b'} is not compatible with #{bar => 'a'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:22:22 │ @@ -68,15 +51,6 @@ Because in the expression's type: Differs from the expected type: binary() , ... } ------------------------------- Detailed message ------------------------------ - - #{'undefined' | binary() => atom()} is not compatible with #{binary() => atom()} - the default associations are not compatible - because - 'undefined' | binary() is not compatible with binary() - because - 'undefined' is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:27:32 │ @@ -89,17 +63,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/error_messages.erl:30:32 │ 30 │ no_record_conversion_2(Foo) -> Foo. - │ ^^^ - │ │ - │ Foo. + │ ^^^ Foo. Expression has type: #foo{} Context expected type: {binary(), atom(), atom()} - │ - - at tuple index 1: - {'foo', atom(), atom()} is not compatible with {binary(), atom(), atom()} - because - 'foo' is not compatible with binary() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:33:27 @@ -118,13 +84,6 @@ Because in the expression's type: Context expects type: binary() , atom()} ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'foo', atom(), atom()} is not compatible with {'foo', binary(), atom()} - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:37:31 │ @@ -143,18 +102,6 @@ Because in the expression's type: The expected map has no corresponding key for: large_map_key_a. , ... } ------------------------------- Detailed message ------------------------------ - - #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}} - because - at key `foo`: - #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}} - because - #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'} - because - #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c'} - key `large_map_key_a` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/error_messages.erl:41:31 │ @@ -173,16 +120,4 @@ Because in the expression's type: The expected map has no corresponding key for: large_map_key_a. , ... } ------------------------------- Detailed message ------------------------------ - - #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'} - because - at key `foo`: - #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'} - because - #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any' - because - #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} - key `large_map_key_a` is declared in the former but not in the latter and the latter map has no default association - 8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty index f6d7701c2e..1a6ae465e8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty @@ -13,10 +13,4 @@ Because in the expression's type: Here the type is: [number() | io_lib:chars()] Context expects type: string() ------------------------------- Detailed message ------------------------------ - - io_lib:chars() is not compatible with string() - because - [number() | io_lib:chars()] is not compatible with string() - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty index 589448836f..1e2c339ecc 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty @@ -29,12 +29,6 @@ Because in the expression's type: Here the type is: number() Context expects type: [term()] ------------------------------- Detailed message ------------------------------ - - n() is not compatible with [term()] - because - number() is not compatible with [term()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:63:9 │ @@ -50,12 +44,6 @@ Because in the expression's type: Here the type is: [dynamic()] Context expects type: number() ------------------------------- Detailed message ------------------------------ - - [dynamic()] is not compatible with n() - because - [dynamic()] is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:70:13 │ @@ -71,12 +59,6 @@ Because in the expression's type: Here the type is: binary() Context expects type: number() ------------------------------- Detailed message ------------------------------ - - binary() is not compatible with n() - because - binary() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:70:35 │ @@ -92,12 +74,6 @@ Because in the expression's type: Here the type is: number() Context expects type: atom() ------------------------------- Detailed message ------------------------------ - - n() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:77:5 │ @@ -116,14 +92,6 @@ Because in the expression's type: Context expects type: 'a' ] ------------------------------- Detailed message ------------------------------ - - [n()] is not compatible with ['a'] - because - n() is not compatible with 'a' - because - number() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:90:18 │ @@ -139,12 +107,6 @@ Because in the expression's type: Here the type is: number() Context expects type: [term()] ------------------------------- Detailed message ------------------------------ - - n() is not compatible with [term()] - because - number() is not compatible with [term()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:90:18 │ @@ -160,12 +122,6 @@ Because in the expression's type: Here the type is: [dynamic()] Context expects type: number() ------------------------------- Detailed message ------------------------------ - - [dynamic()] is not compatible with n() - because - [dynamic()] is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:90:23 │ @@ -181,12 +137,6 @@ Because in the expression's type: Here the type is: number() Context expects type: [term()] ------------------------------- Detailed message ------------------------------ - - n() is not compatible with [term()] - because - number() is not compatible with [term()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:96:31 │ @@ -202,12 +152,6 @@ Because in the expression's type: Here the type is: number() Context expects type: atom() ------------------------------- Detailed message ------------------------------ - - n() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:97:9 │ @@ -223,12 +167,6 @@ Because in the expression's type: Here the type is: number() Context expects type: [term()] ------------------------------- Detailed message ------------------------------ - - n() is not compatible with [term()] - because - number() is not compatible with [term()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:97:9 │ @@ -244,12 +182,6 @@ Because in the expression's type: Here the type is: [dynamic()] Context expects type: number() ------------------------------- Detailed message ------------------------------ - - [dynamic()] is not compatible with n() - because - [dynamic()] is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:97:14 │ @@ -265,12 +197,6 @@ Because in the expression's type: Here the type is: number() Context expects type: [term()] ------------------------------- Detailed message ------------------------------ - - n() is not compatible with [term()] - because - number() is not compatible with [term()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs.erl:104:7 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty index 7b20cba52c..f3a82abfd6 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty @@ -19,14 +19,6 @@ Because in the expression's type: ] ] ------------------------------- Detailed message ------------------------------ - - [[[[]]]] is not compatible with [[[]]] - because - [[[]]] is not compatible with [[]] - because - [[]] is not compatible with [] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs2.erl:54:5 │ @@ -66,12 +58,6 @@ Because in the expression's type: Context expects type: atom() ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((atom()) -> atom()) - because - string() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs2.erl:101:9 │ @@ -90,14 +76,6 @@ Because in the expression's type: Differs from the expected type: atom() ) -> string()) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic() | fun((string()) -> atom())) -> dynamic() | fun((string()) -> atom())) - because - dynamic() | fun((string()) -> atom()) is not compatible with atom() - because - fun((string()) -> atom()) is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs2.erl:101:9 │ @@ -116,14 +94,6 @@ Because in the expression's type: Differs from the expected type: atom() ) -> string()) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> string()) is not compatible with fun((dynamic() | string() | fun((string()) -> atom())) -> dynamic() | string() | fun((string()) -> atom())) - because - dynamic() | string() | fun((string()) -> atom()) is not compatible with atom() - because - string() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/funs2.erl:152:27 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application.pretty index c10af08c9d..ae03bc49c8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application.pretty @@ -31,12 +31,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:38:29 │ @@ -54,14 +48,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [dynamic(atom())] - because - number() is not compatible with dynamic(atom()) - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:43:5 │ @@ -87,12 +73,6 @@ Because in the expression's type: Context expects type: B ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> pid()) is not compatible with fun((number()) -> B) - because - pid() is not compatible with B - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:43:18 │ @@ -111,14 +91,6 @@ Because in the expression's type: Differs from the expected type: atom() ) -> pid()) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> pid()) is not compatible with fun((dynamic() | number()) -> pid()) - because - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:47:33 │ @@ -136,12 +108,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:47:33 │ @@ -159,14 +125,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [dynamic(atom())] - because - number() is not compatible with dynamic(atom()) - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:48:5 │ @@ -192,12 +150,6 @@ Because in the expression's type: Context expects type: B ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> pid()) is not compatible with fun((number()) -> B) - because - pid() is not compatible with B - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:52:22 │ @@ -216,14 +168,6 @@ Because in the expression's type: Differs from the expected type: atom() ) -> pid()) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> pid()) is not compatible with fun((dynamic() | number()) -> pid()) - because - dynamic() | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:53:5 │ @@ -272,10 +216,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: true. ------------------------------- Detailed message ------------------------------ - -key `true` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:99:13 │ @@ -365,15 +305,6 @@ Because in the expression's type: Context expects type: T , ... } ------------------------------- Detailed message ------------------------------ - - #{a => number()} is not compatible with #{atom() => T} - because - #{a => number()} is not compatible with #{atom() => T} - key `a` is declared in the former but not in the latter and key `a` isn't compatible with the default association of the latter map - because - number() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:126:5 │ @@ -414,12 +345,6 @@ Because in the expression's type: However the following candidate: 'b' Differs from the expected type: 'a' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:157:5 │ @@ -444,12 +369,6 @@ Because in the expression's type: However the following candidate: 'b' Differs from the expected type: 'a' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:172:5 │ @@ -466,12 +385,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: 'b' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' is not compatible with 'b' - because - 'a' is not compatible with 'b' - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/generic_fun_application.erl:226:1 │ @@ -521,12 +434,6 @@ Because in the expression's type: Context expects type: X ) ------------------------------- Detailed message ------------------------------ - - fun(() -> A) is not compatible with fun(() -> X) - because - A is not compatible with X - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:276:5 │ @@ -567,12 +474,6 @@ Because in the expression's type: However the following candidate: Last Differs from the expected type: 'first' ------------------------------- Detailed message ------------------------------ - - 'first' | Last is not compatible with 'first' - because - Last is not compatible with 'first' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:296:5 │ @@ -589,12 +490,6 @@ Because in the expression's type: However the following candidate: T Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - atom() | T is not compatible with atom() - because - T is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:322:5 │ @@ -612,13 +507,6 @@ Because in the expression's type: Context expects type: pid() , pid()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {number(), pid()} is not compatible with {pid(), number()} - because - number() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:330:5 │ @@ -677,12 +565,6 @@ Because in the expression's type: Context expects type: fun((A) -> A) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((A) -> A) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((A) -> A) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:346:9 │ @@ -701,12 +583,6 @@ Because in the expression's type: Context expects type: fun((dynamic()) -> dynamic()) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((dynamic()) -> dynamic()) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((dynamic()) -> dynamic()) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:346:9 │ @@ -725,12 +601,6 @@ Because in the expression's type: Context expects type: fun((A) -> A) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((A) -> A) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((A) -> A) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:358:9 │ @@ -781,12 +651,6 @@ Because in the expression's type: Context expects type: fun((Z) -> Z) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((Z) -> Z) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((Z) -> Z) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:358:9 │ @@ -805,12 +669,6 @@ Because in the expression's type: Context expects type: fun((dynamic()) -> dynamic()) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((dynamic()) -> dynamic()) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((dynamic()) -> dynamic()) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:358:9 │ @@ -829,12 +687,6 @@ Because in the expression's type: Context expects type: fun((Z) -> Z) with 0 type parameters The number of type parameters doesn't match. ------------------------------- Detailed message ------------------------------ - - fun((Z) -> Z) | fun((dynamic()) -> dynamic()) is not compatible with fun((Z) -> Z) - because - fun((Z) -> Z) with 1 type parameter is not compatible with fun((Z) -> Z) with 0 type parameters - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:432:18 │ @@ -875,10 +727,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:441:16 │ @@ -895,10 +743,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:446:15 │ @@ -916,12 +760,6 @@ Because in the expression's type: Context expects type: T ) ------------------------------- Detailed message ------------------------------ - - fun((#{a => number()}) -> number()) is not compatible with fun((#{{T} => T}) -> T) - because - number() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:446:15 │ @@ -940,15 +778,6 @@ Because in the expression's type: The expected map has no default association while the type of the expression has one. ) -> number()) ------------------------------- Detailed message ------------------------------ - - fun((#{a => number()}) -> number()) is not compatible with fun((#{{number()} => number()}) -> number()) - because - #{{number()} => number()} is not compatible with #{a => number()} - because - #{{number()} => number()} is not compatible with #{a => number()} - the latter map has no default association while the first map has one - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:471:24 │ @@ -966,12 +795,6 @@ Because in the expression's type: Context expects type: 'c' ) ------------------------------- Detailed message ------------------------------ - - fun(('a') -> 'b') is not compatible with fun(('a') -> 'c') - because - 'b' is not compatible with 'c' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:478:17 │ @@ -988,10 +811,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:478:17 │ @@ -1008,10 +827,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:500:36 │ @@ -1048,16 +863,6 @@ Because in the expression's type: ) -> 'ok') ) -> 'ok') ------------------------------- Detailed message ------------------------------ - - contravar(contravar('a' | 'b')) is not compatible with contravar(contravar('a')) - because - fun((contravar('a' | 'b')) -> 'ok') is not compatible with contravar(contravar('a')) - because - fun((contravar('a' | 'b')) -> 'ok') is not compatible with fun((contravar('a')) -> 'ok') - because - contravar('a') is not compatible with contravar('a' | 'b') - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:563:5 │ @@ -1075,14 +880,6 @@ Because in the expression's type: Context expects type: 'a' ) -> 'a') ------------------------------- Detailed message ------------------------------ - - invar('a') is not compatible with fun((atom()) -> atom()) - because - fun(('a') -> 'a') is not compatible with fun((atom()) -> atom()) - because - atom() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:569:5 │ @@ -1100,16 +897,6 @@ Because in the expression's type: Context expects type: 'a' ) -> 'a') ------------------------------- Detailed message ------------------------------ - - invar('a') is not compatible with invar(atom()) - because - fun(('a') -> 'a') is not compatible with invar(atom()) - because - fun(('a') -> 'a') is not compatible with fun((atom()) -> atom()) - because - atom() is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:585:5 │ @@ -1125,12 +912,6 @@ Because in the expression's type: Here the type is: 'a' Context expects type: fun(('a') -> 'a') ------------------------------- Detailed message ------------------------------ - - 'a' is not compatible with invar('a') - because - 'a' is not compatible with fun(('a') -> 'a') - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/generic_fun_application.erl:625:1 │ @@ -1175,10 +956,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: extra. ------------------------------- Detailed message ------------------------------ - -key `extra` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:721:25 │ @@ -1205,14 +982,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) -> 'a') ------------------------------- Detailed message ------------------------------ - - fun(('a') -> 'a') is not compatible with fun(('a' | 'b') -> 'a' | 'b') - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generic_fun_application.erl:752:32 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions.pretty index 53cd1350ec..9c03aa9a60 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions.pretty @@ -16,15 +16,6 @@ Because in the expression's type: Differs from the expected type: T , T | U} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {T | U, T | U} is not compatible with {T, U} - because - T | U is not compatible with T - because - U is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generics_with_unions.erl:70:19 │ @@ -62,12 +53,6 @@ Because in the expression's type: Here the type is: [prop('a', number())] Context expects type: 'wrong_ret' ------------------------------- Detailed message ------------------------------ - - props('a', number()) is not compatible with 'wrong_ret' - because - [prop('a', number())] is not compatible with 'wrong_ret' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generics_with_unions.erl:217:19 │ @@ -92,12 +77,6 @@ Because in the expression's type: However the following candidate: [T] Differs from the expected type: T ------------------------------- Detailed message ------------------------------ - - [T] | T is not compatible with T - because - [T] is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generics_with_unions.erl:221:17 │ @@ -114,12 +93,6 @@ Because in the expression's type: However the following candidate: [T] Differs from the expected type: T ------------------------------- Detailed message ------------------------------ - - T | [T] is not compatible with T - because - [T] is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generics_with_unions.erl:222:19 │ @@ -171,14 +144,6 @@ Because in the expression's type: Context expects type: K ] ------------------------------- Detailed message ------------------------------ - - [K] | [[K]] is not compatible with [K] - because - [[K]] is not compatible with [K] - because - [K] is not compatible with K - error: ambiguous_union (See https://fb.me/eqwalizer_errors#ambiguous_union) ┌─ check/src/generics_with_unions.erl:332:31 │ @@ -205,17 +170,6 @@ Because in the expression's type: } ) -> 'ok') ------------------------------- Detailed message ------------------------------ - - query() is not compatible with fun(({'i', 'atom'}) -> 'ok') - because - fun(({'a', atom()} | {'b', binary()} | {'i', number()}) -> 'ok') is not compatible with fun(({'i', 'atom'}) -> 'ok') - because - {'i', 'atom'} is not compatible with {'a', atom()} | {'b', binary()} | {'i', number()} - because - at tuple index 2: - {'i', 'atom'} is not compatible with {'i', number()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/generics_with_unions.erl:392:17 │ @@ -235,15 +189,4 @@ Because in the expression's type: } ) -> 'ok') ------------------------------- Detailed message ------------------------------ - - query() is not compatible with fun(({'a', number()}) -> 'ok') - because - fun(({'a', atom()} | {'b', binary()} | {'i', number()}) -> 'ok') is not compatible with fun(({'a', number()}) -> 'ok') - because - {'a', number()} is not compatible with {'a', atom()} | {'b', binary()} | {'i', number()} - because - at tuple index 2: - {'a', number()} is not compatible with {'a', atom()} - 16 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty index bbd9109d68..1987f06692 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty @@ -14,12 +14,6 @@ Because in the expression's type: However the following candidate: 'b' Differs from the expected type: 'c' ------------------------------- Detailed message ------------------------------ - - dyn('a') | 'b' is not compatible with 'c' - because - 'b' is not compatible with 'c' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_bounded.erl:25:26 │ @@ -35,16 +29,6 @@ Because in the expression's type: Here the type is: 'a' Context expects type: 'b' ------------------------------- Detailed message ------------------------------ - - 'a' is not compatible with dyn('b') - because - 'a' is not compatible with dynamic('b') - because - 'a' is not compatible with dynamic('b') - because - 'a' is not compatible with 'b' - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_bounded.erl:44:27 │ @@ -85,12 +69,6 @@ Because in the expression's type: However the following candidate: 'test' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - 'test' | number() is not compatible with number() - because - 'test' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_bounded.erl:89:5 │ @@ -107,10 +85,4 @@ Because in the expression's type: However the following candidate: 'test' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - 'test' | dyn(number()) is not compatible with number() - because - 'test' is not compatible with number() - 8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types.pretty index b7111f9239..a7a81c8fb4 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types.pretty @@ -13,12 +13,6 @@ Because in the expression's type: Here the type is: 'undefined' Context expects type: #{...} ------------------------------- Detailed message ------------------------------ - - 'undefined' is not compatible with complex_map() - because - 'undefined' is not compatible with #{id := number(), {secret, id} => number(), atom() => term()} - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_complex_types.erl:29:25 │ @@ -46,12 +40,6 @@ Because in the expression's type: Here the type is: #{...} Context expects type: [T] ------------------------------- Detailed message ------------------------------ - - complex_map() is not compatible with [T] - because - #{id := number(), {secret, id} => number(), atom() => term()} is not compatible with [T] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_complex_types.erl:48:16 │ @@ -67,12 +55,6 @@ Because in the expression's type: Here the type is: #{...} Context expects type: [dynamic()] ------------------------------- Detailed message ------------------------------ - - complex_map() is not compatible with [dynamic()] - because - #{id := number(), {secret, id} => number(), atom() => term()} is not compatible with [dynamic()] - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_complex_types.erl:55:25 │ @@ -110,11 +92,4 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {dyn_map(), 'ok'} is not compatible with {#{a => 'atom'}, number()} - because - 'ok' is not compatible with number() - 9 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty index 34e16f1479..065a377d0d 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty @@ -100,12 +100,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - dynamic() | 'a' is not compatible with number() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:120:3 │ @@ -202,12 +196,6 @@ Because in the expression's type: Context expects type: 'ok' No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with 'ok' - because - string() | binary() is not compatible with 'ok' - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_custom.erl:210:25 │ @@ -230,12 +218,6 @@ Because in the expression's type: Context expects type: 'ok' No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with 'ok' - because - string() | binary() is not compatible with 'ok' - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_custom.erl:217:25 │ @@ -258,12 +240,6 @@ Because in the expression's type: Context expects type: 'ok' No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with 'ok' - because - string() | binary() is not compatible with 'ok' - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_custom.erl:224:25 │ @@ -286,12 +262,6 @@ Because in the expression's type: Context expects type: 'ok' No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - file:filename_all() is not compatible with 'ok' - because - string() | binary() is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:245:19 │ @@ -308,12 +278,6 @@ Because in the expression's type: However the following candidate: 'undefined' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | 'undefined' is not compatible with number() - because - 'undefined' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:257:19 │ @@ -330,12 +294,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | atom() | binary() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:263:19 │ @@ -352,12 +310,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | dynamic() | number() | atom() | dynamic() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:269:19 │ @@ -374,12 +326,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | dynamic() | atom() | dynamic() is not compatible with number() - because - atom() is not compatible with number() - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/gradual_custom.erl:277:25 │ @@ -402,12 +348,6 @@ Because in the expression's type: However the following candidate: {none()} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - dynamic() | {none()} is not compatible with number() - because - {none()} is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:325:13 │ @@ -424,14 +364,6 @@ Because in the expression's type: However the following candidate: 'undefined' Differs from the expected type: 'foo' | 'bar' | binary() ------------------------------- Detailed message ------------------------------ - - 'bar' | 'undefined' | 'foo' is not compatible with 'foo' | 'bar' | binary() - because - 'undefined' is not compatible with 'foo' | 'bar' | binary() - because - 'undefined' is not compatible with 'foo' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_custom.erl:343:3 │ @@ -448,10 +380,4 @@ Because in the expression's type: However the following candidate: 'bar' Differs from the expected type: 'foo' ------------------------------- Detailed message ------------------------------ - - 'foo' | 'bar' is not compatible with 'foo' - because - 'bar' is not compatible with 'foo' - 37 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty index 5587da0d7e..35a509f2f6 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty @@ -62,14 +62,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: 'b' | 'c' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' | 'c' is not compatible with 'b' | 'c' - because - 'a' is not compatible with 'b' | 'c' - because - 'a' is not compatible with 'b' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_maybe.erl:147:14 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty index 107e1b6e30..28618b5f77 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty @@ -16,16 +16,6 @@ Because in the expression's type: Differs from the expected type: 'a' ) -> 'ok') ------------------------------- Detailed message ------------------------------ - - opaque:contravariant('a') is not compatible with opaque:contravariant('a' | 'b') - because - fun(('a') -> 'ok') is not compatible with opaque:contravariant('a' | 'b') - because - fun(('a') -> 'ok') is not compatible with fun(('a' | 'b') -> 'ok') - because - 'a' | 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/gradual_misc.erl:38:5 │ @@ -45,13 +35,8 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/gradual_misc.erl:46:41 │ 46 │ refine_tuple_neg(T) when is_tuple(T) -> T; - │ ^ - │ │ - │ T. + │ ^ T. Expression has type: {'b', 'c'} Context expected type: 'a' | {none()} - │ - -expected union does not contain any tuple type of size 2 4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections.pretty index 88bcfb1267..74cd6b4ca4 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections.pretty @@ -14,14 +14,6 @@ Because in the expression's type: However the following candidate: #r2{} Differs from the expected type: #r1{} ------------------------------- Detailed message ------------------------------ - - #r1{} | #r2{} is not compatible with r1() - because - #r1{} | #r2{} is not compatible with #r1{} - because - #r2{} is not compatible with #r1{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guard_b_connections.erl:27:3 │ @@ -38,14 +30,6 @@ Because in the expression's type: However the following candidate: #r2{} Differs from the expected type: #r1{} ------------------------------- Detailed message ------------------------------ - - #r1{} | #r2{} is not compatible with r1() - because - #r1{} | #r2{} is not compatible with #r1{} - because - #r2{} is not compatible with #r1{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guard_b_connections.erl:32:3 │ @@ -62,14 +46,6 @@ Because in the expression's type: However the following candidate: #r2{} Differs from the expected type: #r1{} ------------------------------- Detailed message ------------------------------ - - #r1{} | #r2{} is not compatible with r1() - because - #r1{} | #r2{} is not compatible with #r1{} - because - #r2{} is not compatible with #r1{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guard_b_connections.erl:38:3 │ @@ -86,14 +62,6 @@ Because in the expression's type: However the following candidate: #r2{} Differs from the expected type: #r1{} ------------------------------- Detailed message ------------------------------ - - #r1{} | #r2{} is not compatible with r1() - because - #r1{} | #r2{} is not compatible with #r1{} - because - #r2{} is not compatible with #r1{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guard_b_connections.erl:44:3 │ @@ -110,12 +78,4 @@ Because in the expression's type: However the following candidate: #r2{} Differs from the expected type: #r1{} ------------------------------- Detailed message ------------------------------ - - #r1{} | #r2{} is not compatible with r1() - because - #r1{} | #r2{} is not compatible with #r1{} - because - #r2{} is not compatible with #r1{} - 5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty index c287958c68..bc1b2fff4a 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty @@ -30,14 +30,6 @@ Because in the expression's type: However the following candidate: pid() Differs from the expected type: number() | atom() ------------------------------- Detailed message ------------------------------ - - number() | atom() | pid() is not compatible with number() | atom() - because - pid() is not compatible with number() | atom() - because - pid() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guards_logic.erl:73:5 │ @@ -55,13 +47,6 @@ Because in the expression's type: Context expects type: number() , term()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {term(), term()} is not compatible with {number(), number()} - because - term() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/guards_logic.erl:101:46 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty index 2e9f03966d..f990884695 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty @@ -38,12 +38,6 @@ Because in the expression's type: However the following candidate: term() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - term() | 'undefined' is not compatible with atom() - because - term() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/hints.erl:52:5 │ @@ -60,12 +54,6 @@ Because in the expression's type: However the following candidate: term() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - term() | 'undefined' is not compatible with atom() - because - term() is not compatible with atom() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/hints.erl:55:1 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty index bf9d6e219b..00e34f9cb8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty @@ -23,14 +23,6 @@ Because in the expression's type: Differs from the expected type: binary() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [binary()] - because - atom() | binary() is not compatible with binary() - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/iolists.erl:55:22 │ @@ -49,14 +41,6 @@ Because in the expression's type: Differs from the expected type: atom() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [atom()] - because - atom() | binary() is not compatible with atom() - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/iolists.erl:60:27 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty index d0768e5669..4b2ec521dd 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty @@ -16,16 +16,6 @@ Because in the expression's type: Differs from the expected type: atom() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [atom()] | [binary()] - because - [atom() | binary()] is not compatible with [atom()] - because - atom() | binary() is not compatible with atom() - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/lists_tests.erl:16:28 │ @@ -44,14 +34,4 @@ Because in the expression's type: Differs from the expected type: atom() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [atom()] | [binary()] - because - [atom() | binary()] is not compatible with [atom()] - because - atom() | binary() is not compatible with atom() - because - binary() is not compatible with atom() - 2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty index 45a1d69e57..ec963a6249 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty @@ -127,12 +127,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:116:21 │ @@ -150,12 +144,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:120:18 │ @@ -207,12 +195,6 @@ Because in the expression's type: Context expects type: number() ] ------------------------------- Detailed message ------------------------------ - - [atom()] is not compatible with [number()] - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:143:29 │ @@ -323,31 +305,17 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/misc.erl:352:22 │ 352 │ catch test69_pos(atom). - │ ^^^^ - │ │ - │ 'atom'. + │ ^^^^ 'atom'. Expression has type: 'atom' Context expected type: [atom()] | [number()] - │ - - 'atom' is not compatible with [atom()] | [number()] - because - 'atom' is not compatible with [atom()] error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:357:22 │ 357 │ catch test69_pos(atom). - │ ^^^^ - │ │ - │ 'atom'. + │ ^^^^ 'atom'. Expression has type: 'atom' Context expected type: [atom()] | [number()] - │ - - 'atom' is not compatible with [atom()] | [number()] - because - 'atom' is not compatible with [atom()] error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:404:11 @@ -393,17 +361,6 @@ Because in the expression's type: ] , [atom() | number()]} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[number() | atom()], [atom() | number()]} is not compatible with {[atom()], [number()]} - because - [number() | atom()] is not compatible with [atom()] - because - number() | atom() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:461:5 │ @@ -424,17 +381,6 @@ Because in the expression's type: ] , [atom() | number()]} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {[atom() | number()], [atom() | number()]} is not compatible with {[atom()] | [number()], [atom()] | [number()]} - because - [atom() | number()] is not compatible with [atom()] | [number()] - because - [atom() | number()] is not compatible with [atom()] - because - atom() | number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:465:12 │ @@ -509,16 +455,6 @@ Because in the expression's type: Differs from the expected type: atom() | number() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [atom() | number()] - because - atom() | binary() is not compatible with atom() | number() - because - binary() is not compatible with atom() | number() - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:575:14 │ @@ -552,12 +488,6 @@ Because in the expression's type: Context expects type: [A] ] ------------------------------- Detailed message ------------------------------ - - [A] is not compatible with [[A]] - because - A is not compatible with [A] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:611:21 │ @@ -752,13 +682,6 @@ Because in the expression's type: Context expects type: 'ok' } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {none(), 'err'} is not compatible with {'ok', 'ok'} - because - 'err' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:741:5 │ @@ -775,16 +698,6 @@ Because in the expression's type: However the following candidate: 'v2_op' Differs from the expected type: 'stuff1' | 'v0_op2' | 'stuff2' | 'v0_op1' | 'v1_op2' | ... ------------------------------- Detailed message ------------------------------ - - v2_op() is not compatible with v1_op() - because - 'v2_op' | v1_op() is not compatible with v1_op() - because - 'v2_op' | v1_op() is not compatible with 'v1_op1' | 'v1_op2' | stuff() | v0_op() - because - 'v2_op' is not compatible with 'v1_op1' | 'v1_op2' | stuff() | v0_op() - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ check/src/misc.erl:760:1 │ @@ -834,16 +747,6 @@ Because in the expression's type: Context expects type: #set{} | #{term() => []} No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - misc:set() is not compatible with sets:set() - because - [] is not compatible with sets:set() - because - [] is not compatible with sets:set(term()) - because - [] is not compatible with #set{} | #{term() => []} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:850:5 │ @@ -861,13 +764,6 @@ Because in the expression's type: Context expects type: pid() } ------------------------------- Detailed message ------------------------------ - - at tuple index 3: - {'ok', 'lists', number()} is not compatible with {'ok', atom(), pid()} - because - number() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:860:5 │ @@ -907,12 +803,6 @@ Because in the expression's type: Here the type is: {binary()} Context expects type: [binary()] ------------------------------- Detailed message ------------------------------ - - {binary()} is not compatible with erlang:iovec() - because - {binary()} is not compatible with [binary()] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:949:15 │ @@ -939,14 +829,6 @@ Because in the expression's type: No candidate of the expression's type matches the expected type. ] ------------------------------- Detailed message ------------------------------ - - ['MM' | 'MS' | 'EE' | 'MA' | 'GE'] is not compatible with [erlang:priority_level()] - because - 'MM' | 'MS' | 'EE' | 'MA' | 'GE' is not compatible with erlang:priority_level() - because - 'MM' | 'MS' | 'EE' | 'MA' | 'GE' is not compatible with 'low' | 'normal' | 'high' | 'max' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:971:5 │ @@ -962,12 +844,6 @@ Because in the expression's type: Here the type is: {number(), number(), number()} Context expects type: atom() ------------------------------- Detailed message ------------------------------ - - erlang:timestamp() is not compatible with atom() - because - {number(), number(), number()} is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:987:12 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty index 55ce8bf6f5..585b5f8642 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty @@ -39,13 +39,6 @@ Because in the expression's type: Context expects type: atom() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'ok', term()} is not compatible with {atom(), atom()} - because - term() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/neg.erl:27:21 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty index 8760657fa4..8c78c1ffbe 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty @@ -150,12 +150,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - 'a' | number() is not compatible with number() - because - 'a' is not compatible with number() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/numbers.erl:538:1 │ @@ -300,13 +294,6 @@ Because in the expression's type: Context expects type: 'ok' } ------------------------------- Detailed message ------------------------------ - - at tuple index 5: - {number(), number(), number(), number(), 'error'} is not compatible with {number(), number(), number(), number(), 'ok'} - because - 'error' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/numbers.erl:658:3 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty index 765efea8fe..5c207ff164 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty @@ -16,16 +16,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: {ok, error}. ------------------------------- Detailed message ------------------------------ - - sets:set({'ok', 'error'}) is not compatible with sets:set({'ok', 'ok'}) - because - #set{} | #{{ok, error} => []} is not compatible with sets:set({'ok', 'ok'}) - because - #set{} | #{{ok, error} => []} is not compatible with #set{} | #{{ok, ok} => []} - because - #{{ok, error} => []} is not compatible with #set{} | #{{ok, ok} => []} - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/opaque.erl:29:1 │ @@ -47,12 +37,6 @@ Because in the expression's type: Here the type is: {'ok'} Context expects type: none() ------------------------------- Detailed message ------------------------------ - - misc:o() is not compatible with none() - because - {'ok'} is not compatible with none() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/opaque.erl:86:1 │ @@ -93,14 +77,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: {'ok'} ------------------------------- Detailed message ------------------------------ - - misc:o() | 'a' is not compatible with misc:o() - because - misc:o() | 'a' is not compatible with {'ok'} - because - 'a' is not compatible with {'ok'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/opaque.erl:135:18 │ @@ -117,14 +93,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: {'ok'} ------------------------------- Detailed message ------------------------------ - - misc:o() | 'a' is not compatible with misc:o() - because - misc:o() | 'a' is not compatible with {'ok'} - because - 'a' is not compatible with {'ok'} - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ check/src/opaque.erl:150:1 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty index b75aabcf84..da1b4e48cb 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty @@ -13,12 +13,6 @@ Because in the expression's type: Here the type is: term() Context expects type: #{...} ------------------------------- Detailed message ------------------------------ - - term() is not compatible with logger:metadata() - because - term() is not compatible with #{domain => [atom()], file => file:filename(), gl => pid(), line => number(), mfa => {atom(), atom(), number()}, pid => pid(), report_cb => logger:report_cb(), time => logger:timestamp(), atom() => term()} - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/other.erl:59:27 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty index 026d55c435..5113f80c15 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty @@ -14,14 +14,6 @@ Because in the expression's type: Context expects type: 'ok' | 'error' No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'warning' is not compatible with foo() - because - 'warning' is not compatible with 'ok' | 'error' - because - 'warning' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp28.erl:28:5 │ @@ -39,12 +31,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [number()] is not compatible with [atom()] - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp28.erl:40:5 │ @@ -62,12 +48,6 @@ Because in the expression's type: Context expects type: pid() ] ------------------------------- Detailed message ------------------------------ - - [binary()] is not compatible with [pid()] - because - binary() is not compatible with pid() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp28.erl:52:5 │ @@ -95,15 +75,6 @@ Because in the expression's type: } ] ------------------------------- Detailed message ------------------------------ - - [{atom(), binary()}] is not compatible with [{atom(), atom()}] - because - at tuple index 2: - {atom(), binary()} is not compatible with {atom(), atom()} - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp28.erl:68:5 │ @@ -121,13 +92,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{atom() => binary()} is not compatible with #{atom() => atom()} - the default associations are not compatible - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp28.erl:76:5 │ @@ -147,13 +111,4 @@ Because in the expression's type: , atom()} ] ------------------------------- Detailed message ------------------------------ - - [{atom(), binary(), binary(), atom()}] is not compatible with [{atom(), binary(), atom(), binary()}] - because - at tuple index 3: - {atom(), binary(), binary(), atom()} is not compatible with {atom(), binary(), atom(), binary()} - because - binary() is not compatible with atom() - 7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty index b41039f95b..59bd5e6099 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty @@ -17,17 +17,6 @@ Because in the expression's type: , term(), term()} } ------------------------------- Detailed message ------------------------------ - - gb_sets:set(atom()) is not compatible with gb_sets:set(number()) - because - {number(), gb_sets:gb_set_node(atom())} is not compatible with gb_sets:set(number()) - because - at tuple index 2: - {number(), gb_sets:gb_set_node(atom())} is not compatible with {number(), gb_sets:gb_set_node(number())} - because - gb_sets:gb_set_node(atom()) is not compatible with gb_sets:gb_set_node(number()) - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp_opaques.erl:42:5 │ @@ -45,12 +34,6 @@ Because in the expression's type: Context expects type: A ] ------------------------------- Detailed message ------------------------------ - - [[A]] is not compatible with [A] - because - [A] is not compatible with A - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp_opaques.erl:99:17 │ @@ -67,14 +50,6 @@ Because in the expression's type: Context expects type: #set{} | #{a => []} No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - maps:iterator('k', 'v') is not compatible with sets:set('a') - because - {'k', 'v', maps:iterator('k', 'v')} | 'none' | [number()] | [['k']] is not compatible with sets:set('a') - because - {'k', 'v', maps:iterator('k', 'v')} | 'none' | [number()] | [['k']] is not compatible with #set{} | #{a => []} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/otp_opaques.erl:102:13 │ @@ -99,14 +74,6 @@ Because in the expression's type: Context expects type: #set{} | #{a => []} No candidate of the expression's type matches the expected type. ------------------------------- Detailed message ------------------------------ - - maps:iterator('k', 'v') is not compatible with sets:set('a') - because - {'k', 'v', maps:iterator('k', 'v')} | 'none' | [number()] | [['k']] is not compatible with sets:set('a') - because - {'k', 'v', maps:iterator('k', 'v')} | 'none' | [number()] | [['k']] is not compatible with #set{} | #{a => []} - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ check/src/otp_opaques.erl:124:5 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty index 117ed8b349..aa87b295ad 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty @@ -30,26 +30,13 @@ Because in the expression's type: Context expects type: 'b' ) ------------------------------- Detailed message ------------------------------ - - fun(('a') -> 'z') is not compatible with fun(('a') -> 'b') | fun(('a') -> 'c') - because - fun(('a') -> 'z') is not compatible with fun(('a') -> 'b') - because - 'z' is not compatible with 'b' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/overloaded.erl:126:15 │ 126 │ Res = bar({fun(a) -> a end}), - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ {fun}. + │ ^^^^^^^^^^^^^^^^^ {fun}. Expression has type: {fun((dynamic()) -> dynamic())} Context expected type: fun(('a') -> 'b') | fun(('a') -> 'c') - │ - -expected union does not contain any tuple type of size 1 error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/overloaded.erl:198:37 @@ -107,12 +94,6 @@ Because in the expression's type: However the following candidate: {} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | {} is not compatible with number() - because - {} is not compatible with number() - error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) ┌─ check/src/overloaded.erl:261:1 │ @@ -130,16 +111,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/overloaded.erl:269:14 │ 269 │ _ = swap(""), - │ ^^ - │ │ - │ string_lit. + │ ^^ string_lit. Expression has type: [] Context expected type: atom() | binary() - │ - - [] is not compatible with atom() | binary() - because - [] is not compatible with atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/overloaded.erl:290:5 diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union.pretty index 3483635724..6687ec3321 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union.pretty @@ -14,12 +14,6 @@ Because in the expression's type: However the following candidate: pid() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - atom() | pid() is not compatible with atom() - because - pid() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/overloaded_specs_union.erl:19:29 │ @@ -36,12 +30,4 @@ Because in the expression's type: However the following candidate: pid() Differs from the expected type: atom() | binary() ------------------------------- Detailed message ------------------------------ - - atom() | binary() | pid() is not compatible with atom() | binary() - because - pid() is not compatible with atom() | binary() - because - pid() is not compatible with atom() - 2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty index d0d4f19c1a..171c12dc46 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty @@ -14,10 +14,4 @@ Because in the expression's type: However the following candidate: 'error' Differs from the expected type: #{term() => term()} ------------------------------- Detailed message ------------------------------ - - #{a => 'ok'} | 'error' is not compatible with #{term() => term()} - because - 'error' is not compatible with #{term() => term()} - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty index 5b6854f238..f59eda6cd2 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty @@ -18,16 +18,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/records.erl:50:21 │ 50 │ mk_rec_neg(rec2) -> #rec2{}. - │ ^^^^^^^ - │ │ - │ #rec2{...}. + │ ^^^^^^^ #rec2{...}. Expression has type: #rec2{} Context expected type: #rec1{} | #rec3{} - │ - - #rec2{} is not compatible with #rec1{} | #rec3{} - because - #rec2{} is not compatible with #rec1{} error: undefined_field (See https://fb.me/eqwalizer_errors#undefined_field) ┌─ check/src/records.erl:59:11 @@ -108,13 +101,6 @@ Because in the expression's type: Context expects type: number() , number()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {atom(), number()} is not compatible with {number(), atom()} - because - atom() is not compatible with number() - error: undefined_field (See https://fb.me/eqwalizer_errors#undefined_field) ┌─ check/src/records.erl:144:17 │ @@ -181,14 +167,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - #any_box{inner :: 'ok'} is not compatible with int_box() - because - #any_box{inner :: 'ok'} is not compatible with #any_box{inner :: number()} - because - 'ok' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:222:5 │ @@ -206,12 +184,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - #any_box{inner :: 'ok'} is not compatible with #any_box{inner :: number()} - because - 'ok' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:282:27 │ @@ -228,12 +200,6 @@ Because in the expression's type: Context expects type: number() | 'false' | 'true' No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'a' is not compatible with number() | boolean() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:287:23 │ @@ -250,12 +216,6 @@ Because in the expression's type: Context expects type: number() | 'false' | 'true' No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'a' is not compatible with number() | boolean() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:292:5 │ @@ -273,14 +233,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - #int_bool_box{inner :: 'true'} is not compatible with only_int_box() - because - #int_bool_box{inner :: 'true'} is not compatible with #int_bool_box{inner :: number()} - because - 'true' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:308:28 │ @@ -297,12 +249,6 @@ Because in the expression's type: Context expects type: number() | 'false' | 'true' No candidate matches in the expected union. ------------------------------- Detailed message ------------------------------ - - 'a' is not compatible with number() | boolean() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:314:5 │ @@ -320,14 +266,6 @@ Because in the expression's type: Context expects type: boolean() } ------------------------------- Detailed message ------------------------------ - - #int_bool_box{inner :: number()} is not compatible with only_bool_box() - because - #int_bool_box{inner :: number()} is not compatible with #int_bool_box{inner :: boolean()} - because - number() is not compatible with boolean() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:319:5 │ @@ -343,12 +281,6 @@ Because in the expression's type: Here the type is: #int_bool_box{inner :: number()} Context expects type: #any_box{} ------------------------------- Detailed message ------------------------------ - - only_int_box() is not compatible with #any_box{} - because - #int_bool_box{inner :: number()} is not compatible with #any_box{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:319:5 │ @@ -383,15 +315,6 @@ Because in the expression's type: No candidate matches in the expected union. } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'int_bool_box', 'a'} is not compatible with {'int_bool_box', number() | boolean()} - because - 'a' is not compatible with number() | boolean() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:334:5 │ @@ -409,15 +332,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - {'int_bool_box', 'a'} is not compatible with only_int_box() - because - at tuple index 2: - {'int_bool_box', 'a'} is not compatible with {'int_bool_box', number()} - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:352:5 │ @@ -434,12 +348,6 @@ Because in the expression's type: However the following candidate: 'false' Differs from the expected type: 'true' ------------------------------- Detailed message ------------------------------ - - boolean() | 'true' is not compatible with 'true' - because - 'false' is not compatible with 'true' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:355:13 │ @@ -463,12 +371,6 @@ Because in the expression's type: Here the type is: #bad_default{} Context expects type: #int_bool_box{inner :: number()} ------------------------------- Detailed message ------------------------------ - - #bad_default{} is not compatible with only_int_box() - because - #bad_default{} is not compatible with #int_bool_box{inner :: number()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:372:5 │ @@ -486,13 +388,6 @@ Because in the expression's type: Context expects type: 'bad_default' , number()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {'int_bool_box', number()} is not compatible with {'bad_default', number()} - because - 'int_bool_box' is not compatible with 'bad_default' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:388:31 │ @@ -510,12 +405,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - #refined_two_fields{} is not compatible with #refined_two_fields{inner :: number()} - because - term() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:402:5 │ @@ -594,12 +483,6 @@ Because in the expression's type: However the following candidate: 'my_record' Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - 'my_record' | binary() is not compatible with binary() - because - 'my_record' is not compatible with binary() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/records.erl:517:1 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty index 215d6a54ae..ffd83bc179 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty @@ -17,16 +17,6 @@ Because in the expression's type: Context expects type: 'b' , chainA()} ------------------------------- Detailed message ------------------------------ - - chainA() is not compatible with chainB() - because - 'nil' | {'a', chainA()} is not compatible with chainB() - because - 'nil' | {'a', chainA()} is not compatible with 'nil' | {'b', chainB()} - because - {'a', chainA()} is not compatible with 'nil' | {'b', chainB()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/recursive_aliases.erl:62:20 │ @@ -46,16 +36,6 @@ Because in the expression's type: Context expects type: 'a' , chainB()} ------------------------------- Detailed message ------------------------------ - - chainB() is not compatible with chainA() - because - 'nil' | {'b', chainB()} is not compatible with chainA() - because - 'nil' | {'b', chainB()} is not compatible with 'nil' | {'a', chainA()} - because - {'b', chainB()} is not compatible with 'nil' | {'a', chainA()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/recursive_aliases.erl:65:20 │ @@ -75,16 +55,6 @@ Because in the expression's type: Context expects type: 'a' , chainAB()} ------------------------------- Detailed message ------------------------------ - - chainAB() is not compatible with chainA() - because - 'nil' | {'a', chainAB()} | {'b', chainAB()} is not compatible with chainA() - because - 'nil' | {'a', chainAB()} | {'b', chainAB()} is not compatible with 'nil' | {'a', chainA()} - because - {'b', chainAB()} is not compatible with 'nil' | {'a', chainA()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/recursive_aliases.erl:68:20 │ @@ -104,16 +74,6 @@ Because in the expression's type: Context expects type: 'b' , chainAB()} ------------------------------- Detailed message ------------------------------ - - chainAB() is not compatible with chainB() - because - 'nil' | {'a', chainAB()} | {'b', chainAB()} is not compatible with chainB() - because - 'nil' | {'a', chainAB()} | {'b', chainAB()} is not compatible with 'nil' | {'b', chainB()} - because - {'a', chainAB()} is not compatible with 'nil' | {'b', chainB()} - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ check/src/recursive_aliases.erl:70:1 │ @@ -143,17 +103,6 @@ Because in the expression's type: However the following candidate: {'a', atom(), pchainA(atom())} Differs from the expected type: 'nil' | {'a', pchainAB(atom())} | {'b', pchainAB(atom())} ------------------------------- Detailed message ------------------------------ - - pchainA(atom()) is not compatible with pchainAB(atom()) - because - 'nil' | {'a', atom(), pchainA(atom())} is not compatible with pchainAB(atom()) - because - 'nil' | {'a', atom(), pchainA(atom())} is not compatible with 'nil' | {'a', pchainAB(atom())} | {'b', pchainAB(atom())} - because - {'a', atom(), pchainA(atom())} is not compatible with 'nil' | {'a', pchainAB(atom())} | {'b', pchainAB(atom())} - expected union does not contain any tuple type of size 3 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/recursive_aliases.erl:113:23 │ @@ -172,16 +121,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - - mChainB() is not compatible with mChainA() - because - 'nil' | #{b := mChainB()} is not compatible with mChainA() - because - 'nil' | #{b := mChainB()} is not compatible with 'nil' | #{a := mChainA()} - because - #{b := mChainB()} is not compatible with 'nil' | #{a := mChainA()} - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ check/src/recursive_aliases.erl:138:15 │ @@ -210,16 +149,6 @@ Because in the expression's type: No candidate matches in the expected union. , pchainA('a')} ------------------------------- Detailed message ------------------------------ - - pchainA('a') is not compatible with pchainA('b' | 'c') - because - 'nil' | {'a', 'a', pchainA('a')} is not compatible with pchainA('b' | 'c') - because - 'nil' | {'a', 'a', pchainA('a')} is not compatible with 'nil' | {'a', 'b' | 'c', pchainA('b' | 'c')} - because - {'a', 'a', pchainA('a')} is not compatible with 'nil' | {'a', 'b' | 'c', pchainA('b' | 'c')} - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ check/src/recursive_aliases.erl:202:1 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty index b443f787c6..711e1dc2e9 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty @@ -23,13 +23,6 @@ Because in the expression's type: Context expects type: none() , A} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {B, A} is not compatible with {none(), none()} - because - B is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:22:15 │ @@ -55,12 +48,6 @@ Because in the expression's type: Context expects type: none() ) ------------------------------- Detailed message ------------------------------ - - fun((B | A) -> A) is not compatible with fun((A | B) -> none()) - because - A is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:34:16 │ @@ -78,12 +65,6 @@ Because in the expression's type: Context expects type: none() ) ------------------------------- Detailed message ------------------------------ - - fun((atom() | A) -> A) is not compatible with fun((A | atom()) -> none()) - because - A is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:84:27 │ @@ -133,13 +114,6 @@ Because in the expression's type: Context expects type: none() , binary()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {number(), binary()} is not compatible with {none(), none()} - because - number() is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:126:32 │ @@ -157,13 +131,6 @@ Because in the expression's type: Context expects type: none() , atom()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {number(), atom()} is not compatible with {none(), none()} - because - number() is not compatible with none() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:139:28 │ @@ -190,14 +157,6 @@ Because in the expression's type: Differs from the expected type: 'a' ] ------------------------------- Detailed message ------------------------------ - - ['a' | 'b'] is not compatible with ['a'] - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:151:28 │ @@ -224,14 +183,6 @@ Because in the expression's type: Differs from the expected type: 'a' ] ------------------------------- Detailed message ------------------------------ - - ['a' | 'b'] is not compatible with ['a'] - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:166:5 │ @@ -273,13 +224,6 @@ Because in the expression's type: Context expects type: 'not_my_rec' , number(), atom()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {'my_rec', number(), atom()} is not compatible with {'not_my_rec', term(), term()} - because - 'my_rec' is not compatible with 'not_my_rec' - error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) ┌─ check/src/refine.erl:199:10 │ @@ -310,13 +254,6 @@ Because in the expression's type: Context expects type: number() , number()} ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'my_rec', atom(), number()} is not compatible with {'my_rec', number(), atom()} - because - atom() is not compatible with number() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/refine.erl:218:1 │ @@ -374,13 +311,6 @@ Because in the expression's type: Context expects type: number() , number()} ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'my_rec', atom(), number()} is not compatible with {'my_rec', number(), atom()} - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:250:26 │ @@ -398,15 +328,6 @@ Because in the expression's type: Context expects type: number() , number()} ------------------------------- Detailed message ------------------------------ - - {'my_rec', atom(), number()} is not compatible with dynamic(#my_rec{}) - because - at tuple index 2: - {'my_rec', atom(), number()} is not compatible with {'my_rec', number(), atom()} - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:262:26 │ @@ -424,13 +345,6 @@ Because in the expression's type: Context expects type: atom() , atom()} ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'my_rec', number(), atom()} is not compatible with {'my_rec', atom(), number()} - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:262:26 │ @@ -448,15 +362,6 @@ Because in the expression's type: Context expects type: atom() , atom()} ------------------------------- Detailed message ------------------------------ - - #my_rec{} is not compatible with dynamic({'my_rec', atom(), number()}) - because - at tuple index 2: - {'my_rec', number(), atom()} is not compatible with {'my_rec', atom(), number()} - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:278:21 │ @@ -474,13 +379,6 @@ Because in the expression's type: Context expects type: atom() , atom()} ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'my_rec', number(), atom()} is not compatible with {'my_rec', atom(), atom()} - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:282:16 │ @@ -497,12 +395,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - atom() | number() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:286:13 │ @@ -519,12 +411,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: {'my_rec', term(), term()} ------------------------------- Detailed message ------------------------------ - - #my_rec{} | 'a' is not compatible with {'my_rec', term(), term()} - because - 'a' is not compatible with {'my_rec', term(), term()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/refine.erl:296:5 │ @@ -541,12 +427,6 @@ Because in the expression's type: However the following candidate: 'undefined' Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - 'undefined' | binary() is not compatible with binary() - because - 'undefined' is not compatible with binary() - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/refine.erl:378:27 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types.pretty index cc9f9d0634..8943deb90a 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types.pretty @@ -13,12 +13,6 @@ Because in the expression's type: Here the type is: 'undefined' Context expects type: #{...} ------------------------------- Detailed message ------------------------------ - - 'undefined' is not compatible with complex_map() - because - 'undefined' is not compatible with #{id := number(), {secret, id} => number(), atom() => term()} - error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/strict_complex_types.erl:29:25 │ @@ -46,12 +40,6 @@ Because in the expression's type: Here the type is: #{...} Context expects type: [T] ------------------------------- Detailed message ------------------------------ - - complex_map() is not compatible with [T] - because - #{id := number(), {secret, id} => number(), atom() => term()} is not compatible with [T] - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/strict_complex_types.erl:48:16 │ @@ -67,10 +55,4 @@ Because in the expression's type: Here the type is: #{...} Context expects type: [dynamic()] ------------------------------- Detailed message ------------------------------ - - complex_map() is not compatible with [dynamic()] - because - #{id := number(), {secret, id} => number(), atom() => term()} is not compatible with [dynamic()] - 5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty index 1ee53764d2..4413a8fee8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty @@ -31,12 +31,6 @@ Because in the expression's type: Context expects type: atom() ) ------------------------------- Detailed message ------------------------------ - - fun((atom()) -> term()) is not compatible with fun((term()) -> atom()) - because - term() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:27:11 │ @@ -53,14 +47,6 @@ Because in the expression's type: However the following candidate: 'c' Differs from the expected type: 'a' | 'b' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' | 'c' is not compatible with 'a' | 'b' - because - 'c' is not compatible with 'a' | 'b' - because - 'c' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:30:11 │ @@ -87,17 +73,6 @@ Because in the expression's type: Differs from the expected type: 'a' , 'a' | 'b'} ------------------------------- Detailed message ------------------------------ - - {'a' | 'b', 'a' | 'b'} is not compatible with {'a', 'b'} | {'b', 'a'} - because - at tuple index 1: - {'a' | 'b', 'a' | 'b'} is not compatible with {'a', 'b'} - because - 'a' | 'b' is not compatible with 'a' - because - 'b' is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:38:11 │ @@ -116,17 +91,6 @@ Because in the expression's type: Differs from the expected type: 'a' , ab()} ------------------------------- Detailed message ------------------------------ - - pair_ab() is not compatible with pair_diff_elems() - because - {ab(), ab()} is not compatible with pair_diff_elems() - because - {ab(), ab()} is not compatible with {'a', 'b'} | {'b', 'a'} - because - at tuple index 1: - {ab(), ab()} is not compatible with {'a', 'b'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:41:11 │ @@ -151,10 +115,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:52:13 │ @@ -171,10 +131,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:60:13 │ @@ -191,10 +147,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:64:13 │ @@ -212,13 +164,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{term() => number()} is not compatible with #{atom() => number()} - the default associations are not compatible - because - term() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:68:13 │ @@ -236,13 +181,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{atom() => term()} is not compatible with #{atom() => number()} - the default associations are not compatible - because - term() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:72:13 │ @@ -259,13 +197,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{atom() => term()} is not compatible with #{} - because - #{atom() => term()} is not compatible with #{} - the latter map has no default association while the first map has one - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:79:15 │ @@ -291,13 +222,6 @@ Because in the expression's type: Context expects type: 'ok' } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {{}, 'error'} is not compatible with {tuple(), 'ok'} - because - 'error' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:87:5 │ @@ -315,13 +239,6 @@ Because in the expression's type: Context expects type: 'ok' } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {[], 'error'} is not compatible with {[pid()], 'ok'} - because - 'error' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:91:5 │ @@ -339,13 +256,6 @@ Because in the expression's type: Context expects type: 'ok' } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {[], 'error'} is not compatible with {iolist(), 'ok'} - because - 'error' is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/subtype_neg.erl:94:13 │ @@ -382,10 +292,4 @@ Because in the expression's type: Context expects type: none() ] ------------------------------- Detailed message ------------------------------ - - ['a'] | [none()] is not compatible with [] - because - ['a'] is not compatible with [] - 20 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty index d0e08f9ac4..5c2b07ae96 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty @@ -15,13 +15,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{number() => number()} is not compatible with #{number() => atom()} - the default associations are not compatible - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:41:5 │ @@ -40,15 +33,6 @@ Because in the expression's type: Differs from the expected type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{number() => 'zero' | number()} is not compatible with #{number() => atom()} - the default associations are not compatible - because - 'zero' | number() is not compatible with atom() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:56:5 │ @@ -66,15 +50,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{one := 'one', zero := number()} is not compatible with #{one => number(), zero := number()} - because - at key `one`: - #{one := 'one', zero := number()} is not compatible with #{one => number(), zero := number()} - because - 'one' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:80:5 │ @@ -108,15 +83,6 @@ Because in the expression's type: Context expects type: n() , ... } ------------------------------- Detailed message ------------------------------ - - #{b() => term()} is not compatible with #{n() => term()} - the default associations are not compatible - because - b() is not compatible with n() - because - boolean() is not compatible with n() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:140:26 │ @@ -134,17 +100,6 @@ Because in the expression's type: Context expects type: n() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := b()} is not compatible with #{a := n()} - because - at key `a`: - #{a := b()} is not compatible with #{a := n()} - because - b() is not compatible with n() - because - boolean() is not compatible with n() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:145:26 │ @@ -162,17 +117,6 @@ Because in the expression's type: Context expects type: n() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := b()} is not compatible with #{a => n()} - because - at key `a`: - #{a := b()} is not compatible with #{a => n()} - because - b() is not compatible with n() - because - boolean() is not compatible with n() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:155:26 │ @@ -189,10 +133,6 @@ Because in the expression's type: Context expects type: #{a := ..., ...} The type of the expression is missing the following required keys: a. ------------------------------- Detailed message ------------------------------ - -key `a` is declared as required in the latter but not in the former - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/t_maps.erl:165:1 │ @@ -216,17 +156,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := a(), n() => a()} is not compatible with #{a() => a()} - the default associations are not compatible - because - n() is not compatible with a() - because - number() is not compatible with a() - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:208:5 │ @@ -244,17 +173,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := a(), n() => a()} is not compatible with #{n() => a()} - because - #{a := a(), n() => a()} is not compatible with #{n() => a()} - key `a` is declared in the former but not in the latter and key `a` isn't compatible with the default association of the latter map - because - 'a' is not compatible with n() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:216:5 │ @@ -272,17 +190,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{a := a(), n() => a()} is not compatible with #{n() => a()} - because - #{a := a(), n() => a()} is not compatible with #{n() => a()} - key `a` is declared in the former but not in the latter and key `a` isn't compatible with the default association of the latter map - because - 'a' is not compatible with n() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:224:5 │ @@ -300,17 +207,6 @@ Because in the expression's type: Context expects type: number() , ... } ------------------------------- Detailed message ------------------------------ - - #{a => a(), n() => a()} is not compatible with #{n() => a()} - because - #{a => a(), n() => a()} is not compatible with #{n() => a()} - key `a` is declared in the former but not in the latter and key `a` isn't compatible with the default association of the latter map - because - 'a' is not compatible with n() - because - 'a' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:242:5 │ @@ -328,17 +224,6 @@ Because in the expression's type: Context expects type: F1 , ... } ------------------------------- Detailed message ------------------------------ - - #{bar := B1, foo := F1} is not compatible with foo_bar(B1, F1) - because - #{bar := B1, foo := F1} is not compatible with #{bar := F1, foo := B1} - because - at key `bar`: - #{bar := B1, foo := F1} is not compatible with #{bar := F1, foo := B1} - because - B1 is not compatible with F1 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:254:5 │ @@ -356,17 +241,6 @@ Because in the expression's type: Context expects type: F1 , ... } ------------------------------- Detailed message ------------------------------ - - #{bar := B1, foo := F1} is not compatible with foo_bar_opt(B1, F1) - because - #{bar := B1, foo := F1} is not compatible with #{bar => F1, foo => B1} - because - at key `bar`: - #{bar := B1, foo := F1} is not compatible with #{bar => F1, foo => B1} - because - B1 is not compatible with F1 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:266:5 │ @@ -385,17 +259,6 @@ Because in the expression's type: Differs from the expected type: K1 | K2 , ... } ------------------------------- Detailed message ------------------------------ - - #{K1 | V2 => V1 | K2} is not compatible with kv(K1 | K2, V1 | V2) - because - #{K1 | V2 => V1 | K2} is not compatible with #{K1 | K2 => V1 | V2} - the default associations are not compatible - because - K1 | V2 is not compatible with K1 | K2 - because - V2 is not compatible with K1 | K2 - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:283:19 │ @@ -412,10 +275,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:307:22 │ @@ -433,17 +292,6 @@ Because in the expression's type: Context expects type: number() , n()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {a(), n()} is not compatible with {n(), a()} - because - a() is not compatible with n() - because - atom() is not compatible with n() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:321:5 │ @@ -461,17 +309,6 @@ Because in the expression's type: Context expects type: number() , n()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {a(), n()} is not compatible with {n(), a()} - because - a() is not compatible with n() - because - atom() is not compatible with n() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:334:25 │ @@ -489,13 +326,6 @@ Because in the expression's type: Context expects type: K , ... } ------------------------------- Detailed message ------------------------------ - - #{V => K} is not compatible with #{K => V} - the default associations are not compatible - because - V is not compatible with K - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:351:25 │ @@ -515,17 +345,6 @@ Because in the expression's type: Context expects type: atom() , ... } ------------------------------- Detailed message ------------------------------ - - #{a() => n()} | #{n() => a()} | #{id => 'id' | 'no_id'} is not compatible with #{a() | n() => a()} - because - #{a() => n()} is not compatible with #{a() | n() => a()} - the default associations are not compatible - because - n() is not compatible with a() - because - number() is not compatible with a() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/t_maps.erl:356:1 │ @@ -556,15 +375,6 @@ Because in the expression's type: No candidate of the expression's type matches the expected type. , a()} ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {'a' | 'b', a()} is not compatible with {n(), a()} - because - 'a' | 'b' is not compatible with n() - because - 'a' | 'b' is not compatible with number() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ check/src/t_maps.erl:429:1 │ @@ -611,10 +421,6 @@ Because in the expression's type: Context expects type: #{n := ..., ...} The type of the expression is missing the following required keys: n. ------------------------------- Detailed message ------------------------------ - -key `n` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:536:5 │ @@ -633,17 +439,6 @@ Because in the expression's type: Differs from the expected type: 'b' | 'c' , ... } ------------------------------- Detailed message ------------------------------ - - #{a => 'b' | 'c' | 'a'} is not compatible with #{a => 'b' | 'c'} - because - at key `a`: - #{a => 'b' | 'c' | 'a'} is not compatible with #{a => 'b' | 'c'} - because - 'b' | 'c' | 'a' is not compatible with 'b' | 'c' - because - 'a' is not compatible with 'b' | 'c' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:571:5 │ @@ -660,15 +455,6 @@ Because in the expression's type: Context expects type: #{item_v2 := ..., ...} The type of the expression is missing the following required keys: item_v2. ------------------------------- Detailed message ------------------------------ - - rec_shape() is not compatible with rec_shape_v2() - because - #{item := rec_shape() | 'undefined'} is not compatible with rec_shape_v2() - because - #{item := rec_shape() | 'undefined'} is not compatible with #{item_v2 := rec_shape_v2() | 'undefined'} - key `item_v2` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:629:5 │ @@ -687,14 +473,6 @@ Because in the expression's type: Differs from the expected type: 'a' | #{item := 'a' | gen_shape('a')} , ... } ------------------------------- Detailed message ------------------------------ - - gen_shape('a' | 'b') is not compatible with 'a' | #{item := 'a' | #{item := 'a' | gen_shape('a')}} - because - #{item := 'a' | 'b' | gen_shape('a' | 'b')} is not compatible with 'a' | #{item := 'a' | #{item := 'a' | gen_shape('a')}} - because - #{item := 'a' | 'b' | gen_shape('a' | 'b')} is not compatible with 'a' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:641:5 │ @@ -711,15 +489,6 @@ Because in the expression's type: Context expects type: #{item := ..., ...} The type of the expression is missing the following required keys: item. ------------------------------- Detailed message ------------------------------ - - gen_shape_v2('a') is not compatible with gen_shape('a') - because - #{item_v2 := 'a' | gen_shape_v2('a')} is not compatible with gen_shape('a') - because - #{item_v2 := 'a' | gen_shape_v2('a')} is not compatible with #{item := 'a' | gen_shape('a')} - key `item` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:648:5 │ @@ -736,10 +505,6 @@ Because in the expression's type: Context expects type: #{...} The expected map has no corresponding key for: d. ------------------------------- Detailed message ------------------------------ - -key `d` is declared in the former but not in the latter and the latter map has no default association - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:661:5 │ @@ -757,15 +522,6 @@ Because in the expression's type: Context expects type: 'ka' , ... } ------------------------------- Detailed message ------------------------------ - - #{a => 'va', b => 'vb', c => 'vc', d => 'vd', e => 've'} is not compatible with #{a => 'ka', b => 'kb', c => 'kc'} - because - at key `a`: - #{a => 'va', b => 'vb', c => 'vc', d => 'vd', e => 've'} is not compatible with #{a => 'ka', b => 'kb', c => 'kc'} - because - 'va' is not compatible with 'ka' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:675:5 │ @@ -782,10 +538,6 @@ Because in the expression's type: Context expects type: #{c := ..., ...} The type of the expression is missing the following required keys: c. ------------------------------- Detailed message ------------------------------ - -key `c` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:682:5 │ @@ -802,10 +554,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:689:5 │ @@ -822,10 +570,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:710:5 │ @@ -842,10 +586,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:732:27 │ @@ -862,13 +602,6 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{'a' | 'b' => boolean()} is not compatible with #{a => 'true', b => boolean()} - key a is not present in the former map but is incompatible with its default association - because - boolean() is not compatible with 'true' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:737:27 │ @@ -885,10 +618,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/t_maps.erl:742:27 │ @@ -905,11 +634,4 @@ Because in the expression's type: Context expects type: #{...} (no default association) The expected map has no default association while the type of the expression has one. ------------------------------- Detailed message ------------------------------ - - #{'a' | 'b' => boolean()} is not compatible with #{a => boolean()} - because - #{'a' | 'b' => boolean()} is not compatible with #{a => boolean()} - the latter map has no default association while the first map has one - 44 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty index 471b5c8ebe..5f9c8914f4 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty @@ -22,12 +22,6 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - [] | 'error' is not compatible with atom() - because - [] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/tries.erl:75:16 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty index e02af30869..5b2cca1631 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty @@ -16,17 +16,6 @@ Because in the expression's type: Differs from the expected type: 'ok' , 'arg' | 'nil'} ------------------------------- Detailed message ------------------------------ - - t4() is not compatible with t5() - because - {'msg', 'ok' | 'err', 'arg' | 'nil'} is not compatible with t5() - because - {'msg', 'ok' | 'err', 'arg' | 'nil'} is not compatible with {'msg', 'ok', 'arg'} | {'msg', 'err', 'arg'} | {'msg', 'ok', 'nil'} | {'msg', 'err', 'nil'} - because - at tuple index 2: - {'msg', 'ok' | 'err', 'arg' | 'nil'} is not compatible with {'msg', 'ok', 'arg'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/tuple_union.erl:66:26 │ @@ -47,14 +36,4 @@ Because in the expression's type: No candidate of the expression's type matches the expected type. , tree2()} ------------------------------- Detailed message ------------------------------ - - tree3() is not compatible with tree1() - because - {'leaf', atom()} | {'b1' | 'b2' | 'b3', tree2()} is not compatible with tree1() - because - {'leaf', atom()} | {'b1' | 'b2' | 'b3', tree2()} is not compatible with {'leaf', atom()} | {'b1', tree1()} | {'b2', tree1()} - because - {'b1' | 'b2' | 'b3', tree2()} is not compatible with {'leaf', atom()} | {'b1', tree1()} | {'b2', tree1()} - 2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty index c5fb63ed41..069950f003 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty @@ -22,12 +22,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - string() | binary() is not compatible with binary() - because - string() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:63:26 │ @@ -63,17 +57,6 @@ Because in the expression's type: Context expects type: atom() , atom()} ------------------------------- Detailed message ------------------------------ - - 'false' | {number(), atom()} is not compatible with 'false' | {atom(), number()} - because - {number(), atom()} is not compatible with 'false' | {atom(), number()} - because - at tuple index 1: - {number(), atom()} is not compatible with {atom(), number()} - because - number() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:92:3 │ @@ -91,13 +74,6 @@ Because in the expression's type: Context expects type: number() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'false' | number(), term()} is not compatible with {'false' | number(), number()} - because - term() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:105:39 │ @@ -150,17 +126,6 @@ Because in the expression's type: The expected map has no corresponding key for: kb. , ... } ------------------------------- Detailed message ------------------------------ - - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} - because - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} - the default associations are not compatible - because - #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - because - #{ka := 'va', kb := 'vb', kc := 'vc'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:129:9 │ @@ -179,17 +144,6 @@ Because in the expression's type: The expected map has no corresponding key for: kb. , ... } ------------------------------- Detailed message ------------------------------ - - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} - because - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} - the default associations are not compatible - because - #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - because - #{ka := 'va', kb := 'vb', kc := 'vc'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:132:9 │ @@ -208,17 +162,6 @@ Because in the expression's type: The expected map has no corresponding key for: kb. , ... } ------------------------------- Detailed message ------------------------------ - - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} | #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} - because - #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'}} is not compatible with #{dynamic() => #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'}} - the default associations are not compatible - because - #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kb := 'vb', kc := 'vc'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb', kc := 'vc'} | #{kb := 'vb'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - because - #{ka := 'va', kb := 'vb', kc := 'vc'} is not compatible with #{kc := 'vc'} | #{ka := 'va'} | #{ka := 'va', kc := 'vc'} | #{ka := 'va', kb := 'vb'} | #{kb := 'vb'} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/type_asserts.erl:156:24 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual.pretty index d03afc8cf8..b0892292bd 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual.pretty @@ -15,11 +15,4 @@ Because in the expression's type: Context expects type: atom() } ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {binary()} is not compatible with {atom()} - because - binary() is not compatible with atom() - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict.pretty index 12f1d81948..034dbd779d 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict.pretty @@ -15,11 +15,4 @@ Because in the expression's type: Context expects type: atom() } ------------------------------- Detailed message ------------------------------ - - at tuple index 1: - {binary()} is not compatible with {atom()} - because - binary() is not compatible with atom() - 1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty index ab0cebee3f..3c6de10f94 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty @@ -14,12 +14,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - number() | binary() is not compatible with binary() - because - number() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:172:22 │ @@ -36,12 +30,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - number() | binary() is not compatible with binary() - because - number() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:183:7 │ @@ -58,12 +46,6 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - number() | binary() is not compatible with binary() - because - number() is not compatible with binary() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ eqwater/src/eqwater.erl:228:1 │ @@ -86,12 +68,6 @@ Because in the expression's type: However the following candidate: #ab_rec{} Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - #ab_rec{} | atom() is not compatible with atom() - because - #ab_rec{} is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:356:16 │ @@ -108,12 +84,6 @@ Because in the expression's type: However the following candidate: #ab_rec{} Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - #ab_rec{} | atom() is not compatible with atom() - because - #ab_rec{} is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:404:25 │ @@ -152,12 +122,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with number() - because - atom() is not compatible with number() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ eqwater/src/eqwater.erl:419:1 │ @@ -182,12 +146,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:429:25 │ @@ -204,12 +162,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:496:3 │ @@ -235,13 +187,6 @@ Because in the expression's type: Context expects type: atom() } ------------------------------- Detailed message ------------------------------ - - at tuple index 2: - {'union_field', binary()} is not compatible with {'union_field', atom()} - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:559:50 │ @@ -258,12 +203,6 @@ Because in the expression's type: However the following candidate: 'ok' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | 'ok' is not compatible with number() - because - 'ok' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:590:8 │ @@ -280,12 +219,6 @@ Because in the expression's type: However the following candidate: #c{} Differs from the expected type: #b{} ------------------------------- Detailed message ------------------------------ - - #b{} | #c{} is not compatible with #b{} - because - #c{} is not compatible with #b{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:631:27 │ @@ -302,12 +235,6 @@ Because in the expression's type: However the following candidate: A Differs from the expected type: B ------------------------------- Detailed message ------------------------------ - - A | B is not compatible with B - because - A is not compatible with B - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:649:17 │ @@ -324,12 +251,6 @@ Because in the expression's type: However the following candidate: fun() Differs from the expected type: {term()} ------------------------------- Detailed message ------------------------------ - - fun() | {term()} is not compatible with {term()} - because - fun() is not compatible with {term()} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:660:17 │ @@ -346,12 +267,6 @@ Because in the expression's type: However the following candidate: tuple() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - tuple() | atom() is not compatible with atom() - because - tuple() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:839:27 │ @@ -368,12 +283,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | atom() is not compatible with number() - because - atom() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:997:31 │ @@ -412,12 +321,6 @@ Because in the expression's type: However the following candidate: 'undefined' Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - number() | 'undefined' is not compatible with number() - because - 'undefined' is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1122:14 │ @@ -437,14 +340,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [[atom()]] | [atom()] is not compatible with [atom()] - because - [[atom()]] is not compatible with [atom()] - because - [atom()] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1131:15 │ @@ -464,14 +359,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [[atom()]] | [atom()] is not compatible with [atom()] - because - [[atom()]] is not compatible with [atom()] - because - [atom()] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1155:7 │ @@ -488,12 +375,6 @@ Because in the expression's type: However the following candidate: string() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - binary() | string() is not compatible with binary() - because - string() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1181:7 │ @@ -510,12 +391,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - binary() | atom() is not compatible with binary() - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1189:60 │ @@ -532,12 +407,6 @@ Because in the expression's type: However the following candidate: 'ok' Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - 'ok' | binary() is not compatible with binary() - because - 'ok' is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1194:30 │ @@ -554,12 +423,6 @@ Because in the expression's type: However the following candidate: binary() Differs from the expected type: 'ok' ------------------------------- Detailed message ------------------------------ - - binary() | 'ok' is not compatible with 'ok' - because - binary() is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1225:14 │ @@ -576,12 +439,6 @@ Because in the expression's type: However the following candidate: {term(), my_list()} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - {term(), my_list()} | number() is not compatible with number() - because - {term(), my_list()} is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1231:31 │ @@ -606,12 +463,6 @@ Because in the expression's type: However the following candidate: 'a' Differs from the expected type: 'b' ------------------------------- Detailed message ------------------------------ - - 'a' | 'b' is not compatible with 'b' - because - 'a' is not compatible with 'b' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1269:5 │ @@ -624,16 +475,9 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ eqwater/src/eqwater.erl:1286:60 │ 1286 │ negate_atoms_neg(A) when not ((A == a) orelse (A == b)) -> A; - │ ^ - │ │ - │ A. + │ ^ A. Expression has type: 'c' Context expected type: 'a' | 'b' - │ - - 'c' is not compatible with 'a' | 'b' - because - 'c' is not compatible with 'a' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1291:43 diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty index c5cd5a3b25..bfb8103b16 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty @@ -16,14 +16,6 @@ Because in the expression's type: Differs from the expected type: binary() ] ------------------------------- Detailed message ------------------------------ - - [binary() | atom()] is not compatible with [binary()] - because - binary() | atom() is not compatible with binary() - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_lists.erl:48:23 │ @@ -57,12 +49,6 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - atom() | [] is not compatible with atom() - because - [] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_lists.erl:61:19 │ @@ -79,12 +65,6 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - atom() | [] is not compatible with atom() - because - [] is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_lists.erl:69:19 │ @@ -121,14 +101,6 @@ Because in the expression's type: Context expects type: binary() ] ------------------------------- Detailed message ------------------------------ - - [atom()] | [binary()] is not compatible with [binary()] - because - [atom()] is not compatible with [binary()] - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_lists.erl:97:23 │ @@ -155,14 +127,4 @@ Because in the expression's type: Differs from the expected type: atom() ] ------------------------------- Detailed message ------------------------------ - - [atom() | binary()] is not compatible with [atom()] | [binary()] - because - [atom() | binary()] is not compatible with [atom()] - because - atom() | binary() is not compatible with atom() - because - binary() is not compatible with atom() - 8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty index 847411f766..b5f637ead3 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty @@ -14,10 +14,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_maps.erl:27:22 │ @@ -34,10 +30,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_maps.erl:36:22 │ @@ -54,12 +46,6 @@ Because in the expression's type: However the following candidate: #{} Differs from the expected type: 'ok' ------------------------------- Detailed message ------------------------------ - - #{} | 'ok' is not compatible with 'ok' - because - #{} is not compatible with 'ok' - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_maps.erl:42:22 │ @@ -76,10 +62,6 @@ Because in the expression's type: Context expects type: #{b := ..., ...} The type of the expression is missing the following required keys: b. ------------------------------- Detailed message ------------------------------ - -key `b` is declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_maps.erl:55:34 │ @@ -88,4 +70,20 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: #{a := dynamic(), dynamic() => dynamic()} Context expected type: 'err' -5 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:70:29 + │ +70 │ (_, #{a := V}) -> is_ok(V) + │ ^ + │ │ + │ V. +Expression has type: 'ok' | 'err' +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'ok' + However the following candidate: 'err' + Differs from the expected type: 'ok' + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty index 5a01001d2e..245813d1e8 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty @@ -14,14 +14,6 @@ Because in the expression's type: However the following candidate: #rec1{} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - user() is not compatible with number() - because - #rec1{} | number() is not compatible with number() - because - #rec1{} is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_records.erl:40:16 │ @@ -38,14 +30,6 @@ Because in the expression's type: However the following candidate: #rec1{} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - user() is not compatible with number() - because - #rec1{} | number() is not compatible with number() - because - #rec1{} is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_records.erl:47:11 │ @@ -62,14 +46,6 @@ Because in the expression's type: However the following candidate: #rec1{} Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - user() is not compatible with number() - because - #rec1{} | number() is not compatible with number() - because - #rec1{} is not compatible with number() - error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) ┌─ eqwater/src/eqwater_records.erl:104:1 │ @@ -96,15 +72,6 @@ Because in the expression's type: Context expects type: #rec1{} , #rec1{}} ------------------------------- Detailed message ------------------------------ - - {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} is not compatible with {#rec1{}, #rec2{}} - because - at tuple index 1: - {#rec2{}, #rec1{}} is not compatible with {#rec1{}, #rec2{}} - because - #rec2{} is not compatible with #rec1{} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_records.erl:115:25 │ @@ -124,13 +91,4 @@ Because in the expression's type: Context expects type: #rec1{} , #rec1{}} ------------------------------- Detailed message ------------------------------ - - {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} is not compatible with {#rec1{}, #rec2{}} - because - at tuple index 1: - {#rec2{}, #rec1{}} is not compatible with {#rec1{}, #rec2{}} - because - #rec2{} is not compatible with #rec1{} - 6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty index 4ee35cc78d..332e999cda 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty @@ -14,14 +14,6 @@ Because in the expression's type: However the following candidate: binary() Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - ab() is not compatible with atom() - because - atom() | binary() is not compatible with atom() - because - binary() is not compatible with atom() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_sound.erl:50:14 │ @@ -38,14 +30,6 @@ Because in the expression's type: However the following candidate: atom() Differs from the expected type: binary() ------------------------------- Detailed message ------------------------------ - - ab() is not compatible with binary() - because - atom() | binary() is not compatible with binary() - because - atom() is not compatible with binary() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater_sound.erl:53:28 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty index 8b70994f10..c0c48c5e3c 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty @@ -14,12 +14,6 @@ Because in the expression's type: However the following candidate: binary() Differs from the expected type: number() ------------------------------- Detailed message ------------------------------ - - binary() | number() is not compatible with number() - because - binary() is not compatible with number() - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/unlimited_refinement.erl:94:14 │ diff --git a/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance.pretty b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance.pretty index 803ac23f35..554970c163 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance.pretty @@ -416,12 +416,6 @@ Because in the expression's type: Context expects type: atom() ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [atom()] - because - term() is not compatible with atom() - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ fault_tolerance/src/fault_tolerance.erl:169:8 │ @@ -672,12 +666,6 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() ------------------------------- Detailed message ------------------------------ - - dynamic() | [] is not compatible with atom() - because - [] is not compatible with atom() - error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) ┌─ fault_tolerance/src/fault_tolerance.erl:254:11 │ diff --git a/crates/elp/src/resources/test/glean_help.stdout b/crates/elp/src/resources/test/glean_help.stdout index bdde2ec540..c4e938e143 100644 --- a/crates/elp/src/resources/test/glean_help.stdout +++ b/crates/elp/src/resources/test/glean_help.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--module MODULE] [--to TO] [--v2] [--pretty] [--multi] [--prefix ARG] +Usage: [--project PROJECT] [--module MODULE] [--to TO] [--v2] [--pretty] [--multi] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) @@ -7,5 +7,4 @@ Available options: --v2 Produce glean db with macros, types, xrefs. Incompatible with previous --pretty Pretty print --multi Output each fact separately - --prefix Optional prefix to prepend to each fact -h, --help Prints help information diff --git a/crates/elp/src/resources/test/help.stdout b/crates/elp/src/resources/test/help.stdout index acc7ae286d..1510d5bddb 100644 --- a/crates/elp/src/resources/test/help.stdout +++ b/crates/elp/src/resources/test/help.stdout @@ -1,4 +1,4 @@ -Usage: [--log-file LOG_FILE] [--erl ERL] [--escript ESCRIPT] [--no-log-buffering] [--no-buck-generated] [--buck-quick-start] [COMMAND ...] +Usage: [--log-file LOG_FILE] [--erl ERL] [--escript ESCRIPT] [--no-log-buffering] [--no-buck-generated] [--buck-quick-start] [[--color WHEN]] [COMMAND ...] Available options: --log-file @@ -7,6 +7,7 @@ Available options: --no-log-buffering --no-buck-generated When using buck, do not invoke a build step for generated files. --buck-quick-start Use buck2 targets for first stage project loading + --color Use color in output; WHEN is 'always', 'never', or 'auto' -h, --help Prints help information Available commands: @@ -20,6 +21,7 @@ Available commands: dialyze-all Run Dialyzer on the whole project by shelling out to a `dialyzer-run` tool on the path to do the legwork. lint Parse files in project and emit diagnostics, optionally apply fixes. ssr Run SSR (Structural Search and Replace) pattern matching on project files. + search Alias for 'ssr': Run SSR (Structural Search and Replace) pattern matching on project files. parse-all Dump ast for all files in a project for specified rebar.config file parse-elp Tree-sitter parse all files in a project for specified rebar.config file explain Explain a diagnostic code diff --git a/crates/elp/src/resources/test/hierarchical_config/basic.stdout b/crates/elp/src/resources/test/hierarchical_config/basic.stdout new file mode 100644 index 0000000000..de8cb63308 --- /dev/null +++ b/crates/elp/src/resources/test/hierarchical_config/basic.stdout @@ -0,0 +1,5 @@ +Diagnostics reported: +Reporting all diagnostics codes +app_a/src/app_a.erl:6:1-6:5::[Warning] [L1230] function main/0 is unused +app_b/src/app_b.erl:4:9-4:16::[Warning] [W0002] Unused macro (MACRO_B) +app_b/src/app_b.erl:6:1-6:5::[Warning] [L1230] function main/0 is unused diff --git a/crates/elp/src/resources/test/lint_help.stdout b/crates/elp/src/resources/test/lint_help.stdout index 8d03db6ee3..bffdd1fe91 100644 --- a/crates/elp/src/resources/test/lint_help.stdout +++ b/crates/elp/src/resources/test/lint_help.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--module MODULE] [--app APP] [--file FILE] [--rebar] [--as PROFILE] [--include-generated] [--include-tests] [--no-diags] [[--format FORMAT]] [--prefix ARG] [--include-erlc-diagnostics] [--include-ct-diagnostics] [--include-edoc-diagnostics] [--include-eqwalizer-diagnostics] [--include-suppressed] [--use-cli-severity] [--diagnostic-ignore CODE] [--diagnostic-filter CODE] [--experimental] [--read-config] [--config-file CONFIG_FILE] [--apply-fix] [--ignore-fix-only] [--in-place] [--to TO] [--recursive] [--with-check] [--check-eqwalize-all] [--one-shot] [--report-system-stats] ... +Usage: [--project PROJECT] [--module MODULE] [--app APP] [--file FILE] [--rebar] [--as PROFILE] [--include-generated] [--include-tests] [--no-diags] [[--format FORMAT]] [--include-erlc-diagnostics] [--include-ct-diagnostics] [--include-edoc-diagnostics] [--include-eqwalizer-diagnostics] [--include-suppressed] [--use-cli-severity] [--diagnostic-ignore CODE] [--diagnostic-filter CODE] [--experimental] [--read-config] [--config-file CONFIG_FILE] [--apply-fix] [--ignore-fix-only] [--in-place] [--to TO] [--recursive] [--with-check] [--check-eqwalize-all] [--one-shot] [--report-system-stats] [--no-stream] ... Available positional items: Rest of args are space separated list of apps to ignore @@ -14,7 +14,6 @@ Available options: --include-tests Also generate diagnostics for test files --no-diags Do not print the full diagnostics for a file, just the count --format Show diagnostics in JSON format - --prefix Optional prefix to prepend to each diagnostic file path. Only used when --format=json is set --include-erlc-diagnostics Include diagnostics produced by erlc --include-ct-diagnostics Include Common Test diagnostics --include-edoc-diagnostics Include EDoc diagnostics @@ -38,4 +37,5 @@ Available options: --one-shot Apply to all matching diagnostic occurrences at once, rather than one at a time. --report-system-stats Report system memory usage and other statistics + --no-stream Disable streaming of diagnostics when applying fixes (collect all before printing) -h, --help Prints help information diff --git a/crates/elp/src/resources/test/linter/custom_function_matches.stdout b/crates/elp/src/resources/test/linter/custom_function_matches.stdout index 08c0321771..c552fa7dfa 100644 --- a/crates/elp/src/resources/test/linter/custom_function_matches.stdout +++ b/crates/elp/src/resources/test/linter/custom_function_matches.stdout @@ -1,5 +1,4 @@ Reporting all diagnostics codes module specified: custom_function_matches -Diagnostics reported in 1 modules: - custom_function_matches: 1 - 13:4-13:25::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. +Diagnostics reported: +app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. diff --git a/crates/elp/src/resources/test/linter/elp_lint_ct.stdout b/crates/elp/src/resources/test/linter/elp_lint_ct.stdout index db2b6e3afa..471efc7900 100644 --- a/crates/elp/src/resources/test/linter/elp_lint_ct.stdout +++ b/crates/elp/src/resources/test/linter/elp_lint_ct.stdout @@ -1,3 +1,2 @@ -Diagnostics reported in 1 modules: - app_a_unreachable_test_SUITE: 1 - 7:0-7:1::[Error] [W0008] Unreachable test (b/1) +Diagnostics reported: +app_a/test/app_a_unreachable_test_SUITE.erl:8:1-8:2::[Error] [W0008] Unreachable test (b/1) \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout b/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout index 022b3b8972..a667df3930 100644 --- a/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout +++ b/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout @@ -1,3 +1,2 @@ -Diagnostics reported in 1 modules: - app_a_edoc: 1 - 4:0-5:0::[Warning] [O0039] tag @docc not recognized. +Diagnostics reported: +app_a/src/app_a_edoc.erl:5:1-6:1::[Warning] [O0039] tag @docc not recognized. \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint2.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint2.stdout index b0cc8a1337..47dc6d4a5e 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint2.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint2.stdout @@ -1,4 +1,4 @@ module specified: app_a -Diagnostics reported in 1 modules: - app_a: 1 - 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' +Diagnostics reported: +app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' + 7:1-7:5: Mismatched clause name diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_adhoc_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_adhoc_output.stdout index 5adeaace66..0d0d56491d 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_adhoc_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_adhoc_output.stdout @@ -1,13 +1,12 @@ module specified: app_b -Diagnostics reported in 1 modules: - app_b: 2 - 7:4-7:35::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called - 4:4-4:34::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called +Diagnostics reported: +app_b/src/app_b.erl:8:5-8:36::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called +app_b/src/app_b.erl:5:5-5:35::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called --------------------------------------------- Applying fixes in module 'app_b' for - 7:4-7:35::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called - 4:4-4:34::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called + 8:5-8:36::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called + 5:5-5:35::[WeakWarning] [ad-hoc: application:get_env/2] 'application:get_env/2' called @@ -1,8 +1,8 @@ -module(app_b). -export([application_env_error/0, application_env_no_error/0]). diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_app.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_app.stdout index a5c27f4367..edd7c5cf1c 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_app.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_app.stdout @@ -1,3 +1,3 @@ -Diagnostics reported in 1 modules: - app_a: 1 - 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' +Diagnostics reported: +app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' + 7:1-7:5: Mismatched clause name diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_config_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_config_output.stdout index 8b6d166b16..6daa076a31 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_config_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_config_output.stdout @@ -1,13 +1,10 @@ -Diagnostics reported in 4 modules: - app_a: 3 - 8:5-8:6::[Warning] [W0010] this variable is unused - 4:4-4:34::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` - 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' - app_a_unused_param: 2 - 4:4-4:5::[Warning] [W0010] this variable is unused - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - app_b: 1 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` - app_b_unused_param: 2 - 4:4-4:5::[Warning] [W0010] this variable is unused - 4:4-4:5::[Warning] [L1268] variable 'X' is unused + 7:1-7:5: Mismatched clause name +Diagnostics reported: +app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` +app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' +app_a/src/app_a.erl:9:6-9:7::[Warning] [W0010] this variable is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_custom_config_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_custom_config_output.stdout index 50166d0687..90ccbcb5c0 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_custom_config_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_custom_config_output.stdout @@ -1,9 +1,5 @@ -Diagnostics reported in 4 modules: - app_a: 1 - 4:4-4:34::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` - app_a_unused_param: 1 - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - app_b: 1 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` - app_b_unused_param: 1 - 4:4-4:5::[Warning] [L1268] variable 'X' is unused +Diagnostics reported: +app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_erlang_service.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_erlang_service.stdout index 071903fae8..eeb41e5adf 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_erlang_service.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_erlang_service.stdout @@ -1,4 +1,3 @@ module specified: expression_updates_literal -Diagnostics reported in 1 modules: - expression_updates_literal: 1 - 7:6-8:15::[Warning] [L1318] expression updates a literal +Diagnostics reported: +app_a/src/expression_updates_literal.erl:8:7-9:16::[Warning] [L1318] expression updates a literal diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_explicit_enable_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_explicit_enable_output.stdout index cf937cab11..1d7771b3b9 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_explicit_enable_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_explicit_enable_output.stdout @@ -1,19 +1,12 @@ -Diagnostics reported in 7 modules: - app_a: 2 - 4:4-4:34::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - app_a_edoc: 1 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - app_a_ssr: 1 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - app_a_unused_param: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - app_b: 2 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - app_b_unused_param: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - spelling: 1 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +Diagnostics reported: +app_a/src/app_a.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` +app_a/src/app_a_edoc.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_a/src/app_a_ssr.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_a/src/app_a_unused_param.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_a/src/spelling.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_b/src/app_b.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +app_b/src/app_b_unused_param.erl:1:1-1:1::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_fix_ignore.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_fix_ignore.stdout index 4c42e88eec..3e4a25f329 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_fix_ignore.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_fix_ignore.stdout @@ -1,11 +1,10 @@ module specified: app_b -Diagnostics reported in 1 modules: - app_b: 1 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +Diagnostics reported: +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` --------------------------------------------- Applying fix in module 'app_b' for - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` + 5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` @@ -1,7 +1,8 @@ -module(app_b). -export([application_env_error/0, application_env_no_error/0]). diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_fixme_spelling.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_fixme_spelling.stdout index fcba7f3aee..2cec0678d2 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_fixme_spelling.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_fixme_spelling.stdout @@ -1,11 +1,10 @@ module specified: spelling -Diagnostics reported in 1 modules: - spelling: 1 - 1:1-1:9::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' +Diagnostics reported: +app_a/src/spelling.erl:2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' --------------------------------------------- Applying fix in module 'spelling' for - 1:1-1:9::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' + 2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' @@ -1,4 +1,5 @@ -module(spelling). +% elp:fixme W0013 (misspelled_attribute) diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore.stdout index 07a81b80e2..1e7e049554 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore.stdout @@ -1,9 +1,6 @@ -Diagnostics reported in 3 modules: - app_a: 1 - 8:5-8:6::[Warning] [W0010] this variable is unused - app_a_unused_param: 2 - 4:4-4:5::[Warning] [W0010] this variable is unused - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - app_b_unused_param: 2 - 4:4-4:5::[Warning] [W0010] this variable is unused - 4:4-4:5::[Warning] [L1268] variable 'X' is unused +Diagnostics reported: +app_a/src/app_a.erl:9:6-9:7::[Warning] [W0010] this variable is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps.stdout index e7a35d0262..4b8d3cc466 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps.stdout @@ -1,3 +1,2 @@ -Diagnostics reported in 1 modules: - app_b_unused_param: 1 - 4:4-4:5::[Warning] [W0010] this variable is unused +Diagnostics reported: +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout index 00d4e2355e..5a42009118 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout @@ -1,5 +1,3 @@ -Diagnostics reported in 2 modules: - app_a: 1 - 8:5-8:6::[Warning] [W0010] this variable is unused - app_a_unused_param: 1 - 4:4-4:5::[Warning] [W0010] this variable is unused +Diagnostics reported: +app_a/src/app_a.erl:9:6-9:7::[Warning] [W0010] this variable is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_json_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_json_output.stdout index 4fb3478926..5d6b5e78bc 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_json_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_json_output.stdout @@ -1,3 +1,3 @@ {"path":"app_a/src/app_a.erl","line":9,"char":6,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} {"path":"app_a/src/app_a_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} -{"path":"app_b/src/app_b_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} +{"path":"app_b/src/app_b_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_json_output_prefix.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_json_output_prefix.stdout deleted file mode 100644 index 7389cc926a..0000000000 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_json_output_prefix.stdout +++ /dev/null @@ -1,3 +0,0 @@ -{"path":"my/prefix/app_a/src/app_a.erl","line":9,"char":6,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} -{"path":"my/prefix/app_a/src/app_a_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} -{"path":"my/prefix/app_b/src/app_b_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0010 (unused_function_arg)","original":null,"replacement":null,"description":"this variable is unused\n\nFor more information see: /erlang-error-index/w/W0010","docPath":"website/docs/erlang-error-index/w/W0010.md"} diff --git a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout index 8b664bb080..7b6a1e705c 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout @@ -1,30 +1,18 @@ -{"path":"app_a/src/app_a.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0011 (application_get_env)","original":null,"replacement":null,"description":"module `app_a` belongs to app `app_a`, but reads env for `misc`\n\nFor more information see: /erlang-error-index/w/W0011","docPath":"website/docs/erlang-error-index/w/W0011.md"} -{"path":"app_a/src/app_a.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_a/src/app_a.erl","line":9,"char":1,"code":"ELP","severity":"error","name":"P1700 (head_mismatch)","original":null,"replacement":null,"description":"head mismatch 'fooX' vs 'food'\n\nFor more information see: /erlang-error-index/p/P1700","docPath":null} -{"path":"app_a/src/app_a.erl","line":8,"char":7,"code":"ELP","severity":"warning","name":"W0018 (unexpected_semi_or_dot)","original":null,"replacement":null,"description":"Unexpected ';'\n\nFor more information see: /erlang-error-index/w/W0018","docPath":"website/docs/erlang-error-index/w/W0018.md"} -{"path":"app_a/src/app_a.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} -{"path":"app_a/src/app_a.erl","line":13,"char":5,"code":"ELP","severity":"warning","name":"W0026 (unexported_function)","original":null,"replacement":null,"description":"Function 'app_a:baz/2' is not exported.\n\nFor more information see: /erlang-error-index/w/W0026","docPath":"website/docs/erlang-error-index/w/W0026.md"} {"path":"app_a/src/app_a.erl","line":12,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function bar/0 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} +{"path":"app_a/src/app_a.erl","line":13,"char":5,"code":"ELP","severity":"warning","name":"W0026 (unexported_function)","original":null,"replacement":null,"description":"Function 'app_a:baz/2' is not exported.\n\nFor more information see: /erlang-error-index/w/W0026","docPath":"website/docs/erlang-error-index/w/W0026.md"} {"path":"app_a/src/app_a.erl","line":16,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function baz/2 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} -{"path":"app_a/src/app_a_edoc.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_a/src/app_a_edoc.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} -{"path":"app_a/src/app_a_ssr.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_a/src/app_a_ssr.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} -{"path":"app_a/src/app_a_unused_param.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_a/src/app_a_unused_param.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} +{"path":"app_a/src/app_a.erl","line":20,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function bat/2 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} +{"path":"app_a/src/app_a.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0011 (application_get_env)","original":null,"replacement":null,"description":"module `app_a` belongs to app `app_a`, but reads env for `misc`\n\nFor more information see: /erlang-error-index/w/W0011","docPath":"website/docs/erlang-error-index/w/W0011.md"} +{"path":"app_a/src/app_a.erl","line":8,"char":7,"code":"ELP","severity":"warning","name":"W0018 (unexpected_semi_or_dot)","original":null,"replacement":null,"description":"Unexpected ';'\n\nFor more information see: /erlang-error-index/w/W0018","docPath":"website/docs/erlang-error-index/w/W0018.md"} +{"path":"app_a/src/app_a.erl","line":9,"char":1,"code":"ELP","severity":"error","name":"P1700 (head_mismatch)","original":null,"replacement":null,"description":"head mismatch 'fooX' vs 'food'\n\nFor more information see: /erlang-error-index/p/P1700","docPath":null} +{"path":"app_a/src/app_a_ssr.erl","line":7,"char":6,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} +{"path":"app_a/src/app_a_ssr.erl","line":8,"char":6,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} {"path":"app_a/src/app_a_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"L1268 (L1268)","original":null,"replacement":null,"description":"variable 'X' is unused\n\nFor more information see: /erlang-error-index/l/L1268","docPath":null} -{"path":"app_b/src/app_b.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0011 (application_get_env)","original":null,"replacement":null,"description":"module `app_b` belongs to app `app_b`, but reads env for `misc`\n\nFor more information see: /erlang-error-index/w/W0011","docPath":"website/docs/erlang-error-index/w/W0011.md"} -{"path":"app_b/src/app_b.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_b/src/app_b.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} -{"path":"app_b/src/app_b_unused_param.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_b/src/app_b_unused_param.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} -{"path":"app_b/src/app_b_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"L1268 (L1268)","original":null,"replacement":null,"description":"variable 'X' is unused\n\nFor more information see: /erlang-error-index/l/L1268","docPath":null} {"path":"app_a/src/custom_function_matches.erl","line":13,"char":5,"code":"ELP","severity":"warning","name":"W0017 (undefined_function)","original":null,"replacement":null,"description":"Function 'excluded:function/0' is undefined.\n\nFor more information see: /erlang-error-index/w/W0017","docPath":"website/docs/erlang-error-index/w/W0017.md"} {"path":"app_a/src/custom_function_matches.erl","line":14,"char":5,"code":"ELP","severity":"warning","name":"W0017 (undefined_function)","original":null,"replacement":null,"description":"Function 'not_excluded:function/0' is undefined.\n\nFor more information see: /erlang-error-index/w/W0017","docPath":"website/docs/erlang-error-index/w/W0017.md"} {"path":"app_a/src/custom_function_matches.erl","line":15,"char":5,"code":"ELP","severity":"warning","name":"W0017 (undefined_function)","original":null,"replacement":null,"description":"Function 'cross:call/0' is undefined.\n\nFor more information see: /erlang-error-index/w/W0017","docPath":"website/docs/erlang-error-index/w/W0017.md"} -{"path":"app_a/src/expression_updates_literal.erl","line":2,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} {"path":"app_a/src/expression_updates_literal.erl","line":7,"char":1,"code":"ELP","severity":"warning","name":"L1309 (L1309)","original":null,"replacement":null,"description":"missing specification for function a_fun/0\n\nFor more information see: /erlang-error-index/l/L1309","docPath":null} {"path":"app_a/src/expression_updates_literal.erl","line":8,"char":7,"code":"ELP","severity":"warning","name":"L1318 (L1318)","original":null,"replacement":null,"description":"expression updates a literal\n\nFor more information see: /erlang-error-index/l/L1318","docPath":null} {"path":"app_a/src/spelling.erl","line":2,"char":2,"code":"ELP","severity":"error","name":"W0013 (misspelled_attribute)","original":null,"replacement":null,"description":"misspelled attribute, saw 'dyalizer' but expected 'dialyzer'\n\nFor more information see: /erlang-error-index/w/W0013","docPath":"website/docs/erlang-error-index/w/W0013.md"} -{"path":"app_a/src/spelling.erl","line":1,"char":1,"code":"ELP","severity":"error","name":"W0012 (compile-warn-missing-spec)","original":null,"replacement":null,"description":"Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.\n\nFor more information see: /erlang-error-index/w/W0012","docPath":"website/docs/erlang-error-index/w/W0012.md"} -{"path":"app_a/src/spelling.erl","line":1,"char":9,"code":"ELP","severity":"disabled","name":"W0046 (undocumented_module)","original":null,"replacement":null,"description":"The module is not documented.\n\nFor more information see: /erlang-error-index/w/W0046","docPath":"website/docs/erlang-error-index/w/W0046.md"} +{"path":"app_b/src/app_b.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0011 (application_get_env)","original":null,"replacement":null,"description":"module `app_b` belongs to app `app_b`, but reads env for `misc`\n\nFor more information see: /erlang-error-index/w/W0011","docPath":"website/docs/erlang-error-index/w/W0011.md"} +{"path":"app_b/src/app_b_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"L1268 (L1268)","original":null,"replacement":null,"description":"variable 'X' is unused\n\nFor more information see: /erlang-error-index/l/L1268","docPath":null} \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout index 954278d91b..5bcd503e10 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout @@ -1,41 +1,21 @@ + 7:1-7:5: Mismatched clause name +Diagnostics reported: Reporting all diagnostics codes -Diagnostics reported in 9 modules: - app_a: 8 - 4:4-4:34::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' - 7:6-7:7::[Warning] [W0018] Unexpected ';' - 0:8-0:13::[WeakWarning] [W0046] The module is not documented. - 12:4-12:13::[Warning] [W0026] Function 'app_a:baz/2' is not exported. - 11:0-11:3::[Warning] [L1230] function bar/0 is unused - 15:0-15:3::[Warning] [L1230] function baz/2 is unused - app_a_edoc: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:18::[WeakWarning] [W0046] The module is not documented. - app_a_ssr: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:17::[WeakWarning] [W0046] The module is not documented. - app_a_unused_param: 3 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:26::[WeakWarning] [W0046] The module is not documented. - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - app_b: 3 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:13::[WeakWarning] [W0046] The module is not documented. - app_b_unused_param: 3 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:26::[WeakWarning] [W0046] The module is not documented. - 4:4-4:5::[Warning] [L1268] variable 'X' is unused - custom_function_matches: 3 - 12:4-12:21::[Warning] [W0017] Function 'excluded:function/0' is undefined. - 13:4-13:25::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. - 14:4-14:14::[Warning] [W0017] Function 'cross:call/0' is undefined. - expression_updates_literal: 3 - 1:8-1:34::[WeakWarning] [W0046] The module is not documented. - 6:0-6:5::[Warning] [L1309] missing specification for function a_fun/0 - 7:6-8:15::[Warning] [L1318] expression updates a literal - spelling: 3 - 1:1-1:9::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:16::[WeakWarning] [W0046] The module is not documented. +app_a/src/app_a.erl:12:1-12:4::[Warning] [L1230] function bar/0 is unused +app_a/src/app_a.erl:13:5-13:14::[Warning] [W0026] Function 'app_a:baz/2' is not exported. +app_a/src/app_a.erl:16:1-16:4::[Warning] [L1230] function baz/2 is unused +app_a/src/app_a.erl:20:1-20:4::[Warning] [L1230] function bat/2 is unused +app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` +app_a/src/app_a.erl:8:7-8:8::[Warning] [W0018] Unexpected ';' +app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' +app_a/src/app_a_ssr.erl:7:6-7:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_ssr.erl:8:6-8:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused +app_a/src/custom_function_matches.erl:13:5-13:22::[Warning] [W0017] Function 'excluded:function/0' is undefined. +app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. +app_a/src/custom_function_matches.erl:15:5-15:15::[Warning] [W0017] Function 'cross:call/0' is undefined. +app_a/src/expression_updates_literal.erl:7:1-7:6::[Warning] [L1309] missing specification for function a_fun/0 +app_a/src/expression_updates_literal.erl:8:7-9:16::[Warning] [L1318] expression updates a literal +app_a/src/spelling.erl:2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout index 55650830ba..7ded0e8d2f 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout @@ -1,3 +1,3 @@ -Diagnostics reported in 1 modules: - app_a: 1 - 15:12-15:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. +Diagnostics reported: +app_a/src/app_a.erl:16:13-16:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. +app_a/src/app_a.erl:20:13-20:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli.stdout index 2e8c69b6a4..e673a7be31 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli.stdout @@ -1,3 +1,5 @@ -Matches found in 1 modules: - app_a: 1 - 15:12-15:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + app_a: 2 + 16:13-16:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + 20:13-20:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout new file mode 100644 index 0000000000..880b8f3473 --- /dev/null +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout @@ -0,0 +1,12 @@ + +# Add this to your .elp_lint.toml +[[ad_hoc_lints.lints]] +type = "LintMatchSsr" +ssr_pattern = "ssr: ?BAR(_@AA)." +severity = "info" + +[[ad_hoc_lints.lints]] +type = "LintMatchSsr" +ssr_pattern = "ssr: {4}." +severity = "info" + diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout index 16ad74ebac..78f52012ac 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout @@ -1,3 +1,4 @@ -Matches found in 1 modules: app_a_ssr: 1 - 10:10-10:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + 11:11-11:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout index 0e246ef6f5..f5a1e98710 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout @@ -1,3 +1,4 @@ -Matches found in 1 modules: app_a_ssr: 1 - 10:10-10:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ?BAR(_@AA). + 11:11-11:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ?BAR(_@AA). + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout index 351b8ffee2..6f49895580 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout @@ -1,4 +1,5 @@ -Matches found in 1 modules: app_a_ssr: 2 - 10:10-10:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ?BAR(_@AA). - 10:10-10:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + 11:11-11:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ?BAR(_@AA). + 11:11-11:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_multiple.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_multiple.stdout index afffa751be..a71ebede29 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_multiple.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_multiple.stdout @@ -1,4 +1,5 @@ -Matches found in 1 modules: app_a_ssr: 2 - 6:9-6:14::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: 3. - 10:10-10:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + 7:10-7:15::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: 3. + 11:11-11:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {4}. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout index cefa90c758..6938fabb87 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout @@ -1,3 +1,4 @@ -Matches found in 1 modules: app_a_ssr: 1 - 6:9-6:14::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (((3))). + 7:10-7:15::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (((3))). + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout index f830e74c63..a43317a2cf 100644 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout +++ b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout @@ -1,5 +1,6 @@ -Matches found in 1 modules: app_a_ssr: 3 - 6:9-6:14::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). - 6:10-6:13::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). - 7:9-7:12::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). + 7:10-7:15::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). + 7:11-7:14::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). + 8:10-8:13::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: (_@A). + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_context_separator.stdout b/crates/elp/src/resources/test/linter/ssr_context_separator.stdout new file mode 100644 index 0000000000..f791ea85bf --- /dev/null +++ b/crates/elp/src/resources/test/linter/ssr_context_separator.stdout @@ -0,0 +1,20 @@ + app_a: 2 +app_a/src/app_a.erl:16:13-16:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + 14 | app_b:application_env_error(). + 15 | + 16 | baz(A,B) -> {A,B}. + | ^^^^^ + 17 | + 18 | % Some more context, see if it renders + +==== +app_a/src/app_a.erl:20:13-20:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + 18 | % Some more context, see if it renders + 19 | + 20 | bat(A,B) -> {A,B}. + | ^^^^^ + 21 | + 22 | % And some more context, see if it renders + + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/ssr_context_separator_color.stdout b/crates/elp/src/resources/test/linter/ssr_context_separator_color.stdout new file mode 100644 index 0000000000..119d62a52b --- /dev/null +++ b/crates/elp/src/resources/test/linter/ssr_context_separator_color.stdout @@ -0,0 +1,18 @@ + app_a: 2 +app_a/src/app_a.erl:16:13-16:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + 14 | app_b:application_env_error(). + 15 | + 16 | baz(A,B) -> {A,B}. + 17 | + 18 | % Some more context, see if it renders + +==== +app_a/src/app_a.erl:20:13-20:18::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. + 18 | % Some more context, see if it renders + 19 | + 20 | bat(A,B) -> {A,B}. + 21 | + 22 | % And some more context, see if it renders + + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/linter/warnings_as_errors.stdout b/crates/elp/src/resources/test/linter/warnings_as_errors.stdout index 493afa95c7..01007e94c4 100644 --- a/crates/elp/src/resources/test/linter/warnings_as_errors.stdout +++ b/crates/elp/src/resources/test/linter/warnings_as_errors.stdout @@ -1,41 +1,21 @@ + 7:1-7:5: Mismatched clause name +Diagnostics reported: Reporting all diagnostics codes -Diagnostics reported in 9 modules: - app_a: 8 - 4:4-4:34::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' - 7:6-7:7::[Warning] [W0018] Unexpected ';' - 0:8-0:13::[WeakWarning] [W0046] The module is not documented. - 12:4-12:13::[Warning] [W0026] Function 'app_a:baz/2' is not exported. - 11:0-11:3::[Error] [L1230] function bar/0 is unused - 15:0-15:3::[Error] [L1230] function baz/2 is unused - app_a_edoc: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:18::[WeakWarning] [W0046] The module is not documented. - app_a_ssr: 2 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:17::[WeakWarning] [W0046] The module is not documented. - app_a_unused_param: 3 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:26::[WeakWarning] [W0046] The module is not documented. - 4:4-4:5::[Error] [L1268] variable 'X' is unused - app_b: 3 - 4:4-4:34::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:13::[WeakWarning] [W0046] The module is not documented. - app_b_unused_param: 3 - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:26::[WeakWarning] [W0046] The module is not documented. - 4:4-4:5::[Error] [L1268] variable 'X' is unused - custom_function_matches: 3 - 12:4-12:21::[Warning] [W0017] Function 'excluded:function/0' is undefined. - 13:4-13:25::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. - 14:4-14:14::[Warning] [W0017] Function 'cross:call/0' is undefined. - expression_updates_literal: 3 - 1:8-1:34::[WeakWarning] [W0046] The module is not documented. - 6:0-6:5::[Error] [L1309] missing specification for function a_fun/0 - 7:6-8:15::[Error] [L1318] expression updates a literal - spelling: 3 - 1:1-1:9::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' - 0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - 0:8-0:16::[WeakWarning] [W0046] The module is not documented. +app_a/src/app_a.erl:12:1-12:4::[Error] [L1230] function bar/0 is unused +app_a/src/app_a.erl:13:5-13:14::[Warning] [W0026] Function 'app_a:baz/2' is not exported. +app_a/src/app_a.erl:16:1-16:4::[Error] [L1230] function baz/2 is unused +app_a/src/app_a.erl:20:1-20:4::[Error] [L1230] function bat/2 is unused +app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` +app_a/src/app_a.erl:8:7-8:8::[Warning] [W0018] Unexpected ';' +app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' +app_a/src/app_a_ssr.erl:7:6-7:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_ssr.erl:8:6-8:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_unused_param.erl:5:5-5:6::[Error] [L1268] variable 'X' is unused +app_a/src/custom_function_matches.erl:13:5-13:22::[Warning] [W0017] Function 'excluded:function/0' is undefined. +app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. +app_a/src/custom_function_matches.erl:15:5-15:15::[Warning] [W0017] Function 'cross:call/0' is undefined. +app_a/src/expression_updates_literal.erl:7:1-7:6::[Error] [L1309] missing specification for function a_fun/0 +app_a/src/expression_updates_literal.erl:8:7-9:16::[Error] [L1318] expression updates a literal +app_a/src/spelling.erl:2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' +app_b/src/app_b.erl:5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` +app_b/src/app_b_unused_param.erl:5:5-5:6::[Error] [L1268] variable 'X' is unused \ No newline at end of file diff --git a/crates/elp/src/resources/test/linter_config/basic.stdout b/crates/elp/src/resources/test/linter_config/basic.stdout new file mode 100644 index 0000000000..946fd7dc9a --- /dev/null +++ b/crates/elp/src/resources/test/linter_config/basic.stdout @@ -0,0 +1,5 @@ +Diagnostics reported: +Reporting all diagnostics codes +app_a/src/app_a.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_A) +app_a/src/app_a.erl:4:9-4:14::[Warning] [L1260] record rec_a is unused +app_b/src/app_b.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_B) \ No newline at end of file diff --git a/crates/elp/src/resources/test/parse_elp_help.stdout b/crates/elp/src/resources/test/parse_elp_help.stdout index 1457aed5c5..75cc38354e 100644 --- a/crates/elp/src/resources/test/parse_elp_help.stdout +++ b/crates/elp/src/resources/test/parse_elp_help.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--module MODULE] [--file ARG] [--to TO] [--no-diags] [--experimental] [--as PROFILE] [--dump-includes] [--rebar] [--include-generated] [--serial] [--use-cli-severity] [[--format FORMAT]] [--report-system-stats] +Usage: [--project PROJECT] [--module MODULE] [--file ARG] [--to TO] [--no-diags] [--experimental] [--as PROFILE] [--dump-includes] [--rebar] [--include-generated] [--serial] [--use-cli-severity] [[--format FORMAT]] [--report-system-stats] [[--severity SEVERITY]] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) @@ -15,4 +15,5 @@ Available options: --use-cli-severity If specified, use the provided CLI severity mapping instead of the default one --format Show diagnostics in JSON format --report-system-stats Report system memory usage and other statistics + --severity Minimum severity level to report. Valid values: error, warning, weak_warning, information -h, --help Prints help information diff --git a/crates/elp/src/resources/test/ssr_help.stdout b/crates/elp/src/resources/test/ssr_help.stdout index a90220787a..d51bdf1627 100644 --- a/crates/elp/src/resources/test/ssr_help.stdout +++ b/crates/elp/src/resources/test/ssr_help.stdout @@ -1,19 +1,26 @@ -Usage: [--project PROJECT] [--module MODULE] [--app APP] [--file FILE] [--rebar] [--as PROFILE] [--include-generated] [--include-tests] [[--format FORMAT]] [[--macros STRATEGY]] [--parens] [--report-system-stats] ... +Usage: [--project PROJECT] [--module MODULE] [--app APP] [--file FILE] [--rebar] [--as PROFILE] [--include-generated] [--include-tests] [[--format FORMAT]] [[--macros STRATEGY]] [--parens] [--dump-config] [--show-source] [-B NUM] [-A NUM] [-C NUM] [--group-separator SEP] [--no-group-separator] [--report-system-stats] ... Available positional items: SSR specs to use Available options: - --project Path to directory with project, or to a JSON file (defaults to `.`) - --module Parse a single module from the project, not the entire project. - --app Parse a single application from the project, not the entire project. - --file Parse a single file from the project, not the entire project. This can be an include file or escript, etc. - --rebar Run with rebar - --as Rebar3 profile to pickup (default is test) - --include-generated Also generate diagnostics for generated files - --include-tests Also generate diagnostics for test files - --format Show diagnostics in JSON format - --macros Macro expansion strategy: expand | no-expand | visible-expand (default expand) - --parens Explicitly match parentheses. If omitted, they are ignored. - --report-system-stats Report system memory usage and other statistics - -h, --help Prints help information + --project Path to directory with project, or to a JSON file (defaults to `.`) + --module Parse a single module from the project, not the entire project. + --app Parse a single application from the project, not the entire project. + --file Parse a single file from the project, not the entire project. This can be an include file or escript, etc. + --rebar Run with rebar + --as Rebar3 profile to pickup (default is test) + --include-generated Also generate diagnostics for generated files + --include-tests Also generate diagnostics for test files + --format Show diagnostics in JSON format + --macros Macro expansion strategy: expand | no-expand | visible-expand (default expand) + --parens Explicitly match parentheses. If omitted, they are ignored. + --dump-config Dump a configuration snippet that can be put in .elp_lint.toml to match the given SSR patterns + --show-source Show source code context for matches + -B, --before-context Print NUM lines of leading context, enables --show-source + -A, --after-context Print NUM lines of trailing context, enables --show-source + -C, --context Print NUM lines of output context, enables --show-source + --group-separator Print SEP on line between matches with context, enables --show-source + --no-group-separator Do not print separator for matches with context, enables --show-source + --report-system-stats Report system memory usage and other statistics + -h, --help Prints help information diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty b/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty index 08361b2a89..1d868bbbc6 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/test/app_a_SUITE.erl:18:5 │ @@ -179,14 +153,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -204,12 +170,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ @@ -232,6 +192,14 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_b/src/app_b.erl:16:5 │ @@ -240,4 +208,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -20 ERRORS +21 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl index 536452ef3a..b311705d68 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl @@ -2,9 +2,9 @@ {"path":"app_a/src/app_a.erl","line":14,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":18,"char":13,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":56,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: redundant_fixme","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nredundant fixme\n```\n\n> [docs on `redundant_fixme`](https://fb.me/eqwalizer_errors#redundant_fixme)","docPath":null} -{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n\n------------------------------ Detailed message ------------------------------\n\nkeys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_SUITE.erl","line":18,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"app_a_test_helpers:fail()","replacement":null,"description":"```lang=error,counterexample\n`app_a_test_helpers:fail()`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_errors_generated.erl","line":8,"char":10,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'foo'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":576,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"flatmap(thing_to_list/1, List)","replacement":null,"description":"```lang=error,counterexample\n`flatmap(thing_to_list/1, List)`.\n\nExpression has type: [term()]\nContext expected type: string()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} @@ -12,9 +12,10 @@ {"path":"app_a/src/app_a_lists.erl","line":595,"char":29,"code":"ELP","severity":"error","name":"eqWAlizer: recursive_constraint","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nRecursive constraint: DeepList\n```\n\n> [docs on `recursive_constraint`](https://fb.me/eqwalizer_errors#recursive_constraint)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":613,"char":29,"code":"ELP","severity":"error","name":"eqWAlizer: recursive_constraint","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nRecursive constraint: DeepList\n```\n\n> [docs on `recursive_constraint`](https://fb.me/eqwalizer_errors#recursive_constraint)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":1114,"char":36,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"H3","replacement":null,"description":"```lang=error,counterexample\n`H3`.\n\nExpression has type: term()\nContext expected type: [term()]\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a_lists.erl","line":1305,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"filtermap(F, L)","replacement":null,"description":"```lang=error,counterexample\n`filtermap(F, L)`.\n\nExpression has type: [term()]\nContext expected type: [T | X]\n```\n```\nBecause in the expression's type:\n [\n Here the type is: term()\n Context expects type: T | X\n No candidate matches in the expected union.\n ]\n\n------------------------------ Detailed message ------------------------------\n\n [term()] is not compatible with [T | X]\n because\n term() is not compatible with T | X\n because\n term() is not compatible with T\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a_lists.erl","line":1305,"char":15,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"F","replacement":null,"description":"```lang=error,counterexample\n`F`.\n\nExpression has type: fun((T) -> boolean() | {'true', X})\nContext expected type: fun((term()) -> boolean() | {'true', term()})\n```\n```\nBecause in the expression's type:\n fun((\n Here the type is: term()\n Context expects type: T\n ) -> boolean() | {'true', X})\n\n------------------------------ Detailed message ------------------------------\n\n fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()})\n because\n term() is not compatible with T\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a_lists.erl","line":1305,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"filtermap(F, L)","replacement":null,"description":"```lang=error,counterexample\n`filtermap(F, L)`.\n\nExpression has type: [term()]\nContext expected type: [T | X]\n```\n```\nBecause in the expression's type:\n [\n Here the type is: term()\n Context expects type: T | X\n No candidate matches in the expected union.\n ]\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a_lists.erl","line":1305,"char":15,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"F","replacement":null,"description":"```lang=error,counterexample\n`F`.\n\nExpression has type: fun((T) -> boolean() | {'true', X})\nContext expected type: fun((term()) -> boolean() | {'true', term()})\n```\n```\nBecause in the expression's type:\n fun((\n Here the type is: term()\n Context expects type: T\n ) -> boolean() | {'true', X})\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":22,"char":1,"code":"ELP","severity":"error","name":"eqWAlizer: type_alias_is_non_productive","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nrecursive type invalid/0 is not productive\n```\n\n> [docs on `type_alias_is_non_productive`](https://fb.me/eqwalizer_errors#type_alias_is_non_productive)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":31,"char":9,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_test_helpers.erl","line":6,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'error'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/test/app_a_test_helpers_not_opted_in.erl","line":5,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_b/src/app_b.erl","line":16,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"L","replacement":null,"description":"```lang=error,counterexample\n`L`.\n\nExpression has type: [T]\nContext expected type: T\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty index 08361b2a89..1d868bbbc6 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/test/app_a_SUITE.erl:18:5 │ @@ -179,14 +153,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -204,12 +170,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ @@ -232,6 +192,14 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_b/src/app_b.erl:16:5 │ @@ -240,4 +208,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -20 ERRORS +21 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl index 536452ef3a..b311705d68 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl @@ -2,9 +2,9 @@ {"path":"app_a/src/app_a.erl","line":14,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":18,"char":13,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":56,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: redundant_fixme","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nredundant fixme\n```\n\n> [docs on `redundant_fixme`](https://fb.me/eqwalizer_errors#redundant_fixme)","docPath":null} -{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n\n------------------------------ Detailed message ------------------------------\n\nkeys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_SUITE.erl","line":18,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"app_a_test_helpers:fail()","replacement":null,"description":"```lang=error,counterexample\n`app_a_test_helpers:fail()`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_errors_generated.erl","line":8,"char":10,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'foo'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":576,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"flatmap(thing_to_list/1, List)","replacement":null,"description":"```lang=error,counterexample\n`flatmap(thing_to_list/1, List)`.\n\nExpression has type: [term()]\nContext expected type: string()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} @@ -12,9 +12,10 @@ {"path":"app_a/src/app_a_lists.erl","line":595,"char":29,"code":"ELP","severity":"error","name":"eqWAlizer: recursive_constraint","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nRecursive constraint: DeepList\n```\n\n> [docs on `recursive_constraint`](https://fb.me/eqwalizer_errors#recursive_constraint)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":613,"char":29,"code":"ELP","severity":"error","name":"eqWAlizer: recursive_constraint","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nRecursive constraint: DeepList\n```\n\n> [docs on `recursive_constraint`](https://fb.me/eqwalizer_errors#recursive_constraint)","docPath":null} {"path":"app_a/src/app_a_lists.erl","line":1114,"char":36,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"H3","replacement":null,"description":"```lang=error,counterexample\n`H3`.\n\nExpression has type: term()\nContext expected type: [term()]\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a_lists.erl","line":1305,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"filtermap(F, L)","replacement":null,"description":"```lang=error,counterexample\n`filtermap(F, L)`.\n\nExpression has type: [term()]\nContext expected type: [T | X]\n```\n```\nBecause in the expression's type:\n [\n Here the type is: term()\n Context expects type: T | X\n No candidate matches in the expected union.\n ]\n\n------------------------------ Detailed message ------------------------------\n\n [term()] is not compatible with [T | X]\n because\n term() is not compatible with T | X\n because\n term() is not compatible with T\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a_lists.erl","line":1305,"char":15,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"F","replacement":null,"description":"```lang=error,counterexample\n`F`.\n\nExpression has type: fun((T) -> boolean() | {'true', X})\nContext expected type: fun((term()) -> boolean() | {'true', term()})\n```\n```\nBecause in the expression's type:\n fun((\n Here the type is: term()\n Context expects type: T\n ) -> boolean() | {'true', X})\n\n------------------------------ Detailed message ------------------------------\n\n fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()})\n because\n term() is not compatible with T\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a_lists.erl","line":1305,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"filtermap(F, L)","replacement":null,"description":"```lang=error,counterexample\n`filtermap(F, L)`.\n\nExpression has type: [term()]\nContext expected type: [T | X]\n```\n```\nBecause in the expression's type:\n [\n Here the type is: term()\n Context expects type: T | X\n No candidate matches in the expected union.\n ]\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a_lists.erl","line":1305,"char":15,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"F","replacement":null,"description":"```lang=error,counterexample\n`F`.\n\nExpression has type: fun((T) -> boolean() | {'true', X})\nContext expected type: fun((term()) -> boolean() | {'true', term()})\n```\n```\nBecause in the expression's type:\n fun((\n Here the type is: term()\n Context expects type: T\n ) -> boolean() | {'true', X})\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":22,"char":1,"code":"ELP","severity":"error","name":"eqWAlizer: type_alias_is_non_productive","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nrecursive type invalid/0 is not productive\n```\n\n> [docs on `type_alias_is_non_productive`](https://fb.me/eqwalizer_errors#type_alias_is_non_productive)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":31,"char":9,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_test_helpers.erl","line":6,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'error'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/test/app_a_test_helpers_not_opted_in.erl","line":5,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_b/src/app_b.erl","line":16,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"L","replacement":null,"description":"```lang=error,counterexample\n`L`.\n\nExpression has type: [T]\nContext expected type: T\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_a.jsonl b/crates/elp/src/resources/test/standard/eqwalize_app_a.jsonl index f56ea404dc..943c4a231d 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_a.jsonl +++ b/crates/elp/src/resources/test/standard/eqwalize_app_a.jsonl @@ -2,6 +2,6 @@ {"path":"app_a/src/app_a.erl","line":14,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":18,"char":13,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/src/app_a.erl","line":56,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: redundant_fixme","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nredundant fixme\n```\n\n> [docs on `redundant_fixme`](https://fb.me/eqwalizer_errors#redundant_fixme)","docPath":null} -{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n\n------------------------------ Detailed message ------------------------------\n\nkeys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} -{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":78,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n```\n```\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":102,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/src/app_a.erl","line":125,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"X","replacement":null,"description":"```lang=error,counterexample\n`X`.\n\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n```\n```\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n```\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty index 3cfdbd0dc2..f5f3065414 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,15 +85,4 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - 7 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_a_lists_fast.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_a_lists_fast.pretty index f61a47edfe..8c93bd01fe 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_a_lists_fast.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_a_lists_fast.pretty @@ -50,14 +50,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -75,10 +67,4 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - 7 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty index 472665ffe8..482195c7ab 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_errors_generated.erl:8:10 │ @@ -171,14 +145,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -196,12 +162,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen.pretty index 472665ffe8..482195c7ab 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_errors_generated.erl:8:10 │ @@ -171,14 +145,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -196,12 +162,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty index 6782313a4a..043aa00af0 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/test/app_a_SUITE.erl:18:5 │ @@ -179,14 +153,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -204,12 +170,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ @@ -232,4 +192,12 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' -19 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +20 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty index 6782313a4a..043aa00af0 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/test/app_a_SUITE.erl:18:5 │ @@ -179,14 +153,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -204,12 +170,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ @@ -232,4 +192,12 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' -19 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +20 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty b/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty index 472665ffe8..482195c7ab 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty @@ -44,10 +44,6 @@ Because in the expression's type: Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...} The type of the expression is missing the following required keys: k_req3, k_req2, k_req1. ------------------------------- Detailed message ------------------------------ - -keys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:102:5 │ @@ -68,17 +64,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a.erl:125:5 │ @@ -100,17 +85,6 @@ Because in the expression's type: , ... } , ... } ------------------------------- Detailed message ------------------------------ - - id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - at key `b`: - #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}} - because - #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})} - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_errors_generated.erl:8:10 │ @@ -171,14 +145,6 @@ Because in the expression's type: No candidate matches in the expected union. ] ------------------------------- Detailed message ------------------------------ - - [term()] is not compatible with [T | X] - because - term() is not compatible with T | X - because - term() is not compatible with T - error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -196,12 +162,6 @@ Because in the expression's type: Context expects type: T ) -> boolean() | {'true', X}) ------------------------------- Detailed message ------------------------------ - - fun((T) -> boolean() | {'true', X}) is not compatible with fun((term()) -> boolean() | {'true', term()}) - because - term() is not compatible with T - error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) ┌─ app_a/src/app_a_mod2.erl:22:1 │ diff --git a/crates/elp/src/resources/test/xref/unavailable_type.stdout b/crates/elp/src/resources/test/xref/unavailable_type.stdout new file mode 100644 index 0000000000..688be17de3 --- /dev/null +++ b/crates/elp/src/resources/test/xref/unavailable_type.stdout @@ -0,0 +1,5 @@ +Reporting all diagnostics codes +module specified: unavailable_type +Diagnostics reported: +app_a/src/unavailable_type.erl:10:43-10:58::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'root//xref:app_a'). +app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'root//xref:app_a'). diff --git a/crates/elp/src/server.rs b/crates/elp/src/server.rs index 4914cf9f3f..98890640bc 100644 --- a/crates/elp/src/server.rs +++ b/crates/elp/src/server.rs @@ -24,7 +24,7 @@ use anyhow::bail; use capabilities::text_document_symbols_dynamic_registration; use crossbeam_channel::Receiver; use crossbeam_channel::Sender; -use crossbeam_channel::select; +use crossbeam_channel::select_biased; use dispatch::NotificationDispatcher; use elp_eqwalizer::ast::Pos; use elp_eqwalizer::types::Type; @@ -434,27 +434,28 @@ impl Server { } fn next_event(&self) -> Option { - select! { - recv(self.connection.receiver) -> msg => { - msg.ok().map(Event::Lsp) - } - - recv(self.vfs_loader.receiver) -> msg => { - Some(Event::Vfs(msg.unwrap())) - } - - recv(self.task_pool.receiver) -> msg => { - Some(Event::Task(msg.unwrap())) - } - + // Select the next event, in order of priority. + // If multiple operations are ready at the same time, the + // operation nearest to the front of the list is always selected. + select_biased! { + // We want to send progress messages as soon as possible recv(self.progress.receiver()) -> msg => { Some(Event::Task(Task::Progress(msg.unwrap()))) } + // Ditto telemetry recv(telemetry::receiver()) -> msg => { Some(Event::Telemetry(msg.unwrap())) } + recv(self.connection.receiver) -> msg => { + msg.ok().map(Event::Lsp) + } + + recv(self.task_pool.receiver) -> msg => { + Some(Event::Task(msg.unwrap())) + } + recv (self.project_pool.receiver) -> msg => { Some(Event::Task(msg.unwrap())) } @@ -467,6 +468,16 @@ impl Server { Some(Event::Task(msg.unwrap())) } + // We put vfs loader events last, they run with all + // available threads, we do not want to starve the + // other sources. + // Note: when we do process one, we will coalesce + // all pending vfs loader events into a single main loop turn + recv(self.vfs_loader.receiver) -> msg => { + Some(Event::Vfs(msg.unwrap())) + } + + } } @@ -507,6 +518,7 @@ impl Server { a_file_per_project = FxHashSet::default(); } spinner.end(); + log::info!(target: FILE_WATCH_LOGGER_NAME, "Project reloading complete"); self.reload_manager .lock() .set_reload_done(a_file_per_project); @@ -558,6 +570,7 @@ impl Server { let to_reload = self.reload_manager.lock().query_changed_files(); if let Some(to_reload) = to_reload { + log::info!(target: FILE_WATCH_LOGGER_NAME, "Asking for project reload"); let query_config = self.reload_manager.lock().set_reload_active(); self.reload_project(to_reload, query_config); } @@ -622,7 +635,16 @@ impl Server { .diagnostics .diagnostics_for(*file_id) .iter() - .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) + .map(|d| { + let vfs = self.vfs.clone(); + ide_to_lsp_diagnostic(&line_index, d, |related_file_id| { + // Get the line index and URL for the related file + let url = file_id_to_url(&vfs.read(), related_file_id); + let snapshot = self.snapshot(); + let line_index = snapshot.analysis.line_index(related_file_id).ok()?; + Some(((*line_index).clone(), url)) + }) + }) .collect(); self.send_notification::( @@ -826,11 +848,20 @@ impl Server { Arc::make_mut(&mut this.diagnostics) .clear(file_id); if let Ok(line_index) = analysis.line_index(file_id) { + let vfs = this.vfs.clone(); diagnostics = this .diagnostics .diagnostics_for(file_id) .iter() - .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) + .map(|d| { + let snapshot = this.snapshot(); + ide_to_lsp_diagnostic(&line_index, d, |related_file_id| { + // Get the line index and URL for the related file + let url = file_id_to_url(&vfs.read(), related_file_id); + let line_index = snapshot.analysis.line_index(related_file_id).ok()?; + Some(((*line_index).clone(), url)) + }) + }) .collect() } } @@ -1022,8 +1053,10 @@ impl Server { log::info!(target: FILE_WATCH_LOGGER_NAME, "VFS change:{}:{}", &opened, &path); } if !opened { - // This call will add the file to the changed_files, picked - // up in `process_changes`, if it has changed. + // This call will add the file to the changed_files, + // picked up in `process_changes`, only if the new + // content is different from the current, checked via + // a hash. vfs.set_file_contents(path, contents); } } @@ -1396,9 +1429,11 @@ impl Server { fn switch_workspaces(&mut self, spinner: &Spinner, new_projects: Vec) -> Result<()> { if new_projects.is_empty() { log::info!("nothing new, not switching workspaces"); + log::info!(target: FILE_WATCH_LOGGER_NAME, "nothing new, not switching workspaces"); return Ok(()); } log::info!("will switch workspaces"); + log::info!(target: FILE_WATCH_LOGGER_NAME, "will switch workspaces"); let mut projects: Vec = self.projects.iter().cloned().collect(); for project in new_projects { @@ -1429,6 +1464,16 @@ impl Server { self.file_set_config = folders.file_set_config; + let watch_count = folders.watch.len(); + let buck_query_config = self.reload_manager.lock().get_query_config(); + let data = serde_json::json!({ + "app_count": project_apps.all_apps.len(), + "project_count": projects.len(), + "watch_count": watch_count, + "query_config": buck_query_config.to_string(), + }); + telemetry::send("project_size".to_string(), data); + let register_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers: folders.watch, }; @@ -1803,9 +1848,13 @@ impl Server { } for (err, uri) in errors { if let Some(uri) = uri { + // It is not possible to put line breaks or other formatting in the message + let error_message = format!( + "Look at `Details` in the buck UI for more information, there is likely an invalid BUCK file: {err}" + ); let params = lsp_types::ShowMessageRequestParams { typ: lsp_types::MessageType::ERROR, - message: err, + message: error_message, actions: Some(vec![MessageActionItem { title: "Open Buck UI".to_string(), properties: HashMap::from_iter(vec![( @@ -1874,6 +1923,7 @@ impl Server { if !self.reload_manager.lock().ok_to_switch_workspace() { // There are other changed files, abort this reload, to // allow the next one. + log::info!(target: FILE_WATCH_LOGGER_NAME, "Not switching workspaces, more changed config files"); return Ok(false); } spinner.report("Switching to loaded projects".to_string()); @@ -2023,7 +2073,7 @@ impl Server { }; for (_, _, file_id) in module_index.iter_own() { - match snapshot.analysis.should_eqwalize(file_id, false) { + match snapshot.analysis.should_eqwalize(file_id) { Ok(true) => { files.push(file_id); } diff --git a/crates/elp/src/server/setup.rs b/crates/elp/src/server/setup.rs index 6dade5c677..4b8615e07c 100644 --- a/crates/elp/src/server/setup.rs +++ b/crates/elp/src/server/setup.rs @@ -33,6 +33,7 @@ use super::FILE_WATCH_LOGGER_NAME; use super::logger::LspLogger; use crate::config::Config; use crate::from_json; +// @fb-only: use crate::meta_only::get_log_dir; use crate::server::Handle; use crate::server::LOGGER_NAME; use crate::server::Server; @@ -125,8 +126,9 @@ impl ServerSetup { // Set up a logger for tracking down why we are seeing stale // results when branches are switched, as per T218973130 - let log_dir = "/tmp/elp"; - let _ = fs::create_dir_all(log_dir); + // @fb-only: let log_dir = get_log_dir(); + let log_dir = format!("{}/elp", std::env::temp_dir().display()); // @oss-only + let _ = fs::create_dir_all(&log_dir); let log_file = format!( "{}/elp_file_watch_reports-{}.log", log_dir, diff --git a/crates/elp/src/snapshot.rs b/crates/elp/src/snapshot.rs index e79251f73a..69ea6b97e0 100644 --- a/crates/elp/src/snapshot.rs +++ b/crates/elp/src/snapshot.rs @@ -36,9 +36,11 @@ use parking_lot::Mutex; use parking_lot::RwLock; use serde::Deserialize; use serde::Serialize; +use vfs::AnchoredPathBuf; use crate::config::Config; use crate::convert; +use crate::convert::url_from_abs_path; use crate::line_endings::LineEndings; use crate::mem_docs::MemDocs; use crate::server::EqwalizerTypes; @@ -186,6 +188,14 @@ impl Snapshot { self.line_ending_map.read()[&id] } + pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Option { + let mut base = self.vfs.read().file_path(path.anchor).clone(); + base.pop(); + let path = base.join(&path.path)?; + let path = path.as_path()?; + Some(url_from_abs_path(path)) + } + pub fn update_cache_for_file( &self, file_id: FileId, @@ -193,7 +203,7 @@ impl Snapshot { ) -> Result<()> { let _ = self.analysis.def_map(file_id)?; if optimize_for_eqwalizer { - let should_eqwalize = self.analysis.should_eqwalize(file_id, false)?; + let should_eqwalize = self.analysis.should_eqwalize(file_id)?; if should_eqwalize { let _ = self.analysis.module_ast(file_id)?; } @@ -242,7 +252,7 @@ impl Snapshot { let file_ids: Vec = module_index .iter_own() .filter_map(|(_, _, file_id)| { - if let Ok(true) = self.analysis.should_eqwalize(file_id, false) { + if let Ok(true) = self.analysis.should_eqwalize(file_id) { Some(file_id) } else { None diff --git a/crates/elp/src/to_proto.rs b/crates/elp/src/to_proto.rs index f9263cfcab..3e5b1fd1e1 100644 --- a/crates/elp/src/to_proto.rs +++ b/crates/elp/src/to_proto.rs @@ -10,6 +10,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. +use std::mem; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -47,10 +48,11 @@ use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::FilePosition; use elp_ide::elp_ide_db::elp_base_db::FileRange; use elp_ide::elp_ide_db::rename::RenameError; +use elp_ide::elp_ide_db::source_change::FileSystemEdit; use elp_ide::elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::Indel; +use elp_ide_db::text_edit::TextEdit; use elp_project_model::ProjectBuildData; -use elp_text_edit::Indel; -use elp_text_edit::TextEdit; use lsp_types::CompletionItemTag; use lsp_types::Hover; use lsp_types::HoverContents; @@ -121,9 +123,9 @@ pub(crate) fn optional_versioned_text_document_identifier( pub(crate) fn text_document_edit( snap: &Snapshot, file_id: FileId, + text_document: lsp_types::OptionalVersionedTextDocumentIdentifier, edit: TextEdit, ) -> Result { - let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.analysis.line_index(file_id)?; let line_endings = snap.line_endings(file_id); let edits: Vec> = edit @@ -131,34 +133,131 @@ pub(crate) fn text_document_edit( .map(|it| lsp_types::OneOf::Left(text_edit(&line_index, line_endings, it))) .collect(); - // if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { - // for edit in &mut edits { - // edit.annotation_id = Some(outside_workspace_annotation_id()) - // } - // } Ok(lsp_types::TextDocumentEdit { text_document, edits, }) } +pub(crate) fn text_document_ops( + snap: &Snapshot, + file_system_edit: FileSystemEdit, +) -> Cancellable> { + let mut ops = Vec::new(); + match file_system_edit { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + if let Some(uri) = snap.anchored_path(&dst) { + let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile { + uri: uri.clone(), + options: None, + annotation_id: None, + }); + ops.push(lsp_types::DocumentChangeOperation::Op(create_file)); + if !initial_contents.is_empty() { + let text_document = + lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; + let text_edit = lsp_types::TextEdit { + range: lsp_types::Range::default(), + new_text: initial_contents, + }; + let edit_file = lsp_types::TextDocumentEdit { + text_document, + edits: vec![lsp_types::OneOf::Left(text_edit)], + }; + ops.push(lsp_types::DocumentChangeOperation::Edit(edit_file)); + } + } else { + log::warn!("create file failed: {:?}", dst); + } + } + FileSystemEdit::MoveFile { src, dst } => { + if let Some(new_uri) = snap.anchored_path(&dst) { + let old_uri = snap.file_id_to_url(src); + let rename_file = lsp_types::RenameFile { + old_uri, + new_uri, + options: None, + annotation_id: None, + }; + ops.push(lsp_types::DocumentChangeOperation::Op( + lsp_types::ResourceOp::Rename(rename_file), + )) + } else { + log::warn!("rename file failed: {:?} -> {:?}", src, dst); + } + } + } + Ok(ops) +} + pub(crate) fn workspace_edit( snap: &Snapshot, - source_change: SourceChange, + mut source_change: SourceChange, ) -> Result { - let mut edits: Vec<_> = vec![]; - for (file_id, edit) in source_change.source_file_edits { - // let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?; - let edit = text_document_edit(snap, file_id, edit)?; - edits.push(lsp_types::TextDocumentEdit { - text_document: edit.text_document, - edits: edit.edits.into_iter().collect(), - }); + let mut document_changes: Vec = Vec::new(); + + // This is copying RA's order of operations, first file creates, + // then edits, then file moves. + + // This allows us to apply edits to the file once it has + // moved. Except we have no FileId at that point + for op in &mut source_change.file_system_edits { + if let FileSystemEdit::CreateFile { + dst, + initial_contents, + } = op + { + // replace with a placeholder to avoid cloning the edit + let op = FileSystemEdit::CreateFile { + dst: dst.clone(), + initial_contents: mem::take(initial_contents), + }; + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } } - let document_changes = lsp_types::DocumentChanges::Edits(edits); + + for op in source_change.file_system_edits { + if !matches!(op, FileSystemEdit::CreateFile { .. }) { + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } + } + + for (file_id, edit) in source_change.source_file_edits { + let text_document = optional_versioned_text_document_identifier(snap, file_id); + let edit = text_document_edit(snap, file_id, text_document, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } + + // Edits on renamed files. The LineIndex from the original can be used. + for (file_ref, edit) in source_change.new_file_edits { + if let Some(uri) = snap.anchored_path(&file_ref.clone().into()) { + let version = snap.url_file_version(&uri); + let text_document = lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version }; + let edit = text_document_edit(snap, file_ref.anchor, text_document, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } else { + log::warn!("new file edit failed: {:?}", file_ref); + } + } + let workspace_edit = lsp_types::WorkspaceEdit { changes: None, - document_changes: Some(document_changes), + document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), change_annotations: None, }; Ok(workspace_edit) @@ -182,10 +281,6 @@ pub(crate) fn code_action( ) -> Result { let mut res = lsp_types::CodeAction { title: assist.label.to_string(), - // group: assist - // .group - // .filter(|_| snap.config.code_action_group()) - // .map(|gr| gr.0), kind: Some(code_action_kind(assist.id.1)), edit: None, is_preferred: None, diff --git a/crates/elp/tests/slow-tests/buck_tests.rs b/crates/elp/tests/slow-tests/buck_tests.rs index e76fdb8bad..91f2b234d9 100644 --- a/crates/elp/tests/slow-tests/buck_tests.rs +++ b/crates/elp/tests/slow-tests/buck_tests.rs @@ -31,7 +31,7 @@ mod tests { #[test] #[ignore] fn test_success_case() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let cli = Fake::default(); @@ -65,7 +65,7 @@ mod tests { let ast = analysis.module_ast(file_id).unwrap(); assert_eq!(ast.errors, vec![]); let eq_enabled = analysis - .is_eqwalizer_enabled(file_id, false) + .is_eqwalizer_enabled(file_id) .unwrap_or_else(|_| panic!("Failed to check if eqwalizer enabled for {module}")); assert_eq!(eq_enabled, eqwalizer_enabled); let project_data = analysis.project_data(file_id).unwrap(); @@ -76,7 +76,7 @@ mod tests { #[test] #[ignore] fn test_load_buck_targets() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let (elp_config, buck_config) = diff --git a/crates/elp/tests/slow-tests/main.rs b/crates/elp/tests/slow-tests/main.rs index 98455e8850..e256ce2c62 100644 --- a/crates/elp/tests/slow-tests/main.rs +++ b/crates/elp/tests/slow-tests/main.rs @@ -36,7 +36,7 @@ use crate::support::diagnostic_project; fn test_run_mock_lsp() { if cfg!(feature = "buck") { let workspace_root = AbsPathBuf::assert( - Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/end_to_end"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/end_to_end"), ); // Sanity check @@ -70,7 +70,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -99,7 +99,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -128,7 +128,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -157,7 +157,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -175,7 +175,7 @@ fn test_run_mock_lsp() { fn test_e2e_eqwalizer_module() { if cfg!(feature = "buck") { let workspace_root = AbsPathBuf::assert( - Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/standard"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/standard"), ); // Sanity check @@ -268,7 +268,7 @@ fn test_e2e_eqwalizer_module() { "codeDescription": { "href": "https://fb.me/eqwalizer_errors#incompatible_types" }, - "message": "`X`.\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n\n------------------------------ Detailed message ------------------------------\n\nkeys `k_req1`, `k_req2`, `k_req3` are declared as required in the latter but not in the former\n See https://fb.me/eqwalizer_errors#incompatible_types", + "message": "`X`.\nExpression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()}\nContext expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()}\n\nBecause in the expression's type:\n Here the type is: #{k_req2 => ..., k_req1 => ..., ...}\n Context expects type: #{k_req3 := ..., k_req2 := ..., k_req1 := ..., ...}\n The type of the expression is missing the following required keys: k_req3, k_req2, k_req1.\n See https://fb.me/eqwalizer_errors#incompatible_types", "range": { "end": { "character": 5, @@ -287,7 +287,7 @@ fn test_e2e_eqwalizer_module() { "codeDescription": { "href": "https://fb.me/eqwalizer_errors#incompatible_types" }, - "message": "`X`.\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d => atom()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d => atom()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d => atom()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n See https://fb.me/eqwalizer_errors#incompatible_types", + "message": "`X`.\nExpression has type: id(#{a := 'va', b := #{c := #{d => atom()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n Here the type is: #{d => ..., ...}\n Context expects type: #{d := ..., e := ..., ...}\n The type of the expression is missing the following required keys: d, e.\n , ... }\n , ... }\n See https://fb.me/eqwalizer_errors#incompatible_types", "range": { "end": { "character": 5, @@ -306,7 +306,7 @@ fn test_e2e_eqwalizer_module() { "codeDescription": { "href": "https://fb.me/eqwalizer_errors#incompatible_types" }, - "message": "`X`.\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n\n------------------------------ Detailed message ------------------------------\n\n id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}}) is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n at key `b`:\n #{a := 'va', b := #{c := #{d := pid(), e := pid()}}} is not compatible with #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n because\n #{c := #{d := pid(), e := pid()}} is not compatible with #{c := id(#{d := atom(), e := atom()})}\n See https://fb.me/eqwalizer_errors#incompatible_types", + "message": "`X`.\nExpression has type: id(#{a := 'va', b := #{c := #{d := pid(), e := pid()}}})\nContext expected type: #{a := 'va', b := #{c := id(#{d := atom(), e := atom()})}}\n\nBecause in the expression's type:\n #{ b =>\n #{ c =>\n #{ d =>\n Here the type is: pid()\n Context expects type: atom()\n , ... }\n , ... }\n , ... }\n See https://fb.me/eqwalizer_errors#incompatible_types", "range": { "end": { "character": 5, @@ -321,7 +321,7 @@ fn test_e2e_eqwalizer_module() { "source": "eqWAlizer" } ], - "uri": "file:///[..]/test_projects/standard/app_a/src/app_a.erl", + "uri": "file:///[..]/test/test_projects/standard/app_a/src/app_a.erl", "version": 0 }"#]], ); @@ -334,7 +334,7 @@ fn test_e2e_eqwalizer_module() { // #[test] // fn test_e2e_eqwalizer_header() { // let workspace_root = -// AbsPathBuf::assert(Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/standard")); +// AbsPathBuf::assert(Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/standard")); // // Sanity check // assert!(std::fs::metadata(&workspace_root).is_ok()); diff --git a/crates/elp/tests/slow-tests/support.rs b/crates/elp/tests/slow-tests/support.rs index bf482a7816..2c794f6246 100644 --- a/crates/elp/tests/slow-tests/support.rs +++ b/crates/elp/tests/slow-tests/support.rs @@ -408,7 +408,7 @@ impl Drop for TestServer { struct Timeout; fn recv_timeout(receiver: &Receiver) -> Result, Timeout> { - let timeout = Duration::from_secs(60); + let timeout = Duration::from_mins(1); select! { recv(receiver) -> msg => Ok(msg.ok()), recv(after(timeout)) -> _ => Err(Timeout), diff --git a/crates/eqwalizer/src/ipc.rs b/crates/eqwalizer/src/ipc.rs index 30b1dae582..4d10d663ec 100644 --- a/crates/eqwalizer/src/ipc.rs +++ b/crates/eqwalizer/src/ipc.rs @@ -159,8 +159,8 @@ pub struct IpcHandle { _child_for_drop: JodChild, } -const WRITE_TIMEOUT: Duration = Duration::from_secs(240); -const READ_TIMEOUT: Duration = Duration::from_secs(240); +const WRITE_TIMEOUT: Duration = Duration::from_mins(4); +const READ_TIMEOUT: Duration = Duration::from_mins(4); impl IpcHandle { fn spawn_cmd(cmd: &mut Command) -> Result { diff --git a/crates/eqwalizer/src/lib.rs b/crates/eqwalizer/src/lib.rs index e6f49460a1..54333570fd 100644 --- a/crates/eqwalizer/src/lib.rs +++ b/crates/eqwalizer/src/lib.rs @@ -244,7 +244,9 @@ impl EqwalizerExe { impl Eqwalizer { fn cmd(&self) -> Command { let mut exe = EQWALIZER_EXE.lock(); - if !exe.cmd.is_file() { + // This check is for the temporary directory being removed by an external process. + // As such, it is only applicable if ther *is* a temporary directory. + if exe._file.is_some() && !exe.cmd.is_file() { log::error!("Eqwalizer exe has disappeared, recreating"); // We have a problem with the eqwalizer exe file, recreate it *exe = EqwalizerExe::ensure_exe(); diff --git a/crates/erlang_service/src/lib.rs b/crates/erlang_service/src/lib.rs index 16020d78f6..072f439db3 100644 --- a/crates/erlang_service/src/lib.rs +++ b/crates/erlang_service/src/lib.rs @@ -59,12 +59,13 @@ lazy_static! { pub static ref ESCRIPT: RwLock = RwLock::new("escript".to_string()); } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum DiagnosticLocation { Normal(TextRange), Included { - directive_location: TextRange, // Location of include directive in the file compiled - error_location: TextRange, // Location of the error in the included file + file_attribute_location: TextRange, // Location of file_attribute for included file in the file compiled abstract forms + error_path: PathBuf, // Path of the file containing the error. It could be via a deeply nested include + error_location: TextRange, // Location of the error in the included file }, } @@ -735,7 +736,8 @@ fn decode_errors(buf: &[u8]) -> Result> { eetf::Term::decode(buf)? .as_match(pattern::VarList(( - Str, + Str, // path + Str, // error path pattern::Or(( (pattern::U32, pattern::U32), // Normal location pattern::FixList(((pattern::U32, pattern::U32), (pattern::U32, pattern::U32))), // Location in include file @@ -747,7 +749,7 @@ fn decode_errors(buf: &[u8]) -> Result> { .map_err(|err| anyhow!("Failed to decode errors: {:?}", err)) .map(|res| { res.into_iter() - .map(|(path, position, msg, code)| ParseError { + .map(|(path, error_path, position, msg, code)| ParseError { path: path.into(), location: match position { pattern::Union3::A((a, b)) => Some(DiagnosticLocation::Normal( @@ -755,7 +757,8 @@ fn decode_errors(buf: &[u8]) -> Result> { )), pattern::Union3::B(((a, b), (c, d))) => { Some(DiagnosticLocation::Included { - directive_location: safe_textrange(a.into(), b.into()), + file_attribute_location: safe_textrange(a.into(), b.into()), + error_path: error_path.into(), error_location: safe_textrange(c.into(), d.into()), }) } diff --git a/crates/hir/src/body.rs b/crates/hir/src/body.rs index 2f4968b7ba..9ebac17c66 100644 --- a/crates/hir/src/body.rs +++ b/crates/hir/src/body.rs @@ -43,6 +43,7 @@ use crate::FormList; use crate::FunctionClause; use crate::FunctionClauseId; use crate::InFile; +use crate::IncludeAttributeId; use crate::Literal; use crate::Name; use crate::NameArity; @@ -1155,6 +1156,24 @@ pub type ExprSource = InFileAstPtr; pub type MacroSource = InFileAstPtr; +/// Lightweight diagnostic for body lowering +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BodyDiagnostic { + /// Macro failed to expand/resolve + UnresolvedMacro(MacroSource), + /// Include failed to resolve + UnresolvedInclude(InFile), +} + +impl BodyDiagnostic { + pub fn file_id(&self) -> FileId { + match self { + BodyDiagnostic::UnresolvedMacro(src) => src.file_id(), + BodyDiagnostic::UnresolvedInclude(include) => include.file_id, + } + } +} + #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct InFileAstPtr(InFile>) where @@ -1231,6 +1250,7 @@ pub struct BodySourceMap { term_map: FxHashMap, term_map_back: ArenaMap, macro_map: FxHashMap, + diagnostics: Vec, } impl BodySourceMap { @@ -1292,6 +1312,10 @@ impl BodySourceMap { .get(&InFileAstPtr::from_infile(call)) .copied() } + + pub fn diagnostics(&self) -> &[BodyDiagnostic] { + &self.diagnostics + } } #[cfg(test)] diff --git a/crates/hir/src/body/lower.rs b/crates/hir/src/body/lower.rs index 85c3378930..4abf0737b2 100644 --- a/crates/hir/src/body/lower.rs +++ b/crates/hir/src/body/lower.rs @@ -38,6 +38,7 @@ use crate::AttributeBody; use crate::BasedInteger; use crate::BinarySeg; use crate::Body; +use crate::BodyDiagnostic; use crate::BodySourceMap; use crate::CRClause; use crate::CallTarget; @@ -116,6 +117,10 @@ pub struct Ctx<'a> { // This means we need to lower a Var specially when processing a SSR // template, where if it has a prefix of `_@` it is a placeholder. in_ssr: bool, + // Track when we're lowering the RHS of a define preprocessor directive + in_macro_rhs: bool, + // Diagnostics collected during body lowering + diagnostics: Vec, } #[derive(Debug)] @@ -152,6 +157,8 @@ impl<'a> Ctx<'a> { starting_stack_size: 1, macro_source_map: FxHashMap::default(), in_ssr: false, + in_macro_rhs: false, + diagnostics: Vec::new(), } } @@ -216,6 +223,7 @@ impl<'a> Ctx<'a> { assert!(self.body.origin.is_valid()); self.body.shrink_to_fit(); + self.source_map.diagnostics = self.diagnostics; (Arc::new(self.body), self.source_map) } @@ -401,7 +409,9 @@ impl<'a> Ctx<'a> { let replacement = define.replacement(); match replacement { Some(MacroDefReplacement::Expr(expr)) => { + self.in_macro_rhs = true; let expr = self.lower_expr(&expr); + self.in_macro_rhs = false; let (body, source_map) = self.finish(); (DefineBody { body, expr }, source_map) } @@ -812,6 +822,9 @@ impl<'a> Ctx<'a> { ) }) .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + let expansion = self.alloc_pat(Pat::Missing, Some(expr)); let args = call .args() @@ -1169,6 +1182,9 @@ impl<'a> Ctx<'a> { }) .flatten() .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + call.args() .iter() .flat_map(|args| args.args()) @@ -1400,8 +1416,8 @@ impl<'a> Ctx<'a> { let exprs = self.lower_lc_exprs(lc.lc_exprs()); self.alloc_expr(Expr::Comprehension { builder, exprs }, Some(expr)) } - ast::ExprMax::MacroCallExpr(call) => self - .resolve_macro(call, |this, source, replacement| match replacement { + ast::ExprMax::MacroCallExpr(call) => { + self.resolve_macro(call, |this, source, replacement| match replacement { MacroReplacement::BuiltIn(built_in) => { this.lower_built_in_macro(built_in).map(|literal| { let expr_id = this.alloc_expr(Expr::Literal(literal), None); @@ -1464,6 +1480,9 @@ impl<'a> Ctx<'a> { ) }) .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + let expansion = self.alloc_expr(Expr::Missing, Some(expr)); let args = call .args() @@ -1480,7 +1499,8 @@ impl<'a> Ctx<'a> { }, Some(expr), ) - }), + }) + } ast::ExprMax::MacroString(_) => self.alloc_expr(Expr::Missing, Some(expr)), ast::ExprMax::ParenExpr(paren_expr) => { if let Some(inner_expr) = paren_expr.expr() { @@ -1990,6 +2010,9 @@ impl<'a> Ctx<'a> { }) .flatten() .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + call.args() .iter() .flat_map(|args| args.args()) @@ -2170,6 +2193,9 @@ impl<'a> Ctx<'a> { ) }) .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + let expansion = self.alloc_type_expr(TypeExpr::Missing, Some(expr)); let args = call .args() @@ -2536,6 +2562,9 @@ impl<'a> Ctx<'a> { ) }) .unwrap_or_else(|| { + // Record diagnostic for unresolved macro + self.record_unresolved_macro(call); + let expansion = self.alloc_term(Term::Missing, Some(expr)); let args = call .args() @@ -2997,6 +3026,23 @@ impl<'a> Ctx<'a> { self.macro_source_map.insert(name, source); } + fn record_unresolved_macro(&mut self, call: &ast::MacroCallExpr) { + // If we're in a macro RHS and this is ?FUNCTION_NAME or ?FUNCTION_ARITY, skip the diagnostic + if self.in_macro_rhs { + let macro_name = self.macro_call_name(call.name()); + if let MacroCallName::Var(var) = macro_name { + let name_str = var.as_string(self.db.upcast()); + if name_str == "FUNCTION_NAME" || name_str == "FUNCTION_ARITY" { + return; + } + } + } + + let source = InFileAstPtr::new(self.curr_file_id(), AstPtr::new(call).cast().unwrap()); + self.diagnostics + .push(BodyDiagnostic::UnresolvedMacro(source)); + } + fn curr_file_id(&self) -> FileId { self.macro_stack[self.macro_stack_id].file_id } diff --git a/crates/hir/src/body/scope.rs b/crates/hir/src/body/scope.rs index dd1da23e94..4ac4e366d8 100644 --- a/crates/hir/src/body/scope.rs +++ b/crates/hir/src/body/scope.rs @@ -865,7 +865,7 @@ mod tests { f() -> As = [1,2,3], Bs = [4,5,6], - [{X,Y}~ || X <- As && Y <- Bs] + [{X,Y, ~} || X <- As && Y <- Bs] . ", &["Y", "X", "As", "Bs"], diff --git a/crates/hir/src/body/tests.rs b/crates/hir/src/body/tests.rs index c616df812d..eab603a76d 100644 --- a/crates/hir/src/body/tests.rs +++ b/crates/hir/src/body/tests.rs @@ -448,6 +448,7 @@ foo(#record.field) -> #record.field. fn record() { check( r#" +//- expect_parse_errors foo1(#record{field = 1}) -> #record{field = A + B}. foo2(#record{field}) -> #record{field = }. "#, @@ -473,6 +474,7 @@ foo2(#record{field}) -> #record{field = }. fn record_update() { check( r#" +//- expect_parse_errors foo1() -> Expr#record{field = undefined}. foo2() -> Expr#record{field = ok, missing = }. "#, @@ -557,7 +559,7 @@ fn case() { r#" foo() -> case 1 + 2 of - X when X andalso true; X <= 100, X >= 5 -> ok; + X when X andalso true; X =< 100, X >= 5 -> ok; _ -> error end. "#, @@ -566,7 +568,7 @@ foo() -> case (1 + 2) of X when (X andalso true); - (X < 100), + (X =< 100), (X >= 5) -> ok; @@ -836,6 +838,7 @@ foo() -> fn parens() { check( r#" +//- expect_parse_errors foo((ok), ()) -> (ok), (). @@ -995,6 +998,7 @@ foo(fun() -> ok end) -> ok. fn invalid_comprehension() { check( r#" +//- expect_parse_errors foo(<>, [Byte || Byte <- List]]) -> ok. "#, expect![[r#" @@ -1392,6 +1396,7 @@ fn call_type_erlang_bif() { fn record_type() { check( r#" +//- expect_parse_errors -type foo1() :: #record{}. -type foo2(B) :: #record{a :: integer(), b :: B}. -type foo3() :: #record{a ::}. @@ -1529,6 +1534,7 @@ fn record_definition() { fn simple_term() { check( r#" +//- expect_parse_errors -foo(ok). -missing_value(). "#, @@ -2693,6 +2699,7 @@ fn verbatim_binary_sigil_in_type() { // Note: \~ gets replaced by ~ in the fixture parsing check( r#" + //- expect_parse_errors -type foo() :: \~B"ab\"c\"\d"). -type bar() :: "hello"). "#, @@ -2723,6 +2730,7 @@ fn verbatim_binary_sigil_in_term() { fn lowering_with_error_nodes() { check( r#" + //- expect_parse_errors f(1a) -> ok begin 1 end. "#, expect![[r#" @@ -2989,8 +2997,10 @@ fn tree_print_record() { #[test] fn tree_print_attribute() { + // TODO: fix wild attribute parsing, T246546041, to remove expect_parse_errors check_ast( r#" + //- expect_parse_errors -wild(foo, []). -compile({inline, [foo/1]}). -compile({a/a, 1/1}). diff --git a/crates/hir/src/body/tree_print.rs b/crates/hir/src/body/tree_print.rs index 1123c46b75..d6b956690f 100644 --- a/crates/hir/src/body/tree_print.rs +++ b/crates/hir/src/body/tree_print.rs @@ -15,6 +15,8 @@ use std::fmt; use std::fmt::Write as _; use std::str; +use elp_base_db::FileId; + use super::DefineBody; use super::FoldBody; use super::RecordBody; @@ -33,11 +35,15 @@ use crate::ComprehensionExpr; use crate::Define; use crate::Expr; use crate::ExprId; +use crate::FormIdx; use crate::FunType; use crate::FunctionBody; use crate::FunctionClauseBody; +use crate::FunctionDefId; +use crate::InFile; use crate::ListType; use crate::Literal; +use crate::PPDirective; use crate::Pat; use crate::PatId; use crate::Record; @@ -50,6 +56,7 @@ use crate::TermId; use crate::TypeAlias; use crate::TypeExpr; use crate::TypeExprId; +use crate::db::DefDatabase; use crate::db::InternDatabase; use crate::expr::Guards; use crate::expr::MaybeExpr; @@ -332,6 +339,63 @@ pub(crate) fn print_ssr(db: &dyn InternDatabase, body: &SsrBody) -> String { printer.result() } +#[allow(dead_code)] // This is used for debugging +pub fn print_form_list(db: &dyn DefDatabase, file_id: FileId, strategy: Strategy) -> String { + let form_list = db.file_form_list(file_id); + let dbi: &dyn InternDatabase = db; + form_list + .forms() + .iter() + .flat_map(|&form_idx| -> Option { + match form_idx { + FormIdx::FunctionClause(function_id) => { + let body = + db.function_body(InFile::new(file_id, FunctionDefId::new(function_id))); + Some(body.tree_print(dbi, strategy)) + } + FormIdx::TypeAlias(type_alias_id) => { + let type_alias = &form_list[type_alias_id]; + let body = db.type_body(InFile::new(file_id, type_alias_id)); + Some(body.tree_print(dbi, type_alias)) + } + FormIdx::Spec(spec_id) => { + let spec = SpecOrCallback::Spec(form_list[spec_id].clone()); + let body = db.spec_body(InFile::new(file_id, spec_id)); + Some(body.tree_print(dbi, spec)) + } + FormIdx::Callback(callback_id) => { + let spec = SpecOrCallback::Callback(form_list[callback_id].clone()); + let body = db.callback_body(InFile::new(file_id, callback_id)); + Some(body.tree_print(dbi, spec)) + } + FormIdx::Record(record_id) => { + let body = db.record_body(InFile::new(file_id, record_id)); + Some(body.print(dbi, &form_list, record_id)) + } + FormIdx::Attribute(attribute_id) => { + let attribute = AnyAttribute::Attribute(form_list[attribute_id].clone()); + let body = db.attribute_body(InFile::new(file_id, attribute_id)); + Some(body.print(dbi, attribute)) + } + FormIdx::CompileOption(attribute_id) => { + let attribute = AnyAttribute::CompileOption(form_list[attribute_id].clone()); + let body = db.compile_body(InFile::new(file_id, attribute_id)); + Some(body.tree_print(dbi, attribute)) + } + FormIdx::PPDirective(pp) => match form_list[pp] { + PPDirective::Define(define) => { + let body = db.define_body(InFile::new(file_id, define)); + Some(body.tree_print(dbi, &form_list[define])) + } + _ => None, + }, + _ => None, + } + }) + .collect::>() + .join("") +} + struct Printer<'a> { db: &'a dyn InternDatabase, body: &'a FoldBody<'a>, @@ -1523,13 +1587,8 @@ mod tests { use expect_test::Expect; use expect_test::expect; - use crate::AnyAttribute; - use crate::FormIdx; - use crate::FunctionDefId; - use crate::InFile; - use crate::SpecOrCallback; use crate::Strategy; - use crate::db::DefDatabase; + use crate::body::tree_print::print_form_list; use crate::fold::MacroStrategy; use crate::fold::ParenStrategy; use crate::test_db::TestDB; @@ -1546,52 +1605,7 @@ mod tests { #[track_caller] fn check_with_strategy(strategy: Strategy, fixture: &str, expect: Expect) { let (db, file_id) = TestDB::with_single_file(fixture); - let form_list = db.file_form_list(file_id); - let pretty = form_list - .forms() - .iter() - .flat_map(|&form_idx| -> Option { - match form_idx { - FormIdx::FunctionClause(function_id) => { - let body = - db.function_body(InFile::new(file_id, FunctionDefId::new(function_id))); - Some(body.tree_print(&db, strategy)) - } - FormIdx::TypeAlias(type_alias_id) => { - let type_alias = &form_list[type_alias_id]; - let body = db.type_body(InFile::new(file_id, type_alias_id)); - Some(body.tree_print(&db, type_alias)) - } - FormIdx::Spec(spec_id) => { - let spec = SpecOrCallback::Spec(form_list[spec_id].clone()); - let body = db.spec_body(InFile::new(file_id, spec_id)); - Some(body.tree_print(&db, spec)) - } - FormIdx::Callback(callback_id) => { - let spec = SpecOrCallback::Callback(form_list[callback_id].clone()); - let body = db.callback_body(InFile::new(file_id, callback_id)); - Some(body.tree_print(&db, spec)) - } - FormIdx::Record(record_id) => { - let body = db.record_body(InFile::new(file_id, record_id)); - Some(body.print(&db, &form_list, record_id)) - } - FormIdx::Attribute(attribute_id) => { - let attribute = AnyAttribute::Attribute(form_list[attribute_id].clone()); - let body = db.attribute_body(InFile::new(file_id, attribute_id)); - Some(body.print(&db, attribute)) - } - FormIdx::CompileOption(attribute_id) => { - let attribute = - AnyAttribute::CompileOption(form_list[attribute_id].clone()); - let body = db.compile_body(InFile::new(file_id, attribute_id)); - Some(body.tree_print(&db, attribute)) - } - _ => None, - } - }) - .collect::>() - .join(""); + let pretty = print_form_list(&db, file_id, strategy); expect.assert_eq(pretty.trim_start()); } @@ -1999,6 +2013,16 @@ mod tests { foo() -> ?EXPR(2). "#, expect![[r#" + -define(EXPR/1, + Expr<2>:Expr::BinaryOp { + lhs + Expr<0>:Literal(Integer(1)) + rhs + Expr<1>:Expr::Var(X) + op + ArithOp(Add), + } + ). function: foo/0 Clause { pats @@ -2316,7 +2340,7 @@ mod tests { r#" foo() -> case 1 + 2 of - X when X andalso true; X <= 100, X >= 5 -> ok; + X when X andalso true; X =< 100, X >= 5 -> ok; _ -> error end. "#, @@ -2357,7 +2381,7 @@ mod tests { rhs Expr<8>:Literal(Integer(100)) op - CompOp(Ord { ordering: Less, strict: true }), + CompOp(Ord { ordering: Less, strict: false }), }, Expr<12>:Expr::BinaryOp { lhs @@ -3090,6 +3114,7 @@ mod tests { fn type_record() { check( r#" + //- expect_parse_errors -type foo1() :: #record{}. -type foo2(B) :: #record{a :: integer(), b :: B}. -type foo3() :: #record{a ::}. @@ -3420,4 +3445,103 @@ mod tests { "#]], ); } + + #[test] + fn top_level_macro() { + // Note: we currently lower a macro as an Expr only. + // We have special processing in lower_clause_or_macro_body + // to deal with top level macros, at the ast level. + check( + r#" + -define(FOO(X), baz() -> X). + -define(BAR(X), {X}). + ?FOO(42). + foo() -> ?BAR(42). + "#, + expect![[r#" + -define(FOO/1, + Expr<0>:Expr::Missing + ). + + -define(BAR/1, + Expr<1>:Expr::Tuple { + Expr<0>:Expr::Var(X), + } + ). + function: baz/0 + Clause { + pats + guards + exprs + Expr<2>:Literal(Integer(42)), + }. + function: foo/0 + Clause { + pats + guards + exprs + Expr<4>:Expr::MacroCall { + args + Expr<3>:Literal(Integer(42)), + macro_def + Some(InFile { file_id: FileId(0), value: Idx::(1) }) + expansion + Expr<2>:Expr::Tuple { + Expr<1>:Literal(Integer(42)), + } + }, + }. + "#]], + ); + } + + #[test] + fn top_level_forms() { + check( + r#" + //- expect_parse_errors + -module(main). + bug + -compile([export_all]). + -wild('foo'). + -type foo() :: ok. + -spec bar() -> ok. + bar() -> ok. + -callback baz() -> ok. + -record(rec, {f}). + "#, + expect![[r#" + -compile( + Term::List { + exprs + Literal(Atom('export_all')), + tail + } + ). + + -wild(foo). + + -type foo() :: Literal(Atom('ok')). + + -spec bar + () -> + Literal(Atom('ok')). + function: bar/0 + Clause { + pats + guards + exprs + Expr<1>:Literal(Atom('ok')), + }. + + -callback baz + () -> + Literal(Atom('ok')). + + -record(rec, { + f + }). + "#]], + ); + } } diff --git a/crates/hir/src/expr.rs b/crates/hir/src/expr.rs index adf3dea349..114930efe3 100644 --- a/crates/hir/src/expr.rs +++ b/crates/hir/src/expr.rs @@ -581,6 +581,26 @@ impl CallTarget { } } } + + pub fn range(&self, sema: &Semantic, body: &Body) -> Option { + match self { + CallTarget::Local { name } => { + let name_any_id = AnyExprId::TypeExpr(*name); + body.range_for_any(sema, name_any_id) + } + CallTarget::Remote { module, name, .. } => { + let module_any_id = AnyExprId::TypeExpr(*module); + let name_any_id = AnyExprId::TypeExpr(*name); + + let name_range = body.range_for_any(sema, name_any_id)?; + if let Some(module_range) = body.range_for_any(sema, module_any_id) { + module_range.cover(name_range) + } else { + Some(name_range) + } + } + } + } } impl CallTarget { diff --git a/crates/hir/src/fold.rs b/crates/hir/src/fold.rs index 78cd442592..59b12be6fd 100644 --- a/crates/hir/src/fold.rs +++ b/crates/hir/src/fold.rs @@ -339,7 +339,7 @@ pub enum ParentId { #[derive(Debug)] pub struct AnyCallBackCtx<'a> { - pub in_macro: Option, + pub in_macro: Option<(HirIdx, Option>)>, pub parents: &'a Vec, pub item_id: AnyExprId, pub item: AnyExpr, @@ -426,7 +426,7 @@ pub struct FoldCtx<'a, T> { body_origin: BodyOrigin, body: &'a FoldBody<'a>, strategy: Strategy, - macro_stack: Vec, + macro_stack: Vec<(HirIdx, Option>)>, parents: Vec, callback: AnyCallBack<'a, T>, } @@ -594,7 +594,7 @@ impl<'a, T> FoldCtx<'a, T> { .do_fold_pat(pat_id, initial) } - fn in_macro(&self) -> Option { + fn in_macro(&self) -> Option<(HirIdx, Option>)> { self.macro_stack.first().copied() } @@ -752,16 +752,19 @@ impl<'a, T> FoldCtx<'a, T> { crate::Expr::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::Expr(expr_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Expr(expr_id), + }, + *macro_def, + )); let e = self.do_fold_expr(*expansion, acc); self.macro_stack.pop(); e @@ -950,16 +953,19 @@ impl<'a, T> FoldCtx<'a, T> { crate::Pat::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::Pat(pat_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Pat(pat_id), + }, + *macro_def, + )); let e = self.do_fold_pat(*expansion, acc); self.macro_stack.pop(); e @@ -1165,16 +1171,19 @@ impl<'a, T> FoldCtx<'a, T> { TypeExpr::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::TypeExpr(type_expr_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::TypeExpr(type_expr_id), + }, + *macro_def, + )); let e = self.do_fold_type_expr(*expansion, acc); self.macro_stack.pop(); e @@ -2212,7 +2221,9 @@ bar() -> #[test] fn traverse_attribute() { + // TODO: fix wild attribute parsing, T246546041, to remove expect_parse_errors let fixture_str = r#" + //- expect_parse_errors -module(foo). -wild(r1, {f1, f~oo}). "#; diff --git a/crates/hir/src/form_list.rs b/crates/hir/src/form_list.rs index 560befbb4b..a1431d7506 100644 --- a/crates/hir/src/form_list.rs +++ b/crates/hir/src/form_list.rs @@ -188,6 +188,11 @@ impl FormList { self.data.type_aliases.iter() } + /// Returns the -record declarations in the file + pub fn records(&self) -> impl Iterator { + self.data.records.iter() + } + pub fn find_form(&self, form: &ast::Form) -> Option { self.map_back.get(&AstPtr::new(form)).copied() } @@ -650,6 +655,23 @@ impl IncludeAttribute { pub fn range(&self, db: &dyn DefDatabase, file_id: FileId) -> TextRange { self.form_id().get_ast(db, file_id).syntax().text_range() } + + pub fn file_range(&self, db: &dyn DefDatabase, file_id: FileId) -> TextRange { + let form = self.form_id().get_ast(db, file_id); + match form { + ast::Form::PreprocessorDirective(ast::PreprocessorDirective::PpInclude(pp_include)) => { + pp_include + .include_range() + .unwrap_or_else(|| pp_include.text_range()) + } + ast::Form::PreprocessorDirective(ast::PreprocessorDirective::PpIncludeLib( + pp_include_lib, + )) => pp_include_lib + .include_range() + .unwrap_or_else(|| pp_include_lib.text_range()), + _ => form.syntax().text_range(), + } + } } /// -deprecated diff --git a/crates/hir/src/form_list/tests.rs b/crates/hir/src/form_list/tests.rs index 00a5ec4020..79f542fd0d 100644 --- a/crates/hir/src/form_list/tests.rs +++ b/crates/hir/src/form_list/tests.rs @@ -318,6 +318,7 @@ fn export() { fn import() { check( r#" +//- expect_parse_errors -import(, []). -import(foo, []). -import(foo, [foo/1]). diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 11d4b40971..751a2f4907 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -35,6 +35,7 @@ mod test_db; pub use body::AnyAttribute; pub use body::AttributeBody; pub use body::Body; +pub use body::BodyDiagnostic; pub use body::BodyOrigin; pub use body::BodySourceMap; pub use body::DefineBody; @@ -154,7 +155,7 @@ pub use name::MacroName; pub use name::Name; pub use name::NameArity; pub use name::known; -// @fb-only +// @fb-only: pub use name::meta_only; pub use sema::AtomDef; pub use sema::CallDef; pub use sema::DefinitionOrReference; @@ -231,6 +232,10 @@ impl HirIdx { } } + pub fn file_id(&self) -> FileId { + self.body_origin.file_id() + } + /// This function is used to print a representation of the HIR AST /// corresponding to the given `HirIdx`. It is used for debugging /// and testing. diff --git a/crates/hir/src/module_data.rs b/crates/hir/src/module_data.rs index b081cbd539..0420e19bb6 100644 --- a/crates/hir/src/module_data.rs +++ b/crates/hir/src/module_data.rs @@ -615,8 +615,9 @@ impl TypeAliasDef { } pub fn name_range(&self, db: &dyn SourceDatabase) -> Option { - let range = self.source(db).type_name()?.syntax().text_range(); - Some(range) + let type_name = self.source(db).type_name()?; + let name = type_name.name()?; + Some(name.syntax().text_range()) } /// This information is used for completion. diff --git a/crates/hir/src/name.rs b/crates/hir/src/name.rs index 00ce2f89f1..f94f139d38 100644 --- a/crates/hir/src/name.rs +++ b/crates/hir/src/name.rs @@ -10,7 +10,7 @@ //! See [`Name`]. -// @fb-only +// @fb-only: pub mod meta_only; use std::borrow::Cow; use std::collections::HashSet; diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index 78a7db7c3c..68577d2a2e 100644 --- a/crates/hir/src/sema.rs +++ b/crates/hir/src/sema.rs @@ -102,7 +102,7 @@ use crate::resolver::Resolution; use crate::resolver::Resolver; mod find; -// @fb-only +// @fb-only: pub mod meta_only; pub mod to_def; pub struct ModuleIter(Arc); @@ -1006,6 +1006,28 @@ impl Semantic<'_> { // Folds end // ----------------------------------------------------------------- + pub fn bound_vars_by_function( + &self, + file_id: FileId, + ) -> FxHashMap> { + let bound_vars = self.bound_vars_in_pattern_diagnostic(file_id); + let mut bound_vars_by_function: FxHashMap> = + FxHashMap::default(); + bound_vars.iter().for_each(|(function_id, pat_id, _var)| { + bound_vars_by_function + .entry(function_id.value) + .and_modify(|vars| { + vars.insert(*pat_id); + }) + .or_insert_with(|| { + let mut vars = FxHashSet::default(); + vars.insert(*pat_id); + vars + }); + }); + bound_vars_by_function + } + pub fn bound_vars_in_pattern_diagnostic( &self, file_id: FileId, diff --git a/crates/hir/src/sema/to_def.rs b/crates/hir/src/sema/to_def.rs index 5918054fc5..01d17278a2 100644 --- a/crates/hir/src/sema/to_def.rs +++ b/crates/hir/src/sema/to_def.rs @@ -42,7 +42,7 @@ use crate::macro_exp; use crate::macro_exp::BuiltInMacro; use crate::macro_exp::MacroExpCtx; use crate::resolver::Resolver; -// @fb-only +// @fb-only: use crate::sema::meta_only; pub trait ToDef: Clone { type Def; @@ -567,7 +567,7 @@ pub fn resolve_call_target( let fn_name: Name = sema.db.lookup_atom(body[*name].as_atom()?); let mo = None; // @oss-only - // @fb-only + // @fb-only: meta_only::resolve_handle_call_target(sema, arity, file_id, &module_name, &fn_name); if let Some(r) = mo { r } else { @@ -885,12 +885,183 @@ fn add_dynamic_call_patterns(patterns: &mut FxHashMap Self { + Self { + index, + arg_type: ModuleArgType::Atom, + } + } + + /// Creates a pattern where the argument is a list of module atoms. + pub const fn list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::List, + } + } + + /// Creates a pattern where the argument can be either a single atom or a list. + pub const fn atom_or_list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::AtomOrList, + } + } + + /// Returns true if this pattern accepts a single atom. + pub const fn accepts_atom(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::Atom | ModuleArgType::AtomOrList + ) + } + + /// Returns true if this pattern accepts a list of atoms. + pub const fn accepts_list(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::List | ModuleArgType::AtomOrList + ) + } +} + +fn add_module_argument_patterns(patterns: &mut FxHashMap) { + // Each entry follows the format: + // (module, function, arity) -> ModuleArgPattern + // + // Where: + // module: Module name (Some("meck"), Some("application"), etc.) + // function: Function name as string literal (e.g., "new", "get_env") + // arity: Number of arguments this function pattern expects + // ModuleArgPattern: Contains the argument index and the expected type + // + // All indexes are 0-based. + + // meck - mocking library + // meck:new/2 accepts either a single module atom or a list of modules + patterns.insert((Some("meck"), "called", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "called", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "capture", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "capture", 6), ModuleArgPattern::atom(1)); + patterns.insert( + (Some("meck"), "delete", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "delete", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expects", 2), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "history", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "history", 2), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "loop", 4), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 1), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 2), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "num_calls", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "num_calls", 4), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("meck"), "reset", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "sequence", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "unload", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "validate", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "wait", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "wait", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "wait", 6), ModuleArgPattern::atom(1)); + + // code module - module loading and management + // These functions from the Erlang stdlib take module() as their argument + patterns.insert((Some("code"), "load_file", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "ensure_loaded", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "delete", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "soft_purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_loaded", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "get_object_code", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "module_md5", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_sticky", 1), ModuleArgPattern::atom(0)); +} + +// Lazy static initialization for the patterns maps lazy_static! { static ref DYNAMIC_CALL_PATTERNS: FxHashMap = { let mut patterns = FxHashMap::default(); add_dynamic_call_patterns(&mut patterns); - // @fb-only + // @fb-only: meta_only::add_dynamic_call_patterns(&mut patterns); + patterns + }; + static ref MODULE_ARGUMENT_PATTERNS: FxHashMap = { + let mut patterns = FxHashMap::default(); + add_module_argument_patterns(&mut patterns); + // @fb-only: meta_only::add_module_argument_patterns(&mut patterns); + patterns + }; + /// Combined patterns for module argument positions. + /// Merges dynamic call patterns (that have module_arg_index) with simple module argument patterns. + /// Used by rename operations where we only care about the module argument position. + static ref COMBINED_MODULE_ARG_PATTERNS: FxHashMap = { + let mut patterns: FxHashMap = FxHashMap::default(); + // Add module_arg_index from dynamic call patterns (where present) + for (key, pattern) in DYNAMIC_CALL_PATTERNS.iter() { + if let Some(module_idx) = pattern.module_arg_index { + patterns.insert(*key, ModuleArgPattern::atom(module_idx)); + } + } + // Add from simple module argument patterns + for (key, module_arg_pattern) in MODULE_ARGUMENT_PATTERNS.iter() { + patterns.insert(*key, *module_arg_pattern); + } patterns }; } @@ -899,6 +1070,10 @@ fn get_dynamic_call_patterns() -> &'static FxHashMap &'static FxHashMap { + &COMBINED_MODULE_ARG_PATTERNS +} + fn look_for_dynamic_call( sema: &Semantic, file_id: FileId, diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index aba9731379..acba8ef642 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -10,12 +10,11 @@ workspace = true elp_eqwalizer.workspace = true elp_erlang_service.workspace = true elp_ide_assists.workspace = true -elp_ide_completion.workspace = true elp_ide_db.workspace = true +elp_ide_completion.workspace = true elp_ide_ssr.workspace = true elp_project_model.workspace = true elp_syntax.workspace = true -elp_text_edit.workspace = true elp_types_db.workspace = true hir.workspace = true diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 6cbdd3469c..d0ae18c158 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -17,7 +17,7 @@ use elp_syntax::TextRange; use fxhash::FxHashMap; use fxhash::FxHashSet; -// @fb-only +// @fb-only: use crate::meta_only; use crate::runnables::Runnable; use crate::runnables::runnables; @@ -57,11 +57,11 @@ pub struct Link { } #[rustfmt::skip] -// @fb-only +// @fb-only: pub(crate) fn annotations(db: &RootDatabase, file_id: FileId) -> Vec { pub(crate) fn annotations(_db: &RootDatabase, _file_id: FileId) -> Vec { // @oss-only - // @fb-only + // @fb-only: let mut annotations = Vec::default(); let annotations = Vec::default(); // @oss-only - // @fb-only + // @fb-only: meta_only::annotations(db, file_id, &mut annotations); annotations } diff --git a/crates/ide/src/codemod_helpers.rs b/crates/ide/src/codemod_helpers.rs index e06ceafd61..2ecd21e82c 100644 --- a/crates/ide/src/codemod_helpers.rs +++ b/crates/ide/src/codemod_helpers.rs @@ -573,8 +573,8 @@ pub(crate) fn find_call_in_function( }; if let Some(extra) = check_call(context) { // Got one. - let call_expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let call_expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 2078335dc3..eb6bae611b 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; use std::collections::BTreeSet; use std::fmt; +use std::path::Path; use std::sync::Arc; use anyhow::Result; @@ -38,15 +39,18 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileKind; use elp_ide_db::elp_base_db::FileRange; use elp_ide_db::elp_base_db::ProjectId; +use elp_ide_db::elp_base_db::VfsPath; use elp_ide_db::erlang_service::DiagnosticLocation; use elp_ide_db::erlang_service::ParseError; use elp_ide_db::metadata::Kind; use elp_ide_db::metadata::Metadata; use elp_ide_db::metadata::Source; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_ide_ssr::Match; use elp_ide_ssr::SsrSearchScope; use elp_ide_ssr::match_pattern; +use elp_project_model::AppName; use elp_syntax::NodeOrToken; use elp_syntax::Parse; use elp_syntax::SourceFile; @@ -63,11 +67,12 @@ use elp_syntax::ast::edit::IndentLevel; use elp_syntax::ast::edit::start_of_line; use elp_syntax::label::Label; use elp_syntax::ted::Element; -use elp_text_edit::TextEdit; use elp_types_db::TypedSemantic; use fxhash::FxHashMap; use fxhash::FxHashSet; +use hir::FormIdx; use hir::InFile; +use hir::PPDirective; use hir::Semantic; use hir::db::DefDatabase; use hir::fold::MacroStrategy; @@ -92,13 +97,13 @@ mod application_env; mod atoms_exhaustion; mod binary_string_to_sigil; mod boolean_precedence; +mod bound_variable; mod could_be_a_string_literal; mod cross_node_eval; mod debugging_function; mod dependent_header; mod deprecated_function; mod duplicate_module; -mod edoc; mod effect_free_statement; mod equality_check_with_unnecessary_operator; mod eqwalizer_assists; @@ -108,12 +113,14 @@ mod head_mismatch; mod inefficient_enumerate; mod inefficient_flatlength; mod inefficient_last; +mod lists_reverse_append; mod macro_precedence_suprise; mod map_find_to_syntax; mod map_insertion_to_syntax; mod meck; -// @fb-only +// @fb-only: mod meta_only; mod missing_compile_warn_missing_spec; +mod missing_module; mod missing_separator; mod misspelled_attribute; mod module_mismatch; @@ -125,6 +132,7 @@ mod no_garbage_collect; mod no_nowarn_suppressions; mod no_size; mod nonstandard_integer_formatting; +mod old_edoc_syntax; mod record_tuple_match; mod redundant_assignment; mod replace_call; @@ -132,6 +140,7 @@ mod replace_in_spec; mod sets_version_2; mod simplify_negation; mod trivial_match; +mod unavailable_type; mod undefined_function; mod undefined_macro; mod undocumented_function; @@ -224,8 +233,9 @@ impl Diagnostic { self } - pub(crate) fn as_related(&self) -> RelatedInformation { + pub(crate) fn as_related(&self, file_id: FileId) -> RelatedInformation { RelatedInformation { + file_id, range: self.range, message: self.message.clone(), } @@ -376,10 +386,10 @@ impl Diagnostic { format!( "{}:{}-{}:{}::[{:?}] [{}] {}", - start.line, - start.col_utf16, - end.line, - end.col_utf16, + start.line + 1, + start.col_utf16 + 1, + end.line + 1, + end.col_utf16 + 1, self.severity(use_cli_severity), self.code, self.message @@ -444,6 +454,7 @@ impl Diagnostic { #[derive(Debug, Clone)] pub struct RelatedInformation { + pub file_id: FileId, pub range: TextRange, pub message: String, } @@ -458,9 +469,10 @@ impl fmt::Display for Diagnostic { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum Severity { Error, + #[default] Warning, // `WeakWarning` maps onto a Notice warning when used in the LSP // environment, and in VS Code this means it does not show up in @@ -470,12 +482,6 @@ pub enum Severity { Information, } -impl Default for Severity { - fn default() -> Self { - Self::Warning // Pick one - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Category { Experimental, @@ -505,13 +511,13 @@ pub(crate) trait Linter { fn description(&self) -> &'static str; // The severity for the lint issue. It defaults to `Warning`. - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Warning } // For CLI, when using the --use-cli-severity flag. It defaults to `severity()` - fn cli_severity(&self) -> Severity { - self.severity() + fn cli_severity(&self, sema: &Semantic, file_id: FileId) -> Severity { + self.severity(sema, file_id) } // Specify if the linter issues can be suppressed via a `% elp:ignore` comment. @@ -545,12 +551,37 @@ pub(crate) trait Linter { } } +fn should_process_app( + app_name: &Option, + config: &DiagnosticsConfig, + diagnostic_code: &DiagnosticCode, +) -> bool { + let app = match app_name { + Some(app) => app.to_string(), + None => return true, + }; + + if let Some(lint_config) = config.lint_config.as_ref() + && let Some(linter_config) = lint_config.linters.get(diagnostic_code) + && let Some(ref excluded) = linter_config.exclude_apps + && excluded.contains(&app) + { + return false; + } + + true +} + fn should_run( linter: &dyn Linter, config: &DiagnosticsConfig, + app_name: &Option, is_generated: bool, is_test: bool, ) -> bool { + if !should_process_app(app_name, config, &linter.id()) { + return false; + } let is_enabled = if let Some(lint_config) = config.lint_config.as_ref() { lint_config .get_is_enabled_override(&linter.id()) @@ -861,6 +892,7 @@ pub(crate) trait GenericLinter: Linter { fn fixes( &self, _context: &Self::Context, + _range: TextRange, _sema: &Semantic, _file_id: FileId, ) -> Option> { @@ -894,7 +926,7 @@ impl GenericDiagnostics for T { if let Some(matches) = self.matches(sema, file_id) { for matched in matches { let message = self.match_description(&matched.context); - let fixes = self.fixes(&matched.context, sema, file_id); + let fixes = self.fixes(&matched.context, matched.range, sema, file_id); let tag = self.tag(&matched.context); let mut d = Diagnostic::new(self.id(), message, matched.range) .with_fixes(fixes) @@ -1049,7 +1081,7 @@ impl DiagnosticsConfig { let diagnostic_filter = DiagnosticCode::from(diagnostic_filter.as_str()); // Make sure we do not mask the one we explicitly asked for disabled_diagnostics.remove(&diagnostic_filter); - allowed_diagnostics.insert(diagnostic_filter); + allowed_diagnostics.insert(diagnostic_filter.clone()); } // Make sure the enabled ones win out over disabled if a lint appears in both @@ -1067,6 +1099,13 @@ impl DiagnosticsConfig { } self.lints_from_config = lint_config.ad_hoc_lints.clone(); self.lint_config = Some(lint_config.clone()); + + // If a diagnostic_filter is specified, enable the corresponding linter in lint_config + if let Some(diagnostic_filter) = diagnostic_filter { + let diagnostic_filter_code = DiagnosticCode::from(diagnostic_filter.as_str()); + self.set_linter_enabled(diagnostic_filter_code, true); + } + self.request_erlang_service_diagnostics = self.request_erlang_service_diagnostics(); Ok(self) } @@ -1103,15 +1142,31 @@ impl DiagnosticsConfig { } pub fn enable(mut self, code: DiagnosticCode) -> DiagnosticsConfig { - self.enabled.enable(code); + self.enabled.enable(code.clone()); + self.set_linter_enabled(code, true); self } pub fn disable(mut self, code: DiagnosticCode) -> DiagnosticsConfig { - self.disabled.insert(code); + self.disabled.insert(code.clone()); + self.set_linter_enabled(code, false); self } + fn set_linter_enabled(&mut self, code: DiagnosticCode, is_enabled: bool) { + let lint_config = self.lint_config.get_or_insert_with(LintConfig::default); + lint_config + .linters + .entry(code) + .and_modify(|linter_config| { + linter_config.is_enabled = Some(is_enabled); + }) + .or_insert_with(|| LinterConfig { + is_enabled: Some(is_enabled), + ..Default::default() + }); + } + pub fn set_lints_from_config( mut self, lints_from_config: &LintsFromConfig, @@ -1141,6 +1196,29 @@ impl DiagnosticsConfig { } impl LintConfig { + /// Merges this LintConfig with another, with the other config taking precedence. + pub fn merge(mut self, other: LintConfig) -> LintConfig { + self.enabled_lints.extend(other.enabled_lints); + self.disabled_lints.extend(other.disabled_lints); + + if other.erlang_service.warnings_as_errors { + self.erlang_service.warnings_as_errors = true; + } + + self.ad_hoc_lints.lints.extend(other.ad_hoc_lints.lints); + + for (key, other_linter_config) in other.linters { + self.linters + .entry(key) + .and_modify(|existing| { + *existing = existing.clone().merge(other_linter_config.clone()) + }) + .or_insert(other_linter_config); + } + + self + } + /// Get the is_enabled override for a linter based on its diagnostic code pub fn get_is_enabled_override(&self, diagnostic_code: &DiagnosticCode) -> Option { self.linters.get(diagnostic_code)?.is_enabled @@ -1166,6 +1244,16 @@ impl LintConfig { self.linters.get(diagnostic_code)?.experimental } + /// Get the exclude_apps override for a linter based on its diagnostic code. + pub fn get_exclude_apps_override( + &self, + diagnostic_code: &DiagnosticCode, + ) -> Option> { + self.linters + .get(diagnostic_code) + .and_then(|c| c.exclude_apps.clone()) + } + pub fn get_function_call_linter_config( &self, diagnostic_code: &DiagnosticCode, @@ -1217,9 +1305,43 @@ pub struct FunctionCallLinterConfig { exclude: Option>, } +impl FunctionCallLinterConfig { + /// Merges this FunctionCallLinterConfig with another, extending the include and exclude lists. + pub fn merge(self, other: FunctionCallLinterConfig) -> FunctionCallLinterConfig { + let mut merged_include = self.include.unwrap_or_default(); + if let Some(other_include) = other.include { + merged_include.extend(other_include); + } + let include = if merged_include.is_empty() { + None + } else { + Some(merged_include) + }; + + let mut merged_exclude = self.exclude.unwrap_or_default(); + if let Some(other_exclude) = other.exclude { + merged_exclude.extend(other_exclude); + } + let exclude = if merged_exclude.is_empty() { + None + } else { + Some(merged_exclude) + }; + + FunctionCallLinterConfig { include, exclude } + } +} + #[derive(Deserialize, Serialize, Default, Debug, Clone)] pub struct SsrPatternsLinterConfig {} +impl SsrPatternsLinterConfig { + /// Merges this SsrPatternsLinterConfig with another (trivial since it's empty). + pub fn merge(self, _other: SsrPatternsLinterConfig) -> SsrPatternsLinterConfig { + self + } +} + #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(untagged)] pub enum LinterTraitConfig { @@ -1227,6 +1349,25 @@ pub enum LinterTraitConfig { SsrPatternsLinterConfig(SsrPatternsLinterConfig), } +impl LinterTraitConfig { + /// Merges this LinterTraitConfig with another. Only merges if both are the same variant. + /// If variants differ, returns the other config. + pub fn merge(self, other: LinterTraitConfig) -> LinterTraitConfig { + match (self, other) { + ( + LinterTraitConfig::FunctionCallLinterConfig(self_config), + LinterTraitConfig::FunctionCallLinterConfig(other_config), + ) => LinterTraitConfig::FunctionCallLinterConfig(self_config.merge(other_config)), + ( + LinterTraitConfig::SsrPatternsLinterConfig(self_config), + LinterTraitConfig::SsrPatternsLinterConfig(other_config), + ) => LinterTraitConfig::SsrPatternsLinterConfig(self_config.merge(other_config)), + // If variants differ, other takes precedence + (_, other) => other, + } + } +} + /// Configuration for a specific linter that allows overriding default settings #[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct LinterConfig { @@ -1236,10 +1377,33 @@ pub struct LinterConfig { pub include_tests: Option, pub include_generated: Option, pub experimental: Option, + pub exclude_apps: Option>, #[serde(flatten)] pub config: Option, } +impl LinterConfig { + /// Merges this LinterConfig with another, with the other config taking precedence + /// for any fields that are Some. + pub fn merge(self, other: LinterConfig) -> LinterConfig { + let merged_config = match (self.config, other.config) { + (Some(self_cfg), Some(other_cfg)) => Some(self_cfg.merge(other_cfg)), + (None, some_cfg) => some_cfg, + (some_cfg, None) => some_cfg, + }; + + LinterConfig { + is_enabled: other.is_enabled.or(self.is_enabled), + severity: other.severity.or(self.severity), + include_tests: other.include_tests.or(self.include_tests), + include_generated: other.include_generated.or(self.include_generated), + experimental: other.experimental.or(self.experimental), + exclude_apps: other.exclude_apps.or(self.exclude_apps), + config: merged_config, + } + } +} + impl<'de> Deserialize<'de> for Severity { fn deserialize(deserializer: D) -> Result where @@ -1391,14 +1555,8 @@ pub fn native_diagnostics( let labeled_syntax_errors = if report_diagnostics { let sema = Semantic::new(db); - if file_kind.is_module() { - no_module_definition_diagnostic(&mut res, &parse); - if config.include_generated || !db.is_generated(file_id) { - unused_include::unused_includes(&sema, db, &mut res, file_id); - } - } - res.append(&mut form_missing_separator_diagnostics(&parse)); + res.extend(get_hir_diagnostics(db, file_id)); adhoc_semantic_diagnostics .iter() @@ -1406,7 +1564,7 @@ pub fn native_diagnostics( config .lints_from_config .get_diagnostics(&mut res, &sema, file_id); - // @fb-only + // @fb-only: meta_only::diagnostics(&mut res, &sema, file_id, file_kind, config); syntax_diagnostics(&sema, &parse, &mut res, file_id); diagnostics_from_descriptors( &mut res, @@ -1435,6 +1593,7 @@ pub fn native_diagnostics( } else { FxHashMap::default() }; + let app_name = db.file_app_name(file_id); let metadata = db.elp_metadata(file_id); // TODO: can we ever disable DiagnosticCode::SyntaxError? // In which case we must check labeled_syntax_errors @@ -1443,6 +1602,7 @@ pub fn native_diagnostics( && (config.experimental && d.has_category(Category::Experimental) || !d.has_category(Category::Experimental)) && !d.should_be_suppressed(&metadata, config) + && should_process_app(&app_name, config, &d.code) }); LabeledDiagnostics { @@ -1471,22 +1631,12 @@ pub fn diagnostics_descriptors<'a>() -> Vec<&'a DiagnosticDescriptor<'a>> { &map_find_to_syntax::DESCRIPTOR, &expression_can_be_simplified::DESCRIPTOR, &application_env::DESCRIPTOR, - &missing_compile_warn_missing_spec::DESCRIPTOR, &dependent_header::DESCRIPTOR, &deprecated_function::DESCRIPTOR, &head_mismatch::DESCRIPTOR_SEMANTIC, &missing_separator::DESCRIPTOR, - &boolean_precedence::DESCRIPTOR, &record_tuple_match::DESCRIPTOR, &unspecific_include::DESCRIPTOR, - &edoc::DESCRIPTOR, - ¯o_precedence_suprise::DESCRIPTOR, - &undocumented_function::DESCRIPTOR, - &duplicate_module::DESCRIPTOR, - &undocumented_module::DESCRIPTOR, - &no_dialyzer_attribute::DESCRIPTOR, - &no_catch::DESCRIPTOR, - &no_nowarn_suppressions::DESCRIPTOR, ] } @@ -1503,20 +1653,20 @@ pub fn diagnostics_from_descriptors( .db .is_test_suite_or_test_helper(file_id) .unwrap_or(false); + let app_name = sema.db.file_app_name(file_id); descriptors.iter().for_each(|descriptor| { if descriptor.conditions.enabled(config, is_generated, is_test) { - if descriptor.conditions.default_disabled { - // Filter the returned diagnostics to ensure they are - // enabled - let mut diags: Vec = Vec::default(); - (descriptor.checker)(&mut diags, sema, file_id, file_kind); - for diag in diags { - if config.enabled.contains(&diag.code) { - res.push(diag); - } + let mut diags: Vec = Vec::default(); + (descriptor.checker)(&mut diags, sema, file_id, file_kind); + for diag in diags { + // Check if this diagnostic is enabled (for default_disabled descriptors) + // and if the app is not excluded for this diagnostic code + let is_enabled = + !descriptor.conditions.default_disabled || config.enabled.contains(&diag.code); + let app_allowed = should_process_app(&app_name, config, &diag.code); + if is_enabled && app_allowed { + res.push(diag); } - } else { - (descriptor.checker)(res, sema, file_id, file_kind); } } }); @@ -1558,10 +1708,28 @@ const SSR_PATTERN_LINTERS: &[&dyn SsrPatternsDiagnostics] = &[ &binary_string_to_sigil::LINTER, &unnecessary_map_to_list_in_comprehension::LINTER, &could_be_a_string_literal::LINTER, + &lists_reverse_append::LINTER, ]; /// Generic linters -const GENERIC_LINTERS: &[&dyn GenericDiagnostics] = &[&unused_macro::LINTER]; +const GENERIC_LINTERS: &[&dyn GenericDiagnostics] = &[ + &unused_macro::LINTER, + &missing_compile_warn_missing_spec::LINTER, + &undocumented_module::LINTER, + &undocumented_function::LINTER, + &no_catch::LINTER, + &unavailable_type::LINTER, + &no_dialyzer_attribute::LINTER, + &duplicate_module::LINTER, + &no_nowarn_suppressions::LINTER, + ¯o_precedence_suprise::LINTER, + &old_edoc_syntax::LINTER, + &missing_module::LINTER, + &unused_include::LINTER, + &misspelled_attribute::LINTER, + &boolean_precedence::LINTER, + &bound_variable::LINTER, +]; /// Unified registry for all types of linters pub(crate) fn linters() -> Vec { @@ -1589,7 +1757,7 @@ pub(crate) fn linters() -> Vec { ); // Add meta-only linters - // @fb-only + // @fb-only: all_linters.extend(meta_only::linters()); all_linters } @@ -1606,25 +1774,26 @@ fn diagnostics_from_linters( .db .is_test_suite_or_test_helper(file_id) .unwrap_or(false); + let app_name = sema.db.file_app_name(file_id); for l in linters { let linter = l.as_linter(); if linter.should_process_file_id(sema, file_id) - && should_run(linter, config, is_generated, is_test) + && should_run(linter, config, &app_name, is_generated, is_test) { let severity = if let Some(lint_config) = config.lint_config.as_ref() { lint_config .get_severity_override(&linter.id()) - .unwrap_or_else(|| linter.severity()) + .unwrap_or_else(|| linter.severity(sema, file_id)) } else { - linter.severity() + linter.severity(sema, file_id) }; let cli_severity = if let Some(lint_config) = config.lint_config.as_ref() { lint_config .get_severity_override(&linter.id()) - .unwrap_or_else(|| linter.cli_severity()) + .unwrap_or_else(|| linter.cli_severity(sema, file_id)) } else { - linter.cli_severity() + linter.cli_severity(sema, file_id) }; match l { DiagnosticLinter::FunctionCall(function_linter) => { @@ -1724,13 +1893,12 @@ fn widen_range(range: TextRange) -> TextRange { } } -pub fn syntax_diagnostics( +fn syntax_diagnostics( sema: &Semantic, parse: &Parse, res: &mut Vec, file_id: FileId, ) { - misspelled_attribute::misspelled_attribute(sema, res, file_id); for node in parse.tree().syntax().descendants() { head_mismatch::head_mismatch(res, file_id, &node); module_mismatch::module_mismatch(sema, res, file_id, &node); @@ -1741,32 +1909,180 @@ pub fn filter_diagnostics(diagnostics: Vec, code: DiagnosticCode) -> diagnostics.into_iter().filter(|d| d.code == code).collect() } -fn no_module_definition_diagnostic( - diagnostics: &mut Vec, - parse: &Parse, -) { - let mut report = |range| { - let diagnostic = - Diagnostic::new(DiagnosticCode::MissingModule, "no module definition", range); - diagnostics.push(diagnostic); - }; - for form in parse.tree().forms() { - match form { - ast::Form::PreprocessorDirective(_) => { - continue; // skip any directives +/// Retrieve all BodyDiagnostic values from the BodySourceMaps from lowering +/// all the bodies for a given FileId. +/// +/// This function iterates through all forms in the file and collects diagnostics +/// from the BodySourceMaps associated with each form's body. +pub fn collect_body_diagnostics(db: &RootDatabase, file_id: FileId) -> Vec { + let sema = Semantic::new(db); + let form_list = sema.form_list(file_id); + let mut diagnostics = Vec::new(); + + for form_idx in form_list.forms().iter() { + let body_map = match form_idx { + FormIdx::FunctionClause(function_clause_id) => { + let (_, body_map) = sema + .db + .function_clause_body_with_source(InFile::new(file_id, *function_clause_id)); + Some(body_map) } - ast::Form::FileAttribute(_) => { - continue; // skip + FormIdx::TypeAlias(type_alias_id) => { + let (_, body_map) = sema + .db + .type_body_with_source(InFile::new(file_id, *type_alias_id)); + Some(body_map) } - ast::Form::ModuleAttribute(_) => { - break; + FormIdx::Spec(spec_id) => { + let (_, body_map) = sema + .db + .spec_body_with_source(InFile::new(file_id, *spec_id)); + Some(body_map) } - other_form => { - report(other_form.syntax().text_range()); - break; + FormIdx::Callback(callback_id) => { + let (_, body_map) = sema + .db + .callback_body_with_source(InFile::new(file_id, *callback_id)); + Some(body_map) } + FormIdx::Record(record_id) => { + let (_, body_map) = sema + .db + .record_body_with_source(InFile::new(file_id, *record_id)); + Some(body_map) + } + FormIdx::Attribute(attribute_id) => { + let (_, body_map) = sema + .db + .attribute_body_with_source(InFile::new(file_id, *attribute_id)); + Some(body_map) + } + FormIdx::CompileOption(compile_option_id) => { + let (_, body_map) = sema + .db + .compile_body_with_source(InFile::new(file_id, *compile_option_id)); + Some(body_map) + } + FormIdx::PPDirective(idx) => { + match &form_list[*idx] { + PPDirective::Define(define_id) => { + let (_, body_map) = sema + .db + .define_body_with_source(InFile::new(file_id, *define_id)); + Some(body_map) + } + PPDirective::Include(include_id) => { + // Try to resolve the include + if sema + .db + .resolve_include(InFile::new(file_id, *include_id)) + .is_none() + { + // Include resolution failed, create a diagnostic + diagnostics.push(hir::BodyDiagnostic::UnresolvedInclude(InFile::new( + file_id, + *include_id, + ))); + } + None + } + _ => None, + } + } + _ => None, + }; + + if let Some(body_map) = body_map { + diagnostics.extend_from_slice(body_map.diagnostics()); } } + + diagnostics +} + +/// Convert HIR body diagnostics to IDE Diagnostics. +/// This function takes the diagnostics collected during HIR lowering and converts +/// them to the IDE Diagnostic format for display to users. +pub fn get_hir_diagnostics(db: &RootDatabase, file_id: FileId) -> Vec { + let body_diagnostics = collect_body_diagnostics(db, file_id); + + body_diagnostics + .into_iter() + .filter_map(|body_diag| { + // Only include diagnostics for the requested file + if body_diag.file_id() != file_id { + return None; + } + + let (code, message, range) = match &body_diag { + hir::BodyDiagnostic::UnresolvedMacro(macro_source) => { + // Determine range for UnresolvedMacro + let full_range = macro_source.range(); + + // Get the macro call AST node to extract name and arity + let macro_call = macro_source.to_ast(db); + let macro_name = macro_call + .name() + .map(|name| name.to_string()) + .unwrap_or_else(|| "?".to_string()); + + let message = match macro_call.arity() { + Some(arity) => format!("undefined macro '{}/{}'", macro_name, arity), + None => format!("undefined macro '{}'", macro_name), + }; + + // For macros with arguments, only highlight the name part, not the full call + let range = macro_call + .name() + .map(|name| { + // Get the syntax range of just the macro name + let name_range = name.syntax().text_range(); + // Include the '?' prefix by extending one character to the left + if name_range.start() > 0.into() { + TextRange::new( + name_range.start() - TextSize::from(1), + name_range.end(), + ) + } else { + name_range + } + }) + .unwrap_or(full_range.range); + + (DiagnosticCode::HirUnresolvedMacro, message, range) + } + hir::BodyDiagnostic::UnresolvedInclude(include) => { + // Get the include attribute from the form_list + let sema = Semantic::new(db); + let form_list = sema.form_list(file_id); + let include_attr = &form_list[include.value]; + + // Extract path and range from IncludeAttribute + let path = include_attr.path().to_string(); + let range = include_attr.file_range(db, file_id); + + // Use appropriate message based on include type + let message = match include_attr { + hir::IncludeAttribute::Include { .. } => { + format!("can't find include file \"{}\"", path) + } + hir::IncludeAttribute::IncludeLib { .. } => { + format!("can't find include lib \"{}\"", path) + } + }; + + (DiagnosticCode::HirUnresolvedInclude, message, range) + } + }; + + Some( + Diagnostic::new(code, message, range) + // We set the severity to Warning for now, until we have cleaned + // up the code base from this diagnostic + .with_severity(Severity::Warning), + ) + }) + .collect() } fn form_missing_separator_diagnostics(parse: &Parse) -> Vec { @@ -1961,51 +2277,54 @@ pub fn erlang_service_diagnostics( let mut warning_info: BTreeSet<(FileId, TextSize, TextSize, String, String)> = BTreeSet::default(); + // Track related information for L0000 diagnostics from included files + let mut related_info_map: FxHashMap<(FileId, TextSize, TextSize), Vec> = + FxHashMap::default(); + res.errors .iter() - .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d)) + .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d, &mut related_info_map)) .for_each(|val| { error_info.insert(val); }); res.warnings .iter() - .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d)) + .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d, &mut related_info_map)) .for_each(|val| { warning_info.insert(val); }); let warning_severity = config.erlang_service_warning_severity(); + let to_diagnostic = |file_id: FileId, + start: TextSize, + end: TextSize, + code: String, + msg: String, + severity: Severity| { + let range = TextRange::new(start, end); + let mut diagnostic = tag_erlang_service_diagnostic( + Diagnostic::new(DiagnosticCode::ErlangService(code.clone()), msg, range) + .with_severity(severity), + ); + if code == "L0000" + && let Some(related) = related_info_map.get(&(file_id, start, end)) + { + diagnostic = diagnostic.with_related(Some(related.clone())); + } + (file_id, diagnostic) + }; + let diags: Vec<(FileId, Diagnostic)> = error_info .into_iter() .map(|(file_id, start, end, code, msg)| { - ( - file_id, - tag_erlang_service_diagnostic( - Diagnostic::new( - DiagnosticCode::ErlangService(code), - msg, - TextRange::new(start, end), - ) - .with_severity(Severity::Error), - ), - ) + to_diagnostic(file_id, start, end, code, msg, Severity::Error) }) .chain( warning_info .into_iter() .map(|(file_id, start, end, code, msg)| { - ( - file_id, - tag_erlang_service_diagnostic( - Diagnostic::new( - DiagnosticCode::ErlangService(code), - msg, - TextRange::new(start, end), - ) - .with_severity(warning_severity), - ), - ) + to_diagnostic(file_id, start, end, code, msg, warning_severity) }), ) .collect(); @@ -2022,11 +2341,14 @@ pub fn erlang_service_diagnostics( diags }; + let app_name = db.file_app_name(file_id); let metadata = db.elp_metadata(file_id); let diags = diags .into_iter() .filter(|(_file_id, d)| { - !d.should_be_suppressed(&metadata, config) && !config.disabled.contains(&d.code) + !d.should_be_suppressed(&metadata, config) + && !config.disabled.contains(&d.code) + && should_process_app(&app_name, config, &d.code) }) .map(|(file_id, d)| { ( @@ -2317,7 +2639,7 @@ pub fn ct_diagnostics( CommonTestInfo::Result { all, groups } => { let testcases = common_test::runnable_names(&sema, file_id, all, groups).ok(); common_test::unreachable_test(&mut res, &sema, file_id, &testcases); - // @fb-only + // @fb-only: meta_only::ct_diagnostics(&mut res, &sema, file_id, testcases); } CommonTestInfo::EvalError(_error) => { // The error currently does not contain anything useful, so we ignore it @@ -2394,20 +2716,68 @@ fn parse_error_to_diagnostic_info( db: &RootDatabase, file_id: FileId, parse_error: &ParseError, + related_info_map: &mut FxHashMap<(FileId, TextSize, TextSize), Vec>, ) -> Option<(FileId, TextSize, TextSize, String, String)> { - match parse_error.location { + match &parse_error.location { Some(DiagnosticLocation::Included { - directive_location, + file_attribute_location, + error_path, error_location, - }) => included_file_file_id(db, file_id, directive_location).map(|included_file_id| { - ( - included_file_id, - error_location.start(), - error_location.end(), - parse_error.code.clone(), - parse_error.msg.clone(), - ) - }), + }) => { + // Get the FileId for the file containing the error by searching through source roots + + // Note: we can have our .erl file which includes A.hrl which includes B.hrl, and B.hrl has an error. + // In this case, the error location will be in B.hrl, but the file_attribute_location will be 1..1, for the entry into B.hrl. + // The parse_error.path will correctly refer to B.hrl + // In this case, we need to report the error in the .erl file, at the include directive location. + // But how do we detemine that? We need to find an include chain from the .erl file to the included + // file. The A->B chain can be arbitrarily long. + // So, in the erlang service, we must report the chain of `file` attributes seen. + + // For errors in included files, report L0000 at the include directive location + // and add the actual error as related information + if let Some(directive_range) = + include_directive_range(db, file_id, *file_attribute_location) + { + // Store related info about the actual error in the included file + if let Some(error_file_id) = related_file_id(db, file_id, error_path) { + let related_info = RelatedInformation { + file_id: error_file_id, + range: *error_location, + message: format!("{}: {}", parse_error.code, parse_error.msg), + }; + related_info_map + .entry((file_id, directive_range.start(), directive_range.end())) + .or_default() + .push(related_info); + } else { + log::warn!("Could not find file for path {}", error_path.display()); + } + + // Return diagnostic at the include directive with code L0000 + Some(( + file_id, + directive_range.start(), + directive_range.end(), + "L0000".to_string(), + "Issue in included file".to_string(), + )) + } else { + // Fallback: if we can't find the include directive range, try to report + // at the included file itself + included_file_file_id(db, file_id, *file_attribute_location).map( + |included_file_id| { + ( + included_file_id, + error_location.start(), + error_location.end(), + parse_error.code.clone(), + parse_error.msg.clone(), + ) + }, + ) + } + } Some(DiagnosticLocation::Normal(range)) => { let default_range = ( file_id, @@ -2417,11 +2787,13 @@ fn parse_error_to_diagnostic_info( parse_error.msg.clone(), ); match parse_error.code.as_str() { + // Do not report L0000, it is already reported via a DiagnosticLocation::Included parse error + "L0000" => None, // For certain warnings, OTP returns a diagnostic with a wide range (e.g. a full record definition) // That can be very verbose and distracting, so we try restricting the range to the relevant parts only. "L1227" => { let name = function_undefined_from_message(&parse_error.msg); - match exported_function_name_range(db, file_id, name, range) { + match exported_function_name_range(db, file_id, name, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2434,7 +2806,7 @@ fn parse_error_to_diagnostic_info( } "L1295" => { let name = type_undefined_from_message(&parse_error.msg); - match exported_type_name_range(db, file_id, name, range) { + match exported_type_name_range(db, file_id, name, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2445,7 +2817,7 @@ fn parse_error_to_diagnostic_info( None => Some(default_range), } } - "L1230" | "L1309" => match function_name_range(db, file_id, range) { + "L1230" | "L1309" => match function_name_range(db, file_id, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2455,7 +2827,7 @@ fn parse_error_to_diagnostic_info( )), None => Some(default_range), }, - "L1296" => match type_alias_name_range(db, file_id, range) { + "L1296" => match type_alias_name_range(db, file_id, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2465,7 +2837,7 @@ fn parse_error_to_diagnostic_info( )), None => Some(default_range), }, - "L1308" => match spec_name_range(db, file_id, range) { + "L1308" => match spec_name_range(db, file_id, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2475,7 +2847,7 @@ fn parse_error_to_diagnostic_info( )), None => Some(default_range), }, - "L1260" => match record_name_range(db, file_id, range) { + "L1260" => match record_name_range(db, file_id, *range) { Some(name_range) => Some(( file_id, name_range.start(), @@ -2498,6 +2870,22 @@ fn parse_error_to_diagnostic_info( } } +fn related_file_id(db: &RootDatabase, file_id: FileId, path: &Path) -> Option { + db.file_project_id(file_id).and_then(|project_id| { + let vfs_path = VfsPath::new_real_path(path.to_string_lossy().as_ref().to_string()); + + let project_data = db.project_data(project_id); + // Search through all source roots in the project to find the file + project_data + .source_roots + .iter() + .find_map(|&source_root_id| { + let source_root = db.source_root(source_root_id); + source_root.file_for_path(&vfs_path) + }) + }) +} + fn exported_function_name_range( db: &RootDatabase, file_id: FileId, @@ -2582,13 +2970,13 @@ fn type_alias_name_range( pub fn included_file_file_id( db: &RootDatabase, file_id: FileId, - directive_range: TextRange, + file_attribute_location: TextRange, ) -> Option { let parsed = db.parse(file_id); let form_list = db.file_form_list(file_id); let include = form_list.includes().find_map(|(idx, include)| { let form = include.form_id().get(&parsed.tree()); - if form.syntax().text_range().contains(directive_range.start()) { + if form.syntax().text_range().start() >= file_attribute_location.start() { db.resolve_include(InFile::new(file_id, idx)) } else { None @@ -2597,6 +2985,30 @@ pub fn included_file_file_id( Some(include) } +/// For an error in an included file, find the next form occurring after `file_attribute_location` +/// in the file, and return its full syntax range provided it is an include or include_lib +fn include_directive_range( + db: &RootDatabase, + file_id: FileId, + file_attribute_location: TextRange, +) -> Option { + let parsed = db.parse(file_id); + let form_list = db.file_form_list(file_id); + + form_list.includes().find_map(|(_, include)| { + let form = include.form_id().get(&parsed.tree()); + let form_range = form.syntax().text_range(); + + // Check if this form starts after directive_location + if form_range.start() >= file_attribute_location.start() { + // Return the full syntax range of the include directive + Some(form_range) + } else { + None + } + }) +} + /// Given syntax errors from ELP native and erlang service, combine /// them by discarding any erlang service syntax errors for a form if /// ELP has reported any too. This is done by merging labels, with @@ -2619,6 +3031,7 @@ fn combine_syntax_errors(native: &Labeled, erlang_service: &Labeled) -> Labeled /// Combine the ELP and erlang_service diagnostics. In particular, /// flatten any cascading diagnostics if possible. pub fn attach_related_diagnostics( + file_id: FileId, native: LabeledDiagnostics, erlang_service: LabeledDiagnostics, ) -> Vec { @@ -2654,7 +3067,7 @@ pub fn attach_related_diagnostics( .flat_map(|(mfa_label, syntax_error_diags)| { if let Some(related) = erlang_service.labeled_undefined_errors.get(mfa_label) { undefineds_to_remove.insert(mfa_label); - let related_info = related.iter().map(|d| d.as_related()).collect_vec(); + let related_info = related.iter().map(|d| d.as_related(file_id)).collect_vec(); syntax_error_diags .iter() .map(|d| d.clone().with_related(Some(related_info.clone()))) @@ -2675,10 +3088,90 @@ pub fn attach_related_diagnostics( .filter(|(k, _)| !undefineds_to_remove.contains(k)) .flat_map(|(_, v)| v); - native - .normal - .into_iter() - .chain(erlang_service.normal) + // Step 4. + // Split erlang service normal diagnostics into undefined macro diagnostics (E1507/E1508), + // unresolved include diagnostics (E1516), and other diagnostics in a single pass + let mut erlang_service_undefined_macros = Vec::new(); + let mut erlang_service_unresolved_includes = Vec::new(); + let mut erlang_service_other = Vec::new(); + + for d in erlang_service.normal { + match &d.code { + DiagnosticCode::ErlangService(code) if code == "E1507" || code == "E1508" => { + erlang_service_undefined_macros.push(d); + } + DiagnosticCode::ErlangService(code) if code == "E1516" => { + erlang_service_unresolved_includes.push(d); + } + _ => { + erlang_service_other.push(d); + } + } + } + + // Collect E1507/E1508 from labeled_undefined_errors for filtering + let undefined_macros_from_labeled: Vec<_> = erlang_service_undefined_not_related + .clone() + .filter(|d| { + matches!(&d.code, DiagnosticCode::ErlangService(code) if code == "E1507" || code == "E1508") + }) + .cloned() + .collect(); + + // Collect E1516 from labeled_undefined_errors for filtering + let unresolved_includes_from_labeled: Vec<_> = erlang_service_undefined_not_related + .clone() + .filter(|d| matches!(&d.code, DiagnosticCode::ErlangService(code) if code == "E1516")) + .cloned() + .collect(); + + // Combine all E1507/E1508 diagnostics for filtering (clone to avoid borrow issues) + let all_undefined_macros: Vec<_> = erlang_service_undefined_macros + .iter() + .cloned() + .chain(undefined_macros_from_labeled) + .collect(); + + // Combine all E1516 diagnostics for filtering + let all_unresolved_includes: Vec<_> = erlang_service_unresolved_includes + .iter() + .cloned() + .chain(unresolved_includes_from_labeled) + .collect(); + + // Step 5. + // Filter out W0056 diagnostics if there's a matching E1507/E1508 for the same macro + // Filter out W0057 diagnostics if there's a matching E1516 for the same include + let filtered_native_normal = native.normal.into_iter().filter(|d| { + if d.code == DiagnosticCode::HirUnresolvedMacro { + // Check if there's a matching E1507/E1508 diagnostic + let has_matching_erlang_service = all_undefined_macros.iter().any(|es_diag| { + // Check if ranges overlap + d.range.intersect(es_diag.range).is_some() + }); + + // Keep W0056 only if there's no matching E1507/E1508 + return !has_matching_erlang_service; + } + + if d.code == DiagnosticCode::HirUnresolvedInclude { + // Check if there's a matching E1516 diagnostic + let has_matching_erlang_service = all_unresolved_includes.iter().any(|es_diag| { + // Check if ranges overlap + d.range.intersect(es_diag.range).is_some() + }); + + // Keep W0057 only if there's no matching E1516 + return !has_matching_erlang_service; + } + + true // Keep all other diagnostics + }); + + filtered_native_normal + .chain(erlang_service_other) + .chain(erlang_service_undefined_macros) + .chain(erlang_service_unresolved_includes) .chain(syntax_errors_with_related) .chain(erlang_service_undefined_not_related.cloned()) // TODO:AZ: consider returning an iterator @@ -2743,6 +3236,7 @@ mod tests { fn syntax_error() { check_diagnostics( r#" +//- expect_parse_errors -module(main). foo() -> XX 3.0. %% ^^ error: P1711: Syntax Error @@ -2832,53 +3326,6 @@ main(X) -> // ) // } - #[test] - fn fun_decl_module_decl_ok() { - check_diagnostics( - r#" --file("main.erl",1). --define(baz,4). --module(main). -foo(2)->?baz. -"#, - ); - } - - #[test] - fn fun_decl_module_decl_missing() { - check_diagnostics( - r#" - -file("foo.erl",1). - -define(baz,4). - foo(2)->?baz. -%%^^^^^^^^^^^^^ error: L1201: no module definition -"#, - ); - } - - #[test] - fn fun_decl_module_decl_missing_2() { - check_diagnostics( - r#" - baz(1)->4. -%%^^^^^^^^^^ error: L1201: no module definition - foo(2)->3. -"#, - ); - } - - #[test] - fn fun_decl_module_decl_after_preprocessor() { - check_diagnostics( - r#" --ifndef(snmpm_net_if_mt). --module(main). --endif. -baz(1)->4. -"#, - ); - } - #[test] fn filter_diagnostics() { let diag1 = DiagnosticCode::ErlangService("P1700".to_string()); @@ -2967,6 +3414,7 @@ baz(1)->4. #[test] fn label_syntax_error_not_function() { let fixture_str = r#" + //- expect_parse_errors -module(main). -record(person, {(name + XXX)}). %% ^^^^^^^ error: P1711: Syntax Error @@ -2982,7 +3430,7 @@ baz(1)->4. expect![[r#" Some( Range( - 24..56, + 5..45, ), ) "#]] @@ -2994,7 +3442,7 @@ baz(1)->4. check_diagnostics( r#" baz(1)->4. -%%^^^^^^^^^^ error: L1201: no module definition +%%^^^^^^^^^^ 💡 error: L1201: no module definition foo(2)->3. "#, ); @@ -3018,7 +3466,7 @@ baz(1)->4. %% elp:ignore L1201 baz(1)->4. -%%^^^^^^^^^^ error: L1201: no module definition +%%^^^^^^^^^^ 💡 error: L1201: no module definition foo(2)->3. "#, ); @@ -3141,6 +3589,7 @@ baz(1)->4. config, &extra_diags, r#" + //- expect_parse_errors -module(main). -export([foo/0,bar/0]). @@ -3151,6 +3600,9 @@ baz(1)->4. -spec foo() -> ok. foo( -> ok. %% %% ^ error: W0004: Missing ')' + %% | Related info: 0:21-43 function foo/0 undefined + %% | Related info: 0:74-79 function foo/0 undefined + %% | Related info: 0:82-99 spec for undefined function foo/0 "#, ); } @@ -3158,7 +3610,7 @@ baz(1)->4. #[test] fn group_related_diagnostics_elp_only() { // Demonstrate that ELP does not pick up a syntax error in the - // spec, same code as in test_projects/diagnostics/app_a/src/syntax.erl + // spec, same code as in test/test_projects/diagnostics/app_a/src/syntax.erl check_diagnostics( r#" -module(main). @@ -3175,12 +3627,14 @@ baz(1)->4. check_diagnostics( r#" //- erlang_service + //- expect_parse_errors //- /src/a_mod.erl app:app_a -module(a_mod). -export([foo/0]). foo() -> syntax error oops. %% ^^^^^ error: P1711: syntax error before: error + %% | Related info: 0:25-30 function foo/0 undefined "#, ); } @@ -3192,6 +3646,7 @@ baz(1)->4. check_diagnostics( r#" //- erlang_service + //- expect_parse_errors //- native //- /src/a_mod.erl app:app_a -module(a_mod). @@ -3270,6 +3725,7 @@ baz(1)->4. fn test_nested_syntax_errors() { check_diagnostics( r#" + //- expect_parse_errors -module(main). run() -> ExitCode = @@ -3452,6 +3908,37 @@ baz(1)->4. ); } + #[test] + fn erlang_service_nested_include_resolution() { + check_diagnostics( + r#" + //- erlang_service + //- /src/app_a_include.erl + -module(app_a_include). + -export([test/0]). + %% ^^^^^^ error: L1227: function test/0 undefined + + -include("first.hrl"). + %% ^^^^^^^^^^^^^^^^^^^^^^ error: L0000: Issue in included file + %% | Related info: 1:13-30 E1516: can't find include file "second_typo.hrl" + + test() -> + ?FIRST_MACRO, + ?SECOND_MACRO. + %% ^^^^^^^^^^^^^ error: E1507: undefined macro 'SECOND_MACRO' + + //- /src/first.hrl + -include("second_typo.hrl"). + %% ^^^^^^^^^^^^^^^^^ error: E1516: can't find include file "second_typo.hrl" + + -define(FIRST_MACRO, 1). + + //- /src/second.hrl + -define(SECOND_MACRO, "Hello from nested header"). + "#, + ); + } + #[test] fn erlang_service_include_resolution_doc() { check_diagnostics( @@ -3484,6 +3971,7 @@ baz(1)->4. \~"\"\\µA\"" = \~/"\\µA"/ X = 3. %% ^ error: P1711: syntax error before: X + %% | Related info: 0:32-37 function foo/0 undefined "#, ); } @@ -3551,6 +4039,7 @@ baz(1)->4. include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -3593,6 +4082,7 @@ baz(1)->4. include_tests: Some(true), include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -3634,6 +4124,7 @@ baz(1)->4. include_tests: None, include_generated: Some(true), experimental: None, + exclude_apps: None, config: None, }, ); @@ -3676,6 +4167,7 @@ baz(1)->4. include_tests: None, include_generated: None, experimental: Some(true), + exclude_apps: None, config: None, }, ); @@ -3720,6 +4212,7 @@ baz(1)->4. include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -3741,6 +4234,7 @@ baz(1)->4. warning() -> erlang:garbage_collect(). + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0047: Avoid forcing garbage collection. //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 -module(erlang). -export([garbage_collect/0]). @@ -3748,4 +4242,203 @@ baz(1)->4. "#, ); } + + #[test] + fn test_linter_exclude_apps_override() { + let mut lint_config = LintConfig::default(); + lint_config.linters.insert( + DiagnosticCode::NoGarbageCollect, + LinterConfig { + is_enabled: Some(false), + severity: None, + include_tests: None, + include_generated: None, + experimental: None, + exclude_apps: Some(vec!["my_app".to_string()]), + config: None, + }, + ); + + let config = DiagnosticsConfig::default() + .configure_diagnostics( + &lint_config, + &Some("no_garbage_collect".to_string()), + &None, + FallBackToAll::No, + ) + .unwrap(); + check_diagnostics_with_config( + config, + r#" + //- /src/main.erl app:my_app + -module(main). + -export([warning/0]). + + warning() -> + erlang:garbage_collect(). + //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 + -module(erlang). + -export([garbage_collect/0]). + garbage_collect() -> ok. + "#, + ); + } + + #[test] + fn no_unused_macro_in_macro_rhs_for_function_name() { + let config = DiagnosticsConfig::default() + .set_experimental(true) + .disable(DiagnosticCode::UnspecificInclude) + .disable(DiagnosticCode::BinaryStringToSigil); + check_diagnostics_with_config( + config, + r#" + //- /my_app/src/a_file.erl + -module(a_file). + -define(A_MACRO, ?FUNCTION_NAME). + foo() -> ?A_MACRO. + + "#, + ); + } + + #[test] + fn test_lint_config_merge() { + let code1 = DiagnosticCode::from("W0001"); + let code2 = DiagnosticCode::from("W0002"); + let code3 = DiagnosticCode::from("W0003"); + + let mut config1 = LintConfig { + enabled_lints: vec![code1.clone()], + disabled_lints: vec![code2.clone()], + ..Default::default() + }; + config1.erlang_service.warnings_as_errors = false; + + config1 + .ad_hoc_lints + .lints + .push(Lint::ReplaceCall(ReplaceCall { + matcher: FunctionMatch::mf("mod1", "func1"), + action: ReplaceCallAction::Replace(Replacement::UseOk), + })); + + config1.linters.insert( + code1.clone(), + LinterConfig { + is_enabled: Some(true), + severity: Some(Severity::Error), + include_tests: None, + include_generated: None, + experimental: None, + exclude_apps: None, + config: Some(LinterTraitConfig::FunctionCallLinterConfig( + FunctionCallLinterConfig { + include: Some(vec![FunctionMatch::mf("mod_a", "func_a")]), + exclude: None, + }, + )), + }, + ); + + let mut config2 = LintConfig { + enabled_lints: vec![code3.clone()], + disabled_lints: vec![code1.clone()], + ..Default::default() + }; + config2.erlang_service.warnings_as_errors = true; + + config2 + .ad_hoc_lints + .lints + .push(Lint::ReplaceCall(ReplaceCall { + matcher: FunctionMatch::mf("mod2", "func2"), + action: ReplaceCallAction::Replace(Replacement::UseOk), + })); + + config2.linters.insert( + code1.clone(), + LinterConfig { + is_enabled: Some(false), + severity: Some(Severity::Warning), + include_tests: Some(true), + include_generated: None, + experimental: None, + exclude_apps: None, + config: Some(LinterTraitConfig::FunctionCallLinterConfig( + FunctionCallLinterConfig { + include: Some(vec![FunctionMatch::mf("mod_b", "func_b")]), + exclude: Some(vec![FunctionMatch::mf("mod_c", "func_c")]), + }, + )), + }, + ); + + config2.linters.insert( + code2.clone(), + LinterConfig { + is_enabled: Some(true), + severity: None, + include_tests: None, + include_generated: Some(true), + experimental: None, + exclude_apps: None, + config: None, + }, + ); + + let merged = config1.merge(config2); + + assert_eq!(merged.enabled_lints.len(), 2); + assert!(merged.enabled_lints.contains(&code1)); + assert!(merged.enabled_lints.contains(&code3)); + + assert_eq!(merged.disabled_lints.len(), 2); + assert!(merged.disabled_lints.contains(&code2)); + assert!(merged.disabled_lints.contains(&code1)); + + assert!(merged.erlang_service.warnings_as_errors); + + assert_eq!(merged.ad_hoc_lints.lints.len(), 2); + + assert_eq!(merged.linters.len(), 2); + let linter_config1 = merged.linters.get(&code1).unwrap(); + assert_eq!(linter_config1.is_enabled, Some(false)); + assert_eq!(linter_config1.severity, Some(Severity::Warning)); + assert_eq!(linter_config1.include_tests, Some(true)); + + if let Some(LinterTraitConfig::FunctionCallLinterConfig(function_config)) = + &linter_config1.config + { + assert_eq!(function_config.include.as_ref().unwrap().len(), 2); + assert!( + function_config + .include + .as_ref() + .unwrap() + .contains(&FunctionMatch::mf("mod_a", "func_a")) + ); + assert!( + function_config + .include + .as_ref() + .unwrap() + .contains(&FunctionMatch::mf("mod_b", "func_b")) + ); + assert_eq!(function_config.exclude.as_ref().unwrap().len(), 1); + assert!( + function_config + .exclude + .as_ref() + .unwrap() + .contains(&FunctionMatch::mf("mod_c", "func_c")) + ); + } else { + panic!("Expected FunctionCallLinterConfig"); + } + + let linter_config2 = merged.linters.get(&code2).unwrap(); + assert_eq!(linter_config2.is_enabled, Some(true)); + assert_eq!(linter_config2.include_generated, Some(true)); + } } diff --git a/crates/ide/src/diagnostics/application_env.rs b/crates/ide/src/diagnostics/application_env.rs index 1e4fe7c4cf..615b7801f9 100644 --- a/crates/ide/src/diagnostics/application_env.rs +++ b/crates/ide/src/diagnostics/application_env.rs @@ -28,7 +28,7 @@ use crate::codemod_helpers::CheckCallCtx; use crate::codemod_helpers::FunctionMatch; use crate::codemod_helpers::MatchCtx; use crate::codemod_helpers::find_call_in_function; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::Severity; @@ -36,7 +36,7 @@ pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { conditions: DiagnosticConditions { experimental: false, include_generated: true, - include_tests: true, + include_tests: false, default_disabled: false, }, checker: &|diags, sema, file_id, _ext| { @@ -108,7 +108,7 @@ fn check_function(diags: &mut Vec, sema: &Semantic, def: &FunctionDe vec![2, 3], BadEnvCallAction::AppArg(0), ), - // @fb-only + // @fb-only: diagnostics::meta_only::application_env_bad_matches(), ] .into_iter() .flatten() diff --git a/crates/ide/src/diagnostics/atoms_exhaustion.rs b/crates/ide/src/diagnostics/atoms_exhaustion.rs index 4708e35b1f..56a43a50ca 100644 --- a/crates/ide/src/diagnostics/atoms_exhaustion.rs +++ b/crates/ide/src/diagnostics/atoms_exhaustion.rs @@ -13,7 +13,7 @@ use hir::Semantic; use crate::FunctionMatch; use crate::codemod_helpers::CheckCallCtx; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; @@ -35,9 +35,9 @@ impl Linter for AtomsExhaustionLinter { false } #[rustfmt::skip] - // @fb-only + // @fb-only: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only + // @fb-only: diagnostics::meta_only::is_relevant_file(sema.db.upcast(), file_id) true // @oss-only } } @@ -56,16 +56,16 @@ impl FunctionCallLinter for AtomsExhaustionLinter { // FunctionMatch::mfa("erlang", "binary_to_term", 2), ] .into_iter() - // @fb-only + // @fb-only: .chain(diagnostics::meta_only::atoms_exhaustion_matches().into_iter()) .collect::>() ] } fn check_match(&self, context: &CheckCallCtx<'_, ()>) -> Option { #[rustfmt::skip] - // @fb-only - // @fb-only - // @fb-only + // @fb-only: let sema = context.in_clause.sema; + // @fb-only: let is_safe = + // @fb-only: diagnostics::meta_only::atoms_exhaustion_is_safe(sema, context.in_clause, context.parents); let is_safe = false; // @oss-only if !is_safe { match context.args.as_slice() { diff --git a/crates/ide/src/diagnostics/binary_string_to_sigil.rs b/crates/ide/src/diagnostics/binary_string_to_sigil.rs index 60a18aa7e1..91b08f9d1d 100644 --- a/crates/ide/src/diagnostics/binary_string_to_sigil.rs +++ b/crates/ide/src/diagnostics/binary_string_to_sigil.rs @@ -38,7 +38,7 @@ impl Linter for BinaryStringToSigilLinter { "Binary string can be written using sigil syntax." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::WeakWarning } diff --git a/crates/ide/src/diagnostics/boolean_precedence.rs b/crates/ide/src/diagnostics/boolean_precedence.rs index affbdf9b1d..f8f7851f8a 100644 --- a/crates/ide/src/diagnostics/boolean_precedence.rs +++ b/crates/ide/src/diagnostics/boolean_precedence.rs @@ -15,6 +15,7 @@ // https://www.erlang.org/doc/system/expressions.html#operator-precedence // So this often results in incorrect or buggy code. +use std::borrow::Cow; use std::fmt; use std::fmt::Display; @@ -24,11 +25,11 @@ use elp_ide_assists::helpers::unwrap_parens; use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextRange; use elp_syntax::ast; use elp_syntax::ast::BinaryOp; use elp_syntax::ast::LogicOp; -use elp_text_edit::TextEdit; -use elp_text_edit::TextRange; use hir::AnyExpr; use hir::AnyExprId; use hir::AnyExprRef; @@ -45,29 +46,107 @@ use hir::fold::MacroStrategy; use hir::fold::ParenStrategy; use hir::fold::ParentId; -use super::Diagnostic; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use super::Severity; +use super::GenericLinter; +use super::GenericLinterMatchContext; +use super::Linter; +use crate::Assist; use crate::fix; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: true, - default_disabled: false, - }, - checker: &|diags, sema, file_id, _ext| { - boolean_precedence(diags, sema, file_id); - }, -}; +pub(crate) struct BooleanPrecedenceLinter; -fn boolean_precedence(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { - sema.for_each_function(file_id, |def| check_function(diagnostics, sema, def)); +impl Linter for BooleanPrecedenceLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::BooleanPrecedence + } + + fn description(&self) -> &'static str { + "boolean precedence" + } } -fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &FunctionDef) { +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Context { + preceding_ws_range: TextRange, + op: Op, + lhs_complex: bool, + rhs_complex: bool, + add_parens_range: TextRange, +} + +impl GenericLinter for BooleanPrecedenceLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + sema.for_each_function(file_id, |def| { + check_function(&mut res, sema, def); + }); + Some(res) + } + + fn match_description(&self, context: &Self::Context) -> Cow<'_, str> { + format!( + "Consider using the short-circuit expression '{}' instead of '{}'.\nOr add parentheses to avoid potential ambiguity.", + context.op.preferred(), + context.op, + ) + .into() + } + + fn fixes( + &self, + context: &Self::Context, + range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { + let mut fixes = Vec::new(); + + // Add "replace with preferred operator" fix + let assist_message = format!("Replace '{}' with '{}'", context.op, context.op.preferred()); + let edit = TextEdit::replace( + context.op.range(range, context.preceding_ws_range), + context.op.preferred().to_string(), + ); + fixes.push(fix( + "replace_boolean_operator", + &assist_message, + SourceChange::from_text_edit(file_id, edit), + range, + )); + + // Add "add parens" fixes if applicable + if context.lhs_complex { + fixes.push(parens_fix("LHS", file_id, context, range)); + } + if context.rhs_complex { + fixes.push(parens_fix("RHS", file_id, context, range)); + } + + Some(fixes) + } +} + +fn parens_fix(side: &str, file_id: FileId, context: &Context, range: TextRange) -> Assist { + let assist_message = format!("Add parens to {side}"); + let edit = add_parens_edit(&context.add_parens_range); + fix( + "replace_boolean_operator_add_parens", + &assist_message, + SourceChange::from_text_edit(file_id, edit), + range, + ) +} + +fn check_function( + matches: &mut Vec>, + sema: &Semantic, + def: &FunctionDef, +) { let def_fb = def.in_function_body(sema, def); def_fb.clone().fold_function( Strategy { @@ -91,28 +170,20 @@ fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &Func _ => None, }; if let Some(op) = op { - report( - sema, - &def_fb, - def.file.file_id, - clause_id, - ctx, - op, - diagnostics, - ); + collect_match(matches, sema, &def_fb, def.file.file_id, clause_id, ctx, op); } }, ) } -fn report( +fn collect_match( + matches: &mut Vec>, sema: &Semantic, def_fb: &InFunctionBody<&FunctionDef>, file_id: FileId, clause_id: ClauseId, ctx: AnyCallBackCtx, binop: (Op, ExprId, ExprId), - diagnostics: &mut Vec, ) -> Option<()> { /* we have (inserting parens for tree) @@ -156,39 +227,25 @@ fn report( let (_op, token) = binop_ast.op()?; let range = token.text_range(); let preceding_ws_range = include_preceding_whitespace(&token); - let mut d = make_diagnostic(file_id, range, preceding_ws_range, binop) - .with_ignore_fix(sema, def_fb.file_id()); - if lhs_complex { - add_parens_fix(file_id, &range, add_parens_range, "LHS", &mut d); - } - if rhs_complex { - add_parens_fix(file_id, &range, add_parens_range, "RHS", &mut d); - } - diagnostics.push(d); + + matches.push(GenericLinterMatchContext { + range, + context: Context { + preceding_ws_range, + op: binop, + lhs_complex, + rhs_complex, + add_parens_range, + }, + }); } }; Some(()) } -fn add_parens_fix( - file_id: FileId, - range: &TextRange, - expr_range: TextRange, - where_str: &str, - diag: &mut Diagnostic, -) { - let assist_message = format!("Add parens to {where_str}"); - let edit = add_parens_edit(&expr_range); - diag.add_fix(fix( - "replace_boolean_operator_add_parens", - &assist_message, - SourceChange::from_text_edit(file_id, edit.clone()), - *range, - )); -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] enum Op { + #[default] And, AndInGuard, Or, @@ -223,31 +280,7 @@ impl Op { } } -fn make_diagnostic( - file_id: FileId, - range: TextRange, - preceding_ws_range: TextRange, - op: Op, -) -> Diagnostic { - let message = format!( - "Consider using the short-circuit expression '{}' instead of '{}'.\nOr add parentheses to avoid potential ambiguity.", - op.preferred(), - op, - ); - let assist_message = format!("Replace '{}' with '{}'", op, op.preferred()); - let edit = TextEdit::replace( - op.range(range, preceding_ws_range), - op.preferred().to_string(), - ); - Diagnostic::new(DiagnosticCode::BooleanPrecedence, message, range) - .with_severity(Severity::Warning) - .with_fixes(Some(vec![fix( - "replace_boolean_operator", - &assist_message, - SourceChange::from_text_edit(file_id, edit.clone()), - range, - )])) -} +pub static LINTER: BooleanPrecedenceLinter = BooleanPrecedenceLinter; #[cfg(test)] mod tests { diff --git a/crates/ide/src/diagnostics/bound_variable.rs b/crates/ide/src/diagnostics/bound_variable.rs new file mode 100644 index 0000000000..92f0edadfa --- /dev/null +++ b/crates/ide/src/diagnostics/bound_variable.rs @@ -0,0 +1,178 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +// Diagnostic: bound_variable +// +// Return a warning if the LHS of a match already contains a bound variable. +// + +use elp_ide_db::elp_base_db::FileId; +use hir::AnyExpr; +use hir::Expr; +use hir::Semantic; +use hir::Strategy; +use hir::fold::MacroStrategy; +use hir::fold::ParenStrategy; + +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; + +pub(crate) struct BoundVariableLinter; + +impl Linter for BoundVariableLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::BoundVarInLhs + } + + fn description(&self) -> &'static str { + "Match on a bound variable" + } +} + +impl GenericLinter for BoundVariableLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let bound_vars_by_function = sema.bound_vars_by_function(file_id); + let mut res = Vec::new(); + sema.def_map(file_id) + .get_function_clauses() + .for_each(|(_, def)| { + if def.file.file_id == file_id + && let Some(bound_vars) = bound_vars_by_function.get(&def.function_clause_id) + { + let in_clause = def.in_clause(sema, def); + in_clause.fold_clause( + Strategy { + macros: MacroStrategy::ExpandButIncludeMacroCall, + parens: ParenStrategy::InvisibleParens, + }, + (), + &mut |acc, ctx| { + if let AnyExpr::Expr(Expr::Match { lhs, rhs: _ }) = ctx.item + && bound_vars.contains(&lhs) + && let Some(range) = in_clause.range_for_pat(lhs) + && range.file_id == def.file.file_id + && ctx.in_macro.is_none() + { + res.push(GenericLinterMatchContext { + range: range.range, + context: (), + }); + }; + acc + }, + ); + } + }); + + Some(res) + } +} + +pub static LINTER: BoundVariableLinter = BoundVariableLinter; + +#[cfg(test)] +mod test { + use elp_ide_db::DiagnosticCode; + use expect_test::Expect; + + use crate::diagnostics::DiagnosticsConfig; + use crate::tests::check_diagnostics_with_config; + use crate::tests::check_fix_with_config; + + #[track_caller] + pub(crate) fn check_diagnostics(fixture: &str) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_diagnostics_with_config(config, fixture) + } + + #[track_caller] + pub(crate) fn check_fix(fixture_before: &str, fixture_after: Expect) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_fix_with_config(config, fixture_before, fixture_after) + } + #[test] + fn bound_variable() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + AA = bar(). + %% ^^ 💡 warning: W0060: Match on a bound variable + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_case() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo(Val) -> + case Val of + undefined -> ok; + Val when is_list(Val) -> ok + end. + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_macro() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + -include("inc.hrl"). + + foo(Val) -> + ?A_MACRO(Val). + //- /src/inc.hrl + -define(A_MACRO(X), X=X). + "#, + ) + } + + #[test] + fn bound_variable_ignore_fix() { + check_fix( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + A~A = bar(). + "#, + expect_test::expect![[r#" + -module(bound). + + foo() -> + AA = bar(), + % elp:ignore W0060 (bound_var_in_lhs) + AA = bar(). + "#]], + ) + } +} diff --git a/crates/ide/src/diagnostics/could_be_a_string_literal.rs b/crates/ide/src/diagnostics/could_be_a_string_literal.rs index d939163222..03ea0e6274 100644 --- a/crates/ide/src/diagnostics/could_be_a_string_literal.rs +++ b/crates/ide/src/diagnostics/could_be_a_string_literal.rs @@ -39,7 +39,7 @@ impl Linter for CouldBeAStringLiteralLinter { "Could be rewritten as a literal." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Information } } diff --git a/crates/ide/src/diagnostics/cross_node_eval.rs b/crates/ide/src/diagnostics/cross_node_eval.rs index 56cf2e1299..f50ca663e3 100644 --- a/crates/ide/src/diagnostics/cross_node_eval.rs +++ b/crates/ide/src/diagnostics/cross_node_eval.rs @@ -12,6 +12,9 @@ //! //! Return a diagnostic for rpc calls to remote nodes. +use elp_ide_db::elp_base_db::FileId; +use hir::Semantic; + use crate::codemod_helpers::FunctionMatch; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; @@ -28,7 +31,7 @@ impl Linter for CrossNodeEvalLinter { fn description(&self) -> &'static str { "Production code must not use cross node eval (e.g. `rpc:call()`)" } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Error } fn should_process_test_files(&self) -> bool { diff --git a/crates/ide/src/diagnostics/debugging_function.rs b/crates/ide/src/diagnostics/debugging_function.rs index 904ebe3098..1f0099781d 100644 --- a/crates/ide/src/diagnostics/debugging_function.rs +++ b/crates/ide/src/diagnostics/debugging_function.rs @@ -11,7 +11,7 @@ use elp_ide_assists::Assist; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChangeBuilder; -use elp_text_edit::TextRange; +use elp_ide_db::text_edit::TextRange; use hir::Semantic; use crate::FunctionMatch; @@ -22,7 +22,7 @@ use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; use crate::diagnostics::Severity; -// @fb-only +// @fb-only: use crate::diagnostics::meta_only; use crate::lazy_function_matches; pub(crate) struct NoDebuggingFunctionLinter; @@ -34,10 +34,10 @@ impl Linter for NoDebuggingFunctionLinter { fn description(&self) -> &'static str { "Debugging functions should only be used during local debugging and usages should not be checked in." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::WeakWarning } - fn cli_severity(&self) -> Severity { + fn cli_severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Error } fn should_process_generated_files(&self) -> bool { @@ -52,7 +52,7 @@ impl FunctionCallLinter for NoDebuggingFunctionLinter { lazy_function_matches![ vec![FunctionMatch::m("redbug")] .into_iter() - // @fb-only + // @fb-only: .chain(meta_only::debugging_function_matches().into_iter()) .collect::>() ] } diff --git a/crates/ide/src/diagnostics/dependent_header.rs b/crates/ide/src/diagnostics/dependent_header.rs index e0b4d11652..4729f05872 100644 --- a/crates/ide/src/diagnostics/dependent_header.rs +++ b/crates/ide/src/diagnostics/dependent_header.rs @@ -14,10 +14,10 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileKind; +use elp_ide_db::text_edit::TextRange; use elp_syntax::AstNode; use elp_syntax::ast; use elp_syntax::ast::RecordName; -use elp_text_edit::TextRange; use hir::AnyExpr; use hir::InFile; use hir::Name; diff --git a/crates/ide/src/diagnostics/deprecated_function.rs b/crates/ide/src/diagnostics/deprecated_function.rs index 3956282c45..b353d824d2 100644 --- a/crates/ide/src/diagnostics/deprecated_function.rs +++ b/crates/ide/src/diagnostics/deprecated_function.rs @@ -21,10 +21,10 @@ use elp_ide_assists::Assist; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextRange; +use elp_ide_db::text_edit::TextSize; use elp_syntax::AstNode; -use elp_text_edit::TextEdit; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; use hir::AnyExpr; use hir::Expr; use hir::FunctionDef; @@ -41,7 +41,7 @@ use super::DiagnosticDescriptor; use super::Severity; use crate::codemod_helpers::FunctionMatch; use crate::codemod_helpers::FunctionMatcher; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::fix; pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { @@ -88,7 +88,7 @@ fn deprecated_function(diagnostics: &mut Vec, sema: &Semantic, file_ lazy_static! { static ref DEPRECATED_FUNCTIONS: Vec<(FunctionMatch, DeprecationDetails)> = { let matches: Vec> = vec![ - // @fb-only + // @fb-only: diagnostics::meta_only::deprecated_function_matches(), ]; matches.into_iter() .flatten() @@ -134,8 +134,8 @@ fn check_function( ); let details = match_result.map(|(_match, details)| details.clone()); if target_def.deprecated || match_result.is_some() { - let expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; diff --git a/crates/ide/src/diagnostics/duplicate_module.rs b/crates/ide/src/diagnostics/duplicate_module.rs index d9e01ecc53..09eb548b31 100644 --- a/crates/ide/src/diagnostics/duplicate_module.rs +++ b/crates/ide/src/diagnostics/duplicate_module.rs @@ -12,51 +12,58 @@ // // Return a warning if more than one module has the same name -use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::ModuleName; use elp_syntax::AstNode; use hir::Semantic; -use super::Diagnostic; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use crate::diagnostics::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: false, // Allow duplication in test fixtures - default_disabled: false, - }, - checker: &|diags, sema, file_id, _file_kind| { - check_file(diags, sema, &file_id); - }, -}; +pub(crate) struct DuplicateModuleLinter; -fn check_file(acc: &mut Vec, sema: &Semantic, file_id: &FileId) -> Option<()> { - // We cannot ask for the module name from the module_index, as - // this file_id may be discarded as a duplicate - let module_name_ast = sema.module_attribute_name(*file_id)?; - let module_name = ModuleName::new(module_name_ast.text()?.as_str()); - - let app_data = sema.db.file_app_data(*file_id)?; - let module_index = sema.db.module_index(app_data.project_id); - if let Some(_dups) = module_index.duplicates(&module_name) { - let range = module_name_ast.syntax().text_range(); - acc.push( - Diagnostic::new( - DiagnosticCode::DuplicateModule, - "Duplicate module name", - range, - ) - .with_severity(Severity::Warning), - ); +impl Linter for DuplicateModuleLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::DuplicateModule + } + + fn description(&self) -> &'static str { + "A module with this name exists elsewhere" + } + + fn should_process_test_files(&self) -> bool { + false // Allow duplication in test fixtures } - Some(()) } +impl GenericLinter for DuplicateModuleLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + // We cannot ask for the module name from the module_index, as + // this file_id may be discarded as a duplicate + let module_name_ast = sema.module_attribute_name(file_id)?; + let module_name = ModuleName::new(module_name_ast.text()?.as_str()); + + let app_data = sema.db.file_app_data(file_id)?; + let module_index = sema.db.module_index(app_data.project_id); + if let Some(_dups) = module_index.duplicates(&module_name) { + let range = module_name_ast.syntax().text_range(); + res.push(GenericLinterMatchContext { range, context: () }); + } + Some(res) + } +} + +pub static LINTER: DuplicateModuleLinter = DuplicateModuleLinter; + #[cfg(test)] mod test { use crate::tests::check_diagnostics; @@ -67,11 +74,11 @@ mod test { r#" //- /src/dup_mod.erl -module(dup_mod). - %% ^^^^^^^ warning: W0045: Duplicate module name + %% ^^^^^^^ 💡 warning: W0045: A module with this name exists elsewhere //- /src/sub/dup_mod.erl -module(dup_mod). - %% ^^^^^^^ warning: W0045: Duplicate module name + %% ^^^^^^^ 💡 warning: W0045: A module with this name exists elsewhere "#, ) } diff --git a/crates/ide/src/diagnostics/effect_free_statement.rs b/crates/ide/src/diagnostics/effect_free_statement.rs index 72d6c8eb8d..9aec815fbb 100644 --- a/crates/ide/src/diagnostics/effect_free_statement.rs +++ b/crates/ide/src/diagnostics/effect_free_statement.rs @@ -17,11 +17,11 @@ use std::iter; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::AstNode; use elp_syntax::SyntaxElement; use elp_syntax::SyntaxKind; use elp_syntax::ast; -use elp_text_edit::TextEdit; use hir::AnyExprId; use hir::Expr; use hir::ExprId; diff --git a/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs b/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs index ee655d2f8c..081f6e43ae 100644 --- a/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs +++ b/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs @@ -15,8 +15,8 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FilePosition; use elp_ide_db::find_best_token; use elp_ide_db::source_change::SourceChange; -use elp_text_edit::TextEdit; -use elp_text_edit::TextSize; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextSize; use elp_types_db::eqwalizer::StructuredDiagnostic; use elp_types_db::eqwalizer::tc_diagnostics::ExpectedSubtype; use elp_types_db::eqwalizer::tc_diagnostics::TypeError; diff --git a/crates/ide/src/diagnostics/expression_can_be_simplified.rs b/crates/ide/src/diagnostics/expression_can_be_simplified.rs index 4f3ce6ac57..4c9144e5c0 100644 --- a/crates/ide/src/diagnostics/expression_can_be_simplified.rs +++ b/crates/ide/src/diagnostics/expression_can_be_simplified.rs @@ -261,10 +261,19 @@ fn as_expr_id(any_expre_id: hir::AnyExprId) -> Option { #[cfg(test)] mod tests { + use elp_ide_db::DiagnosticCode; + use expect_test::Expect; use expect_test::expect; + use crate::diagnostics::DiagnosticsConfig; use crate::tests::check_diagnostics; - use crate::tests::check_fix; + use crate::tests::check_fix_with_config; + + #[track_caller] + fn check_fix(fixture_before: &str, fixture_after: Expect) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::MissingModule); + check_fix_with_config(config, fixture_before, fixture_after); + } #[test] fn test_generates_diagnostics() { diff --git a/crates/ide/src/diagnostics/from_config.rs b/crates/ide/src/diagnostics/from_config.rs index 678ffbd369..250fa4422d 100644 --- a/crates/ide/src/diagnostics/from_config.rs +++ b/crates/ide/src/diagnostics/from_config.rs @@ -131,6 +131,7 @@ pub struct MatchSsr { pub ssr_pattern: String, pub message: Option, pub strategy: Option, + pub severity: Option, } impl Serialize for MatchSsr { @@ -140,11 +141,14 @@ impl Serialize for MatchSsr { { use serde::ser::SerializeStruct; - let mut state = serializer.serialize_struct("MatchSsr", 4)?; + let mut state = serializer.serialize_struct("MatchSsr", 5)?; state.serialize_field("ssr_pattern", &self.ssr_pattern)?; if let Some(ref message) = self.message { state.serialize_field("message", message)?; } + if let Some(ref severity) = self.severity { + state.serialize_field("severity", severity)?; + } if let Some(strategy) = self.strategy { // Default strategy is Expand and InvisibleParens let is_default = strategy.macros == MacroStrategy::Expand @@ -185,6 +189,8 @@ impl<'de> Deserialize<'de> for MatchSsr { #[serde(default)] message: Option, #[serde(default)] + severity: Option, + #[serde(default)] macro_strategy: Option, #[serde(default)] paren_strategy: Option, @@ -236,6 +242,7 @@ impl<'de> Deserialize<'de> for MatchSsr { ssr_pattern: helper.ssr_pattern, message: helper.message, strategy, + severity: helper.severity, }) } } @@ -256,12 +263,13 @@ impl MatchSsr { .clone() .unwrap_or_else(|| format!("SSR pattern matched: {}", self.ssr_pattern)); + let severity = self.severity.unwrap_or(Severity::WeakWarning); let diag = Diagnostic::new( DiagnosticCode::AdHoc("ssr-match".to_string()), message, matched.range.range, ) - .with_severity(Severity::WeakWarning); + .with_severity(severity); acc.push(diag); } } @@ -657,6 +665,7 @@ mod tests { ssr_pattern: "ssr: _@A = 10.".to_string(), message: Some("Found pattern".to_string()), strategy: None, + severity: None, }) .unwrap(); expect![[r#" @@ -683,6 +692,7 @@ mod tests { "Found pattern", ), strategy: None, + severity: None, } "#]] .assert_debug_eq(&match_ssr); @@ -695,6 +705,7 @@ mod tests { ssr_pattern: "ssr: _@A = 10.".to_string(), message: Some("Found pattern".to_string()), strategy: None, + severity: None, })], }) .unwrap(); @@ -717,6 +728,7 @@ mod tests { macros: MacroStrategy::Expand, parens: ParenStrategy::InvisibleParens, }), + severity: None, })], }) .unwrap(); @@ -739,6 +751,7 @@ mod tests { macros: MacroStrategy::DoNotExpand, parens: ParenStrategy::VisibleParens, }), + severity: None, })], }) .unwrap(); @@ -782,6 +795,7 @@ mod tests { parens: VisibleParens, }, ), + severity: None, }, ), ], @@ -789,4 +803,181 @@ mod tests { "#]] .assert_debug_eq(&match_ssr); } + + #[test] + fn serde_serialize_match_ssr_with_severity_error() { + use crate::diagnostics::Severity; + let result = toml::to_string::(&MatchSsr { + ssr_pattern: "ssr: _@A = 10.".to_string(), + message: Some("Found pattern".to_string()), + strategy: None, + severity: Some(Severity::Error), + }) + .unwrap(); + expect![[r#" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "error" + "#]] + .assert_eq(&result); + } + + #[test] + fn serde_serialize_match_ssr_with_severity_warning() { + use crate::diagnostics::Severity; + let result = toml::to_string::(&MatchSsr { + ssr_pattern: "ssr: _@A = 10.".to_string(), + message: Some("Found pattern".to_string()), + strategy: None, + severity: Some(Severity::Warning), + }) + .unwrap(); + expect![[r#" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "warning" + "#]] + .assert_eq(&result); + } + + #[test] + fn serde_serialize_match_ssr_with_severity_weak() { + use crate::diagnostics::Severity; + let result = toml::to_string::(&MatchSsr { + ssr_pattern: "ssr: _@A = 10.".to_string(), + message: Some("Found pattern".to_string()), + strategy: None, + severity: Some(Severity::WeakWarning), + }) + .unwrap(); + expect![[r#" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "weak" + "#]] + .assert_eq(&result); + } + + #[test] + fn serde_serialize_match_ssr_with_severity_info() { + use crate::diagnostics::Severity; + let result = toml::to_string::(&MatchSsr { + ssr_pattern: "ssr: _@A = 10.".to_string(), + message: Some("Found pattern".to_string()), + strategy: None, + severity: Some(Severity::Information), + }) + .unwrap(); + expect![[r#" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "info" + "#]] + .assert_eq(&result); + } + + #[test] + fn serde_deserialize_match_ssr_with_severity() { + use crate::diagnostics::Severity; + let match_ssr: MatchSsr = toml::from_str( + r#" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "error" + "#, + ) + .unwrap(); + + assert_eq!(match_ssr.severity, Some(Severity::Error)); + } + + #[test] + fn serde_deserialize_lint_match_ssr_with_severity() { + use crate::diagnostics::Severity; + let lints: LintsFromConfig = toml::from_str( + r#" + [[lints]] + type = "LintMatchSsr" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "warning" + "#, + ) + .unwrap(); + + match &lints.lints[0] { + Lint::LintMatchSsr(match_ssr) => { + assert_eq!(match_ssr.severity, Some(Severity::Warning)); + } + _ => panic!("Expected LintMatchSsr"), + } + } + + #[test] + fn serde_serialize_lint_match_ssr_with_severity_and_strategy() { + use crate::diagnostics::Severity; + let result = toml::to_string::(&LintsFromConfig { + lints: vec![Lint::LintMatchSsr(MatchSsr { + ssr_pattern: "ssr: _@A = 10.".to_string(), + message: Some("Found pattern".to_string()), + strategy: Some(Strategy { + macros: MacroStrategy::DoNotExpand, + parens: ParenStrategy::VisibleParens, + }), + severity: Some(Severity::Error), + })], + }) + .unwrap(); + expect![[r#" + [[lints]] + type = "LintMatchSsr" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "error" + macro_strategy = "no-expand" + paren_strategy = "visible" + "#]] + .assert_eq(&result); + } + + #[test] + fn serde_deserialize_lint_match_ssr_with_severity_and_strategy() { + let lints: LintsFromConfig = toml::from_str( + r#" + [[lints]] + type = "LintMatchSsr" + ssr_pattern = "ssr: _@A = 10." + message = "Found pattern" + severity = "error" + macro_strategy = "no-expand" + paren_strategy = "visible" + "#, + ) + .unwrap(); + + expect![[r#" + LintsFromConfig { + lints: [ + LintMatchSsr( + MatchSsr { + ssr_pattern: "ssr: _@A = 10.", + message: Some( + "Found pattern", + ), + strategy: Some( + Strategy { + macros: DoNotExpand, + parens: VisibleParens, + }, + ), + severity: Some( + Error, + ), + }, + ), + ], + } + "#]] + .assert_debug_eq(&lints); + } } diff --git a/crates/ide/src/diagnostics/head_mismatch.rs b/crates/ide/src/diagnostics/head_mismatch.rs index 425ed09e6d..edfb1d8518 100644 --- a/crates/ide/src/diagnostics/head_mismatch.rs +++ b/crates/ide/src/diagnostics/head_mismatch.rs @@ -13,13 +13,13 @@ use std::hash::Hash; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::SyntaxToken; use elp_syntax::TextRange; use elp_syntax::ast; use elp_syntax::ast::AstNode; use elp_syntax::ast::ClauseSeparator; use elp_syntax::syntax_node::SyntaxNode; -use elp_text_edit::TextEdit; use fxhash::FxHashMap; use hir::Semantic; @@ -270,6 +270,7 @@ impl Validate for Name { attr_loc, ) .with_related(Some(vec![RelatedInformation { + file_id, range: ref_loc, message: "Mismatched clause name".to_string(), }])) @@ -293,7 +294,7 @@ impl Validate for Arity { fn make_diagnostic( self, - _file_id: FileId, + file_id: FileId, attr: &usize, hattr: &usize, attr_loc: TextRange, @@ -305,6 +306,7 @@ impl Validate for Arity { attr_loc, ) .with_related(Some(vec![RelatedInformation { + file_id, range: ref_loc, message: "Mismatched clause".to_string(), }])) @@ -394,6 +396,7 @@ mod tests { foo(0) -> 1; boo(1) -> 2. %% ^^^ 💡 error: P1700: head mismatch 'boo' vs 'foo' + %% | Related info: 0:21-24 Mismatched clause name "#, ); check_fix( @@ -419,6 +422,7 @@ mod tests { ok; fooX(_X) -> %% ^^^^ 💡 error: P1700: head mismatch 'fooX' vs 'food' + %% | Related info: 0:21-25 Mismatched clause name no. bar() -> @@ -448,6 +452,7 @@ mod tests { -module(main). foo(0) -> 1; %% ^^^ 💡 error: P1700: head mismatch 'foo' vs 'boo' + %% | Related info: 0:37-40 Mismatched clause name boo(1) -> 2; boo(2) -> 3. "#, @@ -476,6 +481,7 @@ mod tests { foo(0) -> 1; foo(1,0) -> 2. %% ^^^^^^^^^^^^^ error: P1700: head arity mismatch 2 vs 1 + %% | Related info: 0:21-32 Mismatched clause "#, ); } @@ -488,6 +494,7 @@ mod tests { foo(2,0) -> 3; foo(0) -> 1; %% ^^^^^^^^^^^ error: P1700: head arity mismatch 1 vs 2 + %% | Related info: 0:21-34 Mismatched clause foo(1,0) -> 2. "#, ); @@ -514,6 +521,7 @@ mod tests { (0) -> ok; A(N) -> ok %% ^ 💡 error: P1700: head mismatch 'A' vs '' + %% | Related info: 0:44-53 Mismatched clause name end, F(). "#, diff --git a/crates/ide/src/diagnostics/inefficient_last.rs b/crates/ide/src/diagnostics/inefficient_last.rs index 5c1f6af896..c120d7ca7f 100644 --- a/crates/ide/src/diagnostics/inefficient_last.rs +++ b/crates/ide/src/diagnostics/inefficient_last.rs @@ -38,6 +38,7 @@ pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { }, checker: &|acc, sema, file_id, _ext| { inefficient_last_hd_ssr(acc, sema, file_id); + inefficient_last_nth_ssr(acc, sema, file_id); inefficient_last_pat_ssr(acc, sema, file_id); }, }; @@ -62,6 +63,24 @@ fn inefficient_last_hd_ssr(diags: &mut Vec, sema: &Semantic, file_id }); } +// lists:nth(1, L) is functionally equivalent to hd(L) +fn inefficient_last_nth_ssr(diags: &mut Vec, sema: &Semantic, file_id: FileId) { + let matches = match_pattern_in_file_functions( + sema, + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + file_id, + format!("ssr: lists:nth(1, lists:reverse({LIST_VAR})).").as_str(), + ); + matches.matches.iter().for_each(|m| { + if let Some(diagnostic) = make_diagnostic_hd(sema, file_id, m) { + diags.push(diagnostic) + } + }); +} + fn inefficient_last_pat_ssr(diags: &mut Vec, sema: &Semantic, file_id: FileId) { let matches = match_pattern_in_file_functions( sema, @@ -248,4 +267,50 @@ mod tests { "#]], ) } + + #[test] + fn ignores_inefficient_last_via_nth_when_index_is_not_one() { + check_diagnostics( + r#" + //- /src/inefficient_last.erl + -module(inefficient_last). + + % elp:ignore W0017 (undefined_function) + fn(List) -> lists:nth(3, lists:reverse(List)). + "#, + ) + } + + #[test] + fn detects_inefficient_last_via_nth() { + check_diagnostics( + r#" + //- /src/inefficient_last.erl + -module(inefficient_last). + + % elp:ignore W0017 (undefined_function) + fn(List) -> lists:nth(1, lists:reverse(List)). + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0029: Unnecessary intermediate reverse list allocated. + "#, + ) + } + + #[test] + fn fixes_inefficient_last_via_nth() { + check_fix( + r#" + //- /src/inefficient_last.erl + -module(inefficient_last). + + % elp:ignore W0017 (undefined_function) + fn(List) -> lists:nth(1, lists:re~verse(List)). + "#, + expect![[r#" + -module(inefficient_last). + + % elp:ignore W0017 (undefined_function) + fn(List) -> lists:last(List). + "#]], + ) + } } diff --git a/crates/ide/src/diagnostics/lists_reverse_append.rs b/crates/ide/src/diagnostics/lists_reverse_append.rs new file mode 100644 index 0000000000..96743b6f28 --- /dev/null +++ b/crates/ide/src/diagnostics/lists_reverse_append.rs @@ -0,0 +1,187 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +//! Lint: lists_reverse_append +//! +//! Detect patterns of the form `lists:reverse(_@L) ++ _@T` and suggest `lists:reverse(_@L, _@T)` + +use elp_ide_assists::Assist; +use elp_ide_db::DiagnosticCode; +use elp_ide_db::elp_base_db::FileId; +use elp_ide_db::source_change::SourceChangeBuilder; +use hir::Semantic; + +use crate::diagnostics::Linter; +use crate::diagnostics::SsrPatternsLinter; +use crate::fix; + +pub(crate) struct ListsReverseAppendLinter; + +impl Linter for ListsReverseAppendLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::ListsReverseAppend + } + + fn description(&self) -> &'static str { + "Use `lists:reverse/2` instead of `lists:reverse/1 ++ Tail` for better performance." + } +} + +impl SsrPatternsLinter for ListsReverseAppendLinter { + type Context = (); + + fn patterns(&self) -> Vec<(String, Self::Context)> { + vec![(format!("ssr: lists:reverse({LIST_VAR}) ++ {TAIL_VAR}."), ())] + } + + fn fixes( + &self, + _context: &Self::Context, + matched: &elp_ide_ssr::Match, + sema: &Semantic, + _file_id: FileId, + ) -> Option> { + let list_match = matched.placeholder_text(sema, LIST_VAR)?; + let tail_match = matched.placeholder_text(sema, TAIL_VAR)?; + let mut builder = SourceChangeBuilder::new(matched.range.file_id); + let replacement = format!("lists:reverse({list_match}, {tail_match})"); + let range = matched.range.range; + builder.replace(range, replacement); + let fixes = vec![fix( + "lists_reverse_append", + "Use lists:reverse/2", + builder.finish(), + range, + )]; + Some(fixes) + } +} + +pub(crate) static LINTER: ListsReverseAppendLinter = ListsReverseAppendLinter; + +static LIST_VAR: &str = "_@L"; +static TAIL_VAR: &str = "_@T"; + +#[cfg(test)] +mod tests { + + use expect_test::Expect; + use expect_test::expect; + + use crate::diagnostics::Diagnostic; + use crate::diagnostics::DiagnosticCode; + use crate::tests; + + fn filter(d: &Diagnostic) -> bool { + d.code == DiagnosticCode::ListsReverseAppend + } + + #[track_caller] + fn check_diagnostics(fixture: &str) { + tests::check_filtered_diagnostics(fixture, &filter) + } + + #[track_caller] + fn check_fix(fixture_before: &str, fixture_after: Expect) { + tests::check_fix(fixture_before, fixture_after) + } + + #[test] + fn detects_lists_reverse_append() { + check_diagnostics( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + reverse_and_append(List, Tail) -> + lists:reverse(List) ++ Tail. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0056: Use `lists:reverse/2` instead of `lists:reverse/1 ++ Tail` for better performance. + "#, + ) + } + + #[test] + fn detects_lists_reverse_append_with_variables() { + check_diagnostics( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + process(Items, Acc) -> + lists:reverse(Items) ++ Acc. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0056: Use `lists:reverse/2` instead of `lists:reverse/1 ++ Tail` for better performance. + "#, + ) + } + + #[test] + fn ignores_regular_reverse() { + check_diagnostics( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + reverse_only(List) -> + lists:reverse(List). + "#, + ) + } + + #[test] + fn ignores_regular_append() { + check_diagnostics( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + append_only(A, B) -> + A ++ B. + "#, + ) + } + + #[test] + fn fixes_lists_reverse_append() { + check_fix( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + reverse_and_append(List, Tail) -> + lists:re~verse(List) ++ Tail. + "#, + expect![[r#" + -module(lists_reverse_append). + + reverse_and_append(List, Tail) -> + lists:reverse(List, Tail). + "#]], + ) + } + + #[test] + fn fixes_lists_reverse_append_with_complex_expressions() { + check_fix( + r#" + //- /src/lists_reverse_append.erl + -module(lists_reverse_append). + + process([H|T], Acc) -> + lists:rev~erse([H|T]) ++ [1, 2, 3]. + "#, + expect![[r#" + -module(lists_reverse_append). + + process([H|T], Acc) -> + lists:reverse([H|T], [1, 2, 3]). + "#]], + ) + } +} diff --git a/crates/ide/src/diagnostics/macro_precedence_suprise.rs b/crates/ide/src/diagnostics/macro_precedence_suprise.rs index 49c627da28..6cd5fe2429 100644 --- a/crates/ide/src/diagnostics/macro_precedence_suprise.rs +++ b/crates/ide/src/diagnostics/macro_precedence_suprise.rs @@ -15,14 +15,12 @@ use elp_ide_assists::Assist; use elp_ide_assists::helpers::add_parens_edit; -use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; -use elp_text_edit::TextRange; +use elp_ide_db::text_edit::TextRange; use hir::AnyExpr; use hir::AnyExprRef; use hir::Expr; -use hir::ExprSource; use hir::Semantic; use hir::Strategy; use hir::fold::MacroStrategy; @@ -30,92 +28,106 @@ use hir::fold::ParenStrategy; use hir::fold::ParentId; use hir::fold::fold_file_functions; -use super::Diagnostic; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use super::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; use crate::fix; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: true, - include_tests: true, - default_disabled: false, - }, - checker: &|diags, sema, file_id, _file_kind| { - check_file(diags, sema, &file_id); - }, -}; +#[derive(Debug, Default, Clone, PartialEq)] +pub(crate) struct MacroPrecedenceContext; -/// Look for macro expansions. If we find one, check what the top-level expansion gives us. -fn check_file(acc: &mut Vec, sema: &Semantic, file_id: &FileId) { - let fold_strategy = Strategy { - macros: MacroStrategy::ExpandButIncludeMacroCall, - parens: ParenStrategy::VisibleParens, - }; - let index_strategy = Strategy { - macros: MacroStrategy::Expand, - parens: ParenStrategy::VisibleParens, - }; +pub(crate) struct MacroPrecedenceSupriseLinter; - fold_file_functions(sema, fold_strategy, *file_id, (), &mut |_acc, ctx| { - if let AnyExpr::Expr(Expr::MacroCall { expansion, .. }) = &ctx.item - && let Some((body, _body_map, ast)) = ctx.body_with_expr_source(sema) - { - let visible_parens_body = body.index_with_strategy(index_strategy); - if let Expr::BinaryOp { .. } = &visible_parens_body[*expansion] - && let ParentId::HirIdx(hir_idx) = ctx.parent() - && hir_idx.body_origin == ctx.body_origin - { - // We can have nested macro - // calls, which are not - // visible in the - // visible_parens_body. Report - // on the top-level one only. - let fold_body = body.index_with_strategy(fold_strategy); - match &fold_body.get_any(hir_idx.idx) { - AnyExprRef::Expr(Expr::MacroCall { .. }) => {} - _ => { - if let AnyExprRef::Expr(Expr::BinaryOp { .. }) = - &visible_parens_body.get_any(hir_idx.idx) - { - make_diagnostic(acc, file_id, ast); - } - } - }; - }; - } - }); -} +impl Linter for MacroPrecedenceSupriseLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::MacroPrecedenceEscape + } -fn make_diagnostic(acc: &mut Vec, file_id: &FileId, ast: ExprSource) { - let range = ast.range(); - if range.file_id == *file_id { - let fix = add_parens_fix(*file_id, &range.range); - acc.push( - Diagnostic::new( - DiagnosticCode::MacroPrecedenceEscape, - "The macro expansion can have unexpected precedence here", - range.range, - ) - .with_severity(Severity::Warning) - .with_fixes(Some(vec![fix])), - ); + fn description(&self) -> &'static str { + "The macro expansion can have unexpected precedence here" + } + + fn should_process_generated_files(&self) -> bool { + true } } -fn add_parens_fix(file_id: FileId, range: &TextRange) -> Assist { - let assist_message = "Add parens to macro call".to_string(); - let edit = add_parens_edit(range); - fix( - "macro_precedence_add_parens", - &assist_message, - SourceChange::from_text_edit(file_id, edit.clone()), - *range, - ) +impl GenericLinter for MacroPrecedenceSupriseLinter { + type Context = MacroPrecedenceContext; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let fold_strategy = Strategy { + macros: MacroStrategy::ExpandButIncludeMacroCall, + parens: ParenStrategy::VisibleParens, + }; + let index_strategy = Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::VisibleParens, + }; + + let mut res = Vec::new(); + fold_file_functions(sema, fold_strategy, file_id, (), &mut |_acc, ctx| { + if let AnyExpr::Expr(Expr::MacroCall { expansion, .. }) = &ctx.item + && let Some((body, _body_map, ast)) = ctx.body_with_expr_source(sema) + { + let visible_parens_body = body.index_with_strategy(index_strategy); + if let Expr::BinaryOp { .. } = &visible_parens_body[*expansion] + && let ParentId::HirIdx(hir_idx) = ctx.parent() + && hir_idx.body_origin == ctx.body_origin + { + // We can have nested macro + // calls, which are not + // visible in the + // visible_parens_body. Report + // on the top-level one only. + let fold_body = body.index_with_strategy(fold_strategy); + match &fold_body.get_any(hir_idx.idx) { + AnyExprRef::Expr(Expr::MacroCall { .. }) => {} + _ => { + if let AnyExprRef::Expr(Expr::BinaryOp { .. }) = + &visible_parens_body.get_any(hir_idx.idx) + { + let range = ast.range(); + if range.file_id == file_id { + res.push(GenericLinterMatchContext { + range: range.range, + context: MacroPrecedenceContext, + }); + } + } + } + }; + }; + } + }); + Some(res) + } + + fn fixes( + &self, + _context: &Self::Context, + range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { + let edit = add_parens_edit(&range); + let fix = fix( + "macro_precedence_add_parens", + "Add parens to macro call", + SourceChange::from_text_edit(file_id, edit), + range, + ); + Some(vec![fix]) + } } +pub static LINTER: MacroPrecedenceSupriseLinter = MacroPrecedenceSupriseLinter; + #[cfg(test)] mod tests { diff --git a/crates/ide/src/diagnostics/meck.rs b/crates/ide/src/diagnostics/meck.rs index 1fc115638d..8919ddb770 100644 --- a/crates/ide/src/diagnostics/meck.rs +++ b/crates/ide/src/diagnostics/meck.rs @@ -10,8 +10,8 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChangeBuilder; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; +use elp_ide_db::text_edit::TextRange; +use elp_ide_db::text_edit::TextSize; use hir::AnyExprId; use hir::Expr; use hir::FunctionDef; diff --git a/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs b/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs index 6114658bf2..dd0b677dce 100644 --- a/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs +++ b/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs @@ -1,10 +1,11 @@ /* * Copyright (c) Meta Platforms, Inc. and affiliates. * - * This source code is licensed under both the MIT license found in the - * LICENSE-MIT file in the root directory of this source tree and the Apache + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache * License, Version 2.0 found in the LICENSE-APACHE file in the root directory - * of this source tree. + * of this source tree. You may select, at your option, one of the + * above-listed licenses. */ //! Lint/fix: missing_compile_warn_missing_spec @@ -15,11 +16,12 @@ use elp_ide_assists::helpers::add_compile_option; use elp_ide_assists::helpers::rename_atom_in_compile_attribute; +use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileKind; use elp_ide_db::source_change::SourceChangeBuilder; +use elp_ide_db::text_edit::TextRange; use elp_syntax::AstNode; -use elp_text_edit::TextRange; use fxhash::FxHashSet; use hir::AnyExpr; use hir::CompileOptionId; @@ -36,94 +38,160 @@ use hir::known; use lazy_static::lazy_static; use super::DIAGNOSTIC_WHOLE_FILE_RANGE; -use super::Diagnostic; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; +use crate::diagnostics::Severity; use crate::fix; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: false, - default_disabled: true, - }, - checker: &|diags, sema, file_id, file_kind| { - missing_compile_warn_missing_spec(diags, sema, file_id, file_kind); - }, -}; +pub(crate) struct MissingCompileWarnMissingSpec; -fn missing_compile_warn_missing_spec( - diags: &mut Vec, - sema: &Semantic, - file_id: FileId, - file_kind: FileKind, -) { - match file_kind { - FileKind::Header | FileKind::Other | FileKind::OutsideProjectModel => { - return; - } - _ => {} +impl Linter for MissingCompileWarnMissingSpec { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::MissingCompileWarnMissingSpec } - - let form_list = sema.form_list(file_id); - if form_list.compile_attributes().next().is_none() { - report_diagnostic(sema, None, file_id, (Found::No, None), diags); + fn description(&self) -> &'static str { + "Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced." } - let attributes = form_list - .compile_attributes() - .map(|(idx, compile_attribute)| { - let co = sema.db.compile_body(InFile::new(file_id, idx)); - let is_present = FoldCtx::fold_term( - Strategy { - macros: MacroStrategy::Expand, - parens: ParenStrategy::InvisibleParens, - }, - &co.body, - co.value, - (Found::No, None), - &mut |acc, ctx| match &ctx.item { - AnyExpr::Term(Term::Literal(Literal::Atom(atom))) => { - let name = sema.db.lookup_atom(*atom); - if MISSING_SPEC_ALL_OPTIONS.contains(&name) { - (Found::WarnMissingSpecAll, Some(idx)) - } else if MISSING_SPEC_OPTIONS.contains(&name) { - (Found::WarnMissingSpec, Some(idx)) - } else { - acc - } - } - _ => acc, - }, - ); - (is_present, compile_attribute) - }) - .collect::>(); - - let what = attributes - .iter() - .fold((Found::No, None), |acc, ((present, idx), _)| { - if acc.0 == Found::No { - (*present, *idx) - } else { - acc - } - }); - if what.0 != Found::WarnMissingSpecAll { - // Report on first compile attribute only - if let Some((_, compile_attribute)) = attributes.first() { - let range = compile_attribute - .form_id - .get_ast(sema.db, file_id) - .syntax() - .text_range(); - report_diagnostic(sema, Some(range), file_id, what, diags) + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + Severity::Error + } + fn should_process_test_files(&self) -> bool { + false + } + fn is_enabled(&self) -> bool { + false + } + fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { + let file_kind = sema.db.file_kind(file_id); + match file_kind { + FileKind::Header | FileKind::Other | FileKind::OutsideProjectModel => false, + _ => true, } } } -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Context { + found: Found, + compile_option_id: Option, +} + +impl GenericLinter for MissingCompileWarnMissingSpec { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + let form_list = sema.form_list(file_id); + if form_list.compile_attributes().next().is_none() { + res.push(GenericLinterMatchContext { + range: DIAGNOSTIC_WHOLE_FILE_RANGE, + context: Context { + found: Found::No, + compile_option_id: None, + }, + }); + } + let attributes = form_list + .compile_attributes() + .map(|(idx, compile_attribute)| { + let co = sema.db.compile_body(InFile::new(file_id, idx)); + let is_present = FoldCtx::fold_term( + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + &co.body, + co.value, + (Found::No, None), + &mut |acc, ctx| match &ctx.item { + AnyExpr::Term(Term::Literal(Literal::Atom(atom))) => { + let name = sema.db.lookup_atom(*atom); + if MISSING_SPEC_ALL_OPTIONS.contains(&name) { + (Found::WarnMissingSpecAll, Some(idx)) + } else if MISSING_SPEC_OPTIONS.contains(&name) { + (Found::WarnMissingSpec, Some(idx)) + } else { + acc + } + } + _ => acc, + }, + ); + (is_present, compile_attribute) + }) + .collect::>(); + let what = attributes + .iter() + .fold((Found::No, None), |acc, ((present, idx), _)| { + if acc.0 == Found::No { + (*present, *idx) + } else { + acc + } + }); + if what.0 != Found::WarnMissingSpecAll { + // Report on first compile attribute only + if let Some((_, compile_attribute)) = attributes.first() { + let range = compile_attribute + .form_id + .get_ast(sema.db, file_id) + .syntax() + .text_range(); + res.push(GenericLinterMatchContext { + range, + context: Context { + found: what.0, + compile_option_id: what.1, + }, + }); + } + } + Some(res) + } + + fn fixes( + &self, + context: &Self::Context, + range: TextRange, + sema: &Semantic, + file_id: FileId, + ) -> Option> { + let mut builder = SourceChangeBuilder::new(file_id); + if context.found == Found::No { + add_compile_option(sema, file_id, "warn_missing_spec_all", None, &mut builder); + } else { + // We already have warn_missing_spec, upgrade it to warn_missing_spec_all + if let Some(co_id) = context.compile_option_id { + rename_atom_in_compile_attribute( + sema, + file_id, + &co_id, + "warn_missing_spec", + "warn_missing_spec_all", + &mut builder, + ); + } + } + let edit = builder.finish(); + Some(vec![fix( + "add_warn_missing_spec_all", + "Add compile option 'warn_missing_spec_all'", + edit, + range, + )]) + } +} + +pub static LINTER: MissingCompileWarnMissingSpec = MissingCompileWarnMissingSpec; + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] enum Found { + #[default] No, WarnMissingSpec, WarnMissingSpecAll, @@ -146,43 +214,6 @@ lazy_static! { }; } -fn report_diagnostic( - sema: &Semantic, - range: Option, - file_id: FileId, - what: (Found, Option), - diags: &mut Vec, -) { - let range = range.unwrap_or(DIAGNOSTIC_WHOLE_FILE_RANGE); - - let mut builder = SourceChangeBuilder::new(file_id); - if what.0 == Found::No { - add_compile_option(sema, file_id, "warn_missing_spec_all", None, &mut builder); - } else { - // We already have warn_missing_spec, upgrade it to warn_missing_spec_all - if let Some(co_id) = what.1 { - rename_atom_in_compile_attribute( - sema, - file_id, - &co_id, - "warn_missing_spec", - "warn_missing_spec_all", - &mut builder, - ); - } - } - let edit = builder.finish(); - let d = Diagnostic::new( - crate::diagnostics::DiagnosticCode::MissingCompileWarnMissingSpec, - "Please add \"-compile(warn_missing_spec_all).\" to the module. If exported functions are not all specced, they need to be specced.".to_string(), - range, - ).with_fixes(Some(vec![fix("add_warn_missing_spec_all", - "Add compile option 'warn_missing_spec_all'", - edit, range)])) - .with_ignore_fix(sema, file_id); - diags.push(d); -} - #[cfg(test)] mod tests { @@ -197,8 +228,9 @@ mod tests { #[track_caller] pub(crate) fn check_fix(fixture_before: &str, fixture_after: Expect) { - let config = - DiagnosticsConfig::default().enable(DiagnosticCode::MissingCompileWarnMissingSpec); + let config = DiagnosticsConfig::default() + .disable(DiagnosticCode::NoNoWarnSuppressions) + .enable(DiagnosticCode::MissingCompileWarnMissingSpec); check_fix_with_config(config, fixture_before, fixture_after) } @@ -208,17 +240,17 @@ mod tests { fixture_before: &str, fixture_after: Expect, ) { - let config = - DiagnosticsConfig::default().enable(DiagnosticCode::MissingCompileWarnMissingSpec); + let config = DiagnosticsConfig::default() + .disable(DiagnosticCode::NoNoWarnSuppressions) + .enable(DiagnosticCode::MissingCompileWarnMissingSpec); check_specific_fix_with_config(Some(assist_label), fixture_before, fixture_after, config) } #[track_caller] pub(crate) fn check_diagnostics(fixture: &str) { let config = DiagnosticsConfig::default() - .enable(DiagnosticCode::MissingCompileWarnMissingSpec) .disable(DiagnosticCode::NoNoWarnSuppressions) - .disable(DiagnosticCode::UnspecificInclude); + .enable(DiagnosticCode::MissingCompileWarnMissingSpec); check_diagnostics_with_config(config, fixture) } @@ -367,19 +399,20 @@ mod tests { #[test] fn not_in_generated_file() { - check_diagnostics( + check_diagnostics(&format!( r#" //- /erl/my_app/src/main.erl %% -*- coding: utf-8 -*- %% Automatically generated, do not edit - %% @generated from blah + %% @{} from blah %% To generate, see targets and instructions in local Makefile %% Version source: git -module(main). -eqwalizer(ignore). "#, - ) + "generated" // Separate string, to avoid to mark this module itself as generated + )) } #[test] diff --git a/crates/ide/src/diagnostics/missing_module.rs b/crates/ide/src/diagnostics/missing_module.rs new file mode 100644 index 0000000000..3fcbbf0fb7 --- /dev/null +++ b/crates/ide/src/diagnostics/missing_module.rs @@ -0,0 +1,132 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +// Diagnostic: missing-module +// +// Return a diagnostic if a module does not have a module definition + +use elp_ide_db::elp_base_db::FileId; +use elp_syntax::AstNode; +use elp_syntax::ast; +use hir::Semantic; + +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; +use crate::diagnostics::Severity; + +pub(crate) struct MissingModuleLinter; + +impl Linter for MissingModuleLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::MissingModule + } + + fn description(&self) -> &'static str { + "no module definition" + } + + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> super::Severity { + Severity::Error + } + + fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { + let file_kind = sema.db.file_kind(file_id); + file_kind.is_module() + } +} + +impl GenericLinter for MissingModuleLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let parse = sema.db.parse(file_id); + let mut res = Vec::new(); + + for form in parse.tree().forms() { + match form { + ast::Form::PreprocessorDirective(_) => { + continue; // skip any directives + } + ast::Form::FileAttribute(_) => { + continue; // skip + } + ast::Form::ModuleAttribute(_) => { + break; + } + other_form => { + let range = other_form.syntax().text_range(); + res.push(GenericLinterMatchContext { range, context: () }); + break; + } + } + } + Some(res) + } +} + +pub static LINTER: MissingModuleLinter = MissingModuleLinter; + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn fun_decl_module_decl_ok() { + check_diagnostics( + r#" +-file("main.erl",1). +-define(baz,4). +-module(main). +foo(2)->?baz. +"#, + ); + } + + #[test] + fn fun_decl_module_decl_missing() { + check_diagnostics( + r#" + -file("foo.erl",1). + -define(baz,4). + foo(2)->?baz. +%%^^^^^^^^^^^^^💡 error: L1201: no module definition +"#, + ); + } + + #[test] + fn fun_decl_module_decl_missing_2() { + check_diagnostics( + r#" + baz(1)->4. +%%^^^^^^^^^^💡 error: L1201: no module definition + foo(2)->3. +"#, + ); + } + + #[test] + fn fun_decl_module_decl_after_preprocessor() { + check_diagnostics( + r#" +-ifndef(snmpm_net_if_mt). +-module(main). +-endif. +baz(1)->4. +"#, + ); + } +} diff --git a/crates/ide/src/diagnostics/misspelled_attribute.rs b/crates/ide/src/diagnostics/misspelled_attribute.rs index b3939bc9cf..15ad1dd9d8 100644 --- a/crates/ide/src/diagnostics/misspelled_attribute.rs +++ b/crates/ide/src/diagnostics/misspelled_attribute.rs @@ -10,16 +10,18 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::ast::AstNode; -use elp_syntax::ast::WildAttribute; -use elp_text_edit::TextEdit; -use hir::Attribute; use hir::Semantic; -use super::Diagnostic; +use super::DiagnosticCode; +use super::GenericLinter; +use super::GenericLinterMatchContext; +use super::Linter; +use crate::Assist; use crate::TextRange; use crate::TextSize; -use crate::diagnostics::RelatedInformation; +use crate::diagnostics::Severity; use crate::fix; // Diagnostic: misspelled_attribute @@ -34,26 +36,91 @@ use crate::fix; // ``` // -include_lib("/foo/bar/baz.hrl"). // ``` -pub(crate) fn misspelled_attribute( - sema: &Semantic, - diagnostics: &mut Vec, - file_id: FileId, -) { - let form_list = sema.db.file_form_list(file_id); - let potential_misspellings = form_list.attributes().filter_map(|(id, attr)| { - looks_like_misspelling(attr).map(|suggested_rename| (id, attr, suggested_rename)) - }); - potential_misspellings.for_each(|(_id, attr, suggested_rename)| { + +pub(crate) struct MisspelledAttributeLinter; + +impl Linter for MisspelledAttributeLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::MisspelledAttribute + } + + fn description(&self) -> &'static str { + "misspelled attribute" + } + + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> super::Severity { + Severity::Error + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Context { + attr_name: String, + suggested_rename: String, +} + +impl GenericLinter for MisspelledAttributeLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let form_list = sema.db.file_form_list(file_id); let parsed_file = sema.db.parse(file_id); - let attr_form = attr.form_id.get(&parsed_file.tree()); - diagnostics.push(make_diagnostic( - sema, - file_id, - attr, - attr_form, - suggested_rename, - )) - }) + let mut res = Vec::new(); + + for (_id, attr) in form_list.attributes() { + if let Some(suggested_rename) = looks_like_misspelling(attr) { + let attr_form = attr.form_id.get(&parsed_file.tree()); + if let Some(attr_name_node) = attr_form.name() { + let attr_name_range_with_hyphen = attr_name_node.syntax().text_range(); + let attr_name_range = TextRange::new( + attr_name_range_with_hyphen + .start() + .checked_add(TextSize::of('-')) + .unwrap(), + attr_name_range_with_hyphen.end(), + ); + + res.push(GenericLinterMatchContext { + range: attr_name_range, + context: Context { + attr_name: attr.name.to_string(), + suggested_rename: suggested_rename.to_string(), + }, + }); + } + } + } + Some(res) + } + + fn match_description(&self, context: &Self::Context) -> std::borrow::Cow<'_, str> { + format!( + "misspelled attribute, saw '{}' but expected '{}'", + context.attr_name, context.suggested_rename + ) + .into() + } + + fn fixes( + &self, + context: &Self::Context, + range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { + let edit = TextEdit::replace(range, context.suggested_rename.clone()); + let msg = format!("Change to '{}'", context.suggested_rename); + Some(vec![fix( + "fix_misspelled_attribute", + &msg, + SourceChange::from_text_edit(file_id, edit), + range, + )]) + } } const KNOWN_ATTRIBUTES: &[&str] = &[ @@ -85,7 +152,7 @@ const KNOWN_ATTRIBUTES: &[&str] = &[ "doc", ]; -fn looks_like_misspelling(attr: &Attribute) -> Option<&str> { +fn looks_like_misspelling(attr: &hir::Attribute) -> Option<&str> { let mut suggestions: Vec<(&str, f64)> = KNOWN_ATTRIBUTES .iter() .filter(|&known| &attr.name != known) @@ -103,47 +170,7 @@ fn looks_like_misspelling(attr: &Attribute) -> Option<&str> { .copied() } -fn make_diagnostic( - sema: &Semantic, - file_id: FileId, - attr: &Attribute, - attr_form: WildAttribute, - suggested_rename: &str, -) -> Diagnostic { - // Includes the '-', e.g. "-dialyzer" from `-dialyzer([]).`, but we don't - // want to apply another `.name()`, because for attributes with special - // meanings like `-record(foo, ...).` we would get "foo" - let attr_name_range_with_hyphen = attr_form.name().unwrap().syntax().text_range(); - let attr_name_range = TextRange::new( - attr_name_range_with_hyphen - .start() - .checked_add(TextSize::of('-')) - .unwrap(), - attr_name_range_with_hyphen.end(), - ); - - let edit = TextEdit::replace(attr_name_range, suggested_rename.to_string()); - - Diagnostic::new( - super::DiagnosticCode::MisspelledAttribute, - format!( - "misspelled attribute, saw '{}' but expected '{}'", - attr.name, suggested_rename - ), - attr_name_range, - ) - .with_related(Some(vec![RelatedInformation { - range: attr_name_range, - message: "Misspelled attribute".to_string(), - }])) - .with_fixes(Some(vec![fix( - "fix_misspelled_attribute", - format!("Change misspelled attribute to '{suggested_rename}'").as_str(), - SourceChange::from_text_edit(file_id, edit), - attr_name_range, - )])) - .with_ignore_fix(sema, file_id) -} +pub static LINTER: MisspelledAttributeLinter = MisspelledAttributeLinter; // To run the tests via cargo // cargo test --package elp_ide --lib diff --git a/crates/ide/src/diagnostics/module_mismatch.rs b/crates/ide/src/diagnostics/module_mismatch.rs index c1a57dfbd9..77181b55ef 100644 --- a/crates/ide/src/diagnostics/module_mismatch.rs +++ b/crates/ide/src/diagnostics/module_mismatch.rs @@ -15,11 +15,11 @@ use elp_ide_assists::Assist; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::AstNode; use elp_syntax::SyntaxNode; use elp_syntax::TextRange; use elp_syntax::ast; -use elp_text_edit::TextEdit; use hir::Semantic; use crate::Diagnostic; diff --git a/crates/ide/src/diagnostics/mutable_variable.rs b/crates/ide/src/diagnostics/mutable_variable.rs index 52fe32eccf..6878c90d57 100644 --- a/crates/ide/src/diagnostics/mutable_variable.rs +++ b/crates/ide/src/diagnostics/mutable_variable.rs @@ -27,12 +27,8 @@ // use elp_ide_db::elp_base_db::FileId; -use fxhash::FxHashMap; -use fxhash::FxHashSet; use hir::AnyExpr; use hir::Expr; -use hir::FunctionClauseId; -use hir::PatId; use hir::Semantic; use hir::Strategy; use hir::fold::MacroStrategy; @@ -60,21 +56,7 @@ fn mutable_variable_bug( sema: &Semantic, file_id: FileId, ) -> Option<()> { - let mut bound_vars_by_function: FxHashMap> = - FxHashMap::default(); - let bound_vars = sema.bound_vars_in_pattern_diagnostic(file_id); - bound_vars.iter().for_each(|(function_id, pat_id, _var)| { - bound_vars_by_function - .entry(function_id.value) - .and_modify(|vars| { - vars.insert(pat_id); - }) - .or_insert_with(|| { - let mut vars = FxHashSet::default(); - vars.insert(pat_id); - vars - }); - }); + let bound_vars_by_function = sema.bound_vars_by_function(file_id); sema.def_map(file_id) .get_function_clauses() .for_each(|(_, def)| { diff --git a/crates/ide/src/diagnostics/no_catch.rs b/crates/ide/src/diagnostics/no_catch.rs index 3823131724..a804f248bd 100644 --- a/crates/ide/src/diagnostics/no_catch.rs +++ b/crates/ide/src/diagnostics/no_catch.rs @@ -8,7 +8,8 @@ * above-listed licenses. */ -use elp_ide_db::DiagnosticCode; +// Diagnostic: no-catch +use elp_ide_db::elp_base_db::FileId; use elp_syntax::AstNode; use hir::AnyExpr; use hir::AnyExprId; @@ -20,28 +21,48 @@ use hir::Strategy; use hir::fold::MacroStrategy; use hir::fold::ParenStrategy; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use crate::diagnostics::Diagnostic; -use crate::diagnostics::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoCatch; -const DIAGNOSTIC_MESSAGE: &str = "Avoid `catch`."; -const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; +pub(crate) struct NoCatchLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: true, - default_disabled: false, - }, - checker: &|diagnostics, sema, file_id, _ext| { - sema.for_each_function(file_id, |def| check_function(diagnostics, sema, def)); - }, -}; +impl Linter for NoCatchLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::NoCatch + } -fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &FunctionDef) { + fn description(&self) -> &'static str { + "Avoid `catch`." + } +} + +impl GenericLinter for NoCatchLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + sema.def_map_local(file_id) + .get_functions() + .for_each(|(_, def)| { + check_function(&mut res, sema, def); + }); + Some(res) + } +} + +pub static LINTER: NoCatchLinter = NoCatchLinter; + +fn check_function( + matches: &mut Vec>, + sema: &Semantic, + def: &FunctionDef, +) { let def_fb = def.in_function_body(sema, def); def_fb.fold_function( Strategy { @@ -52,15 +73,19 @@ fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &Func &mut |_acc, clause_id, ctx| { if let AnyExpr::Expr(Expr::Catch { expr: _ }) = ctx.item { let map = def_fb.get_body_map(clause_id); - if let Some(diagnostic) = make_diagnostic(sema, &map, ctx.item_id) { - diagnostics.push(diagnostic); + if let Some(match_context) = make_match_context(sema, &map, ctx.item_id) { + matches.push(match_context); } }; }, ) } -fn make_diagnostic(sema: &Semantic, map: &BodySourceMap, item_id: AnyExprId) -> Option { +fn make_match_context( + sema: &Semantic, + map: &BodySourceMap, + item_id: AnyExprId, +) -> Option> { match item_id { AnyExprId::Expr(expr_id) => { let ast_ptr = map.expr(expr_id)?; @@ -68,9 +93,7 @@ fn make_diagnostic(sema: &Semantic, map: &BodySourceMap, item_id: AnyExprId) -> elp_syntax::ast::Expr::CatchExpr(catch_expr) => { let catch_keyword = catch_expr.syntax().first_token()?; let range = catch_keyword.text_range(); - let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, range) - .with_severity(DIAGNOSTIC_SEVERITY); - Some(diagnostic) + Some(GenericLinterMatchContext { range, context: () }) } _ => None, } @@ -93,7 +116,7 @@ mod tests { catcher(X,Y) -> case catch X/Y of - %% ^^^^^ warning: W0052: Avoid `catch`. + %% ^^^^^ 💡 warning: W0052: Avoid `catch`. {'EXIT', {badarith,_}} -> "uh oh"; N -> N end. diff --git a/crates/ide/src/diagnostics/no_dialyzer_attribute.rs b/crates/ide/src/diagnostics/no_dialyzer_attribute.rs index 4c501c5ad5..9a671e6c65 100644 --- a/crates/ide/src/diagnostics/no_dialyzer_attribute.rs +++ b/crates/ide/src/diagnostics/no_dialyzer_attribute.rs @@ -8,50 +8,55 @@ * above-listed licenses. */ -use elp_ide_db::DiagnosticCode; +// Diagnostic: no-dialyzer-attribute use elp_ide_db::elp_base_db::FileId; use hir::Semantic; use hir::known; -use crate::diagnostics::Diagnostic; -use crate::diagnostics::DiagnosticConditions; -use crate::diagnostics::DiagnosticDescriptor; -use crate::diagnostics::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoDialyzerAttribute; -const DIAGNOSTIC_MESSAGE: &str = "Avoid -dialyzer attribute."; -const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; +pub(crate) struct NoDialyzerAttributeLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: false, - default_disabled: false, - }, - checker: &|diags, sema, file_id, _ext| { - no_dialyzer_attribute(diags, sema, file_id); - }, -}; +impl Linter for NoDialyzerAttributeLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::NoDialyzerAttribute + } -fn no_dialyzer_attribute(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { - let form_list = sema.db.file_form_list(file_id); - form_list.attributes().for_each(|(_idx, attr)| { - if attr.name == known::dialyzer - && let Some(diagnostic) = make_diagnostic(sema, file_id, attr) - { - diagnostics.push(diagnostic); - } - }); + fn description(&self) -> &'static str { + "Avoid using the -dialyzer attribute." + } + + fn should_process_test_files(&self) -> bool { + false + } } -fn make_diagnostic(sema: &Semantic, file_id: FileId, attr: &hir::Attribute) -> Option { - let range = attr.name_range(sema.db, file_id)?; - let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, range) - .with_severity(DIAGNOSTIC_SEVERITY); - Some(diagnostic) +impl GenericLinter for NoDialyzerAttributeLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + let form_list = sema.db.file_form_list(file_id); + form_list.attributes().for_each(|(_idx, attr)| { + if attr.name == known::dialyzer + && let Some(range) = attr.name_range(sema.db, file_id) + { + res.push(GenericLinterMatchContext { range, context: () }) + } + }); + Some(res) + } } +pub static LINTER: NoDialyzerAttributeLinter = NoDialyzerAttributeLinter; + #[cfg(test)] mod tests { @@ -63,7 +68,7 @@ mod tests { r#" -module(main). -dialyzer({nowarn_function, foo/0}). - %% ^^^^^^^^^ warning: W0048: Avoid -dialyzer attribute. + %% ^^^^^^^^^ 💡 warning: W0048: Avoid using the -dialyzer attribute. "#, ) } diff --git a/crates/ide/src/diagnostics/no_error_logger.rs b/crates/ide/src/diagnostics/no_error_logger.rs index 08e94133d0..46f3d15e3e 100644 --- a/crates/ide/src/diagnostics/no_error_logger.rs +++ b/crates/ide/src/diagnostics/no_error_logger.rs @@ -8,6 +8,9 @@ * above-listed licenses. */ +use elp_ide_db::elp_base_db::FileId; +use hir::Semantic; + use crate::codemod_helpers::FunctionMatch; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; @@ -23,9 +26,12 @@ impl Linter for NoErrorLoggerLinter { fn description(&self) -> &'static str { "The `error_logger` module is deprecated." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Error } + fn should_process_test_files(&self) -> bool { + false + } } impl FunctionCallLinter for NoErrorLoggerLinter { diff --git a/crates/ide/src/diagnostics/no_nowarn_suppressions.rs b/crates/ide/src/diagnostics/no_nowarn_suppressions.rs index ef258779cd..a0b36f87b8 100644 --- a/crates/ide/src/diagnostics/no_nowarn_suppressions.rs +++ b/crates/ide/src/diagnostics/no_nowarn_suppressions.rs @@ -16,62 +16,63 @@ use hir::Semantic; use lazy_static::lazy_static; use regex::Regex; -use crate::diagnostics::Diagnostic; use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::DiagnosticConditions; -use crate::diagnostics::DiagnosticDescriptor; -use crate::diagnostics::Severity; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoNoWarnSuppressions; -const DIAGNOSTIC_MESSAGE: &str = "Do not suppress compiler warnings at module level."; -const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; +pub(crate) struct NoNoWarnSuppressionsLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: true, - default_disabled: false, - }, - checker: &|diags, sema, file_id, _ext| { - no_warn_suppression(diags, sema, file_id); - }, -}; - -fn no_warn_suppression(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { - lazy_static! { - static ref NOWARN_REGEX: Regex = Regex::new(r"^nowarn_").unwrap(); +impl Linter for NoNoWarnSuppressionsLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::NoNoWarnSuppressions } - let form_list = sema.db.file_form_list(file_id); - for (_compile_option_idx, compile_option) in form_list.compile_attributes() { - let attr = compile_option.form_id.get_ast(sema.db, file_id); - if let Some(expr) = attr.options() { - // Blindly search for any atom matching the nowarn_ prefix - for n in expr.syntax().descendants() { - match_ast! { - match n { - ast::Atom(atom) => { - if let Some(atom_text) = atom.text() - && NOWARN_REGEX.is_match(&atom_text) { - let diagnostic = Diagnostic::new( - DIAGNOSTIC_CODE, - DIAGNOSTIC_MESSAGE, - atom.syntax().text_range(), - ) - .with_ignore_fix(sema, file_id) - .with_severity(DIAGNOSTIC_SEVERITY); - diagnostics.push(diagnostic); - } - }, - _ => {} + fn description(&self) -> &'static str { + "Do not suppress compiler warnings at module level." + } +} + +impl GenericLinter for NoNoWarnSuppressionsLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + lazy_static! { + static ref NOWARN_REGEX: Regex = Regex::new(r"^nowarn_").unwrap(); + } + + let mut res = Vec::new(); + let form_list = sema.db.file_form_list(file_id); + for (_compile_option_idx, compile_option) in form_list.compile_attributes() { + let attr = compile_option.form_id.get_ast(sema.db, file_id); + if let Some(expr) = attr.options() { + // Blindly search for any atom matching the nowarn_ prefix + for n in expr.syntax().descendants() { + match_ast! { + match n { + ast::Atom(atom) => { + if let Some(atom_text) = atom.text() + && NOWARN_REGEX.is_match(&atom_text) { + let range = atom.syntax().text_range(); + res.push(GenericLinterMatchContext { range, context: () }); + } + }, + _ => {} + } } } } } + Some(res) } } +pub static LINTER: NoNoWarnSuppressionsLinter = NoNoWarnSuppressionsLinter; + #[cfg(test)] mod tests { use crate::tests; diff --git a/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs b/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs index d80751c946..63b0a450cb 100644 --- a/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs +++ b/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs @@ -15,8 +15,8 @@ use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChangeBuilder; +use elp_ide_db::text_edit::TextRange; use elp_syntax::AstNode; -use elp_text_edit::TextRange; use hir::AnyExpr; use hir::BasedInteger; use hir::Expr; diff --git a/crates/ide/src/diagnostics/edoc.rs b/crates/ide/src/diagnostics/old_edoc_syntax.rs similarity index 92% rename from crates/ide/src/diagnostics/edoc.rs rename to crates/ide/src/diagnostics/old_edoc_syntax.rs index 53858e7cca..da4c3f2430 100644 --- a/crates/ide/src/diagnostics/edoc.rs +++ b/crates/ide/src/diagnostics/old_edoc_syntax.rs @@ -8,96 +8,138 @@ * above-listed licenses. */ -// Diagnostic: edoc +// Diagnostic: old_edoc_syntax +use elp_ide_assists::Assist; use elp_ide_assists::helpers; use elp_ide_assists::helpers::extend_range; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChangeBuilder; +use elp_ide_db::text_edit::TextRange; +use elp_ide_db::text_edit::TextSize; use elp_syntax::AstNode; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; +use elp_syntax::ast; use fxhash::FxHashSet; use hir::Attribute; +use hir::InFileAstPtr; use hir::Semantic; use hir::edoc::EdocHeader; use hir::edoc::EdocHeaderKind; use hir::known; -use super::Diagnostic; use super::DiagnosticCode; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use super::Severity; +use super::GenericLinter; +use super::GenericLinterMatchContext; +use super::Linter; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::OldEdocSyntax; -const DIAGNOSTIC_MESSAGE: &str = "EDoc style comments are deprecated. Please use Markdown instead."; -const CONVERT_FIX_ID: &str = "convert_to_markdown"; -const CONVERT_FIX_LABEL: &str = "Convert to Markdown"; +pub(crate) struct OldEdocSyntaxLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: true, - default_disabled: false, - }, - checker: &|diags, sema, file_id, _ext| { - check(diags, sema, file_id); - }, -}; +impl Linter for OldEdocSyntaxLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::OldEdocSyntax + } -fn check(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { - if let Some(comments) = sema.file_edoc_comments(file_id) { - for header in comments.values() { - if let Some(doc) = &header.doc { - if let Some(doc_start) = header.start() { - diagnostics.push(old_edoc_syntax_diagnostic( - sema, file_id, doc.range, header, doc_start, - )); - } - } else if let Some(equiv) = &header.equiv { - if let Some(doc_start) = header.start() { - diagnostics.push(old_edoc_syntax_diagnostic( - sema, - file_id, - equiv.range, - header, - doc_start, - )); - } - } else if let Some(deprecated) = &header.deprecated { - if let Some(doc_start) = header.start() { - diagnostics.push(old_edoc_syntax_diagnostic( - sema, - file_id, - deprecated.range, - header, - doc_start, - )); - } - } else if let Some(hidden) = &header.hidden - && let Some(doc_start) = header.start() - { - diagnostics.push(old_edoc_syntax_diagnostic( - sema, - file_id, - hidden.range, - header, - doc_start, - )); - } - } + fn description(&self) -> &'static str { + "EDoc style comments are deprecated. Please use Markdown instead." + } + + fn should_process_test_files(&self) -> bool { + false } } -fn old_edoc_syntax_diagnostic( +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Context { + header_ptr: Option>, + doc_start: TextSize, +} + +impl GenericLinter for OldEdocSyntaxLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + if let Some(comments) = sema.file_edoc_comments(file_id) { + for (header_ptr, header) in comments.iter() { + if let Some(doc) = &header.doc { + if let Some(doc_start) = header.start() { + res.push(GenericLinterMatchContext { + range: doc.range, + context: Context { + header_ptr: Some(*header_ptr), + doc_start, + }, + }); + } + } else if let Some(equiv) = &header.equiv { + if let Some(doc_start) = header.start() { + res.push(GenericLinterMatchContext { + range: equiv.range, + context: Context { + header_ptr: Some(*header_ptr), + doc_start, + }, + }); + } + } else if let Some(deprecated) = &header.deprecated { + if let Some(doc_start) = header.start() { + res.push(GenericLinterMatchContext { + range: deprecated.range, + context: Context { + header_ptr: Some(*header_ptr), + doc_start, + }, + }); + } + } else if let Some(hidden) = &header.hidden + && let Some(doc_start) = header.start() + { + res.push(GenericLinterMatchContext { + range: hidden.range, + context: Context { + header_ptr: Some(*header_ptr), + doc_start, + }, + }); + } + } + } + Some(res) + } + + fn fixes( + &self, + context: &Self::Context, + range: TextRange, + sema: &Semantic, + file_id: FileId, + ) -> Option> { + let comments = sema.file_edoc_comments(file_id)?; + let header_ptr = context.header_ptr.as_ref()?; + let header = comments.get(header_ptr)?; + Some(vec![old_edoc_syntax_fix( + sema, + file_id, + header, + context.doc_start, + range, + )]) + } +} + +pub static LINTER: OldEdocSyntaxLinter = OldEdocSyntaxLinter; + +fn old_edoc_syntax_fix( sema: &Semantic, file_id: FileId, - show_range: TextRange, header: &EdocHeader, start_offset: TextSize, -) -> Diagnostic { + range: TextRange, +) -> Assist { let eep59_insert_offset = match header.kind { EdocHeaderKind::Module => { helpers::moduledoc_insert_offset(sema, file_id).unwrap_or(start_offset) @@ -135,17 +177,12 @@ fn old_edoc_syntax_diagnostic( } builder.insert(eep59_insert_offset, header.to_eep59()); let source_change = builder.finish(); - let fix = crate::fix(CONVERT_FIX_ID, CONVERT_FIX_LABEL, source_change, show_range); - Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, show_range) - .with_severity(severity(sema, file_id)) - .with_fixes(Some(vec![fix])) -} - -fn severity(sema: &Semantic, file_id: FileId) -> Severity { - match sema.db.is_test_suite_or_test_helper(file_id) { - Some(true) => Severity::WeakWarning, - _ => Severity::Warning, - } + crate::fix( + "convert_to_markdown", + "Convert to Markdown", + source_change, + range, + ) } fn author_exists(author: &str, authors: &FxHashSet) -> bool { @@ -257,22 +294,6 @@ mod tests { ) } - #[test] - fn test_function_doc_in_test_file() { - check_diagnostics( - r#" - //- /test/main_SUITE.erl extra:test - -module(main_SUITE). - %% @doc This is the main function documentation. - %% ^^^^ 💡 weak: W0038: EDoc style comments are deprecated. Please use Markdown instead. - main() -> - dep(). - - dep() -> ok. - "#, - ) - } - #[test] fn test_function_doc_different_arities() { check_diagnostics( diff --git a/crates/ide/src/diagnostics/record_tuple_match.rs b/crates/ide/src/diagnostics/record_tuple_match.rs index 5606dce89a..9756bfeb73 100644 --- a/crates/ide/src/diagnostics/record_tuple_match.rs +++ b/crates/ide/src/diagnostics/record_tuple_match.rs @@ -15,7 +15,7 @@ use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::FileRange; -use elp_text_edit::TextRange; +use elp_ide_db::text_edit::TextRange; use hir::AnyExpr; use hir::FunctionDef; use hir::Literal; diff --git a/crates/ide/src/diagnostics/replace_call.rs b/crates/ide/src/diagnostics/replace_call.rs index e83afcb985..fa16acf356 100644 --- a/crates/ide/src/diagnostics/replace_call.rs +++ b/crates/ide/src/diagnostics/replace_call.rs @@ -15,9 +15,9 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::TextRange; use elp_syntax::ast; -use elp_text_edit::TextEdit; use hir::AnyExprId; use hir::CallTarget; use hir::Expr; diff --git a/crates/ide/src/diagnostics/replace_in_spec.rs b/crates/ide/src/diagnostics/replace_in_spec.rs index 12d8aa3f64..a5df5217e2 100644 --- a/crates/ide/src/diagnostics/replace_in_spec.rs +++ b/crates/ide/src/diagnostics/replace_in_spec.rs @@ -16,8 +16,8 @@ use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::SmolStr; -use elp_text_edit::TextEdit; use fxhash::FxHashSet; use hir::AnyExpr; use hir::InFile; diff --git a/crates/ide/src/diagnostics/trivial_match.rs b/crates/ide/src/diagnostics/trivial_match.rs index 56d3881a9a..705cc22d7e 100644 --- a/crates/ide/src/diagnostics/trivial_match.rs +++ b/crates/ide/src/diagnostics/trivial_match.rs @@ -17,9 +17,9 @@ use std::collections::HashMap; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::TextRange; use elp_syntax::ast; -use elp_text_edit::TextEdit; use hir::AnyExpr; use hir::AnyExprId; use hir::BinarySeg; diff --git a/crates/ide/src/diagnostics/unavailable_type.rs b/crates/ide/src/diagnostics/unavailable_type.rs new file mode 100644 index 0000000000..6f2f52c122 --- /dev/null +++ b/crates/ide/src/diagnostics/unavailable_type.rs @@ -0,0 +1,340 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +// Diagnostic: unavailable-type +// +// Return a warning when referring to a type which is not defined in the module's dependencies. +// This diagnostic checks if a type referenced in specs, type definitions, or opaque types exists in: +// 1. The current module (local types) +// 2. Built-in Erlang types +// 3. Types from the module's application dependencies (including OTP apps) + +use std::borrow::Cow; + +use elp_ide_db::elp_base_db::AppData; +use elp_ide_db::elp_base_db::FileId; +use elp_project_model::AppName; +use hir::AnyExpr; +use hir::Callback; +use hir::InFile; +use hir::Record; +use hir::Semantic; +use hir::Spec; +use hir::Strategy; +use hir::TypeAlias; +use hir::TypeExpr; +use hir::fold::Fold; +use hir::fold::MacroStrategy; +use hir::fold::ParenStrategy; + +use super::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; + +pub(crate) struct UnavailableTypeLinter; + +impl Linter for UnavailableTypeLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::UnavailableType + } + fn description(&self) -> &'static str { + "Type is not available through dependencies." + } + fn is_enabled(&self) -> bool { + false + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Context { + type_label: String, + defining_app: String, + referencing_app: String, + referencing_target: String, +} + +impl GenericLinter for UnavailableTypeLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + // Early return if we don't have app data - can't determine type availability + let referencing_app_data = sema.db.file_app_data(file_id)?; + + // Extract target once here - if not available, can't create diagnostics anyway + let referencing_target = referencing_app_data.buck_target_name.as_ref()?; + + let mut res = Vec::new(); + let form_list = sema.form_list(file_id); + + // Check -spec attributes + for (spec_id, _spec) in form_list.specs() { + check_spec( + &mut res, + sema, + file_id, + &referencing_app_data, + referencing_target, + spec_id, + ); + } + + // Check -type and -opaque attributes + for (type_alias_id, _type_alias) in form_list.type_aliases() { + check_type_alias( + &mut res, + sema, + file_id, + &referencing_app_data, + referencing_target, + type_alias_id, + ); + } + + // Check -callback attributes + for (callback_id, _callback) in form_list.callback_attributes() { + check_callback( + &mut res, + sema, + file_id, + &referencing_app_data, + referencing_target, + callback_id, + ); + } + + // Check -record attributes + for (record_id, _record) in form_list.records() { + check_record( + &mut res, + sema, + file_id, + &referencing_app_data, + referencing_target, + record_id, + ); + } + + Some(res) + } + + fn match_description(&self, context: &Self::Context) -> Cow<'_, str> { + Cow::Owned(format!( + "The type '{}' is defined in application '{}', but the application is not a dependency of '{}' (defined in '{}').", + context.type_label, + context.defining_app, + context.referencing_app, + context.referencing_target + )) + } +} + +pub static LINTER: UnavailableTypeLinter = UnavailableTypeLinter; + +fn check_spec( + matches: &mut Vec>, + sema: &Semantic, + file_id: FileId, + referencing_app_data: &AppData, + referencing_target: &String, + spec_id: hir::SpecId, +) { + let spec_id = InFile::new(file_id, spec_id); + let spec_body = sema.db.spec_body(spec_id); + + Spec::fold( + sema, + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + spec_id, + (), + &mut |_acc, ctx| { + check_type_call( + matches, + sema, + file_id, + referencing_app_data, + referencing_target, + &ctx, + &spec_body.body, + ); + }, + ); +} + +fn check_type_alias( + matches: &mut Vec>, + sema: &Semantic, + file_id: FileId, + referencing_app_data: &AppData, + referencing_target: &String, + type_alias_id: hir::TypeAliasId, +) { + let type_alias_id = InFile::new(file_id, type_alias_id); + let type_body = sema.db.type_body(type_alias_id); + + TypeAlias::fold( + sema, + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + type_alias_id, + (), + &mut |_acc, ctx| { + check_type_call( + matches, + sema, + file_id, + referencing_app_data, + referencing_target, + &ctx, + &type_body.body, + ); + }, + ); +} + +fn check_callback( + matches: &mut Vec>, + sema: &Semantic, + file_id: FileId, + referencing_app_data: &AppData, + referencing_target: &String, + callback_id: hir::CallbackId, +) { + let callback_id = InFile::new(file_id, callback_id); + let callback_body = sema.db.callback_body(callback_id); + + Callback::fold( + sema, + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + callback_id, + (), + &mut |_acc, ctx| { + check_type_call( + matches, + sema, + file_id, + referencing_app_data, + referencing_target, + &ctx, + &callback_body.body, + ); + }, + ); +} + +fn check_record( + matches: &mut Vec>, + sema: &Semantic, + file_id: FileId, + referencing_app_data: &AppData, + referencing_target: &String, + record_id: hir::RecordId, +) { + let record_id = InFile::new(file_id, record_id); + let record_body = sema.db.record_body(record_id); + + Record::fold( + sema, + Strategy { + macros: MacroStrategy::Expand, + parens: ParenStrategy::InvisibleParens, + }, + record_id, + (), + &mut |_acc, ctx| { + check_type_call( + matches, + sema, + file_id, + referencing_app_data, + referencing_target, + &ctx, + &record_body.body, + ); + }, + ); +} + +fn is_type_available( + sema: &Semantic, + referencing_app_data: &AppData, + referencing_target: &String, + referencing_app_name: &AppName, + defining_app_name: &AppName, +) -> bool { + // Types from the same app are always available + if referencing_app_name == defining_app_name { + return true; + } + + // Check if type is available through dependencies + if let Some(include_mapping) = &sema + .db + .project_data(referencing_app_data.project_id) + .include_mapping + { + include_mapping.is_dep(referencing_target, defining_app_name) + } else { + // Can't determine - be conservative and allow it + true + } +} + +fn check_type_call( + matches: &mut Vec>, + sema: &Semantic, + file_id: FileId, + referencing_app_data: &AppData, + referencing_target: &String, + ctx: &hir::fold::AnyCallBackCtx<'_>, + body: &hir::Body, +) -> Option<()> { + if let AnyExpr::TypeExpr(TypeExpr::Call { target, args }) = &ctx.item { + let arity = args.len() as u32; + let target_label = target.label(arity, sema, body)?; + let target_range = target.range(sema, body)?; + let type_alias_def = target.resolve_call(arity, sema, file_id, body)?; + let defining_file_id = type_alias_def.file.file_id; + let defining_app_data = sema.db.file_app_data(defining_file_id)?; + let defining_app_name = &defining_app_data.name; + let referencing_app_name = &referencing_app_data.name; + + if !is_type_available( + sema, + referencing_app_data, + referencing_target, + referencing_app_name, + defining_app_name, + ) { + matches.push(GenericLinterMatchContext { + range: target_range.range, + context: Context { + type_label: target_label.to_string(), + defining_app: defining_app_name.to_string(), + referencing_app: referencing_app_name.to_string(), + referencing_target: referencing_target.to_string(), + }, + }); + } + } + Some(()) +} diff --git a/crates/ide/src/diagnostics/undefined_function.rs b/crates/ide/src/diagnostics/undefined_function.rs index 6472a08f07..52fa2cfa3c 100644 --- a/crates/ide/src/diagnostics/undefined_function.rs +++ b/crates/ide/src/diagnostics/undefined_function.rs @@ -43,6 +43,13 @@ impl Linter for UndefinedFunctionLinter { fn should_process_generated_files(&self) -> bool { true } + // Ideally, we would like to report undefined functions in all files, but + // there are too many false positives in test files to do so. + // This is often due to mocked modules and test suite cleverness. + // We can revisit this decision in the future. See T249044930. + fn should_process_test_files(&self) -> bool { + false + } } impl FunctionCallLinter for UndefinedFunctionLinter { @@ -80,19 +87,45 @@ impl FunctionCallLinter for UndefinedFunctionLinter { return None; } - if sema.is_atom_named(name, &known::module_info) && (arity == 0 || arity == 1) - || sema - .resolve_module_expr(def_fb.file_id(), module) - .is_some_and(|module| is_automatically_added(sema, module, name, arity)) - { + if sema.is_atom_named(name, &known::module_info) && (arity == 0 || arity == 1) { return None; } - match context - .target - .resolve_call(arity, sema, def_fb.file_id(), &def_fb.body()) - { - Some(_) => None, - None => Some(context.target.label(arity, sema, &def_fb.body())), + + // Try to resolve the module expression to a static module + let resolved_module = sema.resolve_module_expr(def_fb.file_id(), module); + + match resolved_module { + Some(resolved_module) => { + // Module exists, check if the function exists + match context.target.resolve_call( + arity, + sema, + def_fb.file_id(), + &def_fb.body(), + ) { + Some(_) => None, + None => { + // Function doesn't exist - check if it would be automatically added + if is_automatically_added(sema, resolved_module, name, arity) { + return None; + } + Some(context.target.label(arity, sema, &def_fb.body())) + } + } + } + None => { + // Module cannot be resolved. If the module expression is a static atom, + // it means the module doesn't exist and we should report it. + // If it's a dynamic expression (e.g., a variable or function call), + // we can't determine at compile time whether it's defined. + if module.as_atom().is_some() { + // Static module name that doesn't exist - report as undefined + Some(context.target.label(arity, sema, &def_fb.body())) + } else { + // Dynamic module expression - can't determine at compile time + None + } + } } } // Diagnostic L1227 already covers the case for local calls, so avoid double-reporting @@ -413,4 +446,29 @@ exists() -> ok. "#, ) } + + #[test] + fn test_macro_remote_call() { + check_diagnostics( + r#" + //- /src/main.erl + -module(main). + -export([do/0]). + -define(COUNT_BACKEND, (count_backend:count_module())). + -define(COUNT(Name), ?COUNT_BACKEND:count(Name)). + + do() -> + ?COUNT('error.count'). + + //- /src/stats.erl + -module(stats). + -export([count/1]). + count(_) -> ok. + //- /src/count_backend.erl + -module(count_backend). + -export([count_module/0]). + count_module() -> ?MODULE. + "#, + ) + } } diff --git a/crates/ide/src/diagnostics/undefined_macro.rs b/crates/ide/src/diagnostics/undefined_macro.rs index c90e2d1285..51ee0ceaff 100644 --- a/crates/ide/src/diagnostics/undefined_macro.rs +++ b/crates/ide/src/diagnostics/undefined_macro.rs @@ -15,7 +15,7 @@ use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::path_for_file; use elp_ide_db::source_change::SourceChange; -use elp_text_edit::TextEdit; +use elp_ide_db::text_edit::TextEdit; use fxhash::FxHashSet; use hir::Semantic; use lazy_static::lazy_static; diff --git a/crates/ide/src/diagnostics/undocumented_function.rs b/crates/ide/src/diagnostics/undocumented_function.rs index c895b52622..f1d0aa06b5 100644 --- a/crates/ide/src/diagnostics/undocumented_function.rs +++ b/crates/ide/src/diagnostics/undocumented_function.rs @@ -8,9 +8,10 @@ * above-listed licenses. */ -use elp_ide_assists::helpers::unwrap_parens; // Diagnostic: undocumented-function +use elp_ide_assists::helpers::unwrap_parens; use elp_ide_db::elp_base_db::FileId; +use elp_ide_db::text_edit::TextRange; use elp_syntax::ast; use elp_syntax::ast::Atom; use fxhash::FxHashSet; @@ -21,37 +22,66 @@ use hir::Semantic; use hir::form_list::ModuleDocAttribute; use hir::known; -use super::Diagnostic; -use super::DiagnosticCode; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use super::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; +use crate::diagnostics::Severity; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::UndocumentedFunction; -const DIAGNOSTIC_MESSAGE: &str = "The function is non-trivial, exported, but not documented."; -const DIAGNOSTIC_SEVERITY: Severity = Severity::WeakWarning; +pub(crate) struct UndocumentedFunctionLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: false, - default_disabled: true, - }, - checker: &|diags, sema, file_id, _ext| { - check(diags, sema, file_id); - }, -}; +impl Linter for UndocumentedFunctionLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::UndocumentedFunction + } -fn check(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { - let callbacks = sema.resolve_callbacks(file_id); - if !contains_moduledoc_hidden_attribute(sema, file_id) { - sema.def_map_local(file_id) - .get_functions() - .for_each(|(_arity, def)| check_function(diagnostics, sema, def, &callbacks)); + fn description(&self) -> &'static str { + "The function is non-trivial, exported, but not documented." + } + + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + Severity::WeakWarning + } + + fn is_enabled(&self) -> bool { + false + } + + fn should_process_test_files(&self) -> bool { + false } } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Context { + range: TextRange, +} + +impl GenericLinter for UndocumentedFunctionLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + let callbacks = sema.resolve_callbacks(file_id); + if !contains_moduledoc_hidden_attribute(sema, file_id) { + sema.def_map_local(file_id) + .get_functions() + .for_each(|(_arity, def)| { + if let Some(match_context) = check_function(sema, def, &callbacks) { + res.push(match_context); + } + }); + } + Some(res) + } +} + +pub static LINTER: UndocumentedFunctionLinter = UndocumentedFunctionLinter; + fn contains_moduledoc_hidden_attribute(sema: &Semantic, file_id: FileId) -> bool { sema.form_list(file_id) .moduledoc_attributes() @@ -87,20 +117,22 @@ fn function_should_be_checked( } fn check_function( - diagnostics: &mut Vec, sema: &Semantic, def: &FunctionDef, callbacks: &FxHashSet, -) { +) -> Option> { if function_should_be_checked(sema, def, callbacks) && !def.has_doc_attribute() && !def.has_doc_attribute_metadata() && def.edoc_comments(sema.db).is_none() && let Some(name_range) = def.name_range(sema.db) { - let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, name_range) - .with_severity(DIAGNOSTIC_SEVERITY); - diagnostics.push(diagnostic); + Some(GenericLinterMatchContext { + range: name_range, + context: Context { range: name_range }, + }) + } else { + None } } @@ -130,7 +162,7 @@ mod tests { -module(main). -export([main/0]). main() -> - %% ^^^^ weak: W0040: The function is non-trivial, exported, but not documented. + %% ^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -147,7 +179,7 @@ mod tests { -module(main). -export([main/0]). main() -> - %% ^^^^ weak: W0040: The function is non-trivial, exported, but not documented. + %% ^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -317,7 +349,7 @@ mod tests { -export([handle_call/1]). main() -> - %%<^ weak: W0040: The function is non-trivial, exported, but not documented. + %%<^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -349,7 +381,7 @@ mod tests { ok. complex() -> - %%<^^^^ weak: W0040: The function is non-trivial, exported, but not documented. + %%<^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -370,7 +402,7 @@ mod tests { ok. complex(a) -> - %%<^^^^ weak: W0040: The function is non-trivial, exported, but not documented. + %%<^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. [ok]; complex(b) -> [ok, diff --git a/crates/ide/src/diagnostics/undocumented_module.rs b/crates/ide/src/diagnostics/undocumented_module.rs index ad440d5acd..1b2c80cfc9 100644 --- a/crates/ide/src/diagnostics/undocumented_module.rs +++ b/crates/ide/src/diagnostics/undocumented_module.rs @@ -12,64 +12,94 @@ use elp_ide_assists::Assist; use elp_ide_assists::helpers; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChangeBuilder; +use elp_ide_db::text_edit::TextRange; use elp_syntax::AstNode; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; use hir::Semantic; -use super::Diagnostic; -use super::DiagnosticCode; -use super::DiagnosticConditions; -use super::DiagnosticDescriptor; -use super::Severity; +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; +use crate::diagnostics::Severity; +use crate::fix; -const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::UndocumentedModule; -const DIAGNOSTIC_MESSAGE: &str = "The module is not documented."; -const DIAGNOSTIC_SEVERITY: Severity = Severity::WeakWarning; -const FIX_ID: &str = "add_moduledoc_false"; -const FIX_LABEL: &str = "Add `-moduledoc false.` attribute"; +pub(crate) struct UndocumentedModuleLinter; -pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { - conditions: DiagnosticConditions { - experimental: false, - include_generated: false, - include_tests: false, - default_disabled: true, - }, - checker: &|diags, sema, file_id, _ext| { - check(diags, sema, file_id); - }, -}; - -fn check(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) -> Option<()> { - let def_map = sema.def_map_local(file_id); - let module_attribute = sema.module_attribute(file_id)?; - let module_has_no_docs = def_map.moduledoc.is_empty() - && def_map.moduledoc_metadata.is_empty() - && sema - .module_edoc_header(file_id, &module_attribute) - .is_none(); - if module_has_no_docs { - let module_name = module_attribute.name()?; - let module_name_range = module_name.syntax().text_range(); - let moduledoc_insert_offset = helpers::moduledoc_insert_offset(sema, file_id)?; - let fixes = fixes(file_id, moduledoc_insert_offset, module_name_range); - let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, module_name_range) - .with_severity(DIAGNOSTIC_SEVERITY) - .with_fixes(Some(fixes)); - diagnostics.push(diagnostic); +impl Linter for UndocumentedModuleLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::UndocumentedModule + } + + fn description(&self) -> &'static str { + "The module is not documented." + } + + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + Severity::WeakWarning + } + + fn is_enabled(&self) -> bool { + false + } + + fn should_process_test_files(&self) -> bool { + false } - Some(()) } -fn fixes(file_id: FileId, insert_offset: TextSize, show_range: TextRange) -> Vec { - let mut builder = SourceChangeBuilder::new(file_id); - builder.insert(insert_offset, "-moduledoc false.\n"); - let source_change = builder.finish(); - let fix = crate::fix(FIX_ID, FIX_LABEL, source_change, show_range); - vec![fix] +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Context; + +impl GenericLinter for UndocumentedModuleLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let mut res = Vec::new(); + let def_map = sema.def_map_local(file_id); + let module_attribute = sema.module_attribute(file_id)?; + let module_has_no_docs = def_map.moduledoc.is_empty() + && def_map.moduledoc_metadata.is_empty() + && sema + .module_edoc_header(file_id, &module_attribute) + .is_none(); + if module_has_no_docs { + let module_name = module_attribute.name()?; + let module_name_range = module_name.syntax().text_range(); + res.push(GenericLinterMatchContext { + range: module_name_range, + context: Context, + }); + } + Some(res) + } + + fn fixes( + &self, + _context: &Context, + range: TextRange, + sema: &Semantic, + file_id: FileId, + ) -> Option> { + let insert_offset = helpers::moduledoc_insert_offset(sema, file_id)?; + let mut builder = SourceChangeBuilder::new(file_id); + builder.insert(insert_offset, "-moduledoc false.\n"); + let source_change = builder.finish(); + let fix = fix( + "add_moduledoc_false", + "Add `-moduledoc false.` attribute", + source_change, + range, + ); + Some(vec![fix]) + } } +pub static LINTER: UndocumentedModuleLinter = UndocumentedModuleLinter; + #[cfg(test)] mod tests { diff --git a/crates/ide/src/diagnostics/unexported_function.rs b/crates/ide/src/diagnostics/unexported_function.rs index 7aae73a9f1..2e1ebf7f54 100644 --- a/crates/ide/src/diagnostics/unexported_function.rs +++ b/crates/ide/src/diagnostics/unexported_function.rs @@ -27,7 +27,7 @@ use crate::codemod_helpers::CheckCallCtx; use crate::codemod_helpers::MatchCtx; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; -// @fb-only +// @fb-only: use crate::diagnostics::meta_only; use crate::fix; use crate::lazy_function_matches; @@ -45,9 +45,9 @@ impl Linter for UnexportedFunctionLinter { } #[rustfmt::skip] fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only + // @fb-only: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { true // @oss-only - // @fb-only + // @fb-only: meta_only::should_check_for_unexported(sema, file_id) } } diff --git a/crates/ide/src/diagnostics/unnecessary_fold_to_build_map.rs b/crates/ide/src/diagnostics/unnecessary_fold_to_build_map.rs index 5fea930a9a..40f16da4d8 100644 --- a/crates/ide/src/diagnostics/unnecessary_fold_to_build_map.rs +++ b/crates/ide/src/diagnostics/unnecessary_fold_to_build_map.rs @@ -49,7 +49,7 @@ impl Linter for UnnecessaryFoldToBuildMapLinter { "Unnecessary explicit fold to construct map." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::WeakWarning } } diff --git a/crates/ide/src/diagnostics/unnecessary_map_to_list_in_comprehension.rs b/crates/ide/src/diagnostics/unnecessary_map_to_list_in_comprehension.rs index 6dbf3cd4c7..0c2ed5e140 100644 --- a/crates/ide/src/diagnostics/unnecessary_map_to_list_in_comprehension.rs +++ b/crates/ide/src/diagnostics/unnecessary_map_to_list_in_comprehension.rs @@ -33,7 +33,7 @@ impl Linter for UnnecessaryMapToListInComprehensionLinter { "Unnecessary intermediate list allocated." } - fn severity(&self) -> Severity { + fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::WeakWarning } } diff --git a/crates/ide/src/diagnostics/unspecific_include.rs b/crates/ide/src/diagnostics/unspecific_include.rs index 339874f6ee..431740c085 100644 --- a/crates/ide/src/diagnostics/unspecific_include.rs +++ b/crates/ide/src/diagnostics/unspecific_include.rs @@ -14,9 +14,9 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::elp_base_db::generated_file_include_lib; use elp_ide_db::elp_base_db::path_for_file; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextRange; use elp_syntax::ast; -use elp_text_edit::TextEdit; -use elp_text_edit::TextRange; use hir::InFile; use hir::Semantic; @@ -152,10 +152,12 @@ fn replace_include_path( #[cfg(test)] mod tests { use elp_ide_db::DiagnosticCode; + // @fb-only: use elp_ide_db::meta_only::MetaOnlyDiagnosticCode; use expect_test::Expect; use expect_test::expect; use crate::diagnostics::Diagnostic; + use crate::diagnostics::DiagnosticsConfig; use crate::tests; fn filter(d: &Diagnostic) -> bool { @@ -167,9 +169,13 @@ mod tests { tests::check_filtered_diagnostics(fixture, &filter) } + #[rustfmt::skip] #[track_caller] fn check_fix(fixture_before: &str, fixture_after: Expect) { - tests::check_fix(fixture_before, fixture_after) + let config = DiagnosticsConfig::default() + // @fb-only: .disable(DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::MalformedInclude)) + .disable(DiagnosticCode::UnusedInclude); + tests::check_fix_with_config(config, fixture_before, fixture_after) } #[test] diff --git a/crates/ide/src/diagnostics/unused_function_args.rs b/crates/ide/src/diagnostics/unused_function_args.rs index 53306052ce..ec4825e522 100644 --- a/crates/ide/src/diagnostics/unused_function_args.rs +++ b/crates/ide/src/diagnostics/unused_function_args.rs @@ -18,9 +18,9 @@ use std::collections::HashSet; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextRange; use elp_syntax::ast; -use elp_text_edit::TextEdit; -use elp_text_edit::TextRange; use hir::AnyExpr; use hir::AnyExprId; use hir::FunctionClauseDef; diff --git a/crates/ide/src/diagnostics/unused_include.rs b/crates/ide/src/diagnostics/unused_include.rs index 3a91377338..eb26bcd923 100644 --- a/crates/ide/src/diagnostics/unused_include.rs +++ b/crates/ide/src/diagnostics/unused_include.rs @@ -12,14 +12,17 @@ // // Return a warning if nothing is used from an include file +use std::borrow::Cow; + use elp_ide_assists::helpers::extend_range; use elp_ide_db::SearchScope; use elp_ide_db::SymbolDefinition; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::SmolStr; +use elp_syntax::TextRange; use elp_syntax::ast::AstNode; -use elp_text_edit::TextEdit; use fxhash::FxHashMap; use fxhash::FxHashSet; use hir::FormIdx; @@ -31,9 +34,11 @@ use hir::db::DefDatabase; use hir::known; use lazy_static::lazy_static; -use super::Diagnostic; +use crate::Assist; use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::Severity; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; use crate::fix; lazy_static! { @@ -43,56 +48,114 @@ lazy_static! { .collect(); } -pub(crate) fn unused_includes( - sema: &Semantic, - db: &dyn DefDatabase, - diagnostics: &mut Vec, - file_id: FileId, -) { - let form_list = db.file_form_list(file_id); - let mut cache = Default::default(); - let source_file = db.parse(file_id); - for (include_idx, attr) in form_list.includes() { - if !EXCLUDES.contains(attr.path()) { - let in_file = InFile::new(file_id, include_idx); - if let Some(include_file_id) = db.resolve_include(in_file) { - if is_file_used(sema, db, include_file_id, file_id, &mut cache) { - continue; - } +pub(crate) struct UnusedIncludeLinter; - let path = match attr { - IncludeAttribute::Include { path, .. } => path, - IncludeAttribute::IncludeLib { path, .. } => path, - }; - let attribute = attr.form_id().get(&source_file.tree()); - let attribute_syntax = attribute.syntax(); - let attribute_range = attribute_syntax.text_range(); - let mut edit_builder = TextEdit::builder(); - let extended_attribute_range = extend_range(attribute_syntax); - edit_builder.delete(extended_attribute_range); - let edit = edit_builder.finish(); +impl Linter for UnusedIncludeLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::UnusedInclude + } - let diagnostic = Diagnostic::new( - DiagnosticCode::UnusedInclude, - format!("Unused file: {path}"), - attribute_range, - ) - .with_severity(Severity::Warning) - .with_fixes(Some(vec![fix( - "remove_unused_include", - "Remove unused include", - SourceChange::from_text_edit(file_id, edit.clone()), - attribute_range, - )])); + fn description(&self) -> &'static str { + "Unused include file" + } - log::debug!("Found unused include {path:?}"); + fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { + let file_kind = sema.db.file_kind(file_id); + file_kind.is_module() + } - diagnostics.push(diagnostic); - } + fn should_process_generated_files(&self) -> bool { + true + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Context { + path: SmolStr, + extended_range: TextRange, +} + +impl Default for Context { + fn default() -> Self { + Context { + path: SmolStr::new(""), + extended_range: TextRange::default(), } } } +impl GenericLinter for UnusedIncludeLinter { + type Context = Context; + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let db = sema.db; + let form_list = db.file_form_list(file_id); + let mut cache = Default::default(); + let source_file = db.parse(file_id); + let mut res = Vec::new(); + + for (include_idx, attr) in form_list.includes() { + if !EXCLUDES.contains(attr.path()) { + let in_file = InFile::new(file_id, include_idx); + if let Some(include_file_id) = db.resolve_include(in_file) { + if is_file_used(sema, db, include_file_id, file_id, &mut cache) { + continue; + } + + let path = match attr { + IncludeAttribute::Include { path, .. } => path, + IncludeAttribute::IncludeLib { path, .. } => path, + }; + let attribute = attr.form_id().get(&source_file.tree()); + let attribute_syntax = attribute.syntax(); + let attribute_range = attribute_syntax.text_range(); + let extended_attribute_range = extend_range(attribute_syntax); + + log::debug!("Found unused include {path:?}"); + + res.push(GenericLinterMatchContext { + range: attribute_range, + context: Context { + path: path.clone(), + extended_range: extended_attribute_range, + }, + }); + } + } + } + Some(res) + } + + fn match_description(&self, context: &Self::Context) -> Cow<'_, str> { + Cow::Owned(format!("Unused file: {}", context.path)) + } + + fn fixes( + &self, + context: &Self::Context, + _range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { + let mut edit_builder = TextEdit::builder(); + edit_builder.delete(context.extended_range); + let edit = edit_builder.finish(); + + Some(vec![fix( + "remove_unused_include", + "Remove unused include", + SourceChange::from_text_edit(file_id, edit), + context.extended_range, + )]) + } +} + +pub static LINTER: UnusedIncludeLinter = UnusedIncludeLinter; + fn is_file_used( sema: &Semantic, db: &dyn DefDatabase, diff --git a/crates/ide/src/diagnostics/unused_macro.rs b/crates/ide/src/diagnostics/unused_macro.rs index 89f00e671b..6ea8756b91 100644 --- a/crates/ide/src/diagnostics/unused_macro.rs +++ b/crates/ide/src/diagnostics/unused_macro.rs @@ -19,9 +19,9 @@ use elp_ide_assists::helpers::extend_range; use elp_ide_db::SymbolDefinition; use elp_ide_db::elp_base_db::FileId; use elp_ide_db::source_change::SourceChange; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::AstNode; use elp_syntax::TextRange; -use elp_text_edit::TextEdit; use hir::Semantic; use crate::diagnostics::DiagnosticCode; @@ -88,7 +88,13 @@ impl GenericLinter for UnusedMacroLinter { Some(DiagnosticTag::Unused) } - fn fixes(&self, context: &Context, _sema: &Semantic, file_id: FileId) -> Option> { + fn fixes( + &self, + context: &Context, + _range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { Some(vec![delete_unused_macro( file_id, context.delete_range, @@ -123,7 +129,9 @@ mod tests { #[track_caller] pub(crate) fn check_diagnostics(fixture: &str) { - let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + let config = DiagnosticsConfig::default() + .disable(DiagnosticCode::UndefinedFunction) + .disable(DiagnosticCode::HirUnresolvedMacro); check_diagnostics_with_config(config, fixture) } diff --git a/crates/ide/src/diagnostics_collection.rs b/crates/ide/src/diagnostics_collection.rs index d944e223da..fe1fc759aa 100644 --- a/crates/ide/src/diagnostics_collection.rs +++ b/crates/ide/src/diagnostics_collection.rs @@ -104,7 +104,7 @@ impl DiagnosticCollection { let native = self.native.get(&file_id).unwrap_or(&empty_diags); let erlang_service = self.erlang_service.get(&file_id).unwrap_or(&empty_diags); let mut combined: Vec = - attach_related_diagnostics(native.clone(), erlang_service.clone()); + attach_related_diagnostics(file_id, native.clone(), erlang_service.clone()); let eqwalizer = self.eqwalizer.get(&file_id).into_iter().flatten().cloned(); let eqwalizer_project = self .eqwalizer_project @@ -266,8 +266,8 @@ mod tests { use std::iter::once; use elp_ide_db::elp_base_db::FileId; + use elp_ide_db::text_edit::TextRange; use elp_syntax::label::Label; - use elp_text_edit::TextRange; use fxhash::FxHashMap; use fxhash::FxHashSet; @@ -348,7 +348,7 @@ mod tests { let file_id = *file_id; let diagnostics = diagnostics::native_diagnostics(&db, &config, &vec![], file_id); - let combined = attach_related_diagnostics(diagnostics, extra_diags.clone()); + let combined = attach_related_diagnostics(file_id, diagnostics, extra_diags.clone()); let expected = fixture.annotations_by_file_id(&file_id); let mut actual = combined .into_iter() @@ -415,6 +415,7 @@ mod tests { config, &extra_diags, r#" + //- expect_parse_errors -module(main). -export([foo/0,bar/0]). diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index d13befe8bb..6a85ab3dc3 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -15,9 +15,9 @@ use elp_syntax::AstNode; use hir::InFile; use hir::Semantic; -// @fb-only +// @fb-only: use crate::meta_only::exdoc_links; -// @fb-only +// @fb-only: mod meta_only; mod otp_links; #[derive(Debug, Clone, PartialEq, Eq)] @@ -40,10 +40,10 @@ pub(crate) fn external_docs(db: &RootDatabase, position: &FilePosition) -> Optio if let Some(class) = SymbolClass::classify(&sema, in_file_token.clone()) { class.iter().for_each(|def| { otp_links::links(&mut doc_links, &sema, &def); - // @fb-only + // @fb-only: exdoc_links::links(&mut doc_links, &sema, &def); }); } - // @fb-only + // @fb-only: meta_only::links(&mut doc_links, node, position); Some(doc_links) } diff --git a/crates/ide/src/document_symbols.rs b/crates/ide/src/document_symbols.rs index 390a975132..daed2c6368 100644 --- a/crates/ide/src/document_symbols.rs +++ b/crates/ide/src/document_symbols.rs @@ -264,7 +264,7 @@ mod tests { -record(my_second_record, {my_list :: [] }). %% ^^^^^^^^^^^^^^^^ Record | my_second_record -type my_integer() :: integer(). -%% ^^^^^^^^^^^^ Type | my_integer/0 +%% ^^^^^^^^^^ Type | my_integer/0 -define(MEANING_OF_LIFE, 42). %% ^^^^^^^^^^^^^^^ Define | MEANING_OF_LIFE @@ -340,7 +340,7 @@ mod tests { -record(included_record, {my_field :: integer()}). %% ^^^^^^^^^^^^^^^ Record | included_record -type local_type() :: integer(). -%% ^^^^^^^^^^^^ Type | local_type/0 +%% ^^^^^^^^^^ Type | local_type/0 local_function() -> ok. %% ^^^^^^^^^^^^^^ Function | local_function/0 "#, diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 95735a904d..a5a6f759fc 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -83,7 +83,7 @@ mod tests { check( r#" -module(foo). --bar() -> ?L~INE. +bar() -> ?L~INE. "#, expect![[r#" LINE @@ -97,7 +97,7 @@ mod tests { check( r#" -module(foo). --bar() -> ?F~ILE. +bar() -> ?F~ILE. "#, expect![[r#" FILE @@ -420,7 +420,7 @@ baz() -> maps:get(type, ExpectedQr, missing_expected_type); _ -> Type - end, + end ). baz() -> ?asser~tQrs(AliceWID, ?WA_QR_TYPE_MESSAGE, []), diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index cf16198dfe..88a95e86d7 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -259,6 +259,7 @@ main() -> fn param_hints_variables_missing_param() { check_params( r#" +//- expect_parse_errors -module(main).~ -compile(export_all). sum(A, B) -> A + B. diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index dcaff6de70..ad5c1bb680 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -110,7 +110,7 @@ pub mod diagnostics; pub mod diagnostics_collection; pub mod diff; mod highlight_related; -// @fb-only +// @fb-only: pub mod meta_only; pub use annotations::Annotation; pub use annotations::AnnotationKind; @@ -251,9 +251,9 @@ impl Analysis { }) } - pub fn should_eqwalize(&self, file_id: FileId, include_tests: bool) -> Cancellable { + pub fn should_eqwalize(&self, file_id: FileId) -> Cancellable { let is_in_app = self.file_app_type(file_id).ok() == Some(Some(AppType::App)); - Ok(is_in_app && self.is_eqwalizer_enabled(file_id, include_tests)?) + Ok(is_in_app && self.is_eqwalizer_enabled(file_id)?) } /// Computes the set of eqwalizer diagnostics for the given files, @@ -383,8 +383,8 @@ impl Analysis { /// - the app (the module belongs to) has `.eqwalizer` marker in the roof /// - or the module has `-typing([eqwalizer]).` pragma /// - or the whole project has `enable_all=true` in its `.elp.toml` file - pub fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> Cancellable { - self.with_db(|db| db.is_eqwalizer_enabled(file_id, include_tests)) + pub fn is_eqwalizer_enabled(&self, file_id: FileId) -> Cancellable { + self.with_db(|db| db.is_eqwalizer_enabled(file_id)) } /// ETF for the module's abstract forms diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 2642aed015..ee5bfb7219 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -77,6 +77,28 @@ fn find_definitions( syntax: &SyntaxNode, position: FilePosition, ) -> RenameResult> { + // Try to find a macro name and classify it to resolve definitions (including across includes) + if let Some(macro_name) = algo::find_node_at_offset::(syntax, position.offset) + && let Some(token) = macro_name.syntax().first_token() + { + let location = InFile { + file_id: position.file_id, + value: token, + }; + match SymbolClass::classify(sema, location) { + Some(SymbolClass::Definition(def)) => return Ok(vec![def]), + Some(SymbolClass::Reference { refs, typ: _ }) => match refs { + ReferenceClass::Definition(def) => return Ok(vec![def]), + ReferenceClass::MultiMacro(defs) => { + return Ok(defs.into_iter().map(SymbolDefinition::Define).collect()); + } + _ => {} + }, + None => {} + } + } + + // If not a macro, try to find a general name let symbols = if let Some(name_like) = algo::find_node_at_offset::(syntax, position.offset) { match &name_like { @@ -172,35 +194,53 @@ pub fn rename_var( #[cfg(test)] pub(crate) mod tests { + use elp_ide_db::RootDatabase; + use elp_ide_db::elp_base_db::AnchoredPathBuf; + use elp_ide_db::elp_base_db::FileId; + use elp_ide_db::elp_base_db::VfsPath; use elp_ide_db::elp_base_db::assert_eq_text; + use elp_ide_db::elp_base_db::fixture::ChangeFixture; use elp_ide_db::elp_base_db::fixture::WithFixture as _; + use elp_ide_db::source_change::FileSystemEdit; + use elp_ide_db::text_edit::TextEdit; use elp_project_model::test_fixture::trim_indent; use elp_syntax::AstNode; use elp_syntax::algo; use elp_syntax::ast; - use elp_text_edit::TextEdit; + use fxhash::FxHashSet; use hir::AnyExprId; use hir::InFile; use hir::Semantic; use super::rename_var; + use crate::AnalysisHost; use crate::fixture; #[track_caller] pub(crate) fn check_rename(new_name: &str, fixture_before: &str, fixture_after_str: &str) { let fixture_after_str = &trim_indent(fixture_after_str); - let analysis_after = fixture::multi_file(fixture_after_str); - let (analysis, position, _) = fixture::position(fixture_before); + let (db_before, fixture) = RootDatabase::with_fixture(fixture_before); + let host_before = AnalysisHost { db: db_before }; + let analysis = host_before.analysis(); + let position = fixture.position(); + + let (db_after, fixture_after) = RootDatabase::with_fixture(fixture_after_str); + let host_after = AnalysisHost { db: db_after }; + let analysis_after = host_after.analysis(); + let rename_result = analysis .rename(position, new_name) .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}")); match rename_result { Ok(source_change) => { + let mut file_ids: FxHashSet = FxHashSet::default(); for edit in source_change.source_file_edits { let mut text_edit_builder = TextEdit::builder(); let file_id = edit.0; + // New and old file_id are the same + file_ids.insert(file_id); for indel in edit.1.into_iter() { text_edit_builder.replace(indel.delete, indel.insert); } @@ -210,6 +250,82 @@ pub(crate) mod tests { let expected = analysis_after.file_text(file_id).unwrap().to_string(); assert_eq_text!(&*expected, &*result); } + for op in source_change.file_system_edits { + let expected; + let new_file_id; + match op { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &dst.path + ) + }); + new_file_id = *new_file.1; + expected = initial_contents; + let actual = analysis_after.file_text(new_file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); + } + FileSystemEdit::MoveFile { src: _, dst } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file renamed to '{}'", + &dst.path + ) + }); + new_file_id = *new_file.1; + // We simply record the new file id for checking in `fixture_after``. + // The expected value will be updated by the new_file_edits below, + // and the result asserted there + } + } + file_ids.insert(new_file_id); + } + for (dst, op) in source_change.new_file_edits { + // When renaming a module, we move the original file, then apply fixup edits + // to the new file + let anchored_dst = AnchoredPathBuf { + anchor: dst.anchor, + path: dst.path, + }; + let new_file = + find_new_file_id(&fixture_after, &anchored_dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &anchored_dst.path + ) + }); + + let mut text_edit_builder = TextEdit::builder(); + let file_id = *new_file.1; + // New and old file_id are the same + file_ids.insert(file_id); + for indel in op.iter() { + text_edit_builder.replace(indel.delete, indel.insert.to_string()); + } + let mut result = analysis.file_text(file_id).unwrap().to_string(); + let edit = text_edit_builder.finish(); + edit.apply(&mut result); + let expected = analysis_after.file_text(file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*result); + } + // Check the balance of the expectations in the new fixture. + for file_id in &fixture_after.files { + if !file_ids.contains(file_id) { + let actual = analysis_after.file_text(*file_id).unwrap().to_string(); + let expected = if fixture.files.contains(file_id) { + analysis.file_text(*file_id).unwrap().to_string() + } else { + format!("File {:?} not present in original fixture", file_id) + }; + assert_eq_text!(&*expected, &*actual); + } + } } Err(err) => { if fixture_after_str.starts_with("error:") { @@ -225,6 +341,16 @@ pub(crate) mod tests { }; } + fn find_new_file_id<'a>( + fixture: &'a ChangeFixture, + dst: &'a AnchoredPathBuf, + ) -> Option<(&'a VfsPath, &'a FileId)> { + fixture + .files_by_path + .iter() + .find(|(name, _)| name.as_path().unwrap().to_string().ends_with(&dst.path)) + } + #[test] fn test_rename_var_1() { check_rename("Y", r#"main() -> I~ = 1."#, r#"main() -> Y = 1."#); @@ -1113,6 +1239,326 @@ pub(crate) mod tests { ); } + // --------------------------------- + // Renaming modules + + #[test] + fn rename_module_fails_name_exists() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + + #[test] + fn rename_module_fails_bad_name_1() { + check_rename( + "Main", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: Invalid new module name: 'Main'"#, + ); + } + + #[test] + fn rename_module_simple() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + "#, + r#" + //- /app_a/src/main_2.erl + -module(main_2). + "#, + ); + } + + #[test] + fn rename_module_fails_dup_name() { + check_rename( + "main_2", + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + + #[test] + fn rename_module_with_usage_internal() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + //------------------ + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + bar() -> main_2:foo(). + baz() -> main_2:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main_2:foo(). + "#, + ); + } + #[test] + fn rename_module_with_usage_type() { + // TODO: check for compile errors in the fixture + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_record() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun_arg() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> + meck:new(main, [passthrough]), + meck:new([other, main] , [passthrough]), + meck:unload(main), + apply(main, foo, []), + ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> + meck:new(main_3, [passthrough]), + meck:new([other, main_3] , [passthrough]), + meck:unload(main_3), + apply(main_3, foo, []), + ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:foo/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:foo/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun_as_module() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:main/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:main/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_define() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main:foo(U), + ?FOO(U), + ok. + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main_3:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main_3:foo(U), + ?FOO(U), + ok. + "#, + ); + } + // --------------------------------- #[track_caller] @@ -1183,6 +1629,258 @@ pub(crate) mod tests { ); } + #[test] + fn test_rename_macro() { + check_rename( + "NEW_MACRO", + r#" + -define(OLD_~MACRO, value). + foo() -> ?OLD_MACRO. + "#, + r#" + -define(NEW_MACRO, value). + foo() -> ?NEW_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_from_reference() { + check_rename( + "NEW_MACRO", + r#" + -define(OLD_MACRO, value). + foo() -> ?OLD_~MACRO. + "#, + r#" + -define(NEW_MACRO, value). + foo() -> ?NEW_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_atom() { + check_rename( + "new_macro", + r#" + -define(old_~macro, value). + foo() -> ?old_macro. + "#, + r#" + -define(new_macro, value). + foo() -> ?new_macro. + "#, + ); + } + + #[test] + fn test_rename_macro_atom_from_reference() { + check_rename( + "new_macro", + r#" + -define(old_macro, value). + foo() -> ?old_~macro. + "#, + r#" + -define(new_macro, value). + foo() -> ?new_macro. + "#, + ); + } + + #[test] + fn test_rename_macro_from_definition_multi_file() { + check_rename( + "NEW_MACRO", + r#" + //- /src/common.hrl + -define(OLD_~MACRO, value). + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?OLD_MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?OLD_MACRO. + "#, + r#" + //- /src/common.hrl + -define(NEW_MACRO, value). + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?NEW_MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?NEW_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_from_usage_multi_file() { + check_rename( + "NEW_MACRO", + r#" + //- /src/common.hrl + -define(OLD_MACRO, value). + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?OLD_~MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?OLD_MACRO. + "#, + r#" + //- /src/common.hrl + -define(NEW_MACRO, value). + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?NEW_MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?NEW_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_name_clash() { + check_rename( + "EXISTING_MACRO", + r#" + -define(OLD_~MACRO, value1). + -define(EXISTING_MACRO, value2). + foo() -> ?OLD_MACRO. + "#, + r#"error: Macro 'EXISTING_MACRO' already in scope"#, + ); + } + + #[test] + fn test_rename_macro_from_usage_ifdef() { + check_rename( + "NEW_MACRO", + r#" + //- /src/common.hrl + -ifndef(TEST). + -define(OLD_MACRO, value1). + -else. + -define(OLD_MACRO, value2). + -endif. + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?OLD_~MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?OLD_MACRO. + "#, + r#" + //- /src/common.hrl + -ifndef(TEST). + -define(OLD_MACRO, value1). + -else. + -define(NEW_MACRO, value2). + -endif. + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?NEW_MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + bar() -> ?NEW_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_name_clash_from_reference() { + check_rename( + "EXISTING_MACRO", + r#" + -define(OLD_MACRO, value1). + -define(EXISTING_MACRO, value2). + foo() -> ?OLD_~MACRO. + "#, + r#"error: Macro 'EXISTING_MACRO' already in scope"#, + ); + } + + #[test] + fn test_rename_macro_name_clash_from_reference_multi_file() { + check_rename( + "EXISTING_MACRO", + r#" + //- /src/common.hrl + -define(OLD_MACRO, value1). + + //- /src/module_a.erl + -module(module_a). + -include("common.hrl"). + foo() -> ?OLD_~MACRO. + + //- /src/module_b.erl + -module(module_b). + -include("common.hrl"). + -define(EXISTING_MACRO, value3). + bar() -> ?OLD_MACRO. + "#, + r#"error: Macro 'EXISTING_MACRO' already in scope"#, + ); + } + + #[test] + fn test_rename_macro_different_arity_ok() { + check_rename( + "EXISTING_MACRO", + r#" + -define(OLD_~MACRO, value1). + -define(EXISTING_MACRO(X), X). + foo() -> ?OLD_MACRO. + "#, + r#" + -define(EXISTING_MACRO, value1). + -define(EXISTING_MACRO(X), X). + foo() -> ?EXISTING_MACRO. + "#, + ); + } + + #[test] + fn test_rename_macro_same_arity_conflict() { + check_rename( + "EXISTING_MACRO", + r#" + -define(OLD_~MACRO(X), X). + -define(EXISTING_MACRO(Y), Y). + foo() -> ?OLD_MACRO(1). + "#, + r#"error: Macro 'EXISTING_MACRO' already in scope"#, + ); + } + #[test] fn test_rename_in_rpc_call_4() { check_rename( @@ -2002,20 +2700,403 @@ pub(crate) mod tests { check_rename( "new_name", r#" - //- /src/baz.erl - -module(baz). - foo() -> - erpc:send_request(node, ?MODULE, bar, [], label, collection). + //- /src/baz.erl + -module(baz). + foo() -> + erpc:send_request(node, ?MODULE, bar, [], label, collection). - b~ar() -> - ok."#, + b~ar() -> + ok."#, r#" - -module(baz). - foo() -> - erpc:send_request(node, ?MODULE, new_name, [], label, collection). + -module(baz). + foo() -> + erpc:send_request(node, ?MODULE, new_name, [], label, collection). - new_name() -> - ok."#, + new_name() -> + ok."#, + ); + } + + // --------------------------------- + // Type Renaming Tests + // --------------------------------- + + #[test] + fn test_rename_type_simple() { + check_rename( + "new_type", + r#" + -module(main). + -type ol~d_type() :: ok. + -spec foo(old_type()) -> ok. + foo(_) -> ok. + "#, + r#" + -module(main). + -type new_type() :: ok. + -spec foo(new_type()) -> ok. + foo(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_with_arity() { + check_rename( + "new_type", + r#" + -module(main). + -type old_~type(T) :: {ok, T}. + -spec foo(old_type(integer())) -> ok. + foo(_) -> ok. + "#, + r#" + -module(main). + -type new_type(T) :: {ok, T}. + -spec foo(new_type(integer())) -> ok. + foo(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_from_usage() { + check_rename( + "new_type", + r#" + -module(main). + -type old_type() :: ok. + -spec foo(old_~type()) -> ok. + foo(_) -> ok. + "#, + r#" + -module(main). + -type new_type() :: ok. + -spec foo(new_type()) -> ok. + foo(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_opaque_type() { + check_rename( + "new_type", + r#" + -module(main). + -opaque old_~type() :: {internal, term()}. + -spec create() -> old_type(). + create() -> {internal, data}. + "#, + r#" + -module(main). + -opaque new_type() :: {internal, term()}. + -spec create() -> new_type(). + create() -> {internal, data}. + "#, + ); + } + + #[test] + fn test_rename_type_fails_name_clash() { + check_rename( + "existing_type", + r#" + -module(main). + -type old_~type() :: ok. + -type existing_type() :: error. + "#, + r#"error: Type 'existing_type/0' already in scope"#, + ); + } + + #[test] + fn test_rename_type_allows_different_arity() { + check_rename( + "existing_type", + r#" + -module(main). + -type old_~type() :: ok. + -type existing_type(T) :: {error, T}. + "#, + r#" + -module(main). + -type existing_type() :: ok. + -type existing_type(T) :: {error, T}. + "#, + ); + } + + #[test] + fn test_rename_type_invalid_name() { + check_rename( + "New_Type", + r#" + -module(main). + -type old_~type() :: ok. + "#, + r#"error: Invalid new type name: 'New_Type'"#, + ); + } + + #[test] + fn test_rename_type_remote_reference() { + check_rename( + "new_type", + r#" + //- /src/module_a.erl + -module(module_a). + -export_type([old_~type/0]). + -type old_type() :: {ok, term()}. + + //- /src/module_b.erl + -module(module_b). + -spec foo(module_a:old_type()) -> ok. + foo(_) -> ok. + "#, + r#" + //- /src/module_a.erl + -module(module_a). + -export_type([new_type/0]). + -type new_type() :: {ok, term()}. + + //- /src/module_b.erl + -module(module_b). + -spec foo(module_a:new_type()) -> ok. + foo(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_remote_reference_from_usage() { + check_rename( + "new_type", + r#" + //- /src/module_a.erl + -module(module_a). + -export_type([old_type/0]). + -type old_type() :: {ok, term()}. + + //- /src/module_b.erl + -module(module_b). + -spec foo(module_a:old_~type()) -> ok. + foo(_) -> ok. + "#, + r#" + //- /src/module_a.erl + -module(module_a). + -export_type([new_type/0]). + -type new_type() :: {ok, term()}. + + //- /src/module_b.erl + -module(module_b). + -spec foo(module_a:new_type()) -> ok. + foo(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_from_header_multi_file() { + check_rename( + "new_type", + r#" + //- /src/types.hrl + -type old_~type() :: {ok, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec foo(old_type()) -> ok. + foo(_) -> ok. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec bar(old_type()) -> ok. + bar(_) -> ok. + "#, + r#" + //- /src/types.hrl + -type new_type() :: {ok, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec foo(new_type()) -> ok. + foo(_) -> ok. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec bar(new_type()) -> ok. + bar(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_from_usage_in_header() { + check_rename( + "new_type", + r#" + //- /src/types.hrl + -type old_type() :: {ok, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec foo(old_~type()) -> ok. + foo(_) -> ok. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec bar(old_type()) -> ok. + bar(_) -> ok. + "#, + r#" + //- /src/types.hrl + -type new_type() :: {ok, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec foo(new_type()) -> ok. + foo(_) -> ok. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec bar(new_type()) -> ok. + bar(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_type_name_clash_in_remote_module() { + check_rename( + "existing_type", + r#" + //- /src/module_a.erl + -module(module_a). + -export_type([old_type/0]). + -type old_~type() :: {ok, term()}. + + //- /src/module_b.erl + -module(module_b). + -type existing_type() :: error. + -spec foo(module_a:old_type()) -> ok. + foo(_) -> ok. + "#, + r#"error: Type 'existing_type/0' already in scope"#, + ); + } + + #[test] + fn test_rename_type_name_clash_from_header_multi_file() { + check_rename( + "existing_type", + r#" + //- /src/types.hrl + -type old_type() :: {ok, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec foo(old_~type()) -> ok. + foo(_) -> ok. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -type existing_type() :: error. + -spec bar(old_type()) -> ok. + bar(_) -> ok. + "#, + r#"error: Type 'existing_type/0' already in scope"#, + ); + } + + #[test] + fn test_rename_opaque_type_from_header() { + check_rename( + "new_type", + r#" + //- /src/types.hrl + -opaque old_~type() :: {internal, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec create() -> old_type(). + create() -> {internal, data}. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec use(old_type()) -> ok. + use(_) -> ok. + "#, + r#" + //- /src/types.hrl + -opaque new_type() :: {internal, term()}. + + //- /src/module_a.erl + -module(module_a). + -include("types.hrl"). + -spec create() -> new_type(). + create() -> {internal, data}. + + //- /src/module_b.erl + -module(module_b). + -include("types.hrl"). + -spec use(new_type()) -> ok. + use(_) -> ok. + "#, + ); + } + + #[test] + fn test_rename_macro_separate_definitions() { + check_rename( + "NEW_MACRO", + r#" + //- /src/a.hrl + -define(MACRO, ok). + + //- /src/a.erl + -module(a). + -include("a.hrl"). + foo() -> ?MACRO~. + + //- /src/b.hrl + -define(MACRO, error). + + //- /src/b.erl + -module(b). + -include("b.hrl"). + bar() -> ?MACRO. + "#, + r#" + //- /src/a.hrl + -define(NEW_MACRO, ok). + + //- /src/a.erl + -module(a). + -include("a.hrl"). + foo() -> ?NEW_MACRO. + + //- /src/b.hrl + -define(MACRO, error). + + //- /src/b.erl + -module(b). + -include("b.hrl"). + bar() -> ?MACRO. + "#, ); } } diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 4b2348f610..0aa731c8c9 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -243,7 +243,7 @@ mod tests { //- /my_app/src/runnables.erl ~ -module(runnables). - -export([all/]). + -export([all/0]). main() -> ok. "#, diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index fba6d12d73..d09ec201f8 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -311,6 +311,7 @@ mod tests { fn test_fn_signature_local_two_args() { check( r#" +//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -343,6 +344,7 @@ main() -> ); check( r#" +//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -375,6 +377,7 @@ main() -> ); check( r#" +//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -411,6 +414,7 @@ main() -> fn test_fn_signature_remote_two_args() { check( r#" +//- expect_parse_errors //- /one.erl -module(one). @@ -449,6 +453,7 @@ main() -> ); check( r#" +//- expect_parse_errors //- /one.erl -module(one). @@ -487,6 +492,7 @@ main() -> ); check( r#" +//- expect_parse_errors //- /one.erl -module(one). @@ -529,6 +535,7 @@ main() -> fn test_fn_signature_quoted_remote_two_args() { check( r#" +//- expect_parse_errors //- /Elixir.One.erl -module('Elixir.One'). @@ -576,6 +583,7 @@ main() -> fn test_fn_signature_unclosed_call() { check( r#" +//- expect_parse_errors -module(main). -compile(export_all). @@ -626,6 +634,7 @@ main() -> fn test_fn_signature_doc() { check( r#" +//- expect_parse_errors -module(main). -compile(export_all). @@ -685,6 +694,7 @@ main() -> if supports_eep59_doc_attributes() { check( r#" +//- expect_parse_errors -module(main). -compile(export_all). @@ -753,6 +763,7 @@ main() -> fn test_fn_signature_local_imported() { check( r#" +//- expect_parse_errors //- /one.erl -module(one). -compile(export_all). @@ -794,6 +805,7 @@ main() -> fn test_fn_signature_spec_arg_names() { check( r#" +//- expect_parse_errors //- /one.erl -module(one). -compile(export_all). diff --git a/crates/ide/src/tests.rs b/crates/ide/src/tests.rs index a32b7e3044..55e9494e48 100644 --- a/crates/ide/src/tests.rs +++ b/crates/ide/src/tests.rs @@ -20,8 +20,8 @@ use elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide_db::elp_base_db::assert_eq_text; use elp_ide_db::elp_base_db::fixture::WithFixture; use elp_ide_db::elp_base_db::remove_annotations; +use elp_ide_db::text_edit::TextRange; use elp_project_model::test_fixture::trim_indent; -use elp_text_edit::TextRange; use expect_test::Expect; use itertools::Itertools; @@ -376,7 +376,10 @@ pub(crate) fn check_diagnostics(fixture: &str) { let config = DiagnosticsConfig::default() .set_experimental(true) .disable(DiagnosticCode::UnspecificInclude) - .disable(DiagnosticCode::BinaryStringToSigil); + .disable(DiagnosticCode::BinaryStringToSigil) + .disable(DiagnosticCode::HirUnresolvedMacro) + .disable(DiagnosticCode::BoundVarInLhs) + .disable(DiagnosticCode::HirUnresolvedInclude); check_diagnostics_with_config(config, fixture) } @@ -399,6 +402,23 @@ fn convert_diagnostics_to_annotations(diagnostics: Vec) -> Vec<(Text annotation.push_str(&d.code.as_code()); annotation.push_str(": "); annotation.push_str(&convert_diagnostic_message(&d)); + + // Append related info to the annotation + if let Some(related_info) = &d.related_info { + // Sort related info alphabetically by message for consistent test output + let mut sorted_info = related_info.clone(); + sorted_info.sort_by(|a, b| a.message.cmp(&b.message)); + for info in sorted_info { + annotation.push_str(&format!( + "\nRelated info: {}:{}-{} {}", + info.file_id.index(), + u32::from(info.range.start()), + u32::from(info.range.end()), + info.message + )); + } + } + (d.range, annotation) }) .collect::>(); @@ -511,7 +531,8 @@ pub(crate) fn check_diagnostics_with_config_and_extra( for file_id in &fixture.files { let file_id = *file_id; let diagnostics = diagnostics::native_diagnostics(&analysis.db, &config, &vec![], file_id); - let diagnostics = diagnostics::attach_related_diagnostics(diagnostics, extra_diags.clone()); + let diagnostics = + diagnostics::attach_related_diagnostics(file_id, diagnostics, extra_diags.clone()); let expected = fixture.annotations_by_file_id(&file_id); let actual = convert_diagnostics_to_annotations(diagnostics); @@ -524,6 +545,7 @@ pub fn check_no_parse_errors(analysis: &Analysis, file_id: FileId) { let config = DiagnosticsConfig::default() .disable(DiagnosticCode::UnspecificInclude) .disable(DiagnosticCode::UndefinedFunction) + .disable(DiagnosticCode::HirUnresolvedMacro) .disable(DiagnosticCode::NoCatch); check_no_parse_errors_with_config(analysis, file_id, &config, &vec![]); } @@ -674,7 +696,8 @@ mod test { fn filtered_diagnostics_passes_syntax_errors() { check_filtered_diagnostics( r#" - %%<^^^^^^^^^^^^ error: L1201: no module definition + //- expect_parse_errors + %%<^^^^^^^^^^^^ 💡 error: L1201: no module definition foo() -> bug bug. %% ^^^^ error: P1711: Syntax Error diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml index 167e1f4b11..b2277cd12d 100644 --- a/crates/ide_assists/Cargo.toml +++ b/crates/ide_assists/Cargo.toml @@ -9,7 +9,6 @@ workspace = true [dependencies] elp_ide_db.workspace = true elp_syntax.workspace = true -elp_text_edit.workspace = true hir.workspace = true cov-mark.workspace = true diff --git a/crates/ide_assists/src/handlers/add_doc.rs b/crates/ide_assists/src/handlers/add_doc.rs index b1f51a7b90..87a511fbff 100644 --- a/crates/ide_assists/src/handlers/add_doc.rs +++ b/crates/ide_assists/src/handlers/add_doc.rs @@ -12,9 +12,9 @@ use std::fmt::Write; use elp_ide_db::assists::AssistId; use elp_ide_db::assists::AssistKind; +use elp_ide_db::text_edit::TextSize; use elp_syntax::AstNode; use elp_syntax::ast; -use elp_text_edit::TextSize; use hir::FunctionDef; use crate::AssistContext; diff --git a/crates/ide_assists/src/handlers/flip_sep.rs b/crates/ide_assists/src/handlers/flip_sep.rs index 662e31b3e9..65ae663518 100644 --- a/crates/ide_assists/src/handlers/flip_sep.rs +++ b/crates/ide_assists/src/handlers/flip_sep.rs @@ -10,12 +10,12 @@ use elp_ide_db::assists::AssistId; use elp_ide_db::assists::AssistKind; +use elp_ide_db::text_edit::TextRange; use elp_syntax::AstNode; use elp_syntax::Direction; use elp_syntax::SyntaxKind; use elp_syntax::SyntaxToken; use elp_syntax::algo::non_trivia_sibling; -use elp_text_edit::TextRange; use fxhash::FxHashSet; use hir::InFile; diff --git a/crates/ide_assists/src/handlers/implement_behaviour.rs b/crates/ide_assists/src/handlers/implement_behaviour.rs index 384ac8a65b..2821e9214e 100644 --- a/crates/ide_assists/src/handlers/implement_behaviour.rs +++ b/crates/ide_assists/src/handlers/implement_behaviour.rs @@ -12,10 +12,10 @@ use std::cmp::max; use elp_ide_db::assists::AssistId; use elp_ide_db::assists::AssistKind; +use elp_ide_db::text_edit::TextRange; +use elp_ide_db::text_edit::TextSize; use elp_syntax::AstNode; use elp_syntax::ast::BehaviourAttribute; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; use hir::Callback; use hir::CallbackId; use hir::InFile; diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs index 69dbb1c55d..055395af52 100644 --- a/crates/ide_assists/src/handlers/inline_function.rs +++ b/crates/ide_assists/src/handlers/inline_function.rs @@ -22,6 +22,7 @@ use elp_ide_db::elp_base_db::FileRange; use elp_ide_db::find_best_token; use elp_ide_db::helpers::get_call; use elp_ide_db::rename::SafetyChecks; +use elp_ide_db::text_edit::TextEdit; use elp_syntax::AstNode; use elp_syntax::NodeOrToken; use elp_syntax::SyntaxKind; @@ -32,7 +33,6 @@ use elp_syntax::TextSize; use elp_syntax::ast; use elp_syntax::ast::HasArity; use elp_syntax::ast::edit::IndentLevel; -use elp_text_edit::TextEdit; use fxhash::FxHashSet; use hir::FunctionDef; use hir::InFile; diff --git a/crates/ide_assists/src/helpers.rs b/crates/ide_assists/src/helpers.rs index bb2a38cf1b..121b1dda41 100644 --- a/crates/ide_assists/src/helpers.rs +++ b/crates/ide_assists/src/helpers.rs @@ -20,6 +20,8 @@ use elp_ide_db::elp_base_db::FileId; use elp_ide_db::helpers::top_insert_position; use elp_ide_db::rename::is_safe_function; use elp_ide_db::source_change::SourceChangeBuilder; +use elp_ide_db::text_edit::TextEdit; +use elp_ide_db::text_edit::TextSize; use elp_syntax::AstNode; use elp_syntax::AstPtr; use elp_syntax::Direction; @@ -34,8 +36,6 @@ use elp_syntax::algo; use elp_syntax::algo::skip_inline_comment; use elp_syntax::ast; use elp_syntax::match_ast; -use elp_text_edit::TextEdit; -use elp_text_edit::TextSize; use fxhash::FxHashSet; use hir::Attribute; use hir::Body; diff --git a/crates/ide_completion/src/attributes.rs b/crates/ide_completion/src/attributes.rs index fc97832d30..f381df147d 100644 --- a/crates/ide_completion/src/attributes.rs +++ b/crates/ide_completion/src/attributes.rs @@ -166,6 +166,7 @@ mod test { fn test_error_recovery() { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample1). % U.S. English @@ -180,6 +181,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample1). % U.K. English @@ -197,6 +199,7 @@ mod test { fn test_typing_attribute() { check( r#" + //- expect_parse_errors -module(sample). -typ~ "#, @@ -211,6 +214,7 @@ mod test { fn test_module_attribute() { check( r#" + //- expect_parse_errors -mod~ "#, None, @@ -224,6 +228,7 @@ mod test { fn test_module_attribute_hyphen() { check( r#" + //- expect_parse_errors //- /src/my-module.erl -mod~ "#, @@ -238,6 +243,7 @@ mod test { fn test_module_attribute_at() { check( r#" + //- expect_parse_errors //- /src/my@module.erl -mod~ "#, @@ -252,6 +258,7 @@ mod test { fn test_module_attribute_underscore() { check( r#" + //- expect_parse_errors //- /src/my_module.erl -mod~ "#, @@ -266,6 +273,7 @@ mod test { fn test_module_attribute_uppercase() { check( r#" + //- expect_parse_errors //- /src/Module.erl -mod~ "#, @@ -280,6 +288,7 @@ mod test { fn test_module_attribute_uppercase_middle() { check( r#" + //- expect_parse_errors //- /src/moDule.erl -mod~ "#, diff --git a/crates/ide_completion/src/ctx.rs b/crates/ide_completion/src/ctx.rs index 22e385e3fa..0d9381e7ec 100644 --- a/crates/ide_completion/src/ctx.rs +++ b/crates/ide_completion/src/ctx.rs @@ -281,6 +281,7 @@ mod ctx_tests { fn expr_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> ~X. @@ -290,6 +291,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> case 1 of. @@ -301,6 +303,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> fun(_) -> ~X end. @@ -310,6 +313,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> try 1 @@ -353,6 +357,7 @@ mod ctx_tests { fn ctx_pattern() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(Y, X) -> ~Y = X. @@ -362,6 +367,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(X) -> case rand:uniform(1) of @@ -373,6 +379,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(X) -> fun(X~) -> 1 end. @@ -382,6 +389,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> receive @@ -393,6 +401,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> try [1] @@ -407,6 +416,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(X) -> if @@ -429,6 +439,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -440,6 +451,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -457,6 +469,7 @@ mod ctx_tests { fn ctx_pattern_error_recovery_wip() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -469,6 +482,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -485,6 +499,7 @@ mod ctx_tests { fn test_type_param_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -type ty(s~) :: ok. "#), @@ -496,6 +511,7 @@ mod ctx_tests { fn test_export_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -export([ f~ @@ -509,6 +525,7 @@ mod ctx_tests { fn test_export_type_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -export_type([ t~ @@ -522,6 +539,7 @@ mod ctx_tests { fn test_spec_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -spec t~ table() -> ok. @@ -535,6 +553,7 @@ mod ctx_tests { fn test_type_ctx() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -spec test() -> ~ test() -> ok. @@ -544,6 +563,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -spec test() -> o~k test() -> ok. @@ -553,6 +573,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -spec test(o~) -> ok. test() -> ok. @@ -562,6 +583,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -record(foo, {field1, field2 :: X~}). "#), @@ -570,6 +592,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -opaque test() :: ~. "#), @@ -578,6 +601,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -nominal test() :: ~. "#), @@ -586,6 +610,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -type test() :: m~ "#), @@ -594,6 +619,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -spec test() -> ~ok. "#), @@ -605,6 +631,7 @@ mod ctx_tests { fn test_ctx_error_recovery() { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> ~ @@ -614,6 +641,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> X + ~ @@ -623,6 +651,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> X + ~. @@ -632,6 +661,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> case rand:uniform(1) of @@ -643,6 +673,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> (erlang:term_to_binary(~ @@ -653,6 +684,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). test() -> (erlang:term_to_binary(~. @@ -663,6 +695,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -type ty() :: ~ "#), @@ -671,6 +704,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -type ty() :: l~. "#), @@ -679,6 +713,7 @@ mod ctx_tests { assert_eq!( ctx(r#" + //- expect_parse_errors -module(sample). -record(rec, {field = lists:map(fun(X) -> X + 1 end, [1, ~])}). "#), diff --git a/crates/ide_completion/src/export_functions.rs b/crates/ide_completion/src/export_functions.rs index f362ea830a..ef803eb9ab 100644 --- a/crates/ide_completion/src/export_functions.rs +++ b/crates/ide_completion/src/export_functions.rs @@ -83,6 +83,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -export([ foo~ @@ -106,6 +107,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -export([ function_a/0, diff --git a/crates/ide_completion/src/export_types.rs b/crates/ide_completion/src/export_types.rs index 9e80828b66..c21a37339e 100644 --- a/crates/ide_completion/src/export_types.rs +++ b/crates/ide_completion/src/export_types.rs @@ -79,6 +79,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -export_type([ foo~ diff --git a/crates/ide_completion/src/functions.rs b/crates/ide_completion/src/functions.rs index 4ce641d0e9..eab033765f 100644 --- a/crates/ide_completion/src/functions.rs +++ b/crates/ide_completion/src/functions.rs @@ -243,6 +243,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -265,6 +266,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -338,6 +340,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -360,6 +363,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -379,12 +383,14 @@ mod test { {label:foon/2, kind:Function, contents:Snippet("foon(${1:A}, ${2:B})"), position:Some(FilePosition { file_id: FileId(1), offset: 86 })}"#]], ); } + #[test] fn test_remote_calls_deprecated() { assert!(serde_json::to_string(&lsp_types::CompletionItemKind::MODULE).unwrap() == "9"); check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -413,6 +419,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -441,6 +448,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -466,6 +474,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -584,6 +593,7 @@ mod test { fn test_remote_fun_exprs_with_trigger() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). main(_) -> @@ -607,6 +617,7 @@ mod test { fn test_local_fun_exprs_with_trigger() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -622,6 +633,7 @@ mod test { fn test_local_fun_exprs_no_trigger() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -637,6 +649,7 @@ mod test { fn function_error_recovery() { check( r#" + //- expect_parse_errors -module(sample1). foo() -> b~ @@ -650,6 +663,7 @@ mod test { ); check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -668,6 +682,7 @@ mod test { fn test_local_and_remote() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). samba() -> ok. @@ -688,6 +703,7 @@ mod test { fn test_remote_call_broken_case() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). test() -> @@ -709,6 +725,7 @@ mod test { fn test_local_call_broken_case() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -802,6 +819,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -820,6 +838,7 @@ mod test { ); check( r#" +//- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -839,6 +858,7 @@ foo(X, Y) -> ok. ); check( r#" +//- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -902,6 +922,7 @@ foo(X, Y) -> ok. fn test_remote_call_same_module_macro_prefix() { check( r#" + //- expect_parse_errors -module(main). -export([main/0, do_not_use_me/0]). -deprecated({do_not_use_me, 0, "Because I said so"}). @@ -922,6 +943,7 @@ foo(X, Y) -> ok. fn test_in_dialyzer_attribute() { check( r#" + //- expect_parse_errors -module(main). -export([ foo/1, @@ -942,6 +964,7 @@ foo(X, Y) -> ok. fn test_quoted_local_call() { check( r#" + //- expect_parse_errors -module(sample). test() -> fo~(something). diff --git a/crates/ide_completion/src/keywords.rs b/crates/ide_completion/src/keywords.rs index 734afc80a5..dc60ae8158 100644 --- a/crates/ide_completion/src/keywords.rs +++ b/crates/ide_completion/src/keywords.rs @@ -141,6 +141,7 @@ mod test { fn test_no_keywords() { check( r#" + //- expect_parse_errors -module(sample). test(X) -> a~ @@ -150,6 +151,7 @@ mod test { ); check( r#" + //- expect_parse_errors -module(sample). test(~X) -> X. @@ -160,6 +162,7 @@ mod test { check( r#" + //- expect_parse_errors -module(m~). "#, None, @@ -168,6 +171,7 @@ mod test { check( r#" + //- expect_parse_errors -module(m). -~ "#, @@ -177,6 +181,7 @@ mod test { check( r#" + //- expect_parse_errors -module(m). -type foo() :: ~. "#, @@ -191,6 +196,7 @@ mod test { fn test_keywords_error_recovery() { check( r#" + //- expect_parse_errors -module(sample). test(X) -> X ~ @@ -234,6 +240,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). test(X) -> ~ diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 71b4ff02c0..f41aecdd67 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs @@ -40,7 +40,7 @@ mod helpers; mod keywords; mod macros; mod maps; -// @fb-only +// @fb-only: mod meta_only; mod modules; mod records; mod spec; @@ -176,7 +176,7 @@ pub fn completions( } CtxKind::Other => { let _ = attributes::add_completions(&mut acc, ctx) - // @fb-only + // @fb-only: || meta_only::add_completions(&mut acc, ctx) || vars::add_completions(&mut acc, ctx) || maps::add_completions(&mut acc, ctx) || records::add_completions(&mut acc, ctx); diff --git a/crates/ide_completion/src/macros.rs b/crates/ide_completion/src/macros.rs index 3c4b77cfad..e86cf3ba93 100644 --- a/crates/ide_completion/src/macros.rs +++ b/crates/ide_completion/src/macros.rs @@ -253,6 +253,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample1). -define(FOO, 1). -define(FOO(), 1). @@ -271,6 +272,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample1). -define(FOO, 1). -define(BAR, 1). @@ -282,6 +284,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample1). -define(FOO, ok). -define(BAR, 1). diff --git a/crates/ide_completion/src/maps.rs b/crates/ide_completion/src/maps.rs index 4371793356..e232a7d8bc 100644 --- a/crates/ide_completion/src/maps.rs +++ b/crates/ide_completion/src/maps.rs @@ -214,6 +214,7 @@ mod test { fn test_local_type() { check( r#" + //- expect_parse_errors -module(test_local_type). -type my_map() :: #{field1 := integer(), field2 => boolean()}. foo(X) -> #~ @@ -229,6 +230,7 @@ mod test { fn test_included_type() { check( r#" + //- expect_parse_errors //- /include/test_included_type.hrl include_path:/include -type my_included_map() :: #{field1 := integer(), field2 => integer()}. //- /src/test_included_type.erl @@ -250,6 +252,7 @@ mod test { fn test_empty_map_type() { check( r#" + //- expect_parse_errors -module(test_empty_map_type). -type my_map() :: #{}. foo(X) -> #~ @@ -265,6 +268,7 @@ mod test { fn test_arity_1_map_type() { check( r#" + //- expect_parse_errors -module(test_arity_1_map_type). -type my_map() :: #{field1 := true }. foo(X) -> #~ @@ -280,6 +284,7 @@ mod test { fn test_mixed_map_and_record_types() { check( r#" + //- expect_parse_errors -module(test_mixed_map_and_record_types). -type my_map() :: #{field1 := true }. -record(my_record, {field1}). @@ -297,6 +302,7 @@ mod test { fn test_nested_map_type() { check( r#" + //- expect_parse_errors -module(test_nested_map_type). -type my_map() :: #{field1 := true, field2 => false}. -type my_nested_map() :: #{field1 := #{field1 => my_map()}}. @@ -315,11 +321,12 @@ mod test { fn test_type_from_spec() { check( r#" + //- expect_parse_errors //- /src/one.erl -module(one). -type my_map(X) :: #{field1 := X}. -export_type([my_map/0]). - + //- /src/two.erl -module(two). -spec foo(one:my_map(integer())) -> one:my_map(integer()). @@ -338,11 +345,12 @@ mod test { fn test_type_from_unowned_spec() { check( r#" + //- expect_parse_errors //- /src/one.erl -module(one). -type my_map(X) :: #{field1 := X}. -export_type([my_map/0]). - + //- /src/two.erl -module(two). -spec foo(one:my_map(integer())) -> one:my_map(integer()). @@ -359,6 +367,7 @@ mod test { fn test_local_type_in_function_argument() { check( r#" + //- expect_parse_errors -module(test_local_type). -type my_map() :: #{field1 := integer(), field2 => boolean()}. foo(#~ diff --git a/crates/ide_completion/src/modules.rs b/crates/ide_completion/src/modules.rs index 820d04cfe8..3823339f78 100644 --- a/crates/ide_completion/src/modules.rs +++ b/crates/ide_completion/src/modules.rs @@ -132,6 +132,7 @@ foo() -> check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). -export([ diff --git a/crates/ide_completion/src/records.rs b/crates/ide_completion/src/records.rs index cf8e0abc7c..d66e054423 100644 --- a/crates/ide_completion/src/records.rs +++ b/crates/ide_completion/src/records.rs @@ -320,6 +320,7 @@ mod test { fn test_record_name() { check( r#" + //- expect_parse_errors -module(sample). -record(this_record, {field1=1, field2=2}). -record(that_record, {}). @@ -334,6 +335,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -record(this_record, {field1=1, field2=2}). -record(that_record, {}). @@ -353,6 +355,7 @@ mod test { // Irregular names are quoted. check( r#" + //- expect_parse_errors -module(sample). -record('this.record', {field1=1, field2=2}). -record('that$record', {}). @@ -369,6 +372,7 @@ mod test { fn test_record_error_recovery() { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> #rec{field1 = 1, field2~. @@ -381,6 +385,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> X#rec{field1 = 1, field2~. @@ -393,6 +398,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -404,6 +410,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -417,6 +424,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -433,6 +441,7 @@ mod test { fn test_record_name_in_function_signature() { check( r#" + //- expect_parse_errors -module(sample). -record(this_record, {field1=1, field2=2}). -record(that_record, {}). @@ -450,6 +459,7 @@ mod test { fn test_record_field_in_function_signature() { check( r#" + //- expect_parse_errors -module(sample). -record(rec, {field1, field2, other}). foo(#rec{fie~ diff --git a/crates/ide_completion/src/spec.rs b/crates/ide_completion/src/spec.rs index 6a0061e914..cc853af865 100644 --- a/crates/ide_completion/src/spec.rs +++ b/crates/ide_completion/src/spec.rs @@ -109,6 +109,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). frog() -> ok. @@ -129,6 +130,7 @@ mod test { check( r#" + //- expect_parse_errors -module(sample). frog(A, B) -> {A, B}. diff --git a/crates/ide_completion/src/types.rs b/crates/ide_completion/src/types.rs index 3800a97b5c..e232ab2944 100644 --- a/crates/ide_completion/src/types.rs +++ b/crates/ide_completion/src/types.rs @@ -169,6 +169,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample). -type alias() :: ok. @@ -197,6 +198,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample). -type alias() :: ok. @@ -225,6 +227,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample). -spec foo() -> sample2:~. @@ -248,6 +251,7 @@ mod test { // something reasonable (show nothing) check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample). -spec foo() -> sample~:. @@ -263,6 +267,7 @@ mod test { check( r#" + //- expect_parse_errors //- /src/sample.erl -module(sample). -spec foo() -> sample~:. diff --git a/crates/ide_completion/src/vars.rs b/crates/ide_completion/src/vars.rs index 65740d56a4..5544475ed3 100644 --- a/crates/ide_completion/src/vars.rs +++ b/crates/ide_completion/src/vars.rs @@ -100,6 +100,7 @@ mod test { fn test_local_variables_1() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). test(AnArg1,Blah) -> @@ -117,6 +118,7 @@ mod test { fn test_local_variables_limit_to_current_function() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). another(AnArgNotMatched) -> @@ -142,6 +144,7 @@ mod test { fn test_local_variables_none_if_space() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). test(AnArg1,Blah) -> @@ -157,6 +160,7 @@ mod test { fn test_local_variables_no_duplicates() { check( r#" + //- expect_parse_errors //- /src/sample1.erl -module(sample1). handle_update(Config, Contents) -> diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml index babeee31b9..927391ba93 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml @@ -12,15 +12,16 @@ elp_eqwalizer.workspace = true elp_erlang_service.workspace = true elp_project_model.workspace = true elp_syntax.workspace = true -elp_text_edit.workspace = true elp_types_db.workspace = true hir.workspace = true anyhow.workspace = true +cov-mark.workspace = true eetf.workspace = true either.workspace = true fxhash.workspace = true indexmap.workspace = true +itertools.workspace = true lazy_static.workspace = true log.workspace = true memchr.workspace = true @@ -35,6 +36,7 @@ stdx.workspace = true strum.workspace = true strum_macros.workspace = true tempfile.workspace = true +text-size.workspace = true toml.workspace = true tracing.workspace = true diff --git a/crates/ide_db/src/diagnostic_code.rs b/crates/ide_db/src/diagnostic_code.rs index d3e73636bd..ec1e1d76cf 100644 --- a/crates/ide_db/src/diagnostic_code.rs +++ b/crates/ide_db/src/diagnostic_code.rs @@ -20,9 +20,9 @@ use serde::de; use strum::IntoEnumIterator; use strum_macros::EnumIter; -// @fb-only +// @fb-only: use crate::meta_only::MetaOnlyDiagnosticCode; -// @fb-only +// @fb-only: pub const BASE_URL: &str = crate::meta_only::BASE_URL; pub const BASE_URL: &str = "https://whatsapp.github.io/erlang-language-platform/docs"; // @oss-only #[derive(Clone, Debug, PartialEq, Eq, Hash, EnumIter)] @@ -52,6 +52,7 @@ pub enum DiagnosticCode { DependentHeader, DeprecatedFunction, UndefinedFunction, + UnavailableType, Unexpected(String), ExpressionCanBeSimplified, CannotEvaluateCTCallbacks, @@ -89,6 +90,10 @@ pub enum DiagnosticCode { NoErrorLogger, NoNoWarnSuppressions, CouldBeAStringLiteral, + ListsReverseAppend, + HirUnresolvedMacro, + HirUnresolvedInclude, + BoundVarInLhs, // Wrapper for erlang service diagnostic codes ErlangService(String), @@ -96,7 +101,7 @@ pub enum DiagnosticCode { Eqwalizer(String), // Used for ad-hoc diagnostics via lints/codemods AdHoc(String), - // @fb-only + // @fb-only: MetaOnly(MetaOnlyDiagnosticCode), } // These namespaces map the error codes returned by the Erlang Service. @@ -112,7 +117,7 @@ pub enum Namespace { Parser, EDoc, WhatsApp, - // @fb-only + // @fb-only: MetaOnly, } impl fmt::Display for Namespace { @@ -127,7 +132,7 @@ impl fmt::Display for Namespace { Namespace::Parser => "p", Namespace::EDoc => "o", Namespace::WhatsApp => "w", - // @fb-only + // @fb-only: Namespace::MetaOnly => "meta_only", }; write!(f, "{namespace}") } @@ -160,7 +165,7 @@ impl Namespace { pub fn supports_doc_path(&self) -> bool { match self { Namespace::WhatsApp => true, - // @fb-only + // @fb-only: Namespace::MetaOnly => true, _ => false, } } @@ -248,10 +253,15 @@ impl DiagnosticCode { DiagnosticCode::NoErrorLogger => "W0053".to_string(), DiagnosticCode::NoNoWarnSuppressions => "W0054".to_string(), DiagnosticCode::CouldBeAStringLiteral => "W0055".to_string(), + DiagnosticCode::ListsReverseAppend => "W0056".to_string(), + DiagnosticCode::HirUnresolvedMacro => "W0057".to_string(), + DiagnosticCode::HirUnresolvedInclude => "W0058".to_string(), + DiagnosticCode::UnavailableType => "W0059".to_string(), + DiagnosticCode::BoundVarInLhs => "W0060".to_string(), DiagnosticCode::ErlangService(c) => c.to_string(), DiagnosticCode::Eqwalizer(c) => format!("eqwalizer: {c}"), DiagnosticCode::AdHoc(c) => format!("ad-hoc: {c}"), - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(c) => c.as_code(), } } @@ -263,6 +273,7 @@ impl DiagnosticCode { DiagnosticCode::HeadMismatch => "head_mismatch".to_string(), DiagnosticCode::SyntaxError => "syntax_error".to_string(), DiagnosticCode::BoundVarInPattern => "bound_var_in_pattern".to_string(), + DiagnosticCode::BoundVarInLhs => "bound_var_in_lhs".to_string(), DiagnosticCode::ModuleMismatch => "module_mismatch".to_string(), DiagnosticCode::UnusedMacro => "unused_macro".to_string(), DiagnosticCode::UnusedRecordField => "unused_record_field".to_string(), @@ -344,11 +355,15 @@ impl DiagnosticCode { DiagnosticCode::NoErrorLogger => "no_error_logger".to_string(), DiagnosticCode::NoNoWarnSuppressions => "no_nowarn_suppressions".to_string(), DiagnosticCode::CouldBeAStringLiteral => "could_be_a_binary_string_literal".to_string(), + DiagnosticCode::ListsReverseAppend => "lists_reverse_append".to_string(), + DiagnosticCode::HirUnresolvedMacro => "hir_unresolved_macro".to_string(), + DiagnosticCode::HirUnresolvedInclude => "hir_unresolved_include".to_string(), + DiagnosticCode::UnavailableType => "unavailable_type".to_string(), DiagnosticCode::ErlangService(c) => c.to_string(), DiagnosticCode::Eqwalizer(c) => c.to_string(), DiagnosticCode::AdHoc(c) => format!("ad-hoc: {c}"), - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(c) => c.as_label(), } } @@ -359,7 +374,7 @@ impl DiagnosticCode { pub fn maybe_from_string(s: &str) -> Option { DIAGNOSTIC_CODE_LOOKUPS .get(s).cloned() - // @fb-only + // @fb-only: .or_else(|| MetaOnlyDiagnosticCode::from_str(s).ok().map(DiagnosticCode::MetaOnly)) .or_else( || // Look for ErlangService and AdHoc if let Some(code) = Self::is_adhoc(s) { @@ -376,7 +391,7 @@ impl DiagnosticCode { match self { DiagnosticCode::DefaultCodeForEnumIter => None, DiagnosticCode::AdHoc(_) => None, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(_) => Some(Namespace::MetaOnly), DiagnosticCode::ErlangService(code) => Namespace::from_str(code).ok(), _ => Namespace::from_str(&self.as_code()).ok(), } @@ -385,7 +400,7 @@ impl DiagnosticCode { pub fn supports_doc_path(&self) -> bool { match self { DiagnosticCode::DefaultCodeForEnumIter => false, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::DefaultCodeForEnumIter) => false, _ => true, } } @@ -474,6 +489,7 @@ impl DiagnosticCode { DiagnosticCode::ModuleMismatch => false, DiagnosticCode::UnusedInclude => false, DiagnosticCode::BoundVarInPattern => false, + DiagnosticCode::BoundVarInLhs => false, DiagnosticCode::UnusedMacro => false, DiagnosticCode::UnusedRecordField => false, DiagnosticCode::MutableVarBug => false, @@ -488,6 +504,7 @@ impl DiagnosticCode { DiagnosticCode::DependentHeader => false, DiagnosticCode::DeprecatedFunction => false, DiagnosticCode::UndefinedFunction => false, + DiagnosticCode::UnavailableType => false, DiagnosticCode::Unexpected(_) => false, DiagnosticCode::ExpressionCanBeSimplified => false, DiagnosticCode::UnnecessaryFlatteningToFindFlatLength => false, @@ -520,12 +537,15 @@ impl DiagnosticCode { DiagnosticCode::NoCatch => false, DiagnosticCode::NoErrorLogger => false, DiagnosticCode::NoNoWarnSuppressions => false, + DiagnosticCode::ListsReverseAppend => false, + DiagnosticCode::HirUnresolvedMacro => false, + DiagnosticCode::HirUnresolvedInclude => false, DiagnosticCode::BinaryStringToSigil => false, DiagnosticCode::ErlangService(_) => false, DiagnosticCode::Eqwalizer(_) => false, DiagnosticCode::AdHoc(_) => false, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(code) => code.allows_fixme_comment(), } } diff --git a/crates/ide_db/src/eqwalizer.rs b/crates/ide_db/src/eqwalizer.rs index 4deed79778..fca577bb21 100644 --- a/crates/ide_db/src/eqwalizer.rs +++ b/crates/ide_db/src/eqwalizer.rs @@ -12,7 +12,6 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::FileRange; -use elp_base_db::FileSource; use elp_base_db::ModuleName; use elp_base_db::ProjectId; use elp_base_db::SourceDatabase; @@ -89,7 +88,7 @@ pub trait EqwalizerDatabase: fn types_for_file(&self, file_id: FileId) -> Option>>; fn has_eqwalizer_module_marker(&self, file_id: FileId) -> bool; fn has_eqwalizer_ignore_marker(&self, file_id: FileId) -> bool; - fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> bool; + fn is_eqwalizer_enabled(&self, file_id: FileId) -> bool; } pub fn eqwalizer_diagnostics_by_project( @@ -114,7 +113,7 @@ fn type_at_position( db: &dyn EqwalizerDatabase, range: FileRange, ) -> Option> { - if !db.is_eqwalizer_enabled(range.file_id, false) { + if !db.is_eqwalizer_enabled(range.file_id) { return None; } let project_id = db.file_app_data(range.file_id)?.project_id; @@ -149,7 +148,7 @@ fn type_at_position( } fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option>> { - if !db.is_eqwalizer_enabled(file_id, false) { + if !db.is_eqwalizer_enabled(file_id) { return None; } let project_id = db.file_app_data(file_id)?.project_id; @@ -162,7 +161,7 @@ fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option bool { +fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId) -> bool { if !otp_supported_by_eqwalizer() { return false; } @@ -178,11 +177,8 @@ fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId, include_tes let project = db.project_data(project_id); let eqwalizer_config = &project.eqwalizer_config; let module_index = db.module_index(project_id); - let is_src = module_index.file_source_for_file(file_id) == Some(FileSource::Src); - let is_test_opted_in = db.is_test_suite_or_test_helper(file_id) == Some(true) && include_tests; let global_opt_in = eqwalizer_config.enable_all; - let opt_in = - (global_opt_in && (is_src || is_test_opted_in)) || db.has_eqwalizer_module_marker(file_id); + let opt_in = global_opt_in || db.has_eqwalizer_module_marker(file_id); let ignored_in_config = if let Some(module_name) = module_index.module_for_file(file_id) { eqwalizer_config .ignore_modules_compiled_patterns diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index cad66d9e48..038b41f0be 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -15,9 +15,9 @@ use elp_syntax::SourceFile; use elp_syntax::SyntaxKind; use elp_syntax::SyntaxNode; use elp_syntax::SyntaxToken; +use elp_syntax::TextSize; use elp_syntax::TokenAtOffset; use elp_syntax::ast; -use elp_text_edit::TextSize; use hir::FormList; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -43,11 +43,22 @@ pub fn pick_best_token( tokens.max_by_key(move |t| f(t.kind())) } +/// Given a syntax node, check it it is immediately enclosed in a call, +/// which can represent a function call or a type. +/// For a remote call, the node can be the module or the function name. +/// In the former case, there is an extra level of nesting, so we need +/// to check up to 3 steps up pub fn get_call(syntax: &SyntaxNode) -> Option { - if let Some(call) = ast::Call::cast(syntax.parent()?) { - Some(call) + ast::Call::cast(syntax.parent()?) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?)) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?.parent()?)) +} + +pub fn get_external_fun(syntax: &SyntaxNode) -> Option { + if let Some(external_fun) = ast::ExternalFun::cast(syntax.parent()?) { + Some(external_fun) } else { - ast::Call::cast(syntax.parent()?.parent()?) + ast::ExternalFun::cast(syntax.parent()?.parent()?) } } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 0240d8498d..aecfd86473 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -57,15 +57,17 @@ pub mod docs; pub mod eqwalizer; mod erl_ast; mod line_index; -// @fb-only +// @fb-only: pub mod meta_only; pub mod metadata; mod search; +pub mod text_edit; // --------------------------------------------------------------------- pub mod assists; pub mod helpers; pub mod rename; pub mod source_change; +pub mod tree_diff; pub use defs::ReferenceClass; pub use defs::ReferenceType; @@ -383,7 +385,7 @@ impl TypedSemantic for RootDatabase { let project_id = app_data.project_id; - let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id, false); + let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id); if !eqwalizer_enabled { return Some(vec![]); } @@ -419,8 +421,8 @@ impl TypedSemantic for RootDatabase { mod tests { use elp_base_db::SourceDatabase; use elp_base_db::fixture::WithFixture; - use elp_text_edit::TextRange; + use super::text_edit::TextRange; use crate::RootDatabase; #[test] @@ -446,4 +448,17 @@ mod tests { let position = fixture.position(); debug_assert_eq!(db.clamp_offset(position.file_id, 2000.into()), 15.into()) } + + #[test] + #[should_panic(expected = "Fixture validation failed: syntax errors found in test fixture")] + fn validate_fixture_with_parse_error() { + let fixture = r#" +//- /src/test.erl +-module(test). +foo( -> ok. +"#; + + let (db, fixture) = RootDatabase::with_fixture(fixture); + fixture.validate(&db); + } } diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index c25ee41e2a..bd5a49f8cf 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -15,19 +15,24 @@ use std::fmt; use std::iter::once; +use elp_base_db::AnchoredPathBuf; use elp_base_db::FileId; use elp_base_db::FileRange; +use elp_base_db::ModuleName; use elp_syntax::AstNode; use elp_syntax::ast; use elp_syntax::ast::in_erlang_module; -use elp_text_edit::TextEdit; use hir::InFile; use hir::Semantic; use crate::SymbolDefinition; use crate::helpers::get_call; +use crate::helpers::get_external_fun; use crate::search::NameLike; +use crate::source_change::FileSystemEdit; use crate::source_change::SourceChange; +use crate::text_edit::TextEdit; +use crate::text_edit::TextEditBuilder; pub type RenameResult = Result; @@ -79,6 +84,45 @@ pub fn is_valid_function_name(new_name: &String) -> bool { } } +// Delegate checking macro name validity to the parser +// Macros can be either atoms or variables +pub fn is_valid_macro_name(new_name: &String) -> bool { + let parse = ast::SourceFile::parse_text(format!("-define({new_name}, value).").as_str()); + matches!( + parse.tree().forms().next(), + Some(ast::Form::PreprocessorDirective( + ast::PreprocessorDirective::PpDefine(_) + )) + ) +} + +// Delegate checking type name validity to the parser +pub fn is_valid_type_name(new_name: &String) -> bool { + let parse = ast::SourceFile::parse_text(format!("-type {new_name}() :: ok.").as_str()); + // Check that we got a TypeAlias form + if let Some(ast::Form::TypeAlias(type_alias)) = parse.tree().forms().next() { + // Check that the name is an atom (not a variable) + if let Some(type_name) = type_alias.name() + && let Some(ast::Name::Atom(_)) = type_name.name() + { + return true; + } + } + false +} + +// Delegate checking module name validity to the parser +pub fn is_valid_module_name(new_name: &String) -> bool { + let parse = ast::SourceFile::parse_text(format!("-module({}).", new_name).as_str()); + match parse.tree().forms().next() { + Some(ast::Form::ModuleAttribute(ma)) => match ma.name() { + Some(ast::Name::Atom(atom)) => atom.syntax().text().to_string() == *new_name, + _ => false, + }, + _ => false, + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SafetyChecks { Yes, @@ -95,7 +139,10 @@ impl SymbolDefinition { ) -> RenameResult { match self.clone() { SymbolDefinition::Module(_) => { - rename_error!("Cannot rename module") + if safety_check == SafetyChecks::Yes && !is_valid_module_name(new_name) { + rename_error!("Invalid new module name: '{}'", new_name); + } + self.rename_reference(sema, new_name, parens_needed_in_context, safety_check) } SymbolDefinition::Function(fun) => { if safety_check == SafetyChecks::Yes && !is_valid_function_name(new_name) { @@ -117,14 +164,58 @@ impl SymbolDefinition { SymbolDefinition::RecordField(_) => { rename_error!("Cannot rename record field") } - SymbolDefinition::Type(_) => { - rename_error!("Cannot rename type") + SymbolDefinition::Type(type_alias) => { + if safety_check == SafetyChecks::Yes && !is_valid_type_name(new_name) { + rename_error!("Invalid new type name: '{}'", new_name); + } + + let arity = type_alias.name().arity(); + if safety_check == SafetyChecks::Yes { + // Check safety in the file where the type is defined + if !is_safe_type(sema, type_alias.file.file_id, new_name, arity) { + rename_error!("Type '{}/{}' already in scope", new_name, arity); + } + + // Also check safety in all files where the type is used + let usages = self.clone().usages(sema).all(); + for (file_id, _refs) in usages.iter() { + if file_id != type_alias.file.file_id + && !is_safe_type(sema, file_id, new_name, arity) + { + rename_error!("Type '{}/{}' already in scope", new_name, arity); + } + } + } + + self.rename_reference(sema, new_name, parens_needed_in_context, safety_check) } SymbolDefinition::Callback(_) => { rename_error!("Cannot rename callback") } - SymbolDefinition::Define(_) => { - rename_error!("Cannot rename define") + SymbolDefinition::Define(define) => { + if safety_check == SafetyChecks::Yes && !is_valid_macro_name(new_name) { + rename_error!("Invalid new macro name: '{}'", new_name); + } + + let arity = define.define.name.arity(); + if safety_check == SafetyChecks::Yes { + // Check safety in the file where the macro is defined + if !is_safe_macro(sema, define.file.file_id, new_name, arity) { + rename_error!("Macro '{}' already in scope", new_name); + } + + // Also check safety in all files where the macro is used + let usages = self.clone().usages(sema).all(); + for (file_id, _refs) in usages.iter() { + if file_id != define.file.file_id + && !is_safe_macro(sema, file_id, new_name, arity) + { + rename_error!("Macro '{}' already in scope", new_name); + } + } + } + + self.rename_reference(sema, new_name, parens_needed_in_context, safety_check) } SymbolDefinition::Header(_) => { rename_error!("Cannot rename header") @@ -151,6 +242,27 @@ impl SymbolDefinition { range, }) } + SymbolDefinition::Define(d) => { + // Get the macro definition location + let source = d.source(sema.db.upcast()); + if let Some(name) = source.name() { + let range = name.syntax().text_range(); + Some(FileRange { + file_id: d.file.file_id, + range, + }) + } else { + None + } + } + SymbolDefinition::Type(t) => { + // Get the type definition location + let range = t.name_range(sema.db.upcast())?; + Some(FileRange { + file_id: t.file.file_id, + range, + }) + } _ => None, } } @@ -215,6 +327,40 @@ impl SymbolDefinition { ); Ok(source_change) } + SymbolDefinition::Define(_define) => { + // Find all usages of the macro + let usages = self.clone().usages(sema).all(); + + // Also need to rename the definition itself + // Get the macro definition location + let (file_id, def_edit) = source_edit_from_def(sema, self.clone(), new_name)?; + source_change.insert_source_edit(file_id, def_edit); + + source_edit_from_usages( + &mut source_change, + usages.iter().collect(), + new_name, + parens_needed_in_context, + ); + Ok(source_change) + } + SymbolDefinition::Type(_type_alias) => { + // Find all usages of the type + let usages = self.clone().usages(sema).all(); + + // Also need to rename the definition itself + // Get the type definition location + let (file_id, def_edit) = source_edit_from_def(sema, self.clone(), new_name)?; + source_change.insert_source_edit(file_id, def_edit); + + source_edit_from_usages( + &mut source_change, + usages.iter().collect(), + new_name, + parens_needed_in_context, + ); + Ok(source_change) + } SymbolDefinition::Var(var) => { let usages = sema .find_local_usages_ast(InFile { @@ -249,6 +395,7 @@ impl SymbolDefinition { ); Ok(source_change) } + SymbolDefinition::Module(_module) => self.rename_module(sema, new_name, safety_check), // Note: This is basically an internal error, this function is called from // SymbolDefinition::rename which already weeds them out _ => { @@ -256,6 +403,184 @@ impl SymbolDefinition { } } } + + fn rename_module( + &self, + sema: &Semantic, + new_name: &str, + safety_check: SafetyChecks, + ) -> RenameResult { + let file_id = self.file().file_id; + if let Some(project_id) = sema.db.file_project_id(file_id) { + let module_index = sema.db.module_index(project_id); + if safety_check == SafetyChecks::Yes { + let new_name_module = ModuleName::new(new_name); + if module_index + .all_modules() + .iter() + .any(|name| name == &new_name_module) + { + rename_error!("module '{}' already exists", new_name); + } + } + + let mut source_change = SourceChange::default(); + // Step 1, rename all references + let usages = self.clone().usages(sema).all(); + let mut renamed_module_edit: TextEdit = TextEdit::default(); + rename_remote_module_call_refs( + usages, + file_id, + new_name, + &mut source_change, + &mut renamed_module_edit, + ); + + // Step 2: Rename the module attribute in the module being renamed + let form_list = sema.form_list(file_id); + if let Some(module_attribute) = form_list.module_attribute() { + let ast = module_attribute.form_id.get_ast(sema.db, file_id); + if let Some(name) = ast.name() { + let range = name.syntax().text_range(); + let mut builder = TextEdit::builder(); + builder.replace(range, new_name.to_string()); + renamed_module_edit + .union(builder.finish()) + .expect("Could not combine TextEdits"); + } + } + + let anchor = file_id; + let path = format!("{new_name}.erl"); + let dst = AnchoredPathBuf { anchor, path }; + source_change.insert_new_source_edit(dst.clone().into(), renamed_module_edit); + source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst }); + Ok(source_change) + } else { + rename_error!( + "Could not find project for '{:?}'", + self.file().name(sema.db.upcast()) + ) + } + } +} + +fn rename_remote_module_call_refs( + usages: crate::UsageSearchResult, + file_id: FileId, + new_name: &str, + source_change: &mut SourceChange, + renamed_module_edit: &mut TextEdit, +) { + usages.iter().for_each(|(usage_file_id, refs)| { + if let Some(edit) = rename_module_in_refs(refs, new_name) { + if usage_file_id == file_id { + renamed_module_edit + .union(edit) + .expect("Could not combine TextEdits"); + } else { + source_change.insert_source_edit(usage_file_id, edit); + } + }; + }); +} + +fn rename_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { + let mut builder = TextEdit::builder(); + for usage in refs { + // Note: we cannot blindly replace all occurrences of an + // atom that happens to be a module name + // We will flesh out other usages as we need them + let _ = rename_call_module_in_ref(usage, &mut builder, new_name); + let _ = rename_external_fun_module_in_ref(usage, &mut builder, new_name); + } + Some(builder.finish()) +} + +fn rename_call_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let call = get_call(usage.syntax())?; + // We can only rename an atom usage + let usage_atom = match usage { + NameLike::Name(ast::Name::Atom(atom)) => atom, + _ => return Some(()), + }; + + // First check if this is the module part of a remote call (e.g., module:function()) + if let Some(ast::Expr::Remote(remote)) = call.expr() + && let Some(module) = remote.module() + && let Some(ast::ExprMax::Atom(mod_atom)) = module.module() + && mod_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + return Some(()); + } + + // Check if this is a known function call that takes a module as an argument + // Extract function name and optional module name based on call type + let (module_name, function_name) = match call.expr()? { + ast::Expr::Remote(remote) => { + let module = remote.module()?; + let mod_atom = match module.module()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + let fun_atom = match remote.fun()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + (Some(mod_atom.text()?), fun_atom.text()?) + } + ast::Expr::ExprMax(ast::ExprMax::Atom(fun_atom)) => (None, fun_atom.text()?), + _ => return Some(()), + }; + + let args = call.args()?; + let args_vec: Vec<_> = args.args().collect(); + let arity = args_vec.len(); + let pattern_key = (module_name.as_deref(), function_name.as_str(), arity); + + // Use combined patterns that merge dynamic call patterns and module argument patterns + let combined_patterns = hir::sema::to_def::get_module_arg_patterns(); + if let Some(pattern) = combined_patterns.get(&pattern_key) + && let Some(arg) = args_vec.get(pattern.index) + { + match arg { + ast::Expr::ExprMax(ast::ExprMax::Atom(arg_atom)) + if pattern.accepts_atom() && arg_atom.syntax() == usage_atom.syntax() => + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + } + ast::Expr::ExprMax(ast::ExprMax::List(list)) if pattern.accepts_list() => { + // Handle list of modules (e.g., meck:new([mod1, mod2], Options)) + for expr in list.exprs() { + if let ast::Expr::ExprMax(ast::ExprMax::Atom(list_atom)) = expr + && list_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + break; + } + } + } + _ => {} + } + } + + Some(()) +} + +fn rename_external_fun_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let external_fun = get_external_fun(usage.syntax())?; + let module = external_fun.module()?; + builder.replace(module.name()?.syntax().text_range(), new_name.to_string()); + Some(()) } fn source_edit_from_usages( @@ -388,6 +713,32 @@ pub fn is_safe_function(sema: &Semantic, file_id: FileId, new_name: &str, arity: scope_ok && !in_erlang_module(new_name, arity as usize) } +/// Check that the new macro name is not already defined in scope. +/// Macros are identified by both name and arity, similar to functions. +pub fn is_safe_macro(sema: &Semantic, file_id: FileId, new_name: &str, arity: Option) -> bool { + sema.db + .def_map(file_id) + .get_macros() + .iter() + .all(|(name, _)| { + // A macro is considered different if either the name or arity differs + *name.name().to_string() != *new_name || name.arity() != arity + }) +} + +/// Check that the new type name is not already defined in scope. +/// Types are identified by both name and arity, similar to functions and macros. +pub fn is_safe_type(sema: &Semantic, file_id: FileId, new_name: &str, arity: u32) -> bool { + sema.db + .def_map(file_id) + .get_types() + .iter() + .all(|(name, _)| { + // A type is considered different if either the name or arity differs + *name.name().to_string() != *new_name || name.arity() != arity + }) +} + /// Check that the new function name is not in scope already in the /// module via an explicit import. pub fn is_safe_remote_function( diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 20b4f050b4..1e66598359 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -325,7 +325,7 @@ impl<'a> FindUsages<'a> { /// Represents possible ast reference points - /// a string for header, or ast::Name for everything else -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NameLike { Name(ast::Name), String(ast::String), diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index bb64431772..60b62279fe 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs @@ -20,19 +20,46 @@ use std::mem; use elp_base_db::AnchoredPathBuf; use elp_base_db::FileId; use elp_syntax::SyntaxNode; -use elp_syntax::algo; -use elp_text_edit::TextEdit; -use elp_text_edit::TextEditBuilder; -use elp_text_edit::TextRange; -use elp_text_edit::TextSize; +use elp_syntax::TextRange; +use elp_syntax::TextSize; use fxhash::FxHashMap; use stdx::never; use crate::helpers::SnippetCap; +use crate::text_edit::TextEdit; +use crate::text_edit::TextEditBuilder; +use crate::tree_diff::diff; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)] +pub struct HashableAnchoredPathBuf { + /// File that this path is relative to. + pub anchor: FileId, + /// Path relative to `anchor`'s containing directory. + pub path: String, +} + +impl From for HashableAnchoredPathBuf { + fn from(value: AnchoredPathBuf) -> Self { + HashableAnchoredPathBuf { + anchor: value.anchor, + path: value.path, + } + } +} + +impl From for AnchoredPathBuf { + fn from(value: HashableAnchoredPathBuf) -> Self { + AnchoredPathBuf { + anchor: value.anchor, + path: value.path, + } + } +} #[derive(Default, Debug, Clone)] pub struct SourceChange { pub source_file_edits: FxHashMap, + pub new_file_edits: FxHashMap, pub file_system_edits: Vec, pub is_snippet: bool, } @@ -46,6 +73,7 @@ impl SourceChange { ) -> Self { SourceChange { source_file_edits, + new_file_edits: FxHashMap::default(), file_system_edits, is_snippet: false, } @@ -74,6 +102,22 @@ impl SourceChange { } } + /// Inserts a [`TextEdit`] for the given [`AnchoredPathBuf`]. This properly handles merging existing + /// edits for a file if some already exist. + pub fn insert_new_source_edit(&mut self, file_id: HashableAnchoredPathBuf, edit: TextEdit) { + match self.new_file_edits.entry(file_id) { + Entry::Occupied(mut entry) => { + never!( + entry.get_mut().union(edit).is_err(), + "overlapping edits for same file" + ); + } + Entry::Vacant(entry) => { + entry.insert(edit); + } + } + } + pub fn push_file_system_edit(&mut self, edit: FileSystemEdit) { self.file_system_edits.push(edit); } @@ -85,12 +129,15 @@ impl SourceChange { pub fn merge(mut self, other: SourceChange) -> SourceChange { self.extend(other.source_file_edits); self.extend(other.file_system_edits); + self.extend(other.new_file_edits); self.is_snippet |= other.is_snippet; self } pub fn is_empty(&self) -> bool { - self.source_file_edits.is_empty() && self.file_system_edits.is_empty() + self.source_file_edits.is_empty() + && self.file_system_edits.is_empty() + && self.new_file_edits.is_empty() } pub fn text_range(&self, file_id: FileId) -> Option { @@ -116,10 +163,18 @@ impl Extend for SourceChange { } } +impl Extend<(HashableAnchoredPathBuf, TextEdit)> for SourceChange { + fn extend>(&mut self, iter: T) { + iter.into_iter() + .for_each(|(file_id, edit)| self.insert_new_source_edit(file_id, edit)); + } +} + impl From> for SourceChange { fn from(source_file_edits: FxHashMap) -> SourceChange { SourceChange { source_file_edits, + new_file_edits: FxHashMap::default(), file_system_edits: Vec::new(), is_snippet: false, } @@ -171,7 +226,7 @@ impl SourceChangeBuilder { fn commit(&mut self) { if let Some(tm) = self.mutated_tree.take() { - algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) + diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) } let edit = mem::take(&mut self.edit).finish(); @@ -265,6 +320,7 @@ impl From for SourceChange { fn from(edit: FileSystemEdit) -> SourceChange { SourceChange { source_file_edits: Default::default(), + new_file_edits: Default::default(), file_system_edits: vec![edit], is_snippet: false, } diff --git a/crates/text_edit/src/lib.rs b/crates/ide_db/src/text_edit.rs similarity index 99% rename from crates/text_edit/src/lib.rs rename to crates/ide_db/src/text_edit.rs index 42dac0f3ac..0410102569 100644 --- a/crates/text_edit/src/lib.rs +++ b/crates/ide_db/src/text_edit.rs @@ -15,7 +15,6 @@ //! `rust-analyzer` never mutates text itself and only sends diffs to clients, //! so `TextEdit` is the ultimate representation of the work done by //! rust-analyzer. - use std::cmp::max; use itertools::Itertools; diff --git a/crates/ide_db/src/tree_diff.rs b/crates/ide_db/src/tree_diff.rs new file mode 100644 index 0000000000..5bca5c2ec2 --- /dev/null +++ b/crates/ide_db/src/tree_diff.rs @@ -0,0 +1,507 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +//! Basic tree diffing functionality. + +use std::hash::BuildHasherDefault; + +use elp_syntax::NodeOrToken; +use elp_syntax::SyntaxElement; +use elp_syntax::SyntaxNode; +use fxhash::FxHashMap; +use indexmap::IndexMap; + +use crate::text_edit::TextEditBuilder; + +type FxIndexMap = IndexMap>; + +#[derive(Debug, Hash, PartialEq, Eq)] +enum TreeDiffInsertPos { + After(SyntaxElement), + AsFirstChild(SyntaxElement), +} + +#[derive(Debug)] +pub struct TreeDiff { + replacements: FxHashMap, + deletions: Vec, + // the vec as well as the indexmap are both here to preserve order + insertions: FxIndexMap>, +} + +impl TreeDiff { + pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { + let _p = tracing::info_span!("into_text_edit").entered(); + + for (anchor, to) in self.insertions.iter() { + let offset = match anchor { + TreeDiffInsertPos::After(it) => it.text_range().end(), + TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(), + }; + to.iter() + .for_each(|to| builder.insert(offset, to.to_string())); + } + for (from, to) in self.replacements.iter() { + builder.replace(from.text_range(), to.to_string()) + } + for text_range in self.deletions.iter().map(SyntaxElement::text_range) { + builder.delete(text_range); + } + } + + pub fn is_empty(&self) -> bool { + self.replacements.is_empty() && self.deletions.is_empty() && self.insertions.is_empty() + } +} + +/// Finds a (potentially minimal) diff, which, applied to `from`, will result in `to`. +/// +/// Specifically, returns a structure that consists of a replacements, insertions and deletions +/// such that applying this map on `from` will result in `to`. +/// +/// This function tries to find a fine-grained diff. +pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { + let _p = tracing::info_span!("diff").entered(); + + let mut diff = TreeDiff { + replacements: FxHashMap::default(), + insertions: FxIndexMap::default(), + deletions: Vec::new(), + }; + let (from, to) = (from.clone().into(), to.clone().into()); + + if !syntax_element_eq(&from, &to) { + go(&mut diff, from, to); + } + return diff; + + fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool { + lhs.kind() == rhs.kind() + && lhs.text_range().len() == rhs.text_range().len() + && match (&lhs, &rhs) { + (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { + lhs == rhs || lhs.text() == rhs.text() + } + (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), + _ => false, + } + } + + // FIXME: this is horribly inefficient. I bet there's a cool algorithm to diff trees properly. + fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) { + let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { + Some((lhs, rhs)) => (lhs, rhs), + _ => { + cov_mark::hit!(diff_node_token_replace); + diff.replacements.insert(lhs, rhs); + return; + } + }; + + let mut look_ahead_scratch = Vec::default(); + + let mut rhs_children = rhs.children_with_tokens(); + let mut lhs_children = lhs.children_with_tokens(); + let mut last_lhs = None; + loop { + let lhs_child = lhs_children.next(); + match (lhs_child.clone(), rhs_children.next()) { + (None, None) => break, + (None, Some(element)) => { + let insert_pos = match last_lhs.clone() { + Some(prev) => { + cov_mark::hit!(diff_insert); + TreeDiffInsertPos::After(prev) + } + // first iteration, insert into out parent as the first child + None => { + cov_mark::hit!(diff_insert_as_first_child); + TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) + } + }; + diff.insertions.entry(insert_pos).or_default().push(element); + } + (Some(element), None) => { + cov_mark::hit!(diff_delete); + diff.deletions.push(element); + } + (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} + (Some(lhs_ele), Some(rhs_ele)) => { + // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up + // until that element as insertions. This is important to keep the diff minimal + // in regards to insertions that have been actually done, this is important for + // use insertions as we do not want to replace the entire module node. + look_ahead_scratch.push(rhs_ele.clone()); + let mut rhs_children_clone = rhs_children.clone(); + let mut insert = false; + for rhs_child in rhs_children_clone.by_ref() { + if syntax_element_eq(&lhs_ele, &rhs_child) { + cov_mark::hit!(diff_insertions); + insert = true; + break; + } else { + look_ahead_scratch.push(rhs_child); + } + } + let drain = look_ahead_scratch.drain(..); + if insert { + let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { + TreeDiffInsertPos::After(prev) + } else { + cov_mark::hit!(insert_first_child); + TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) + }; + + diff.insertions.entry(insert_pos).or_default().extend(drain); + rhs_children = rhs_children_clone; + } else { + go(diff, lhs_ele, rhs_ele) + } + } + } + last_lhs = lhs_child.or(last_lhs); + } + } +} + +#[cfg(test)] +mod tests { + use elp_syntax::AstNode; + use elp_syntax::SourceFile; + use elp_syntax::SyntaxElement; + use elp_syntax::SyntaxKind; + use expect_test::Expect; + use expect_test::expect; + use itertools::Itertools; + + use crate::text_edit::TextEdit; + + #[test] + fn replace_node_token() { + cov_mark::check!(diff_node_token_replace); + check_diff( + r#"-module(foo)."#, + r#"ident"#, + expect![[r#" + insertions: + + + + replacements: + + Line 0: Token(ANON_DASH@0..1 "-") -> ident + + deletions: + + Line 1: module + Line 1: ( + Line 1: foo + Line 1: ) + Line 1: . + "#]], + ); + } + + #[test] + fn replace_parent() { + cov_mark::check!(diff_node_token_replace); + check_diff( + r#""#, + r#"-module(foo)."#, + expect![[r#" + insertions: + + + + replacements: + + Line 0: Token(SOURCE_FILE@0..0 "") -> -module(foo). + + deletions: + + + "#]], + ); + } + + #[test] + fn insert_last() { + cov_mark::check!(diff_insert); + check_diff( + r#" +-module(foo). +-use(foo). +-use(bar)."#, + r#" +-module(foo). +-use(foo). +-use(bar). +-use(baz)."#, + expect![[r#" + insertions: + + Line 3: After(Node(WILD_ATTRIBUTE@26..36)) + -> "\n" + -> -use(baz). + + replacements: + + + + deletions: + + + "#]], + ); + } + + #[test] + fn insert_middle() { + check_diff( + r#" +-module(foo). +-use(foo). +-use(bar)."#, + r#" +-module(foo). +-use(foo). +-use(baz). +-use(bar)."#, + expect![[r#" + insertions: + + Line 3: After(Token(WHITESPACE@25..26 "\n")) + -> -use(baz). + -> "\n" + + replacements: + + + + deletions: + + + "#]], + ) + } + + #[test] + fn insert_first() { + check_diff( + r#" +-use(bar). +-use(baz)."#, + r#" +-export([foo/0]). +-use(bar). +-use(baz)."#, + expect![[r#" + insertions: + + Line 0: After(Token(WHITESPACE@0..1 "\n")) + -> -export([foo/0]). + -> "\n" + + replacements: + + + + deletions: + + + "#]], + ) + } + + #[test] + fn first_child_insertion() { + // cov_mark::check!(insert_first_child); + check_diff( + r#" +main() -> + ok."#, + r#" +-module(foo). + +main() -> + ok."#, + expect![[r#" + insertions: + + Line 0: After(Token(WHITESPACE@0..1 "\n")) + -> -module(foo). + -> "\n\n" + + replacements: + + + + deletions: + + + "#]], + ); + } + + #[test] + fn delete_last() { + cov_mark::check!(diff_delete); + check_diff( + r#"-module(foo). + -bar([baz])."#, + r#"-module(foo)."#, + expect![[r#" + insertions: + + + + replacements: + + + + deletions: + + Line 1: "\n " + Line 2: -bar([baz]). + "#]], + ); + } + + #[test] + fn delete_middle() { + // cov_mark::check!(diff_insertions); + check_diff( + r#" +-export([foo/0,bar/1]). +-bar(aaa). + +-foo(bbb). +"#, + r#" +-export([foo/0,bar/1]). + +-foo(bbb). +"#, + expect![[r#" + insertions: + + Line 1: After(Node(EXPORT_ATTRIBUTE@1..24)) + -> "\n\n" + -> -foo(bbb). + + replacements: + + + + deletions: + + Line 2: -bar(aaa). + Line 3: "\n\n" + Line 4: -foo(bbb). + Line 5: "\n" + "#]], + ) + } + + #[test] + fn delete_first() { + check_diff( + r#" +-export([foo/0,bar/1]). + +-foo(bbb). +"#, + r#" +-foo(bbb). +"#, + expect![[r#" + insertions: + + + + replacements: + + Line 1: Token(ANON_DASH@1..2 "-") -> -foo + Line 2: Token(ANON_EXPORT@2..8 "export") -> (bbb) + Line 2: Token(ANON_LPAREN@8..9 "(") -> . + Line 2: Token(WHITESPACE@24..26 "\n\n") -> "\n" + + deletions: + + Line 2: [ + Line 2: foo/0 + Line 2: , + Line 2: bar/1 + Line 2: ] + Line 2: ) + Line 2: . + Line 3: -foo(bbb). + Line 4: "\n" + "#]], + ) + } + + fn check_diff(from: &str, to: &str, expected_diff: Expect) { + let from_node = SourceFile::parse_text(from).tree().syntax().clone(); + let to_node = SourceFile::parse_text(to).tree().syntax().clone(); + let diff = super::diff(&from_node, &to_node); + + let line_number = + |syn: &SyntaxElement| from[..syn.text_range().start().into()].lines().count(); + + let fmt_syntax = |syn: &SyntaxElement| match syn.kind() { + SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()), + _ => format!("{syn}"), + }; + + let insertions = + diff.insertions + .iter() + .format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> { + f(&format!( + "Line {}: {:?}\n-> {}", + line_number(match k { + super::TreeDiffInsertPos::After(syn) => syn, + super::TreeDiffInsertPos::AsFirstChild(syn) => syn, + }), + k, + v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) + )) + }); + + let replacements = diff + .replacements + .iter() + .sorted_by_key(|(syntax, _)| syntax.text_range().start()) + .format_with("\n", |(k, v), f| { + f(&format!( + "Line {}: {:?} -> {}", + line_number(k), + k, + fmt_syntax(v) + )) + }); + + let deletions = diff.deletions.iter().format_with("\n", |v, f| { + f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))) + }); + + let actual = format!( + "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n" + ); + expected_diff.assert_eq(&actual); + + let mut from = from.to_owned(); + let mut text_edit = TextEdit::builder(); + diff.into_text_edit(&mut text_edit); + text_edit.finish().apply(&mut from); + assert_eq!(&*from, to, "diff did not turn `from` to `to`"); + } +} diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs index 108680853e..881a5ac123 100644 --- a/crates/ide_ssr/src/tests.rs +++ b/crates/ide_ssr/src/tests.rs @@ -1288,7 +1288,7 @@ fn ssr_do_not_match_pattern_missing() { r#" g() -> <<$",$\\,194,181,$A,$">> = - ~b""" + """ "\\µA" """. "#, @@ -1327,7 +1327,7 @@ fn ssr_comments_in_match() { <<{ % preceding comment 3}, { 3 % following comment - >>. + }>>. "#; let strategy = Strategy { macros: MacroStrategy::Expand, @@ -1536,7 +1536,7 @@ fn ssr_predicates_on_match_expr_pat() { %-compile({"str", var, atom}). %-type foo(V) :: {"str", V, atom}. foo({"hello", Var, atom}) -> {"str", Var, aa}. - + "#; let strategy = Strategy { macros: MacroStrategy::Expand, @@ -1636,7 +1636,7 @@ fn ssr_predicates_on_match_type() { let pattern = "ssr: {_@V, _@A}."; let code = r#" -type foo(V) :: {V, atom}. - + "#; let strategy = Strategy { macros: MacroStrategy::Expand, diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 064c93de26..45c6b7f732 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -58,6 +58,7 @@ lazy_static! { } const ERL_EXT: &str = "erl"; +const BUCK_ISOLATION_DIR: &str = "lsp"; #[derive( Debug, @@ -88,6 +89,14 @@ pub struct BuckConfig { #[serde(default)] pub excluded_targets: Vec, pub(crate) source_root: Option, + /// Buck2 labels that denote test applications. + /// Defaults to ["test_application"] if not specified. + #[serde(default = "default_test_application_labels")] + pub test_application_labels: Vec, +} + +fn default_test_application_labels() -> Vec { + vec!["test_application".to_string()] } impl BuckConfig { @@ -100,7 +109,7 @@ impl BuckConfig { cmd.env_remove("RUST_BACKTRACE") .env_remove("RUST_LIB_BACKTRACE"); cmd.arg("--isolation-dir"); - cmd.arg("lsp"); + cmd.arg(BUCK_ISOLATION_DIR); cmd.current_dir(self.buck_root()); CommandProxy::new(guard, cmd) } @@ -726,7 +735,7 @@ fn load_buck_targets_bxl( root, name, buck_target, - buck_config.build_deps, + buck_config, targets_include_prelude, &mut dep_path, &mut target_info, @@ -747,7 +756,7 @@ fn make_buck_target( root: &AbsPathBuf, name: &String, target: &BuckTarget, - build_deps: bool, + buck_config: &BuckConfig, targets_include_prelude: bool, dep_path: &mut FxHashMap, target_info: &mut TargetInfo, @@ -763,7 +772,12 @@ fn make_buck_target( (src, TargetType::ErlangTest, false, None) } else { let mut private_header = false; - let target_type = compute_target_type(name, target, targets_include_prelude); + let target_type = compute_target_type( + name, + target, + targets_include_prelude, + &buck_config.test_application_labels, + ); let mut src_files = vec![]; for src in &target.srcs { let src = json::canonicalize(buck_path_to_abs_path(root, src).unwrap())?; @@ -774,7 +788,7 @@ fn make_buck_target( } let ebin = match target_type { - TargetType::ThirdParty if build_deps => dep_path + TargetType::ThirdParty if buck_config.build_deps => dep_path .remove(name) .map(|dir| dir.join(Utf8PathBuf::from("ebin"))), TargetType::ThirdParty => Some(dir.clone()), @@ -817,19 +831,20 @@ fn compute_target_type( name: &TargetFullName, target: &BuckTarget, targets_include_prelude: bool, + test_application_labels: &[String], ) -> TargetType { // Check if we are trying to work on the prelude itself let is_prelude_as_third_party = !targets_include_prelude && name.starts_with("prelude//"); if is_prelude_as_third_party || name.contains("//third-party") { TargetType::ThirdParty } else { - let test_utils = target.labels.contains("test_utils"); - let test_application = target.labels.contains("test_application"); - let elp_enabled = target.labels.contains("elp_enabled"); - match (elp_enabled, test_application, test_utils) { - (true, _, _) => TargetType::ErlangApp, - (false, false, false) => TargetType::ErlangApp, - (_, _, _) => TargetType::ErlangTestUtils, + let is_test_application = test_application_labels + .iter() + .any(|label| target.labels.contains(label)); + if is_test_application { + TargetType::ErlangTestUtils + } else { + TargetType::ErlangApp } } } @@ -873,6 +888,16 @@ pub enum BuckQueryConfig { BuckTargetsOnly, } +impl fmt::Display for BuckQueryConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BuckQueryConfig::BuildGeneratedCode => write!(f, "BuildGeneratedCode"), + BuckQueryConfig::NoBuildGeneratedCode => write!(f, "NoBuildGeneratedCode"), + BuckQueryConfig::BuckTargetsOnly => write!(f, "BuckTargetsOnly"), + } + } +} + fn filter_buck_targets( buck_config: &BuckConfig, result: FxHashMap, @@ -1338,36 +1363,56 @@ fn include_path_from_file(path: &AbsPath) -> AbsPathBuf { } } +fn check_buck_output_success(mut command: CommandProxy<'_>) -> Result { + let output = command.output()?; + if output.status.success() { + return String::from_utf8(output.stdout) + .map_err(|e| anyhow::anyhow!("Invalid UTF-8 in stdout for `{command}`: {e}")); + } + let reason = match output.status.code() { + Some(code) => format!("Exited with status code: {code}"), + None => "Process terminated by signal".to_string(), + }; + let details = String::from_utf8(output.stderr).unwrap_or_default(); + bail!("Command `{command}` failed. Reason: {reason}. Details: {details}"); +} + /// This is used in tests pub fn get_prelude_cell(buck_config: &BuckConfig) -> Result { - let output = buck_config - .buck_command() + let mut command = buck_config.buck_command(); + command .arg("audit") .arg("cell") .arg("prelude") - .output()?; - if !output.status.success() { - let reason = match output.status.code() { - Some(code) => format!("Exited with status code: {code}"), - None => "Process terminated by signal".to_string(), - }; - let details = match String::from_utf8(output.stderr) { - Ok(err) => err, - Err(_) => "".to_string(), - }; - bail!("Error evaluating Buck2 query Reason: {reason}. Details: {details}",); - } - let raw_output = String::from_utf8(output.stdout)?; + .arg("--json"); + let raw_output = check_buck_output_success(command)?; - lazy_static! { - static ref RE: Regex = Regex::new(r"^prelude: ([^\s]+)").unwrap(); + let json: serde_json::Value = serde_json::from_str(&raw_output)?; + let prelude_path = json + .get("prelude") + .and_then(|v| v.as_str()) + .ok_or_else(|| anyhow::anyhow!("Could not find prelude path in Buck2 output"))? + .to_string(); + + if Path::new(&prelude_path).exists() { + Ok(prelude_path) + } else { + get_prelude_cell_bundled(buck_config) } - let string = RE - .captures_iter(&raw_output) - .next() - .map(|c| c[1].to_string()) - .unwrap(); - Ok(string) +} + +fn get_prelude_cell_bundled(buck_config: &BuckConfig) -> Result { + let mut command = buck_config.buck_command(); + command.arg("root"); + let root = check_buck_output_success(command)?; + let root = root.trim(); + let bundled_prelude_path = Path::new(&root) + .join("buck-out") + .join(BUCK_ISOLATION_DIR) + .join("external_cells") + .join("bundled") + .join("prelude"); + Ok(bundled_prelude_path.to_string_lossy().to_string()) } #[cfg(test)] @@ -1408,6 +1453,10 @@ mod tests { "#; let dir = FixtureWithProjectMeta::gen_project(spec); let root = AbsPath::assert(Utf8Path::from_path(dir.path()).unwrap()); + let _guard = set_test_cell_info(vec![( + "cell".to_string(), + dir.path().to_string_lossy().into_owned(), + )]); let target_name = "cell//app_a:app_a".to_string(); let target = BuckTarget { name: "app_a".to_string(), @@ -1436,6 +1485,10 @@ mod tests { "#; let dir = FixtureWithProjectMeta::gen_project(spec); let root = AbsPath::assert(Utf8Path::from_path(dir.path()).unwrap()); + let _guard = set_test_cell_info(vec![( + "cell".to_string(), + dir.path().to_string_lossy().into_owned(), + )]); let target_name = "cell//app_a:app_a".to_string(); let target = BuckTarget { name: "app_a".to_string(), @@ -1464,6 +1517,10 @@ mod tests { "#; let dir = FixtureWithProjectMeta::gen_project(spec); let root = AbsPath::assert(Utf8Path::from_path(dir.path()).unwrap()); + let _guard = set_test_cell_info(vec![( + "cell".to_string(), + dir.path().to_string_lossy().into_owned(), + )]); let target_name = "cell//app_a:app_a".to_string(); let target = BuckTarget { name: "app_a".to_string(), @@ -1562,6 +1619,10 @@ mod tests { "#; let dir = FixtureWithProjectMeta::gen_project(spec); let root = AbsPath::assert(Utf8Path::from_path(dir.path()).unwrap()); + let _guard = set_test_cell_info(vec![( + "cell".to_string(), + dir.path().to_string_lossy().into_owned(), + )]); let target_name = "cell//app_a:app_a".to_string(); let target = BuckTarget { name: "app_a".to_string(), @@ -1582,71 +1643,66 @@ mod tests { assert_eq!(expected, actual) } - // TODO: enable when buck is properly set up on github project - // @fb-only - const BUCK_TESTS_ENABLED: bool = false; // @oss-only - #[track_caller] fn check_buck_bxl_query(build_generated: bool, expect: Expect) { - if BUCK_TESTS_ENABLED { - let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); - // We only need buck_config to get the buck command, everything but the buck root is ignored. - let buck_config = BuckConfig { - config_path: None, - buck_root: Some(buck_root), - enabled: true, - deps_target: None, - deps_targets: vec![], - build_deps: false, - included_targets: vec![], - excluded_targets: vec![], - source_root: None, - }; - let generated_args = if build_generated { - vec!["--build_generated_code", "true"] - } else { - vec![] - }; - let output = buck_config - .buck_command() - .arg("bxl") - .arg("prelude//erlang/elp.bxl:elp_config") - .arg("--") - .args(generated_args) - .arg("--included_targets") - .arg("fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/...") - .output() - .unwrap(); - if !output.status.success() { - panic!("{output:#?}"); - } - let string = String::from_utf8(output.stdout).unwrap(); - let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); - let string = string.replace(&prelude_cell, "/[prelude]/"); - - let to_replace = env!("CARGO_WORKSPACE_DIR"); - let string = string.replace(to_replace, "/[..]/"); - expect.assert_eq(&string); + let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); + // We only need buck_config to get the buck command, everything but the buck root is ignored. + let buck_config = BuckConfig { + config_path: None, + buck_root: Some(buck_root), + enabled: true, + deps_target: None, + deps_targets: vec![], + build_deps: false, + included_targets: vec![], + excluded_targets: vec![], + source_root: None, + test_application_labels: vec!["test_application".to_string()], + }; + let generated_args = if build_generated { + vec!["--build_generated_code", "true"] + } else { + vec![] + }; + let output = buck_config + .buck_command() + .arg("bxl") + .arg("prelude//erlang/elp.bxl:elp_config") + .arg("--") + .args(generated_args) + .arg("--included_targets") + .arg("root//buck_tests_2/auto_gen/...") + .output() + .unwrap(); + if !output.status.success() { + panic!("{output:#?}"); } + let string = String::from_utf8(output.stdout).unwrap(); + let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); + let string = string.replace(&prelude_cell, "/[prelude]/"); + + let to_replace = env!("CARGO_WORKSPACE_DIR"); + let string = string.replace(to_replace, "/[..]/"); + expect.assert_eq(&string); } #[test] #[ignore] fn build_info_buck_bxl_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { check_buck_bxl_query( false, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1656,7 +1712,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, @@ -1799,23 +1855,23 @@ mod tests { #[test] #[ignore] fn build_info_buck_bxl_generated_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { // Note that there is now a value for `srcs` in the - // "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" + // "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target check_buck_bxl_query( true, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1825,12 +1881,12 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" ], "includes": [], "labels": [ diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 53e550d05a..233c687afc 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -339,7 +339,13 @@ impl ProjectManifest { pub fn discover_in_place(path: &AbsPath, rebar_profile: Profile) -> Result { let _timer = timeit!("discover projects in place"); // We skip looking for the TOML file since we have already found it. - if let Some(r) = Self::discover_rebar(path, Some(rebar_profile), IncludeParentDirs::No)? { + if let Some(elp_config_basedir) = path.parent() + && let Some(r) = Self::discover_rebar( + &elp_config_basedir.to_path_buf(), + Some(rebar_profile), + IncludeParentDirs::No, + )? + { return Ok(r); } if let Some(s) = Self::discover_static(path, IncludeParentDirs::No)? { @@ -1571,6 +1577,60 @@ mod tests { .assert_eq(&debug_normalise_temp_dir(dir, &manifest)); } + #[test] + fn test_toml_empty_rebar_config() { + if cfg!(feature = "buck") { + let spec = r#" + //- /root/.elp.toml + //- /root/rebar.config + //- /root/app_a/src/app.erl + -module(app). + "#; + let dir = FixtureWithProjectMeta::gen_project(spec); + let discovered = ProjectManifest::discover( + &to_abs_path_buf(&dir.path().join("root/app_a/src/app.erl")).unwrap(), + ); + expect![[r#" + Ok( + ( + ElpConfig { + config_path: Some( + AbsPathBuf( + "TMPDIR/root/.elp.toml", + ), + ), + build_info: None, + buck: None, + eqwalizer: EqwalizerConfig { + enable_all: true, + max_tasks: 4, + ignore_modules: [], + ignore_modules_compiled_patterns: [], + }, + rebar: ElpRebarConfig { + profile: "test", + }, + otp: OtpConfig { + exclude_apps: [], + }, + }, + Rebar( + RebarConfig { + config_file: AbsPathBuf( + "TMPDIR/root/rebar.config", + ), + profile: Profile( + "test", + ), + }, + ), + ), + ) + "#]] + .assert_eq(&debug_normalise_temp_dir(dir, &discovered)); + } + } + #[test] fn test_toml_empty() { // This one is a real worst-case. We force discovery to happen in @@ -1839,6 +1899,7 @@ mod tests { "root//target/four".to_string(), ], source_root: Some(PathBuf::from("path/to/root")), + test_application_labels: vec!["test_application".to_string()], }), eqwalizer: EqwalizerConfig { enable_all: true, @@ -1864,6 +1925,7 @@ mod tests { included_targets = ["root//target/one", "root//target/two"] excluded_targets = ["root//target/three", "root//target/four"] source_root = "path/to/root" + test_application_labels = ["test_application"] [eqwalizer] enable_all = true @@ -1937,6 +1999,9 @@ mod tests { source_root: Some( "path/to/root", ), + test_application_labels: [ + "test_application", + ], }, ), eqwalizer: EqwalizerConfig { diff --git a/crates/project_model/src/test_fixture.rs b/crates/project_model/src/test_fixture.rs index 769891190a..140baec0ee 100644 --- a/crates/project_model/src/test_fixture.rs +++ b/crates/project_model/src/test_fixture.rs @@ -187,6 +187,7 @@ impl DiagnosticsEnabled { pub struct FixtureWithProjectMeta { pub fixture: Vec, pub diagnostics_enabled: DiagnosticsEnabled, + pub expect_parse_errors: bool, } impl FixtureWithProjectMeta { @@ -204,6 +205,7 @@ impl FixtureWithProjectMeta { let mut fixture = fixture.as_str(); let mut res: Vec = Vec::new(); let mut diagnostics_enabled = DiagnosticsEnabled::default(); + let mut expect_parse_errors = false; // --------------------------------------- // Each of the following is optional, but they must always @@ -232,6 +234,12 @@ impl FixtureWithProjectMeta { fixture = remain; } + if let Some(meta) = fixture.strip_prefix("//- expect_parse_errors") { + let (_meta, remain) = meta.split_once('\n').unwrap(); + expect_parse_errors = true; + fixture = remain; + } + if let Some(meta) = fixture.strip_prefix("//- native") { let (_meta, remain) = meta.split_once('\n').unwrap(); diagnostics_enabled.use_native = true; @@ -277,6 +285,12 @@ impl FixtureWithProjectMeta { if let Some(entry) = res.last_mut() { entry.text.push_str(line); + } else if line.chars().any(|c| !c.is_whitespace()) { + panic!( + "Fixture has content before the first file marker (`//- /path/to/file.erl`). \ + Did you forget to add a file marker at the beginning?\n\ + The offending line: {line:?}" + ); } } } @@ -298,6 +312,7 @@ impl FixtureWithProjectMeta { FixtureWithProjectMeta { fixture: res, diagnostics_enabled, + expect_parse_errors, } } @@ -472,6 +487,14 @@ pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option syntax error oops. +/// %% ^^^^^ error: P1711: syntax error before: error +/// %% | Related info: 0:25-30 function foo/0 undefined +/// ``` +/// /// Annotations point to the last line that actually was long enough for the /// range, not counting annotations themselves. So overlapping annotations are /// possible: @@ -551,10 +574,16 @@ pub fn extract_annotations(text: &str) -> (Vec<(TextRange, String)>, String) { if !res.is_empty() { offset += annotation_offset; this_line_annotations.push((offset, res.len() - 1)); - let &(_, idx) = prev_line_annotations + // Try to find a previous annotation at the same offset + let idx = if let Some(&(_, idx)) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - .unwrap(); + { + idx + } else { + // If no exact offset match, append to the most recent annotation + res.len() - 1 + }; res[idx].1.push('\n'); res[idx].1.push_str(&content); } @@ -824,6 +853,19 @@ mod tests { use crate::test_fixture::extract_tags; use crate::test_fixture::remove_annotations; + #[test] + #[should_panic(expected = "Fixture has content before the first file marker")] + fn parse_fixture_panics_on_content_before_first_file_marker() { + FixtureWithProjectMeta::parse( + r#" + -module(main). + foo() -> ok. + //- /src/erl_eval.erl + -module(erl_eval). + "#, + ); + } + #[test] #[should_panic] fn parse_fixture_checks_further_indented_metadata() { diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 081efa08d4..1f8d1aaa63 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -7,9 +7,6 @@ version.workspace = true workspace = true [dependencies] -elp_text_edit.workspace = true - -cov-mark.workspace = true eetf.workspace = true fxhash.workspace = true indexmap.workspace = true diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index af72d7de98..08b7ed9a1f 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs @@ -10,12 +10,8 @@ //! Collection of assorted algorithms for syntax trees. -use std::hash::BuildHasherDefault; use std::ops::RangeInclusive; -use elp_text_edit::TextEditBuilder; -use fxhash::FxHashMap; -use indexmap::IndexMap; use itertools::Itertools; use rowan::NodeOrToken; @@ -178,157 +174,6 @@ pub enum InsertPosition { After(T), } -type FxIndexMap = IndexMap>; - -#[derive(Debug, Hash, PartialEq, Eq)] -enum TreeDiffInsertPos { - After(SyntaxElement), - AsFirstChild(SyntaxElement), -} - -#[derive(Debug)] -pub struct TreeDiff { - replacements: FxHashMap, - deletions: Vec, - // the vec as well as the indexmap are both here to preserve order - insertions: FxIndexMap>, -} - -impl TreeDiff { - pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { - let _p = tracing::info_span!("into_text_edit").entered(); - - for (anchor, to) in self.insertions.iter() { - let offset = match anchor { - TreeDiffInsertPos::After(it) => it.text_range().end(), - TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(), - }; - to.iter() - .for_each(|to| builder.insert(offset, to.to_string())); - } - for (from, to) in self.replacements.iter() { - builder.replace(from.text_range(), to.to_string()) - } - for text_range in self.deletions.iter().map(SyntaxElement::text_range) { - builder.delete(text_range); - } - } - - pub fn is_empty(&self) -> bool { - self.replacements.is_empty() && self.deletions.is_empty() && self.insertions.is_empty() - } -} - -/// Finds a (potentially minimal) diff, which, applied to `from`, will result in `to`. -/// -/// Specifically, returns a structure that consists of a replacements, insertions and deletions -/// such that applying this map on `from` will result in `to`. -/// -/// This function tries to find a fine-grained diff. -pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { - let _p = tracing::info_span!("diff").entered(); - - let mut diff = TreeDiff { - replacements: FxHashMap::default(), - insertions: FxIndexMap::default(), - deletions: Vec::new(), - }; - let (from, to) = (from.clone().into(), to.clone().into()); - - if !syntax_element_eq(&from, &to) { - go(&mut diff, from, to); - } - return diff; - - fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool { - lhs.kind() == rhs.kind() - && lhs.text_range().len() == rhs.text_range().len() - && match (&lhs, &rhs) { - (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { - lhs == rhs || lhs.text() == rhs.text() - } - (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(), - _ => false, - } - } - - // FIXME: this is horribly inefficient. I bet there's a cool algorithm to diff trees properly. - fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) { - let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { - Some((lhs, rhs)) => (lhs, rhs), - _ => { - cov_mark::hit!(diff_node_token_replace); - diff.replacements.insert(lhs, rhs); - return; - } - }; - - let mut look_ahead_scratch = Vec::default(); - - let mut rhs_children = rhs.children_with_tokens(); - let mut lhs_children = lhs.children_with_tokens(); - let mut last_lhs = None; - loop { - let lhs_child = lhs_children.next(); - match (lhs_child.clone(), rhs_children.next()) { - (None, None) => break, - (None, Some(element)) => { - let insert_pos = match last_lhs.clone() { - Some(prev) => { - cov_mark::hit!(diff_insert); - TreeDiffInsertPos::After(prev) - } - // first iteration, insert into out parent as the first child - None => { - cov_mark::hit!(diff_insert_as_first_child); - TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) - } - }; - diff.insertions.entry(insert_pos).or_default().push(element); - } - (Some(element), None) => { - cov_mark::hit!(diff_delete); - diff.deletions.push(element); - } - (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} - (Some(lhs_ele), Some(rhs_ele)) => { - // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up - // until that element as insertions. This is important to keep the diff minimal - // in regards to insertions that have been actually done, this is important for - // use insertions as we do not want to replace the entire module node. - look_ahead_scratch.push(rhs_ele.clone()); - let mut rhs_children_clone = rhs_children.clone(); - let mut insert = false; - for rhs_child in rhs_children_clone.by_ref() { - if syntax_element_eq(&lhs_ele, &rhs_child) { - cov_mark::hit!(diff_insertions); - insert = true; - break; - } else { - look_ahead_scratch.push(rhs_child); - } - } - let drain = look_ahead_scratch.drain(..); - if insert { - let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { - TreeDiffInsertPos::After(prev) - } else { - cov_mark::hit!(insert_first_child); - TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) - }; - - diff.insertions.entry(insert_pos).or_default().extend(drain); - rhs_children = rhs_children_clone; - } else { - go(diff, lhs_ele, rhs_ele) - } - } - } - last_lhs = lhs_child.or(last_lhs); - } - } -} - /// Adds specified children (tokens or nodes) to the current node at the /// specific position. /// @@ -452,337 +297,3 @@ fn to_green_element(element: SyntaxElement) -> NodeOrToken it.green().to_owned().into(), } } - -#[cfg(test)] -mod tests { - use elp_text_edit::TextEdit; - use expect_test::Expect; - use expect_test::expect; - use itertools::Itertools; - - use crate::AstNode; - use crate::SyntaxElement; - use crate::SyntaxKind; - - #[test] - fn replace_node_token() { - cov_mark::check!(diff_node_token_replace); - check_diff( - r#"-module(foo)."#, - r#"ident"#, - expect![[r#" - insertions: - - - - replacements: - - Line 0: Token(ANON_DASH@0..1 "-") -> ident - - deletions: - - Line 1: module - Line 1: ( - Line 1: foo - Line 1: ) - Line 1: . - "#]], - ); - } - - #[test] - fn replace_parent() { - cov_mark::check!(diff_node_token_replace); - check_diff( - r#""#, - r#"-module(foo)."#, - expect![[r#" - insertions: - - - - replacements: - - Line 0: Token(SOURCE_FILE@0..0 "") -> -module(foo). - - deletions: - - - "#]], - ); - } - - #[test] - fn insert_last() { - cov_mark::check!(diff_insert); - check_diff( - r#" --module(foo). --use(foo). --use(bar)."#, - r#" --module(foo). --use(foo). --use(bar). --use(baz)."#, - expect![[r#" - insertions: - - Line 3: After(Node(WILD_ATTRIBUTE@26..36)) - -> "\n" - -> -use(baz). - - replacements: - - - - deletions: - - - "#]], - ); - } - - #[test] - fn insert_middle() { - check_diff( - r#" --module(foo). --use(foo). --use(bar)."#, - r#" --module(foo). --use(foo). --use(baz). --use(bar)."#, - expect![[r#" - insertions: - - Line 3: After(Token(WHITESPACE@25..26 "\n")) - -> -use(baz). - -> "\n" - - replacements: - - - - deletions: - - - "#]], - ) - } - - #[test] - fn insert_first() { - check_diff( - r#" --use(bar). --use(baz)."#, - r#" --export([foo/0]). --use(bar). --use(baz)."#, - expect![[r#" - insertions: - - Line 0: After(Token(WHITESPACE@0..1 "\n")) - -> -export([foo/0]). - -> "\n" - - replacements: - - - - deletions: - - - "#]], - ) - } - - #[test] - fn first_child_insertion() { - // cov_mark::check!(insert_first_child); - check_diff( - r#" -main() -> - ok."#, - r#" --module(foo). - -main() -> - ok."#, - expect![[r#" - insertions: - - Line 0: After(Token(WHITESPACE@0..1 "\n")) - -> -module(foo). - -> "\n\n" - - replacements: - - - - deletions: - - - "#]], - ); - } - - #[test] - fn delete_last() { - cov_mark::check!(diff_delete); - check_diff( - r#"-module(foo). - -bar([baz])."#, - r#"-module(foo)."#, - expect![[r#" - insertions: - - - - replacements: - - - - deletions: - - Line 1: "\n " - Line 2: -bar([baz]). - "#]], - ); - } - - #[test] - fn delete_middle() { - // cov_mark::check!(diff_insertions); - check_diff( - r#" --export([foo/0,bar/1]). --bar(aaa). - --foo(bbb). -"#, - r#" --export([foo/0,bar/1]). - --foo(bbb). -"#, - expect![[r#" - insertions: - - Line 1: After(Node(EXPORT_ATTRIBUTE@1..24)) - -> "\n\n" - -> -foo(bbb). - - replacements: - - - - deletions: - - Line 2: -bar(aaa). - Line 3: "\n\n" - Line 4: -foo(bbb). - Line 5: "\n" - "#]], - ) - } - - #[test] - fn delete_first() { - check_diff( - r#" --export([foo/0,bar/1]). - --foo(bbb). -"#, - r#" --foo(bbb). -"#, - expect![[r#" - insertions: - - - - replacements: - - Line 1: Token(ANON_DASH@1..2 "-") -> -foo - Line 2: Token(ANON_EXPORT@2..8 "export") -> (bbb) - Line 2: Token(ANON_LPAREN@8..9 "(") -> . - Line 2: Token(WHITESPACE@24..26 "\n\n") -> "\n" - - deletions: - - Line 2: [ - Line 2: foo/0 - Line 2: , - Line 2: bar/1 - Line 2: ] - Line 2: ) - Line 2: . - Line 3: -foo(bbb). - Line 4: "\n" - "#]], - ) - } - - fn check_diff(from: &str, to: &str, expected_diff: Expect) { - let from_node = crate::SourceFile::parse_text(from).tree().syntax().clone(); - let to_node = crate::SourceFile::parse_text(to).tree().syntax().clone(); - let diff = super::diff(&from_node, &to_node); - - let line_number = - |syn: &SyntaxElement| from[..syn.text_range().start().into()].lines().count(); - - let fmt_syntax = |syn: &SyntaxElement| match syn.kind() { - SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()), - _ => format!("{syn}"), - }; - - let insertions = - diff.insertions - .iter() - .format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> { - f(&format!( - "Line {}: {:?}\n-> {}", - line_number(match k { - super::TreeDiffInsertPos::After(syn) => syn, - super::TreeDiffInsertPos::AsFirstChild(syn) => syn, - }), - k, - v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) - )) - }); - - let replacements = diff - .replacements - .iter() - .sorted_by_key(|(syntax, _)| syntax.text_range().start()) - .format_with("\n", |(k, v), f| { - f(&format!( - "Line {}: {:?} -> {}", - line_number(k), - k, - fmt_syntax(v) - )) - }); - - let deletions = diff.deletions.iter().format_with("\n", |v, f| { - f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))) - }); - - let actual = format!( - "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n" - ); - expected_diff.assert_eq(&actual); - - let mut from = from.to_owned(); - let mut text_edit = TextEdit::builder(); - diff.into_text_edit(&mut text_edit); - text_edit.finish().apply(&mut from); - assert_eq!(&*from, to, "diff did not turn `from` to `to`"); - } -} diff --git a/crates/syntax/src/syntax_kind/generated.rs b/crates/syntax/src/syntax_kind/generated.rs index 716d46d25a..781d35aaaf 100644 --- a/crates/syntax/src/syntax_kind/generated.rs +++ b/crates/syntax/src/syntax_kind/generated.rs @@ -1,7 +1,8 @@ //! @generated file, do not edit by hand, see `xtask/src/codegen.rs` #![allow(bad_style, missing_docs, unreachable_pub)] -use num_derive::{FromPrimitive, ToPrimitive}; +use num_derive::FromPrimitive; +use num_derive::ToPrimitive; #[doc = r" The kind of syntax node, e.g. `ATOM`, `IF_KW`, or `DOT`."] #[derive( Clone, diff --git a/crates/text_edit/Cargo.toml b/crates/text_edit/Cargo.toml deleted file mode 100644 index 59d333adc7..0000000000 --- a/crates/text_edit/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "elp_text_edit" -edition.workspace = true -version.workspace = true - -[lints] -workspace = true - -[dependencies] -itertools.workspace = true -text-size.workspace = true diff --git a/editors/code/README.md b/editors/code/README.md index b03ce757f2..ff5663d5ca 100644 --- a/editors/code/README.md +++ b/editors/code/README.md @@ -1,19 +1,82 @@ # Erlang Language Platform -Provide support for the [Erlang](https://www.erlang.org/) Programming Language. +The **Erlang Language Platform (ELP)** is a modern Language Server Protocol +(LSP) implementation for Erlang, developed by WhatsApp. ELP provides +comprehensive IDE support for Erlang development, bringing features you'd expect +from modern development tools to the Erlang ecosystem. + +Built with scalability and performance in mind, ELP uses incremental analysis to +handle large codebases efficiently, making it suitable for projects of any size. ## Features -* Syntax Highlighting -* Go To Definition -* Find References -* Auto-completion -* Call Hierarchy -* Signature Help -* Diagnostics -* Inlay Hints -* ... +### Code Intelligence + +- **📍 Go To Definition** - Jump to function, type, record definitions, etc +- **🔍 Find References** - Find all usages of functions, types, and variables +- **✨ Auto-completion** - Intelligent code completion for functions, variables, + modules and more +- **🏷️ Hover Information** - View documentation and type information on hover +- **📞 Call Hierarchy** - Explore caller/callee relationships +- **✍️ Signature Help** - Parameter hints while typing function calls +- **📋 Inlay Hints** - Display parameter names inline +- **🎨 Syntax Highlighting** - Full Erlang syntax support +- **📝 Code Lenses** - Inline actions for running tests and debugging +- **🔄 Workspace Symbol Search** - Quickly find symbols across your project + +### Code Quality + +- **🔧 Diagnostics** - Real-time error and warning detection +- **💡 Code Actions** - Quick fixes and refactoring suggestions +- **🧪 Eqwalizer Integration** - Type checking with + [Eqwalizer](https://github.com/WhatsApp/eqwalizer) + +### Debugging & Testing + +- **🐞 Integrated Debugger** - Debug Erlang applications with breakpoints and + variable inspection ## Documentation -See https://whatsapp.github.io/erlang-language-platform/ for more information. +For comprehensive documentation, visit: + +- **📚 + [Official Documentation](https://whatsapp.github.io/erlang-language-platform/)** +- **🚀 + [Getting Started Guide](https://whatsapp.github.io/erlang-language-platform/docs/get-started/)** +- **⚙️ + [Configuration Reference](https://whatsapp.github.io/erlang-language-platform/docs/configuration/)** +- **❓ [FAQ](https://whatsapp.github.io/erlang-language-platform/docs/faq/)** + +## Contributing + +We welcome contributions! Please see our +[Contributing Guide](https://github.com/WhatsApp/erlang-language-platform/blob/main/CONTRIBUTING.md) +for details. + +## Community & Support + +- **🐛 Issues**: + [GitHub Issues](https://github.com/WhatsApp/erlang-language-platform/issues) +- **💬 Discussions**: + [GitHub Discussions](https://github.com/WhatsApp/erlang-language-platform/discussions) + +## License + +ELP is dual-licensed under: + +- [Apache License 2.0](https://github.com/WhatsApp/erlang-language-platform/blob/main/LICENSE-APACHE) +- [MIT License](https://github.com/WhatsApp/erlang-language-platform/blob/main/LICENSE-MIT) + +## Acknowledgments + +ELP was designed at **WhatsApp** and inspired by the +[Rust Analyzer](https://rust-analyzer.github.io/) project. Special thanks to all +[contributors](https://github.com/WhatsApp/erlang-language-platform/graphs/contributors) +who have helped make ELP better. + +--- + +

+ Made with ❤️ by the WhatsApp team +

diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 1e62d62162..6309d4c81e 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1,12 +1,12 @@ { "name": "erlang-language-platform", - "version": "0.44.0", + "version": "0.47.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "erlang-language-platform", - "version": "0.44.0", + "version": "0.47.0", "hasInstallScript": true, "license": "Apache2", "devDependencies": { @@ -1448,10 +1448,11 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1624,9 +1625,9 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -3399,9 +3400,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -3532,9 +3533,9 @@ } }, "glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "requires": { "foreground-child": "^3.1.0", diff --git a/editors/code/package.json b/editors/code/package.json index d54a4de805..eb7ced3148 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1,9 +1,10 @@ { "name": "erlang-language-platform", - "description": "Erlang language server", + "displayName": "Erlang Language Platform", + "description": "Erlang Language Support for VS Code, by WhatsApp.", "author": "Meta Platforms, Inc", "license": "Apache2", - "version": "0.44.0", + "version": "0.47.0", "icon": "images/elp-logo-color.png", "homepage": "https://whatsapp.github.io/erlang-language-platform/", "repository": { @@ -20,7 +21,8 @@ "Testing" ], "keywords": [ - "elp" + "elp", + "erlang" ], "engines": { "vscode": "^1.75.0" diff --git a/elisp/dotemacs.el b/editors/emacs/dotemacs.el similarity index 100% rename from elisp/dotemacs.el rename to editors/emacs/dotemacs.el diff --git a/eqwalizer b/eqwalizer index bed8636fa6..0f514eb389 160000 --- a/eqwalizer +++ b/eqwalizer @@ -1 +1 @@ -Subproject commit bed8636fa6b926e2292697be35e096db2386a78f +Subproject commit 0f514eb3893fa7070835c83ecb49fbea31b0426d diff --git a/erlang_service/src/elp_lint.erl b/erlang_service/src/elp_lint.erl index 609e8c0712..31d2c70248 100644 --- a/erlang_service/src/elp_lint.erl +++ b/erlang_service/src/elp_lint.erl @@ -4372,14 +4372,14 @@ is_format_function(io, fwrite) -> true; is_format_function(io, format) -> true; is_format_function(io_lib, fwrite) -> true; is_format_function(io_lib, format) -> true; -% @fb-only -% @fb-only +% @fb-only: is_format_function(wa_log, send_if) -> true; +% @fb-only: is_format_function(wa_string, format) -> true; is_format_function(M, F) when is_atom(M), is_atom(F) -> false. %% check_format_1([Arg]) -> ok | {warn,Level,Format,[Arg]}. -% @fb-only -% @fb-only +% @fb-only[end= ]: format_args(wa_log, send_if, [_Level, _Meta, _Opts, Format, Args]) -> [Format, Args]; +% @fb-only[end= ]: format_args(wa_string, format, [Format, Args, _Options]) -> [Format, Args]; format_args(_M, _F, As) -> As. diff --git a/erlang_service/src/erlang_service_lint.erl b/erlang_service/src/erlang_service_lint.erl index 77223da49a..bc5a2faf3b 100644 --- a/erlang_service/src/erlang_service_lint.erl +++ b/erlang_service/src/erlang_service_lint.erl @@ -178,6 +178,7 @@ format_error(_Forms, _OriginalPath, Path, {Line, Mod, Reason}) when is_integer(L [ { unicode:characters_to_list(Path), + unicode:characters_to_list("ignored"), none, unicode:characters_to_list( io_lib:format("~p: ~ts", [Line, Mod:format_error(Reason)]) @@ -189,6 +190,7 @@ format_error(_Forms, SamePath, SamePath, {Location, Mod, Reason}) -> [ { unicode:characters_to_list(SamePath), + unicode:characters_to_list("ignored"), % Location would be {Line, Col} for a erlc compiler error/warning, % but {ByteStart, ByteEnd} for an eqwalizer diagnostic. % This is deciphered on elp side. @@ -199,18 +201,19 @@ format_error(_Forms, SamePath, SamePath, {Location, Mod, Reason}) -> erlang_service_error_codes:make_code(Mod, Reason) } ]; -format_error(Forms, OriginalPath, Path, {Location, Mod, Reason}) -> +format_error(Forms, IncluderPath, ErrorPath, {Location, Mod, Reason}) -> %% The original path and reported error path are different, the %% error is in an included file. %% This can be from an include, include_lib, behaviour or parse_transform - IncludeLocation = inclusion_range(Forms, Path), + IncludeLocation = inclusion_range(Forms, ErrorPath), %% We return an error at the location the error occurs, as well as %% a list of the errors in the included file. ELP will determine %% the appropriate FileId and emit diagnostics for the include file. [ { - unicode:characters_to_list(OriginalPath), + unicode:characters_to_list(IncluderPath), + unicode:characters_to_list(ErrorPath), % Location would be {Line, Col} for a erlc compiler error/warning, % but {ByteStart, ByteEnd} for an eqwalizer diagnostic. % This is deciphered on elp side. @@ -219,7 +222,8 @@ format_error(Forms, OriginalPath, Path, {Location, Mod, Reason}) -> erlang_service_error_codes:make_code(erlang_service_error_codes, "Issue in included file") }, { - unicode:characters_to_list(Path), + unicode:characters_to_list(IncluderPath), + unicode:characters_to_list(ErrorPath), % Location would be {Line, Col} for a erlc compiler error/warning, % but {ByteStart, ByteEnd} for an eqwalizer diagnostic. % This is deciphered on elp side. @@ -232,13 +236,96 @@ format_error(Forms, OriginalPath, Path, {Location, Mod, Reason}) -> ]. inclusion_range(Forms, Path) -> - case [Location || {attribute, Location, file, {FormPath, _}} <- Forms, FormPath == Path] of - [{Loc, _}] -> - {Loc, Loc}; + %% The `include` or `include_lib` directive is processed by epp, so + %% it does not show up in the abstract forms directly. We infer its + %% location by looking for the `file` attribute with the same path, + %% which is inserted when epp starts processing a new file. + %% The wrinkle is that the range of the `file` attribute does not + %% correspond to the location of the `include` directive, but the last + %% thing processed in the current file. + + %% Track the full include context for a given path. + %% In the forms, process the file attributes. + %% Every time it has a Location of {0,0}, the file has been entered or re-entered + %% Keep a stack for the current location, pushing to it when a {0,0} is found for it, + %% popping when a {0,0} with the same path is found. + FileAttrs = [{Loc, FormPath} || {attribute, Loc, file, {FormPath, _}} <- Forms], + + Context = build_include_context(FileAttrs, Path), + + case Context of + [ _, {IncludeLoc, _IncludePath} | _Rest] -> + %% Return the location from the second entry (the include directive location in the parent file) + %% Extract the position from the location tuple and return as a range + case IncludeLoc of + {Pos, _} -> {Pos, Pos}; + Pos when is_integer(Pos) -> {Pos, Pos} + end; _ -> {1, 1} end. +%% Build the include context stack for a given path +build_include_context(FileAttrs, TargetPath) -> + {_FinalStack, Context} = lists:foldl( + fun({Location, FilePath}, {Stack, ContextAcc}) -> + case Location of + {0, 0} -> + %% Entering or re-entering a file + case find_in_stack(Stack, FilePath) of + not_found -> + %% First time seeing this path - push to stack + NewStack = [{Location, FilePath} | Stack], + NewContextAcc = + case FilePath of + TargetPath -> + %% Found our target path, capture current stack (reversed to get bottom-up order) + lists:reverse(NewStack); + _ -> + ContextAcc + end, + {NewStack, NewContextAcc}; + _Found -> + %% Re-entering a file that was on the stack - pop back to it + NewStack = pop_to_path(Stack, FilePath), + {NewStack, ContextAcc} + end; + _ -> + %% Non-zero location - this is an include directive + %% Push this file onto the stack + NewStack = [{Location, FilePath} | Stack], + NewContextAcc = + case FilePath of + TargetPath -> + %% Found our target path, capture current stack (reversed to get bottom-up order) + lists:reverse(NewStack); + _ -> + ContextAcc + end, + {NewStack, NewContextAcc} + end + end, + {[], []}, + FileAttrs + ), + Context. + +%% Find a path in the stack +find_in_stack([], _Path) -> + not_found; +find_in_stack([{Loc, Path} | _Rest], Path) -> + {found, Loc}; +find_in_stack([_ | Rest], Path) -> + find_in_stack(Rest, Path). + +%% Pop the stack until we find the given path (inclusive) +pop_to_path([], _Path) -> + []; +pop_to_path([{_Loc, Path} | _Rest] = Stack, Path) -> + Stack; +pop_to_path([_ | Rest], Path) -> + pop_to_path(Rest, Path). + extract_forms(Id, FileName, FileId, FileText, Options) -> case filename:extension(FileName) of ".erl" -> diff --git a/test/test_projects/.buckconfig b/test/test_projects/.buckconfig new file mode 100644 index 0000000000..f14e564a7d --- /dev/null +++ b/test/test_projects/.buckconfig @@ -0,0 +1,24 @@ +[cells] + root = . + prelude = prelude + toolchains = toolchains + none = none + +[cell_aliases] + config = prelude + ovr_config = prelude + fbcode = none + fbsource = none + fbcode_macros = none + buck = none + +[external_cells] + prelude = bundled + +[parser] + target_platform_detector_spec = target:root//...->prelude//platforms:default \ + target:prelude//...->prelude//platforms:default \ + target:toolchains//...->prelude//platforms:default + +[build] + execution_platforms = prelude//platforms:default diff --git a/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer b/test/test_projects/.buckroot similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer rename to test/test_projects/.buckroot diff --git a/test_projects/.gitignore b/test/test_projects/.gitignore similarity index 85% rename from test_projects/.gitignore rename to test/test_projects/.gitignore index b34578734c..2672c17cc4 100644 --- a/test_projects/.gitignore +++ b/test/test_projects/.gitignore @@ -3,3 +3,4 @@ *wa.build_info *_build/ *rebar.lock +buck-out/ diff --git a/test_projects/README.md b/test/test_projects/README.md similarity index 100% rename from test_projects/README.md rename to test/test_projects/README.md diff --git a/test/test_projects/buck_bad_config/.elp.toml b/test/test_projects/buck_bad_config/.elp.toml new file mode 100644 index 0000000000..963072284c --- /dev/null +++ b/test/test_projects/buck_bad_config/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//buck_bad_config/..." ] +source_root = "buck_bad_config" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/buck_bad_config/BUCK b/test/test_projects/buck_bad_config/BUCK new file mode 100644 index 0000000000..498c769dac --- /dev/null +++ b/test/test_projects/buck_bad_config/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "bad_app", + srcs = glob(["src/*.erl"]), + applications = [ + "root//buck_bad_config/non_existent:missing", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test_projects/buck_bad_config/src/bad_app.erl b/test/test_projects/buck_bad_config/src/bad_app.erl similarity index 100% rename from test_projects/buck_bad_config/src/bad_app.erl rename to test/test_projects/buck_bad_config/src/bad_app.erl diff --git a/test/test_projects/buck_tests/.elp.toml b/test/test_projects/buck_tests/.elp.toml new file mode 100644 index 0000000000..b38d6f043f --- /dev/null +++ b/test/test_projects/buck_tests/.elp.toml @@ -0,0 +1,9 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//buck_tests/..." ] +excluded_targets = [ "buck_tests:test_elp_ignored" ] +source_root = "buck_tests" + +[eqwalizer] +enable_all = false diff --git a/test_projects/buck_tests/TARGETS.v2_ b/test/test_projects/buck_tests/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/TARGETS.v2_ rename to test/test_projects/buck_tests/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ similarity index 77% rename from test_projects/buck_tests/test_elp/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp/TARGETS.v2_ index 83089813da..7e5bd95db5 100644 --- a/test_projects/buck_tests/test_elp/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ @@ -7,11 +7,11 @@ erlang_application( ]), app_src = "src/test_elp.app.src", applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_direct_dep:test_elp_direct_dep", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_private_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_public_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_flat_outside_target", - "//whatsapp/elp/test_projects/buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", + "//buck_tests/test_elp_direct_dep:test_elp_direct_dep", + "//buck_tests:test_elp_no_private_headers", + "//buck_tests:test_elp_no_public_headers", + "//buck_tests:test_elp_flat_outside_target", + "//buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp/include/test_elp.hrl b/test/test_projects/buck_tests/test_elp/include/test_elp.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/include/test_elp.hrl rename to test/test_projects/buck_tests/test_elp/include/test_elp.hrl diff --git a/test_projects/buck_tests/test_elp/src/test_elp.app.src b/test/test_projects/buck_tests/test_elp/src/test_elp.app.src similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.app.src rename to test/test_projects/buck_tests/test_elp/src/test_elp.app.src diff --git a/test_projects/buck_tests/test_elp/src/test_elp.erl b/test/test_projects/buck_tests/test_elp/src/test_elp.erl similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.erl rename to test/test_projects/buck_tests/test_elp/src/test_elp.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ similarity index 62% rename from test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ index d0fea8149b..a752b99087 100644 --- a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ @@ -5,10 +5,10 @@ erlang_application( "src/*.hrl", ]), applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", + "//buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", ], extra_includes = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp:test_elp", + "//buck_tests/test_elp:test_elp", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl diff --git a/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl b/test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl similarity index 100% rename from test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl rename to test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl b/test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl rename to test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl b/test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl rename to test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl diff --git a/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl b/test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl rename to test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl diff --git a/test/test_projects/buck_tests_2/.elp.toml b/test/test_projects/buck_tests_2/.elp.toml new file mode 100644 index 0000000000..e71eb6a819 --- /dev/null +++ b/test/test_projects/buck_tests_2/.elp.toml @@ -0,0 +1,12 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ + "root//buck_tests_2/util/app_a/...", + "root//buck_tests_2:check_include" + ] +excluded_targets = [ "root//buck_tests_2:test_elp_ignored" ] +source_root = "buck_tests_2" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/buck_tests_2/BUCK b/test/test_projects/buck_tests_2/BUCK new file mode 100644 index 0000000000..b01fdb868c --- /dev/null +++ b/test/test_projects/buck_tests_2/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check_include", + srcs = [ + "check_include/src/top_includer.erl", + ], + applications = [ + "common_test", + "stdlib", + "root//buck_tests_2:check_include_separate_1", + "root//buck_tests_2:check_include_separate_2", + ], + includes = [], + labels = [], + resources = [], +) + +erlang_application( + name = "check_include_separate_1", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = [ + "check_include_separate_1/include/top_includer.hrl", + ], + resources = [], +) + +erlang_application( + name = "check_include_separate_2", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = glob(["check_include_separate_2/include/*.hrl"]), + resources = [], +) diff --git a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK new file mode 100644 index 0000000000..f021317020 --- /dev/null +++ b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "auto_gen_a", + srcs = glob([ + "src/*.erl", + "src/*.hrl", + ]), + includes = glob(["include/*.hrl"]), + visibility = ["//buck_tests_2/..."], +) + +erlang_application( + name = "generated_srcs", + srcs = [ + ":generated.erl", + ], + labels = ["generated"], + visibility = ["//buck_tests_2/..."], +) + +export_file( + name = "generated.erl", + src = "out/pretend_generated.erl", +) diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl diff --git a/test/test_projects/buck_tests_2/check_include/src/top_includer.erl b/test/test_projects/buck_tests_2/check_include/src/top_includer.erl new file mode 100644 index 0000000000..6f12fdb31d --- /dev/null +++ b/test/test_projects/buck_tests_2/check_include/src/top_includer.erl @@ -0,0 +1,15 @@ +-module(top_includer). + +-compile(warn_missing_spec_all). + +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("check_include_separate_1/include/top_includer.hrl"). + +-define(A_MACRO, ?FUNCTION_NAME). + +foo() -> + ?FIRST, + ?A_MACRO + ?SECOND, + ?THIRD(41,34), + ?FUNCTION_NAME. diff --git a/test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl b/test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl new file mode 100644 index 0000000000..82ab343676 --- /dev/null +++ b/test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl @@ -0,0 +1,5 @@ +%% This file has a deliberate bug in it, to ensure we get a diagnostic +%% in the including file. +%% + +type foo() = ?FOO. diff --git a/test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl b/test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl new file mode 100644 index 0000000000..b67b5b8b2b --- /dev/null +++ b/test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl @@ -0,0 +1,6 @@ + +-include_lib("check_include_separate_2/include/separate_include.hrl"). +-include("does_not_exist.hrl"). +-include("include_with_bug.hrl"). + +-define(FIRST, 1). diff --git a/test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl b/test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl new file mode 100644 index 0000000000..1e0c48bf56 --- /dev/null +++ b/test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl @@ -0,0 +1,2 @@ + +-define(SECOND, ok). diff --git a/test/test_projects/buck_tests_2/generated/BUCK b/test/test_projects/buck_tests_2/generated/BUCK new file mode 100644 index 0000000000..8eec40830b --- /dev/null +++ b/test/test_projects/buck_tests_2/generated/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "generated_headers", + includes = [ + "out/generated_header.hrl", + ], + labels = ["generated"], + visibility = [ + "PUBLIC", + ], +) diff --git a/test_projects/buck_tests_2/generated/out/generated_header.hrl b/test/test_projects/buck_tests_2/generated/out/generated_header.hrl similarity index 100% rename from test_projects/buck_tests_2/generated/out/generated_header.hrl rename to test/test_projects/buck_tests_2/generated/out/generated_header.hrl diff --git a/test/test_projects/buck_tests_2/util/app_a/BUCK b/test/test_projects/buck_tests_2/util/app_a/BUCK new file mode 100644 index 0000000000..4231c1c6e8 --- /dev/null +++ b/test/test_projects/buck_tests_2/util/app_a/BUCK @@ -0,0 +1,16 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a_target", + srcs = glob(["src/*.erl"]), + app_name = "app_a", + applications = [ + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a", + "root//buck_tests_2/generated:generated_headers", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test_projects/buck_tests_2/util/app_a/include/junk.hrl b/test/test_projects/buck_tests_2/util/app_a/include/junk.hrl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/include/junk.hrl rename to test/test_projects/buck_tests_2/util/app_a/include/junk.hrl diff --git a/test_projects/buck_tests_2/util/app_a/src/app_a.erl b/test/test_projects/buck_tests_2/util/app_a/src/app_a.erl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/src/app_a.erl rename to test/test_projects/buck_tests_2/util/app_a/src/app_a.erl diff --git a/test_projects/end_to_end/.elp.toml b/test/test_projects/codegen_test/.elp.toml similarity index 50% rename from test_projects/end_to_end/.elp.toml rename to test/test_projects/codegen_test/.elp.toml index f015956e45..f21fe1bc73 100644 --- a/test_projects/end_to_end/.elp.toml +++ b/test/test_projects/codegen_test/.elp.toml @@ -1,7 +1,7 @@ [buck] enabled = true build_deps = false -included_targets = ["fbcode//whatsapp/elp/test_projects/end_to_end/..."] +included_targets = ["root//codegen_test/..."] [eqwalizer] enable_all = false diff --git a/test/test_projects/codegen_test/BUCK b/test/test_projects/codegen_test/BUCK new file mode 100644 index 0000000000..1df5f86eba --- /dev/null +++ b/test/test_projects/codegen_test/BUCK @@ -0,0 +1,73 @@ +oncall("vscode_erlang") + +# Code generation rule - generates Erlang modules from template files +genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", +) + +genrule( + name = "example_service_client_erl", + srcs = [ + "templates/example_service_client.erl", + ], + outs = { + "example_service_client.erl": ["example_service_client.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_client.erl $OUT/example_service_client.erl", +) + +genrule( + name = "example_service_types_hrl", + srcs = [ + "templates/example_service_types.hrl", + ], + outs = { + "example_service_types.hrl": ["example_service_types.hrl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.hrl $OUT/example_service_types.hrl", +) + +# Erlang library containing only the generated code +erlang_app( + name = "example_service_generated", + srcs = [ + # Include generated Erlang modules from genrule output + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + # Include generated header files from genrule output + ":example_service_types_hrl[example_service_types.hrl]", + ], +) + +# Erlang application that uses the generated code (non-generated files only) +erlang_application( + name = "codegen_test_app", + srcs = glob(["app_a/src/*.erl"]), + app_name = "codegen_test", + app_src = "app_a/src/codegen_test.app.src", + applications = [ + "kernel", + "stdlib", + ":example_service_generated", + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +# Test to verify the generated code works +erlang_tests( + suites = [ + "app_a/test/codegen_test_SUITE.erl", + ], + deps = [":codegen_test_app"], +) diff --git a/test/test_projects/codegen_test/README.md b/test/test_projects/codegen_test/README.md new file mode 100644 index 0000000000..1f73475d9d --- /dev/null +++ b/test/test_projects/codegen_test/README.md @@ -0,0 +1,138 @@ +# Code Generation Test Project + +This test project demonstrates Buck2-based code generation for Erlang +applications using `genrule`. + +## Project Structure + +``` +codegen_test/ +├── .elp.toml # ELP configuration +├── BUCK # Buck build configuration with genrule +├── README.md # This file +├── templates/ # Erlang template files (input) +│ ├── example_service_types.erl # Type definitions template +│ ├── example_service_client.erl # Client stubs template +│ └── example_service_types.hrl # Header file template +├── generated/ # Pre-generated code (for reference) +│ ├── example_service_types.erl # Generated type module +│ ├── example_service_client.erl # Generated client stubs +│ └── example_service_types.hrl # Generated header file +└── app_a/ + ├── src/ + │ ├── codegen_test.app.src # Application metadata + │ └── example_usage.erl # Example using generated code + └── test/ + └── codegen_test_SUITE.erl # Test suite +``` + +## How It Works + +### 1. Template Files + +The templates directory contains well-formed Erlang files that serve as input to +the code generation process. These are complete, valid Erlang files that are +copied to the output directory during the build. + +### 2. Code Generation + +The code is generated automatically during the build process using a +`genrule`: + +```python +genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", +) +``` + +The genrule: + +- Takes the template file as input (`srcs`) +- Defines named outputs using `outs` parameter (creates subtargets for each + file) +- Uses `cp` command to copy template files to `$OUT` +- Outputs files to `$OUT` directory in `buck-out/` + +### 3. Build Integration + +The `BUCK` file references the generated files using subtarget syntax: + +```python +erlang_app( + name = "example_service_generated", + srcs = [ + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + ":example_service_types_hrl[example_service_types.hrl]", + ], +) +``` + +When Buck builds the application: + +1. The genrule runs first, copying the template Erlang files to the output +2. The generated files are placed in + `buck-out/v2/gen/.../example_service_types_erl/` +3. The `erlang_app` consumes these files via subtarget references (`[filename]` + syntax) +4. The files are copied to the application's build directory in `buck-out/` + +### 4. Using Generated Code + +Application code includes the generated header and uses the types: + +```erlang +-module(example_usage). +-include("example_service_types.hrl"). + +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +get_user_by_id(UserId) -> + example_service_client:get_user(UserId). +``` + +## Building and Testing + +```bash +# Build just the code generation step +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:example_service_types_erl + +# Build the application (automatically runs code generation) +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_app + +# Run tests +buck2 test fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_SUITE + +# View generated files in buck-out +find buck-out -path "*codegen_test*" -name "example_service_*.erl" +``` + +## Test Coverage + +The test suite (`app_a/test/codegen_test_SUITE.erl`) verifies: + +- Generated record types work correctly +- Generated client functions are callable +- Record fields have correct types and defaults +- Integration with application code + +All tests pass: + +``` +Tests finished: Pass 3. Fail 0. Fatal 0. Skip 0. Build failure 0 +``` diff --git a/test/test_projects/codegen_test/app_a/src/codegen_test.app.src b/test/test_projects/codegen_test/app_a/src/codegen_test.app.src new file mode 100644 index 0000000000..7a38d49a24 --- /dev/null +++ b/test/test_projects/codegen_test/app_a/src/codegen_test.app.src @@ -0,0 +1,14 @@ +{application, codegen_test, + [{description, "Test application demonstrating code generation"}, + {vsn, "1.0.0"}, + {registered, []}, + {applications, + [kernel, + stdlib, + example_service_generated + ]}, + {env,[]}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/test/test_projects/codegen_test/app_a/src/example_usage.erl b/test/test_projects/codegen_test/app_a/src/example_usage.erl new file mode 100644 index 0000000000..a624c2089b --- /dev/null +++ b/test/test_projects/codegen_test/app_a/src/example_usage.erl @@ -0,0 +1,51 @@ +-module(example_usage). +-compile([warn_missing_spec_all]). +-moduledoc """ +Example module that demonstrates using generated code. +This module uses the types and client functions generated from +the example_service.schema file. +""". + +%% Include the generated header file +-include_lib("example_service_generated/include/example_service_types.hrl"). + +%% API exports +-export([ + create_sample_user/0, + create_query_request/1, + get_user_by_id/1 +]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-doc """ +Creates a sample user_info record using generated types +""". +-spec create_sample_user() -> #user_info{}. +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +-doc """ +Creates a query_request record +""". +-spec create_query_request(binary()) -> #query_request{}. +create_query_request(QueryId) -> + #query_request{ + query_id = QueryId, + filters = [{age, greater_than, 18}] + }. + +-doc """ +Uses the generated client to get user information +""". +-spec get_user_by_id(binary()) -> {ok, term()} | {error, term()}. +get_user_by_id(UserId) -> + %% This calls the generated client function + example_service_client:get_user(UserId). diff --git a/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl new file mode 100644 index 0000000000..3c0135dcee --- /dev/null +++ b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl @@ -0,0 +1,73 @@ +%%% Test suite for code generation functionality +-module(codegen_test_SUITE). + +-include_lib("stdlib/include/assert.hrl"). +-include("example_service_types.hrl"). + +%% CT callbacks +-export([all/0, init_per_suite/1, end_per_suite/1]). + +%% Test cases +-export([ + test_generated_types/1, + test_generated_client/1, + test_user_record_creation/1 +]). + +%%%=================================================================== +%%% CT Callbacks +%%%=================================================================== + +all() -> + [ + test_generated_types, + test_generated_client, + test_user_record_creation + ]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +%%%=================================================================== +%%% Test Cases +%%%=================================================================== + +test_generated_types(_Config) -> + %% Test that we can create a user_info record + User = #user_info{ + user_id = <<"test_123">>, + username = <<"testuser">>, + age = 30, + is_active = true + }, + + %% Verify the record fields + ?assertEqual(<<"test_123">>, User#user_info.user_id), + ?assertEqual(<<"testuser">>, User#user_info.username), + ?assertEqual(30, User#user_info.age), + ?assertEqual(true, User#user_info.is_active), + + ok. + +test_generated_client(_Config) -> + %% Test that the generated client module exists and can be called + Result = example_service_client:get_user(<<"user_123">>), + + %% The generated stub returns {ok, generated_response} + ?assertMatch({ok, _}, Result), + + ok. + +test_user_record_creation(_Config) -> + %% Test the example_usage module + User = example_usage:create_sample_user(), + + %% Verify it's a valid user_info record + ?assertMatch(#user_info{}, User), + ?assertEqual(<<"user_123">>, User#user_info.user_id), + ?assertEqual(25, User#user_info.age), + + ok. diff --git a/test/test_projects/codegen_test/generated/example_service_client.erl b/test/test_projects/codegen_test/generated/example_service_client.erl new file mode 100644 index 0000000000..6b38c39425 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_client.erl @@ -0,0 +1,37 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. diff --git a/test/test_projects/codegen_test/generated/example_service_types.erl b/test/test_projects/codegen_test/generated/example_service_types.erl new file mode 100644 index 0000000000..3f89adcba9 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_types.erl @@ -0,0 +1,55 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. diff --git a/test/test_projects/codegen_test/generated/example_service_types.hrl b/test/test_projects/codegen_test/generated/example_service_types.hrl new file mode 100644 index 0000000000..4c957a0906 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_types.hrl @@ -0,0 +1,27 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). diff --git a/test/test_projects/codegen_test/templates/example_service_client.erl b/test/test_projects/codegen_test/templates/example_service_client.erl new file mode 100644 index 0000000000..ab30a89f74 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_client.erl @@ -0,0 +1,38 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. + diff --git a/test/test_projects/codegen_test/templates/example_service_types.erl b/test/test_projects/codegen_test/templates/example_service_types.erl new file mode 100644 index 0000000000..833df167c6 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_types.erl @@ -0,0 +1,56 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. + diff --git a/test/test_projects/codegen_test/templates/example_service_types.hrl b/test/test_projects/codegen_test/templates/example_service_types.hrl new file mode 100644 index 0000000000..88a2e7aed2 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_types.hrl @@ -0,0 +1,28 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). + diff --git a/test_projects/custom_build_tool/.elp.toml b/test/test_projects/custom_build_tool/.elp.toml similarity index 100% rename from test_projects/custom_build_tool/.elp.toml rename to test/test_projects/custom_build_tool/.elp.toml diff --git a/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl b/test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/include/app_a.hrl rename to test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl diff --git a/test_projects/custom_build_tool/apps/app_a/src/app_a.erl b/test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/src/app_a.erl rename to test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl diff --git a/test_projects/custom_build_tool/apps/app_b/src/app_b.erl b/test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_b/src/app_b.erl rename to test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl diff --git a/test/test_projects/diagnostics/.elp.toml b/test/test_projects/diagnostics/.elp.toml new file mode 100644 index 0000000000..504ef547af --- /dev/null +++ b/test/test_projects/diagnostics/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//diagnostics/..." ] +source_root = "diagnostics" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/diagnostics/BUCK b/test/test_projects/diagnostics/BUCK new file mode 100644 index 0000000000..19c15e5acf --- /dev/null +++ b/test/test_projects/diagnostics/BUCK @@ -0,0 +1,20 @@ +oncall("vscode_erlang") + +erlang_application( + name = "diagnostics_app_a_target", + srcs = glob(["app_a/src/*.erl"]), + app_name = "diagnostics_app_a", + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":diagnostics_app_a_target"], +) diff --git a/test_projects/diagnostics/README.md b/test/test_projects/diagnostics/README.md similarity index 100% rename from test_projects/diagnostics/README.md rename to test/test_projects/diagnostics/README.md diff --git a/test_projects/diagnostics/app_a/extra/app_a.erl b/test/test_projects/diagnostics/app_a/extra/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/extra/app_a.erl rename to test/test_projects/diagnostics/app_a/extra/app_a.erl diff --git a/test_projects/diagnostics/app_a/include/app_a.hrl b/test/test_projects/diagnostics/app_a/include/app_a.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/app_a.hrl rename to test/test_projects/diagnostics/app_a/include/app_a.hrl diff --git a/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/broken_diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/include/diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/src/app_a.app.src b/test/test_projects/diagnostics/app_a/src/app_a.app.src similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.app.src rename to test/test_projects/diagnostics/app_a/src/app_a.app.src diff --git a/test_projects/diagnostics/app_a/src/app_a.erl b/test/test_projects/diagnostics/app_a/src/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.erl rename to test/test_projects/diagnostics/app_a/src/app_a.erl diff --git a/test_projects/diagnostics/app_a/src/broken_parse_trans.erl b/test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/broken_parse_trans.erl rename to test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl diff --git a/test_projects/diagnostics/app_a/src/cascading.erl b/test/test_projects/diagnostics/app_a/src/cascading.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/cascading.erl rename to test/test_projects/diagnostics/app_a/src/cascading.erl diff --git a/test_projects/diagnostics/app_a/src/crlf.erl b/test/test_projects/diagnostics/app_a/src/crlf.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/crlf.erl rename to test/test_projects/diagnostics/app_a/src/crlf.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.erl b/test/test_projects/diagnostics/app_a/src/diagnostics.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.erl rename to test/test_projects/diagnostics/app_a/src/diagnostics.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.escript b/test/test_projects/diagnostics/app_a/src/diagnostics.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_errors.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_errors.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_warnings.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript diff --git a/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl b/test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl rename to test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl diff --git a/test_projects/diagnostics/app_a/src/file_attribute.erl b/test/test_projects/diagnostics/app_a/src/file_attribute.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/file_attribute.erl rename to test/test_projects/diagnostics/app_a/src/file_attribute.erl diff --git a/test_projects/diagnostics/app_a/src/lint_recursive.erl b/test/test_projects/diagnostics/app_a/src/lint_recursive.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lint_recursive.erl rename to test/test_projects/diagnostics/app_a/src/lint_recursive.erl diff --git a/test_projects/diagnostics/app_a/src/lints.erl b/test/test_projects/diagnostics/app_a/src/lints.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lints.erl rename to test/test_projects/diagnostics/app_a/src/lints.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_docstrings.erl b/test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_docstrings.erl rename to test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_sigils.erl b/test/test_projects/diagnostics/app_a/src/otp27_sigils.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_sigils.erl rename to test/test_projects/diagnostics/app_a/src/otp27_sigils.erl diff --git a/test_projects/diagnostics/app_a/src/otp_7655.erl b/test/test_projects/diagnostics/app_a/src/otp_7655.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp_7655.erl rename to test/test_projects/diagnostics/app_a/src/otp_7655.erl diff --git a/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl b/test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl rename to test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl diff --git a/test_projects/diagnostics/app_a/src/suppressed.erl b/test/test_projects/diagnostics/app_a/src/suppressed.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/suppressed.erl rename to test/test_projects/diagnostics/app_a/src/suppressed.erl diff --git a/test_projects/diagnostics/app_a/src/syntax.erl b/test/test_projects/diagnostics/app_a/src/syntax.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/syntax.erl rename to test/test_projects/diagnostics/app_a/src/syntax.erl diff --git a/test_projects/diagnostics/app_a/test/app_a_SUITE.erl b/test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/diagnostics/app_a/test/app_a_SUITE.erl rename to test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl diff --git a/test_projects/diagnostics/erlang_ls.config b/test/test_projects/diagnostics/erlang_ls.config similarity index 100% rename from test_projects/diagnostics/erlang_ls.config rename to test/test_projects/diagnostics/erlang_ls.config diff --git a/test_projects/diagnostics/rebar.config b/test/test_projects/diagnostics/rebar.config similarity index 100% rename from test_projects/diagnostics/rebar.config rename to test/test_projects/diagnostics/rebar.config diff --git a/test_projects/diagnostics/test_build_info.json b/test/test_projects/diagnostics/test_build_info.json similarity index 100% rename from test_projects/diagnostics/test_build_info.json rename to test/test_projects/diagnostics/test_build_info.json diff --git a/test/test_projects/end_to_end/.elp.toml b/test/test_projects/end_to_end/.elp.toml new file mode 100644 index 0000000000..acbcd6146f --- /dev/null +++ b/test/test_projects/end_to_end/.elp.toml @@ -0,0 +1,7 @@ +[buck] +enabled = true +build_deps = false +included_targets = ["root//end_to_end/..."] + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/end_to_end/assist_examples/BUCK b/test/test_projects/end_to_end/assist_examples/BUCK new file mode 100644 index 0000000000..60d39de125 --- /dev/null +++ b/test/test_projects/end_to_end/assist_examples/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "assist_examples", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/assist_examples.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/assist_examples/src/add_doc.erl b/test/test_projects/end_to_end/assist_examples/src/add_doc.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/add_doc.erl rename to test/test_projects/end_to_end/assist_examples/src/add_doc.erl diff --git a/test_projects/end_to_end/assist_examples/src/assist_examples.app.src b/test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src similarity index 100% rename from test_projects/end_to_end/assist_examples/src/assist_examples.app.src rename to test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src diff --git a/test_projects/end_to_end/assist_examples/src/code_completion.erl b/test/test_projects/end_to_end/assist_examples/src/code_completion.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/code_completion.erl rename to test/test_projects/end_to_end/assist_examples/src/code_completion.erl diff --git a/test_projects/end_to_end/assist_examples/src/extract_function.erl b/test/test_projects/end_to_end/assist_examples/src/extract_function.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/extract_function.erl rename to test/test_projects/end_to_end/assist_examples/src/extract_function.erl diff --git a/test_projects/end_to_end/assist_examples/src/head_mismatch.erl b/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/head_mismatch.erl rename to test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl diff --git a/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl b/test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl similarity index 100% rename from test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl rename to test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl diff --git a/test/test_projects/end_to_end/definitions/BUCK b/test/test_projects/end_to_end/definitions/BUCK new file mode 100644 index 0000000000..c9e6b204f4 --- /dev/null +++ b/test/test_projects/end_to_end/definitions/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "definitions", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/definitions.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/definitions/README.md b/test/test_projects/end_to_end/definitions/README.md similarity index 100% rename from test_projects/end_to_end/definitions/README.md rename to test/test_projects/end_to_end/definitions/README.md diff --git a/test_projects/end_to_end/definitions/src/definitions.app.src b/test/test_projects/end_to_end/definitions/src/definitions.app.src similarity index 100% rename from test_projects/end_to_end/definitions/src/definitions.app.src rename to test/test_projects/end_to_end/definitions/src/definitions.app.src diff --git a/test_projects/end_to_end/definitions/src/local_def.erl b/test/test_projects/end_to_end/definitions/src/local_def.erl similarity index 100% rename from test_projects/end_to_end/definitions/src/local_def.erl rename to test/test_projects/end_to_end/definitions/src/local_def.erl diff --git a/test/test_projects/end_to_end/docs/BUCK b/test/test_projects/end_to_end/docs/BUCK new file mode 100644 index 0000000000..7429863b6e --- /dev/null +++ b/test/test_projects/end_to_end/docs/BUCK @@ -0,0 +1,16 @@ +load("@waserver//buck2/whatsapp:erlang.bzl", "erlang_application") + +oncall("vscode_erlang") + +erlang_application( + name = "docs", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/docs.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/docs/src/docs.app.src b/test/test_projects/end_to_end/docs/src/docs.app.src similarity index 100% rename from test_projects/end_to_end/docs/src/docs.app.src rename to test/test_projects/end_to_end/docs/src/docs.app.src diff --git a/test_projects/end_to_end/docs/src/docs.erl b/test/test_projects/end_to_end/docs/src/docs.erl similarity index 100% rename from test_projects/end_to_end/docs/src/docs.erl rename to test/test_projects/end_to_end/docs/src/docs.erl diff --git a/test_projects/end_to_end/erlang_ls.config b/test/test_projects/end_to_end/erlang_ls.config similarity index 100% rename from test_projects/end_to_end/erlang_ls.config rename to test/test_projects/end_to_end/erlang_ls.config diff --git a/test_projects/end_to_end/fixtures/erlang-stacktrace.txt b/test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt similarity index 100% rename from test_projects/end_to_end/fixtures/erlang-stacktrace.txt rename to test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt diff --git a/test/test_projects/end_to_end/hover/BUCK b/test/test_projects/end_to_end/hover/BUCK new file mode 100644 index 0000000000..7732dc473e --- /dev/null +++ b/test/test_projects/end_to_end/hover/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "hover", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/hover.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/hover/README.md b/test/test_projects/end_to_end/hover/README.md similarity index 100% rename from test_projects/end_to_end/hover/README.md rename to test/test_projects/end_to_end/hover/README.md diff --git a/test_projects/end_to_end/hover/src/doc_examples.erl b/test/test_projects/end_to_end/hover/src/doc_examples.erl similarity index 100% rename from test_projects/end_to_end/hover/src/doc_examples.erl rename to test/test_projects/end_to_end/hover/src/doc_examples.erl diff --git a/test_projects/end_to_end/hover/src/hover.app.src b/test/test_projects/end_to_end/hover/src/hover.app.src similarity index 100% rename from test_projects/end_to_end/hover/src/hover.app.src rename to test/test_projects/end_to_end/hover/src/hover.app.src diff --git a/test_projects/end_to_end/rebar.config b/test/test_projects/end_to_end/rebar.config similarity index 100% rename from test_projects/end_to_end/rebar.config rename to test/test_projects/end_to_end/rebar.config diff --git a/test/test_projects/end_to_end/single_errors/BUCK b/test/test_projects/end_to_end/single_errors/BUCK new file mode 100644 index 0000000000..a8f6e478b0 --- /dev/null +++ b/test/test_projects/end_to_end/single_errors/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "single_errors", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/single_errors.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/single_errors/README.md b/test/test_projects/end_to_end/single_errors/README.md similarity index 100% rename from test_projects/end_to_end/single_errors/README.md rename to test/test_projects/end_to_end/single_errors/README.md diff --git a/test_projects/end_to_end/single_errors/src/as_you_type.erl b/test/test_projects/end_to_end/single_errors/src/as_you_type.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/as_you_type.erl rename to test/test_projects/end_to_end/single_errors/src/as_you_type.erl diff --git a/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl b/test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl rename to test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl diff --git a/test_projects/end_to_end/single_errors/src/single_errors.app.src b/test/test_projects/end_to_end/single_errors/src/single_errors.app.src similarity index 100% rename from test_projects/end_to_end/single_errors/src/single_errors.app.src rename to test/test_projects/end_to_end/single_errors/src/single_errors.app.src diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 diff --git a/test_projects/end_to_end/single_errors/src/types_on_hover.erl b/test/test_projects/end_to_end/single_errors/src/types_on_hover.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/types_on_hover.erl rename to test/test_projects/end_to_end/single_errors/src/types_on_hover.erl diff --git a/test_projects/end_to_end/single_errors/src/unused_macro.erl b/test/test_projects/end_to_end/single_errors/src/unused_macro.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/unused_macro.erl rename to test/test_projects/end_to_end/single_errors/src/unused_macro.erl diff --git a/test_projects/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/eqwalizer_callers/.elp.toml b/test/test_projects/eqwalizer_callers/.elp.toml new file mode 100644 index 0000000000..68476bcf2e --- /dev/null +++ b/test/test_projects/eqwalizer_callers/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//eqwalizer_callers/..." ] +source_root = "eqwalizer_callers" diff --git a/test_projects/eqwalizer_callers/.gitignore b/test/test_projects/eqwalizer_callers/.gitignore similarity index 100% rename from test_projects/eqwalizer_callers/.gitignore rename to test/test_projects/eqwalizer_callers/.gitignore diff --git a/test/test_projects/eqwalizer_callers/BUCK b/test/test_projects/eqwalizer_callers/BUCK new file mode 100644 index 0000000000..32012a0dc0 --- /dev/null +++ b/test/test_projects/eqwalizer_callers/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) diff --git a/test_projects/eqwalizer_callers/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.app.src b/test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.app.src rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_b.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_b.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_b.erl diff --git a/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl b/test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl rename to test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl diff --git a/test_projects/eqwalizer_callers/rebar.config b/test/test_projects/eqwalizer_callers/rebar.config similarity index 100% rename from test_projects/eqwalizer_callers/rebar.config rename to test/test_projects/eqwalizer_callers/rebar.config diff --git a/test_projects/eqwalizer_ignore_modules/.elp.toml b/test/test_projects/eqwalizer_ignore_modules/.elp.toml similarity index 100% rename from test_projects/eqwalizer_ignore_modules/.elp.toml rename to test/test_projects/eqwalizer_ignore_modules/.elp.toml diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl diff --git a/test/test_projects/eqwalizer_tests/.elp.toml b/test/test_projects/eqwalizer_tests/.elp.toml new file mode 100644 index 0000000000..5dac3aa6a6 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//eqwalizer_tests/..." ] +source_root = "eqwalizer_tests" + +[eqwalizer] +enable_all = true diff --git a/test_projects/eqwalizer_tests/.gitignore b/test/test_projects/eqwalizer_tests/.gitignore similarity index 100% rename from test_projects/eqwalizer_tests/.gitignore rename to test/test_projects/eqwalizer_tests/.gitignore diff --git a/test_projects/eqwalizer_tests/.rebar.root b/test/test_projects/eqwalizer_tests/.rebar.root similarity index 100% rename from test_projects/eqwalizer_tests/.rebar.root rename to test/test_projects/eqwalizer_tests/.rebar.root diff --git a/test/test_projects/eqwalizer_tests/BUCK b/test/test_projects/eqwalizer_tests/BUCK new file mode 100644 index 0000000000..0817873cb0 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/BUCK @@ -0,0 +1,77 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check", + srcs = glob(["check/src/**/*.erl"]), + applications = [":eqwalizer"], + includes = glob([ + "check/include/*.hrl", + "eqwalizer/include/*.hrl", + ]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "check_gradual", + srcs = glob(["check_gradual/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "check/test/check_SUITE.erl", + ], + deps = [":check"], +) + +erlang_application( + name = "debug", + srcs = glob(["debug/src/*.erl"]), + applications = [":eqwalizer"], + includes = glob(["debug/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elm_core", + srcs = glob(["elm_core/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwater", + srcs = glob(["eqwater/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "fault_tolerance", + srcs = glob(["fault_tolerance/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "options", + srcs = glob(["options/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + includes = glob(["eqwalizer/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/eqwalizer_tests/check/include/my_header.hrl b/test/test_projects/eqwalizer_tests/check/include/my_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/check/include/my_header.hrl rename to test/test_projects/eqwalizer_tests/check/include/my_header.hrl diff --git a/test_projects/eqwalizer_tests/check/src/any_fun_type.erl b/test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/any_fun_type.erl rename to test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl diff --git a/test_projects/eqwalizer_tests/check/src/apply_none.erl b/test/test_projects/eqwalizer_tests/check/src/apply_none.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/apply_none.erl rename to test/test_projects/eqwalizer_tests/check/src/apply_none.erl diff --git a/test_projects/eqwalizer_tests/check/src/approx.erl b/test/test_projects/eqwalizer_tests/check/src/approx.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/approx.erl rename to test/test_projects/eqwalizer_tests/check/src/approx.erl diff --git a/test_projects/eqwalizer_tests/check/src/as_pat.erl b/test/test_projects/eqwalizer_tests/check/src/as_pat.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/as_pat.erl rename to test/test_projects/eqwalizer_tests/check/src/as_pat.erl diff --git a/test_projects/eqwalizer_tests/check/src/auto_imports.erl b/test/test_projects/eqwalizer_tests/check/src/auto_imports.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/auto_imports.erl rename to test/test_projects/eqwalizer_tests/check/src/auto_imports.erl diff --git a/test_projects/eqwalizer_tests/check/src/behave.erl b/test/test_projects/eqwalizer_tests/check/src/behave.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/behave.erl rename to test/test_projects/eqwalizer_tests/check/src/behave.erl diff --git a/test_projects/eqwalizer_tests/check/src/binaries.erl b/test/test_projects/eqwalizer_tests/check/src/binaries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/binaries.erl rename to test/test_projects/eqwalizer_tests/check/src/binaries.erl diff --git a/test_projects/eqwalizer_tests/check/src/booleans.erl b/test/test_projects/eqwalizer_tests/check/src/booleans.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/booleans.erl rename to test/test_projects/eqwalizer_tests/check/src/booleans.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl similarity index 70% rename from test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl index 8152487669..854feec923 100644 --- a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl +++ b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl @@ -3,11 +3,12 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(callbacks3_neg). % @OTPVersionDependent +-module(callbacks3_neg). % @OTP27Only -export([ init/1, handle_call/3, - handle_cast/2 + handle_cast/2, + handle_info/2 ]). -behavior(gen_server). @@ -19,3 +20,7 @@ handle_call(_, _From, State) -> -spec handle_cast(ok, ok) -> wrong_ret. handle_cast(_, _) -> wrong_ret. + +-spec handle_info(ok, ok) -> {noreply, ok, wrong_atom}. +handle_info(_, _) -> + {noreply, ok, wrong_atom}. diff --git a/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/case_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/case_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/case_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/case_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/check.app.src b/test/test_projects/eqwalizer_tests/check/src/check.app.src similarity index 100% rename from test_projects/eqwalizer_tests/check/src/check.app.src rename to test/test_projects/eqwalizer_tests/check/src/check.app.src diff --git a/test_projects/eqwalizer_tests/check/src/compiler_macro.erl b/test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/compiler_macro.erl rename to test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl diff --git a/test_projects/eqwalizer_tests/check/src/complex_maps.erl b/test/test_projects/eqwalizer_tests/check/src/complex_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/complex_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/complex_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/comprehensions.erl b/test/test_projects/eqwalizer_tests/check/src/comprehensions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/comprehensions.erl rename to test/test_projects/eqwalizer_tests/check/src/comprehensions.erl diff --git a/test_projects/eqwalizer_tests/check/src/contravariant.erl b/test/test_projects/eqwalizer_tests/check/src/contravariant.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/contravariant.erl rename to test/test_projects/eqwalizer_tests/check/src/contravariant.erl diff --git a/test_projects/eqwalizer_tests/check/src/custom.erl b/test/test_projects/eqwalizer_tests/check/src/custom.erl similarity index 99% rename from test_projects/eqwalizer_tests/check/src/custom.erl rename to test/test_projects/eqwalizer_tests/check/src/custom.erl index 911bfa3a0a..b6910b0fc7 100644 --- a/test_projects/eqwalizer_tests/check/src/custom.erl +++ b/test/test_projects/eqwalizer_tests/check/src/custom.erl @@ -3,7 +3,7 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(custom). % @OTPVersionDependent +-module(custom). % @OTP27Only -import(maps, [get/2, get/3]). -compile([export_all, nowarn_export_all]). diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs1.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs1.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs2.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs2.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_refine.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/elab_clause.erl b/test/test_projects/eqwalizer_tests/check/src/elab_clause.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/elab_clause.erl rename to test/test_projects/eqwalizer_tests/check/src/elab_clause.erl diff --git a/test_projects/eqwalizer_tests/check/src/error_messages.erl b/test/test_projects/eqwalizer_tests/check/src/error_messages.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/error_messages.erl rename to test/test_projects/eqwalizer_tests/check/src/error_messages.erl diff --git a/test_projects/eqwalizer_tests/check/src/fancy_generics.erl b/test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fancy_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/format.erl b/test/test_projects/eqwalizer_tests/check/src/format.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/format.erl rename to test/test_projects/eqwalizer_tests/check/src/format.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats2.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats2.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs.erl b/test/test_projects/eqwalizer_tests/check/src/funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs.erl rename to test/test_projects/eqwalizer_tests/check/src/funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs2.erl b/test/test_projects/eqwalizer_tests/check/src/funs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs2.erl rename to test/test_projects/eqwalizer_tests/check/src/funs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl b/test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs_uncommon.erl rename to test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl diff --git a/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl b/test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generic_fun_application.erl rename to test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl diff --git a/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl b/test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generics_with_unions.erl rename to test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_bounded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_custom.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_custom.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_misc.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_misc.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_untyped.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl diff --git a/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl b/test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guard_b_connections.erl rename to test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards.erl b/test/test_projects/eqwalizer_tests/check/src/guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards.erl rename to test/test_projects/eqwalizer_tests/check/src/guards.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_logic.erl b/test/test_projects/eqwalizer_tests/check/src/guards_logic.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_logic.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_logic.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_simple.erl b/test/test_projects/eqwalizer_tests/check/src/guards_simple.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_simple.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_simple.erl diff --git a/test_projects/eqwalizer_tests/check/src/hints.erl b/test/test_projects/eqwalizer_tests/check/src/hints.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/hints.erl rename to test/test_projects/eqwalizer_tests/check/src/hints.erl diff --git a/test_projects/eqwalizer_tests/check/src/index1.erl b/test/test_projects/eqwalizer_tests/check/src/index1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index1.erl rename to test/test_projects/eqwalizer_tests/check/src/index1.erl diff --git a/test_projects/eqwalizer_tests/check/src/index2.erl b/test/test_projects/eqwalizer_tests/check/src/index2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index2.erl rename to test/test_projects/eqwalizer_tests/check/src/index2.erl diff --git a/test_projects/eqwalizer_tests/check/src/iolists.erl b/test/test_projects/eqwalizer_tests/check/src/iolists.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/iolists.erl rename to test/test_projects/eqwalizer_tests/check/src/iolists.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_01.erl b/test/test_projects/eqwalizer_tests/check/src/kp_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_01.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_02.erl b/test/test_projects/eqwalizer_tests/check/src/kp_02.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_02.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_02.erl diff --git a/test_projects/eqwalizer_tests/check/src/lists_tests.erl b/test/test_projects/eqwalizer_tests/check/src/lists_tests.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/lists_tests.erl rename to test/test_projects/eqwalizer_tests/check/src/lists_tests.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc.erl b/test/test_projects/eqwalizer_tests/check/src/misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc.erl rename to test/test_projects/eqwalizer_tests/check/src/misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc_lib.erl b/test/test_projects/eqwalizer_tests/check/src/misc_lib.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc_lib.erl rename to test/test_projects/eqwalizer_tests/check/src/misc_lib.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_header.erl b/test/test_projects/eqwalizer_tests/check/src/my_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_header.erl rename to test/test_projects/eqwalizer_tests/check/src/my_header.erl diff --git a/test_projects/eqwalizer_tests/check/src/neg.erl b/test/test_projects/eqwalizer_tests/check/src/neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/neg.erl rename to test/test_projects/eqwalizer_tests/check/src/neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl diff --git a/test_projects/eqwalizer_tests/check/src/nowarn.erl b/test/test_projects/eqwalizer_tests/check/src/nowarn.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nowarn.erl rename to test/test_projects/eqwalizer_tests/check/src/nowarn.erl diff --git a/test_projects/eqwalizer_tests/check/src/number_comparisons.erl b/test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/number_comparisons.erl rename to test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl diff --git a/test_projects/eqwalizer_tests/check/src/numbers.erl b/test/test_projects/eqwalizer_tests/check/src/numbers.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/numbers.erl rename to test/test_projects/eqwalizer_tests/check/src/numbers.erl diff --git a/test_projects/eqwalizer_tests/check/src/opaque.erl b/test/test_projects/eqwalizer_tests/check/src/opaque.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/opaque.erl rename to test/test_projects/eqwalizer_tests/check/src/opaque.erl diff --git a/test_projects/eqwalizer_tests/check/src/other.erl b/test/test_projects/eqwalizer_tests/check/src/other.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/other.erl rename to test/test_projects/eqwalizer_tests/check/src/other.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp28.erl b/test/test_projects/eqwalizer_tests/check/src/otp28.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp28.erl rename to test/test_projects/eqwalizer_tests/check/src/otp28.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp_opaques.erl b/test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp_opaques.erl rename to test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/parametricity.erl b/test/test_projects/eqwalizer_tests/check/src/parametricity.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/parametricity.erl rename to test/test_projects/eqwalizer_tests/check/src/parametricity.erl diff --git a/test_projects/eqwalizer_tests/check/src/pats.erl b/test/test_projects/eqwalizer_tests/check/src/pats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pats.erl rename to test/test_projects/eqwalizer_tests/check/src/pats.erl diff --git a/test_projects/eqwalizer_tests/check/src/pinned.erl b/test/test_projects/eqwalizer_tests/check/src/pinned.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pinned.erl rename to test/test_projects/eqwalizer_tests/check/src/pinned.erl diff --git a/test_projects/eqwalizer_tests/check/src/pos.erl b/test/test_projects/eqwalizer_tests/check/src/pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pos.erl rename to test/test_projects/eqwalizer_tests/check/src/pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/records.erl b/test/test_projects/eqwalizer_tests/check/src/records.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/records.erl rename to test/test_projects/eqwalizer_tests/check/src/records.erl diff --git a/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/recursive_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/refine.erl b/test/test_projects/eqwalizer_tests/check/src/refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/refine.erl rename to test/test_projects/eqwalizer_tests/check/src/refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/scoping.erl b/test/test_projects/eqwalizer_tests/check/src/scoping.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/scoping.erl rename to test/test_projects/eqwalizer_tests/check/src/scoping.erl diff --git a/test_projects/eqwalizer_tests/check/src/skip.erl b/test/test_projects/eqwalizer_tests/check/src/skip.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/skip.erl rename to test/test_projects/eqwalizer_tests/check/src/skip.erl diff --git a/test_projects/eqwalizer_tests/check/src/static_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/static_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/static_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/static_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_fun.erl b/test/test_projects/eqwalizer_tests/check/src/strict_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_receive.erl b/test/test_projects/eqwalizer_tests/check/src/strict_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_neg.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_pos.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/t_maps.erl b/test/test_projects/eqwalizer_tests/check/src/t_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/t_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/t_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl b/test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tagged_tuples.erl rename to test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl diff --git a/test_projects/eqwalizer_tests/check/src/test.erl b/test/test_projects/eqwalizer_tests/check/src/test.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/test.erl rename to test/test_projects/eqwalizer_tests/check/src/test.erl diff --git a/test_projects/eqwalizer_tests/check/src/tries.erl b/test/test_projects/eqwalizer_tests/check/src/tries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tries.erl rename to test/test_projects/eqwalizer_tests/check/src/tries.erl diff --git a/test_projects/eqwalizer_tests/check/src/tuple_union.erl b/test/test_projects/eqwalizer_tests/check/src/tuple_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tuple_union.erl rename to test/test_projects/eqwalizer_tests/check/src/tuple_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/type_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/type_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_asserts.erl b/test/test_projects/eqwalizer_tests/check/src/type_asserts.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_asserts.erl rename to test/test_projects/eqwalizer_tests/check/src/type_asserts.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/type_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/type_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/united_fun.erl b/test/test_projects/eqwalizer_tests/check/src/united_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/united_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/united_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/unspecced.erl b/test/test_projects/eqwalizer_tests/check/src/unspecced.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/unspecced.erl rename to test/test_projects/eqwalizer_tests/check/src/unspecced.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars1.erl b/test/test_projects/eqwalizer_tests/check/src/vars1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars1.erl rename to test/test_projects/eqwalizer_tests/check/src/vars1.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars2.erl b/test/test_projects/eqwalizer_tests/check/src/vars2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars2.erl rename to test/test_projects/eqwalizer_tests/check/src/vars2.erl diff --git a/test_projects/eqwalizer_tests/check/test/check_SUITE.erl b/test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/test/check_SUITE.erl rename to test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl diff --git a/test_projects/eqwalizer_tests/debug/include/debug_header.hrl b/test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/debug/include/debug_header.hrl rename to test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl diff --git a/test_projects/eqwalizer_tests/debug/src/attributes.erl b/test/test_projects/eqwalizer_tests/debug/src/attributes.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/attributes.erl rename to test/test_projects/eqwalizer_tests/debug/src/attributes.erl diff --git a/test_projects/eqwalizer_tests/debug/src/debug.app.src b/test/test_projects/eqwalizer_tests/debug/src/debug.app.src similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug.app.src rename to test/test_projects/eqwalizer_tests/debug/src/debug.app.src diff --git a/test_projects/eqwalizer_tests/debug/src/debug_header.erl b/test/test_projects/eqwalizer_tests/debug/src/debug_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug_header.erl rename to test/test_projects/eqwalizer_tests/debug/src/debug_header.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expand.erl b/test/test_projects/eqwalizer_tests/debug/src/expand.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expand.erl rename to test/test_projects/eqwalizer_tests/debug/src/expand.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr1.erl b/test/test_projects/eqwalizer_tests/debug/src/expr1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr1.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr2.erl b/test/test_projects/eqwalizer_tests/debug/src/expr2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr2.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/records_wip.erl b/test/test_projects/eqwalizer_tests/debug/src/records_wip.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/records_wip.erl rename to test/test_projects/eqwalizer_tests/debug/src/records_wip.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types1.erl b/test/test_projects/eqwalizer_tests/debug/src/types1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types1.erl rename to test/test_projects/eqwalizer_tests/debug/src/types1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types2.erl b/test/test_projects/eqwalizer_tests/debug/src/types2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types2.erl rename to test/test_projects/eqwalizer_tests/debug/src/types2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/wip_maps.erl b/test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/wip_maps.erl rename to test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/basics.erl b/test/test_projects/eqwalizer_tests/elm_core/src/basics.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/basics.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/basics.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src b/test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src rename to test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src diff --git a/test_projects/eqwalizer_tests/elm_core/src/list.erl b/test/test_projects/eqwalizer_tests/elm_core/src/list.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/list.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/list.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/maybe.erl b/test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/maybe.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/result.erl b/test/test_projects/eqwalizer_tests/elm_core/src/result.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/result.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/result.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/tuple.erl b/test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/tuple.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl b/test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl rename to test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl diff --git a/test_projects/eqwalizer_tests/eqwater/readme.md b/test/test_projects/eqwalizer_tests/eqwater/readme.md similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/readme.md rename to test/test_projects/eqwalizer_tests/eqwater/readme.md diff --git a/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl b/test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl similarity index 89% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl index 78ae852b8a..68d938f790 100644 --- a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl +++ b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl @@ -59,3 +59,13 @@ map_occ_08_neg(_) -> err. map_occ_09(#{a := undefined}) -> 1; map_occ_09(#{a := Map}) -> Map#{2 => 2}; map_occ_09(_) -> 3. + +-spec is_ok(ok) -> ok. +is_ok(ok) -> ok. + +-spec map_occ_foreach_neg(#{term() => #{a => ok | err}}) -> ok. +map_occ_foreach_neg(M) -> + maps:foreach(fun + (_, #{a := err}) -> ok; + (_, #{a := V}) -> is_ok(V) + end, M). diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl b/test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl diff --git a/test_projects/standard/app_a/.eqwalizer b/test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer similarity index 100% rename from test_projects/standard/app_a/.eqwalizer rename to test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl diff --git a/test_projects/eqwalizer_tests/options/src/bad_maps.erl b/test/test_projects/eqwalizer_tests/options/src/bad_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/bad_maps.erl rename to test/test_projects/eqwalizer_tests/options/src/bad_maps.erl diff --git a/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl b/test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl rename to test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl diff --git a/test_projects/eqwalizer_tests/options/src/options.app.src b/test/test_projects/eqwalizer_tests/options/src/options.app.src similarity index 100% rename from test_projects/eqwalizer_tests/options/src/options.app.src rename to test/test_projects/eqwalizer_tests/options/src/options.app.src diff --git a/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl b/test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl rename to test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl diff --git a/test_projects/eqwalizer_tests/options/src/redundant_guards.erl b/test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/redundant_guards.erl rename to test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl diff --git a/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl b/test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl rename to test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl diff --git a/test_projects/eqwalizer_tests/rebar.config b/test/test_projects/eqwalizer_tests/rebar.config similarity index 100% rename from test_projects/eqwalizer_tests/rebar.config rename to test/test_projects/eqwalizer_tests/rebar.config diff --git a/test/test_projects/hierarchical_config/.elp.toml b/test/test_projects/hierarchical_config/.elp.toml new file mode 100644 index 0000000000..e4d04eb6e0 --- /dev/null +++ b/test/test_projects/hierarchical_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//hierarchical_config/..." ] +source_root = "hierarchical_config" diff --git a/test/test_projects/hierarchical_config/.elp_lint.toml b/test/test_projects/hierarchical_config/.elp_lint.toml new file mode 100644 index 0000000000..6cc536f399 --- /dev/null +++ b/test/test_projects/hierarchical_config/.elp_lint.toml @@ -0,0 +1,5 @@ +enabled_lints = [] +disabled_lints = [ + "W0012", + "W0046" +] diff --git a/test/test_projects/hierarchical_config/BUCK b/test/test_projects/hierarchical_config/BUCK new file mode 100644 index 0000000000..bc78a42d57 --- /dev/null +++ b/test/test_projects/hierarchical_config/BUCK @@ -0,0 +1,23 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + applications = [ + ], + includes = glob(["app_b/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/hierarchical_config/app_a/.elp_lint.toml b/test/test_projects/hierarchical_config/app_a/.elp_lint.toml new file mode 100644 index 0000000000..7445514e73 --- /dev/null +++ b/test/test_projects/hierarchical_config/app_a/.elp_lint.toml @@ -0,0 +1,4 @@ +enabled_lints = [] +disabled_lints = [ + "W0002" +] diff --git a/test_projects/in_place_tests/app_a/src/app_a.app.src b/test/test_projects/hierarchical_config/app_a/src/app_a.app.src similarity index 100% rename from test_projects/in_place_tests/app_a/src/app_a.app.src rename to test/test_projects/hierarchical_config/app_a/src/app_a.app.src diff --git a/test/test_projects/hierarchical_config/app_a/src/app_a.erl b/test/test_projects/hierarchical_config/app_a/src/app_a.erl new file mode 100644 index 0000000000..5b856d4f02 --- /dev/null +++ b/test/test_projects/hierarchical_config/app_a/src/app_a.erl @@ -0,0 +1,7 @@ +-module(app_a). + +-define(MACRO_A, 1). +-define(MACRO_B, 1). + +main() -> + ?MACRO_A. diff --git a/test_projects/linter/app_b/src/app_b.app.src b/test/test_projects/hierarchical_config/app_b/src/app_b.app.src similarity index 100% rename from test_projects/linter/app_b/src/app_b.app.src rename to test/test_projects/hierarchical_config/app_b/src/app_b.app.src diff --git a/test/test_projects/hierarchical_config/app_b/src/app_b.erl b/test/test_projects/hierarchical_config/app_b/src/app_b.erl new file mode 100644 index 0000000000..ad2453d0de --- /dev/null +++ b/test/test_projects/hierarchical_config/app_b/src/app_b.erl @@ -0,0 +1,7 @@ +-module(app_b). + +-define(MACRO_A, 1). +-define(MACRO_B, 1). + +main() -> + ?MACRO_A. diff --git a/test/test_projects/hierarchical_config/rebar.config b/test/test_projects/hierarchical_config/rebar.config new file mode 100644 index 0000000000..2dcacbc5b3 --- /dev/null +++ b/test/test_projects/hierarchical_config/rebar.config @@ -0,0 +1,7 @@ +{project_app_dirs, [ + "app_a", + "app_b" +]}. + +{erl_opts, [debug_info]}. +{deps, []}. diff --git a/test/test_projects/in_place_tests/.elp.toml b/test/test_projects/in_place_tests/.elp.toml new file mode 100644 index 0000000000..70edcc3f03 --- /dev/null +++ b/test/test_projects/in_place_tests/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//in_place_tests/..." ] +source_root = "in_place_tests" diff --git a/test/test_projects/in_place_tests/BUCK b/test/test_projects/in_place_tests/BUCK new file mode 100644 index 0000000000..f4f354e7ee --- /dev/null +++ b/test/test_projects/in_place_tests/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "in_place_tests_app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":in_place_tests_app_a"], +) diff --git a/test_projects/in_place_tests/README.md b/test/test_projects/in_place_tests/README.md similarity index 100% rename from test_projects/in_place_tests/README.md rename to test/test_projects/in_place_tests/README.md diff --git a/test_projects/in_place_tests/app_a/extra/app_a.erl b/test/test_projects/in_place_tests/app_a/extra/app_a.erl similarity index 100% rename from test_projects/in_place_tests/app_a/extra/app_a.erl rename to test/test_projects/in_place_tests/app_a/extra/app_a.erl diff --git a/test_projects/in_place_tests/app_a/include/app_a.hrl b/test/test_projects/in_place_tests/app_a/include/app_a.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/app_a.hrl rename to test/test_projects/in_place_tests/app_a/include/app_a.hrl diff --git a/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/in_place_tests/app_a/include/diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/diagnostics.hrl diff --git a/test_projects/linter/app_a/src/app_a.app.src b/test/test_projects/in_place_tests/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter/app_a/src/app_a.app.src rename to test/test_projects/in_place_tests/app_a/src/app_a.app.src diff --git a/test_projects/in_place_tests/app_a/src/app_a.erl b/test/test_projects/in_place_tests/app_a/src/app_a.erl similarity index 100% rename from test_projects/in_place_tests/app_a/src/app_a.erl rename to test/test_projects/in_place_tests/app_a/src/app_a.erl diff --git a/test_projects/in_place_tests/app_a/src/lints.erl b/test/test_projects/in_place_tests/app_a/src/lints.erl similarity index 100% rename from test_projects/in_place_tests/app_a/src/lints.erl rename to test/test_projects/in_place_tests/app_a/src/lints.erl diff --git a/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl b/test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/in_place_tests/app_a/test/app_a_SUITE.erl rename to test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl diff --git a/test_projects/in_place_tests/erlang_ls.config b/test/test_projects/in_place_tests/erlang_ls.config similarity index 100% rename from test_projects/in_place_tests/erlang_ls.config rename to test/test_projects/in_place_tests/erlang_ls.config diff --git a/test_projects/in_place_tests/rebar.config b/test/test_projects/in_place_tests/rebar.config similarity index 100% rename from test_projects/in_place_tests/rebar.config rename to test/test_projects/in_place_tests/rebar.config diff --git a/test/test_projects/include_lib_dependency_test/.elp.toml b/test/test_projects/include_lib_dependency_test/.elp.toml new file mode 100644 index 0000000000..9d1c859339 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//include_lib_dependency_test/..." ] +source_root = "include_lib_dependency_test" diff --git a/test/test_projects/include_lib_dependency_test/BUCK b/test/test_projects/include_lib_dependency_test/BUCK new file mode 100644 index 0000000000..54b81c47b5 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/BUCK @@ -0,0 +1,43 @@ +oncall("vscode_erlang") + +# Main application that will try to include_lib from an app it doesn't depend on +erlang_application( + name = "main_app", + srcs = glob(["main_app/src/*.erl"]), + app_src = "main_app/src/main_app.app.src", + applications = [ + # Note: deliberately NOT including :external_app as a dependency + "root//include_lib_dependency_test:normal_dep", + ], + extra_includes = ["root//include_lib_dependency_test:extra_app"], + labels = ["user_application"], + version = "1.0.0", +) + +# External application that main_app will try to include_lib from +erlang_application( + name = "external_app", + srcs = glob(["external_app/src/*.erl"]), + app_src = "external_app/src/external_app.app.src", + includes = glob(["external_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "extra_app", + srcs = glob(["extra_app/src/*.erl"]), + app_src = "extra_app/src/extra_app.app.src", + includes = glob(["extra_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "normal_dep", + srcs = glob(["normal_dep/src/*.erl"]), + app_src = "normal_dep/src/normal_dep.app.src", + includes = glob(["normal_dep/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl b/test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/include/external_header.hrl rename to test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.app.src rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.erl b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.erl rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl diff --git a/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl b/test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl rename to test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.app.src rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.erl b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.erl rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl diff --git a/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl b/test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl rename to test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl diff --git a/test_projects/include_lib_dependency_test/rebar.config b/test/test_projects/include_lib_dependency_test/rebar.config similarity index 100% rename from test_projects/include_lib_dependency_test/rebar.config rename to test/test_projects/include_lib_dependency_test/rebar.config diff --git a/test/test_projects/linter/.elp.toml b/test/test_projects/linter/.elp.toml new file mode 100644 index 0000000000..4820625b2d --- /dev/null +++ b/test/test_projects/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter/.elp_lint.toml b/test/test_projects/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter/.elp_lint.toml rename to test/test_projects/linter/.elp_lint.toml diff --git a/test_projects/linter/.gitignore b/test/test_projects/linter/.gitignore similarity index 100% rename from test_projects/linter/.gitignore rename to test/test_projects/linter/.gitignore diff --git a/test/test_projects/linter/BUCK b/test/test_projects/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test_projects/linter/app_a/include/app_a.hrl b/test/test_projects/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter/app_a/include/app_a.hrl rename to test/test_projects/linter/app_a/include/app_a.hrl diff --git a/test_projects/linter_bad_config/app_a/src/app_a.app.src b/test/test_projects/linter/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter_bad_config/app_a/src/app_a.app.src rename to test/test_projects/linter/app_a/src/app_a.app.src diff --git a/test_projects/linter/app_a/src/app_a.erl b/test/test_projects/linter/app_a/src/app_a.erl similarity index 70% rename from test_projects/linter/app_a/src/app_a.erl rename to test/test_projects/linter/app_a/src/app_a.erl index 167b8e6d6d..86593395dd 100644 --- a/test_projects/linter/app_a/src/app_a.erl +++ b/test/test_projects/linter/app_a/src/app_a.erl @@ -14,3 +14,9 @@ bar() -> app_b:application_env_error(). baz(A,B) -> {A,B}. + +% Some more context, see if it renders + +bat(A,B) -> {A,B}. + +% And some more context, see if it renders diff --git a/test_projects/linter/app_a/src/app_a_edoc.erl b/test/test_projects/linter/app_a/src/app_a_edoc.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_edoc.erl rename to test/test_projects/linter/app_a/src/app_a_edoc.erl diff --git a/test_projects/linter/app_a/src/app_a_ssr.erl b/test/test_projects/linter/app_a/src/app_a_ssr.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_ssr.erl rename to test/test_projects/linter/app_a/src/app_a_ssr.erl diff --git a/test_projects/linter/app_a/src/app_a_unused_param.erl b/test/test_projects/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_unused_param.erl rename to test/test_projects/linter/app_a/src/app_a_unused_param.erl diff --git a/test_projects/linter/app_a/src/custom_function_matches.erl b/test/test_projects/linter/app_a/src/custom_function_matches.erl similarity index 100% rename from test_projects/linter/app_a/src/custom_function_matches.erl rename to test/test_projects/linter/app_a/src/custom_function_matches.erl diff --git a/test_projects/linter/app_a/src/expression_updates_literal.erl b/test/test_projects/linter/app_a/src/expression_updates_literal.erl similarity index 100% rename from test_projects/linter/app_a/src/expression_updates_literal.erl rename to test/test_projects/linter/app_a/src/expression_updates_literal.erl diff --git a/test_projects/linter/app_a/src/spelling.erl b/test/test_projects/linter/app_a/src/spelling.erl similarity index 100% rename from test_projects/linter/app_a/src/spelling.erl rename to test/test_projects/linter/app_a/src/spelling.erl diff --git a/test_projects/linter/app_a/test/app_a_SUITE.erl b/test/test_projects/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_SUITE.erl rename to test/test_projects/linter/app_a/test/app_a_SUITE.erl diff --git a/test_projects/linter/app_a/test/app_a_test_helpers.erl b/test/test_projects/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_test_helpers.erl rename to test/test_projects/linter/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl b/test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl rename to test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl diff --git a/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/linter/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src b/test/test_projects/linter/app_b/src/app_b.app.src similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b.app.src rename to test/test_projects/linter/app_b/src/app_b.app.src diff --git a/test_projects/linter/app_b/src/app_b.erl b/test/test_projects/linter/app_b/src/app_b.erl similarity index 100% rename from test_projects/linter/app_b/src/app_b.erl rename to test/test_projects/linter/app_b/src/app_b.erl diff --git a/test_projects/linter/app_b/src/app_b_unused_param.erl b/test/test_projects/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test_projects/linter/app_b/src/app_b_unused_param.erl rename to test/test_projects/linter/app_b/src/app_b_unused_param.erl diff --git a/test_projects/linter/elp_lint_adhoc.toml b/test/test_projects/linter/elp_lint_adhoc.toml similarity index 100% rename from test_projects/linter/elp_lint_adhoc.toml rename to test/test_projects/linter/elp_lint_adhoc.toml diff --git a/test_projects/linter/elp_lint_custom_function_matches.toml b/test/test_projects/linter/elp_lint_custom_function_matches.toml similarity index 100% rename from test_projects/linter/elp_lint_custom_function_matches.toml rename to test/test_projects/linter/elp_lint_custom_function_matches.toml diff --git a/test_projects/linter/elp_lint_ssr_adhoc.toml b/test/test_projects/linter/elp_lint_ssr_adhoc.toml similarity index 100% rename from test_projects/linter/elp_lint_ssr_adhoc.toml rename to test/test_projects/linter/elp_lint_ssr_adhoc.toml diff --git a/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml b/test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml similarity index 100% rename from test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml rename to test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml diff --git a/test_projects/linter/elp_lint_test1.toml b/test/test_projects/linter/elp_lint_test1.toml similarity index 100% rename from test_projects/linter/elp_lint_test1.toml rename to test/test_projects/linter/elp_lint_test1.toml diff --git a/test_projects/linter/elp_lint_test2.toml b/test/test_projects/linter/elp_lint_test2.toml similarity index 54% rename from test_projects/linter/elp_lint_test2.toml rename to test/test_projects/linter/elp_lint_test2.toml index 22878a22a6..0b70173c8b 100644 --- a/test_projects/linter/elp_lint_test2.toml +++ b/test/test_projects/linter/elp_lint_test2.toml @@ -1,2 +1,5 @@ enabled_lints =['L1268', 'W0011', 'W0012'] disabled_lints = [] + +[linters.compile-warn-missing-spec] +enabled = true diff --git a/test_projects/linter/elp_lint_test_ignore.toml b/test/test_projects/linter/elp_lint_test_ignore.toml similarity index 100% rename from test_projects/linter/elp_lint_test_ignore.toml rename to test/test_projects/linter/elp_lint_test_ignore.toml diff --git a/test_projects/linter/elp_lint_warnings_as_errors.toml b/test/test_projects/linter/elp_lint_warnings_as_errors.toml similarity index 100% rename from test_projects/linter/elp_lint_warnings_as_errors.toml rename to test/test_projects/linter/elp_lint_warnings_as_errors.toml diff --git a/test_projects/linter/rebar.config b/test/test_projects/linter/rebar.config similarity index 100% rename from test_projects/linter/rebar.config rename to test/test_projects/linter/rebar.config diff --git a/test/test_projects/linter_bad_config/.elp.toml b/test/test_projects/linter_bad_config/.elp.toml new file mode 100644 index 0000000000..4820625b2d --- /dev/null +++ b/test/test_projects/linter_bad_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter_bad_config/.elp_lint.toml b/test/test_projects/linter_bad_config/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/.elp_lint.toml rename to test/test_projects/linter_bad_config/.elp_lint.toml diff --git a/test_projects/linter_bad_config/.gitignore b/test/test_projects/linter_bad_config/.gitignore similarity index 100% rename from test_projects/linter_bad_config/.gitignore rename to test/test_projects/linter_bad_config/.gitignore diff --git a/test/test_projects/linter_bad_config/BUCK b/test/test_projects/linter_bad_config/BUCK new file mode 100644 index 0000000000..a0817a6ad6 --- /dev/null +++ b/test/test_projects/linter_bad_config/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/linter_bad_config/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/app_a/include/app_a.hrl diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src b/test/test_projects/linter_bad_config/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a.app.src rename to test/test_projects/linter_bad_config/app_a/src/app_a.app.src diff --git a/test_projects/linter_bad_config/app_a/src/app_a.erl b/test/test_projects/linter_bad_config/app_a/src/app_a.erl similarity index 100% rename from test_projects/linter_bad_config/app_a/src/app_a.erl rename to test/test_projects/linter_bad_config/app_a/src/app_a.erl diff --git a/test/test_projects/linter_bad_config/linter/.elp.toml b/test/test_projects/linter_bad_config/linter/.elp.toml new file mode 100644 index 0000000000..4820625b2d --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter_bad_config/linter/.elp_lint.toml b/test/test_projects/linter_bad_config/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/linter/.elp_lint.toml rename to test/test_projects/linter_bad_config/linter/.elp_lint.toml diff --git a/test_projects/linter_bad_config/linter/.gitignore b/test/test_projects/linter_bad_config/linter/.gitignore similarity index 100% rename from test_projects/linter_bad_config/linter/.gitignore rename to test/test_projects/linter_bad_config/linter/.gitignore diff --git a/test/test_projects/linter_bad_config/linter/BUCK b/test/test_projects/linter_bad_config/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl diff --git a/test_projects/standard/app_a/src/app_a.app.src b/test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src similarity index 100% rename from test_projects/standard/app_a/src/app_a.app.src rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a.erl b/test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a.erl rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl b/test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test_projects/standard/app_b/src/app_b.app.src b/test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src similarity index 100% rename from test_projects/standard/app_b/src/app_b.app.src rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b.erl b/test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b.erl rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl b/test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl diff --git a/test_projects/linter_bad_config/linter/rebar.config b/test/test_projects/linter_bad_config/linter/rebar.config similarity index 100% rename from test_projects/linter_bad_config/linter/rebar.config rename to test/test_projects/linter_bad_config/linter/rebar.config diff --git a/test_projects/linter_bad_config/rebar.config b/test/test_projects/linter_bad_config/rebar.config similarity index 100% rename from test_projects/linter_bad_config/rebar.config rename to test/test_projects/linter_bad_config/rebar.config diff --git a/test/test_projects/linter_config/.elp.toml b/test/test_projects/linter_config/.elp.toml new file mode 100644 index 0000000000..c490cc155e --- /dev/null +++ b/test/test_projects/linter_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter_config/..." ] +source_root = "linter_config" diff --git a/test/test_projects/linter_config/.elp_lint.toml b/test/test_projects/linter_config/.elp_lint.toml new file mode 100644 index 0000000000..47886dd507 --- /dev/null +++ b/test/test_projects/linter_config/.elp_lint.toml @@ -0,0 +1,5 @@ +[linters.L1260] # Unused record, produced by the Erlang Service +exclude_apps = ["app_b", "app_c"] + +[linters.unused_macro] +exclude_apps = ["app_c"] diff --git a/test/test_projects/linter_config/BUCK b/test/test_projects/linter_config/BUCK new file mode 100644 index 0000000000..c1101daea2 --- /dev/null +++ b/test/test_projects/linter_config/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_c", + srcs = glob(["app_c/src/*.erl"]), + app_src = "app_c/src/app_c.app.src", + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/linter_config/app_a/src/app_a.app.src b/test/test_projects/linter_config/app_a/src/app_a.app.src new file mode 100644 index 0000000000..1381435a46 --- /dev/null +++ b/test/test_projects/linter_config/app_a/src/app_a.app.src @@ -0,0 +1,3 @@ +{application, app_a, + [{description, "example app A"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_a/src/app_a.erl b/test/test_projects/linter_config/app_a/src/app_a.erl new file mode 100644 index 0000000000..c374c0b518 --- /dev/null +++ b/test/test_projects/linter_config/app_a/src/app_a.erl @@ -0,0 +1,4 @@ +-module(app_a). + +-define(MACRO_A, a). +-record(rec_a, {a :: atom()}). diff --git a/test/test_projects/linter_config/app_b/src/app_b.app.src b/test/test_projects/linter_config/app_b/src/app_b.app.src new file mode 100644 index 0000000000..4112b4129f --- /dev/null +++ b/test/test_projects/linter_config/app_b/src/app_b.app.src @@ -0,0 +1,3 @@ +{application, app_b, + [{description, "example app B"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_b/src/app_b.erl b/test/test_projects/linter_config/app_b/src/app_b.erl new file mode 100644 index 0000000000..ca7ecf985b --- /dev/null +++ b/test/test_projects/linter_config/app_b/src/app_b.erl @@ -0,0 +1,4 @@ +-module(app_b). + +-define(MACRO_B, b). +-record(rec_b, {b :: atom()}). diff --git a/test/test_projects/linter_config/app_c/src/app_c.app.src b/test/test_projects/linter_config/app_c/src/app_c.app.src new file mode 100644 index 0000000000..c278bbce23 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.app.src @@ -0,0 +1,3 @@ +{application, app_c, + [{description, "example app C"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_c/src/app_c.erl b/test/test_projects/linter_config/app_c/src/app_c.erl new file mode 100644 index 0000000000..57573554b6 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.erl @@ -0,0 +1,4 @@ +-module(app_c). + +-define(MACRO_C, c). +-record(rec_c, {c :: atom()}). diff --git a/test/test_projects/linter_config/rebar.config b/test/test_projects/linter_config/rebar.config new file mode 100644 index 0000000000..d508bd24de --- /dev/null +++ b/test/test_projects/linter_config/rebar.config @@ -0,0 +1,9 @@ +{checkouts_dir, ["."]}. +{project_app_dirs, [ + "app_a", + "app_b", + "app_c" +]}. + +{erl_opts, [debug_info]}. +{deps, []}. diff --git a/test/test_projects/parse_error/.elp.toml b/test/test_projects/parse_error/.elp.toml new file mode 100644 index 0000000000..1a06a42f0d --- /dev/null +++ b/test/test_projects/parse_error/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//parse_error/..." ] +source_root = "parse_error" + +[eqwalizer] +enable_all = false diff --git a/test_projects/parse_error/.gitignore b/test/test_projects/parse_error/.gitignore similarity index 100% rename from test_projects/parse_error/.gitignore rename to test/test_projects/parse_error/.gitignore diff --git a/test_projects/parse_error/.rebar.root b/test/test_projects/parse_error/.rebar.root similarity index 100% rename from test_projects/parse_error/.rebar.root rename to test/test_projects/parse_error/.rebar.root diff --git a/test/test_projects/parse_error/BUCK b/test/test_projects/parse_error/BUCK new file mode 100644 index 0000000000..ed65c267d0 --- /dev/null +++ b/test/test_projects/parse_error/BUCK @@ -0,0 +1,9 @@ +oncall("vscode_erlang") + +erlang_application( + name = "elp_test_parse_a", + srcs = glob(["parse_error_a/src/*.erl"]), + app_src = "parse_error_a/src/parse_error_a.app.src", + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.app.src rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl diff --git a/test_projects/parse_error/rebar.config b/test/test_projects/parse_error/rebar.config similarity index 100% rename from test_projects/parse_error/rebar.config rename to test/test_projects/parse_error/rebar.config diff --git a/test/test_projects/standard/.elp.toml b/test/test_projects/standard/.elp.toml new file mode 100644 index 0000000000..f5e2ac1943 --- /dev/null +++ b/test/test_projects/standard/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//standard/..." ] +source_root = "standard" diff --git a/test_projects/standard/.gitignore b/test/test_projects/standard/.gitignore similarity index 100% rename from test_projects/standard/.gitignore rename to test/test_projects/standard/.gitignore diff --git a/test_projects/standard/.rebar.root b/test/test_projects/standard/.rebar.root similarity index 100% rename from test_projects/standard/.rebar.root rename to test/test_projects/standard/.rebar.root diff --git a/test/test_projects/standard/BUCK b/test/test_projects/standard/BUCK new file mode 100644 index 0000000000..3f1e9ee947 --- /dev/null +++ b/test/test_projects/standard/BUCK @@ -0,0 +1,49 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_standard_app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + app_src = "eqwalizer/src/eqwalizer.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test/test_projects/standard/app_a/.eqwalizer b/test/test_projects/standard/app_a/.eqwalizer new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test_projects/standard/app_a/extra/app_a.erl b/test/test_projects/standard/app_a/extra/app_a.erl similarity index 100% rename from test_projects/standard/app_a/extra/app_a.erl rename to test/test_projects/standard/app_a/extra/app_a.erl diff --git a/test_projects/standard/app_a/include/app_a.hrl b/test/test_projects/standard/app_a/include/app_a.hrl similarity index 100% rename from test_projects/standard/app_a/include/app_a.hrl rename to test/test_projects/standard/app_a/include/app_a.hrl diff --git a/test/test_projects/standard/app_a/src/app_a.app.src b/test/test_projects/standard/app_a/src/app_a.app.src new file mode 100644 index 0000000000..1381435a46 --- /dev/null +++ b/test/test_projects/standard/app_a/src/app_a.app.src @@ -0,0 +1,3 @@ +{application, app_a, + [{description, "example app A"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test_projects/standard/app_a/src/app_a.erl b/test/test_projects/standard/app_a/src/app_a.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a.erl rename to test/test_projects/standard/app_a/src/app_a.erl diff --git a/test_projects/standard/app_a/src/app_a_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_fixme.erl b/test/test_projects/standard/app_a/src/app_a_fixme.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_fixme.erl rename to test/test_projects/standard/app_a/src/app_a_fixme.erl diff --git a/test_projects/standard/app_a/src/app_a_ignored.erl b/test/test_projects/standard/app_a/src/app_a_ignored.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_ignored.erl rename to test/test_projects/standard/app_a/src/app_a_ignored.erl diff --git a/test_projects/standard/app_a/src/app_a_lists.erl b/test/test_projects/standard/app_a/src/app_a_lists.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_lists.erl rename to test/test_projects/standard/app_a/src/app_a_lists.erl diff --git a/test_projects/standard/app_a/src/app_a_mod2.erl b/test/test_projects/standard/app_a/src/app_a_mod2.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_mod2.erl rename to test/test_projects/standard/app_a/src/app_a_mod2.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors.erl b/test/test_projects/standard/app_a/src/app_a_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_a_SUITE.erl b/test/test_projects/standard/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_SUITE.erl rename to test/test_projects/standard/app_a/test/app_a_SUITE.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl diff --git a/test/test_projects/standard/app_b/src/app_b.app.src b/test/test_projects/standard/app_b/src/app_b.app.src new file mode 100644 index 0000000000..4112b4129f --- /dev/null +++ b/test/test_projects/standard/app_b/src/app_b.app.src @@ -0,0 +1,3 @@ +{application, app_b, + [{description, "example app B"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test_projects/standard/app_b/src/app_b.erl b/test/test_projects/standard/app_b/src/app_b.erl similarity index 100% rename from test_projects/standard/app_b/src/app_b.erl rename to test/test_projects/standard/app_b/src/app_b.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.app.src b/test/test_projects/standard/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/standard/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/standard/erlang_ls.config b/test/test_projects/standard/erlang_ls.config similarity index 100% rename from test_projects/standard/erlang_ls.config rename to test/test_projects/standard/erlang_ls.config diff --git a/test_projects/standard/rebar.config b/test/test_projects/standard/rebar.config similarity index 100% rename from test_projects/standard/rebar.config rename to test/test_projects/standard/rebar.config diff --git a/test/test_projects/toolchains/BUCK b/test/test_projects/toolchains/BUCK new file mode 100644 index 0000000000..770ac61f67 --- /dev/null +++ b/test/test_projects/toolchains/BUCK @@ -0,0 +1,5 @@ +load("@prelude//toolchains:demo.bzl", "system_demo_toolchains") + +oncall("vscode_erlang") + +system_demo_toolchains() diff --git a/test/test_projects/xref/.elp.toml b/test/test_projects/xref/.elp.toml new file mode 100644 index 0000000000..87c2f17334 --- /dev/null +++ b/test/test_projects/xref/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//xref/..." ] +source_root = "xref" diff --git a/test/test_projects/xref/BUCK b/test/test_projects/xref/BUCK new file mode 100644 index 0000000000..bf44968f02 --- /dev/null +++ b/test/test_projects/xref/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + applications = [ + ":app_b", + ], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_c", + srcs = glob(["app_c/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/xref/app_a/src/unavailable_type.erl b/test/test_projects/xref/app_a/src/unavailable_type.erl new file mode 100644 index 0000000000..ddf4fc00d2 --- /dev/null +++ b/test/test_projects/xref/app_a/src/unavailable_type.erl @@ -0,0 +1,12 @@ +-module(unavailable_type). +-export([test_types/0]). + +-record(my_record, { + field_a :: app_b:my_type_b(), + field_b :: app_c:my_type_c(), + field_c :: binary() +}). + +-spec test_types() -> {app_b:my_type_b(), app_c:my_type_c(), #my_record{}}. +test_types() -> + {"hello", 42, #my_record{field_a = "", field_b = 42, field_c = <<>>}}. diff --git a/test/test_projects/xref/app_b/src/app_b.erl b/test/test_projects/xref/app_b/src/app_b.erl new file mode 100644 index 0000000000..d56609b179 --- /dev/null +++ b/test/test_projects/xref/app_b/src/app_b.erl @@ -0,0 +1,8 @@ +-module(app_b). +-export([my_function/0]). + +-type my_type_b() :: string(). +-export_type([my_type_b/0]). + +-spec my_function() -> my_type_b(). +my_function() -> "hello from b". diff --git a/test/test_projects/xref/app_c/src/app_c.erl b/test/test_projects/xref/app_c/src/app_c.erl new file mode 100644 index 0000000000..040f77f26f --- /dev/null +++ b/test/test_projects/xref/app_c/src/app_c.erl @@ -0,0 +1,8 @@ +-module(app_c). +-export([my_function/0]). + +-type my_type_c() :: integer(). +-export_type([my_type_c/0]). + +-spec my_function() -> my_type_c(). +my_function() -> 42. diff --git a/test/test_projects/xref/elp_lint_unavailable_type.toml b/test/test_projects/xref/elp_lint_unavailable_type.toml new file mode 100644 index 0000000000..629a5228bf --- /dev/null +++ b/test/test_projects/xref/elp_lint_unavailable_type.toml @@ -0,0 +1,2 @@ +[linters.unavailable_type] +enabled = true diff --git a/test_projects/buck_bad_config/.elp.toml b/test_projects/buck_bad_config/.elp.toml deleted file mode 100644 index a891a3dfc5..0000000000 --- a/test_projects/buck_bad_config/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/buck_bad_config/..." ] -source_root = "whatsapp/elp/test_projects/buck_bad_config" - -[eqwalizer] -enable_all = false diff --git a/test_projects/buck_tests/.elp.toml b/test_projects/buck_tests/.elp.toml deleted file mode 100644 index acb0799975..0000000000 --- a/test_projects/buck_tests/.elp.toml +++ /dev/null @@ -1,9 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests/..." ] -excluded_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests:test_elp_ignored" ] -source_root = "whatsapp/elp/test_projects/buck_tests" - -[eqwalizer] -enable_all = false diff --git a/test_projects/buck_tests_2/.elp.toml b/test_projects/buck_tests_2/.elp.toml deleted file mode 100644 index 4ea681809f..0000000000 --- a/test_projects/buck_tests_2/.elp.toml +++ /dev/null @@ -1,9 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests_2/util/app_a/..." ] -excluded_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests_2:test_elp_ignored" ] -source_root = "whatsapp/elp/test_projects/buck_tests_2" - -[eqwalizer] -enable_all = false diff --git a/test_projects/diagnostics/.elp.toml b/test_projects/diagnostics/.elp.toml deleted file mode 100644 index e5ee70c992..0000000000 --- a/test_projects/diagnostics/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/diagnostics/..." ] -source_root = "whatsapp/elp/test_projects/diagnostics" - -[eqwalizer] -enable_all = false diff --git a/test_projects/eqwalizer_callers/.elp.toml b/test_projects/eqwalizer_callers/.elp.toml deleted file mode 100644 index f4b7f6f6ac..0000000000 --- a/test_projects/eqwalizer_callers/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_callers/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_callers" diff --git a/test_projects/eqwalizer_tests/.elp.toml b/test_projects/eqwalizer_tests/.elp.toml deleted file mode 100644 index ed5c60502f..0000000000 --- a/test_projects/eqwalizer_tests/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_tests/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_tests" - -[eqwalizer] -enable_all = true diff --git a/test_projects/in_place_tests/.elp.toml b/test_projects/in_place_tests/.elp.toml deleted file mode 100644 index b6c251f03c..0000000000 --- a/test_projects/in_place_tests/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/in_place_tests/..." ] -source_root = "whatsapp/elp/test_projects/in_place_tests" diff --git a/test_projects/include_lib_dependency_test/.elp.toml b/test_projects/include_lib_dependency_test/.elp.toml deleted file mode 100644 index 65e106a9e2..0000000000 --- a/test_projects/include_lib_dependency_test/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/include_lib_dependency_test/..." ] -source_root = "whatsapp/elp/test_projects/include_lib_dependency_test" diff --git a/test_projects/linter/.elp.toml b/test_projects/linter/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/linter_bad_config/.elp.toml b/test_projects/linter_bad_config/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter_bad_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/linter_bad_config/linter/.elp.toml b/test_projects/linter_bad_config/linter/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter_bad_config/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/parse_error/.elp.toml b/test_projects/parse_error/.elp.toml deleted file mode 100644 index bf2c1ad580..0000000000 --- a/test_projects/parse_error/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/parse_error/..." ] -source_root = "whatsapp/elp/test_projects/parse_error" - -[eqwalizer] -enable_all = false diff --git a/test_projects/standard/.elp.toml b/test_projects/standard/.elp.toml deleted file mode 100644 index 09ff5a4d5a..0000000000 --- a/test_projects/standard/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/standard/..." ] -source_root = "whatsapp/elp/test_projects/standard" diff --git a/website/docs/erlang-error-index/w/W0045.md b/website/docs/erlang-error-index/w/W0045.md index 13c908d8a0..c68f83ef4d 100644 --- a/website/docs/erlang-error-index/w/W0045.md +++ b/website/docs/erlang-error-index/w/W0045.md @@ -8,7 +8,7 @@ sidebar_position: 45 ```erlang -module(my_module). - %% ^^^^^^^^^ warning: Duplicate module name + %% ^^^^^^^^^ warning: A module with this name exists elsewhere ``` ## Explanation diff --git a/website/docs/erlang-error-index/w/W0048.md b/website/docs/erlang-error-index/w/W0048.md index 42d21cec9c..a6e1631148 100644 --- a/website/docs/erlang-error-index/w/W0048.md +++ b/website/docs/erlang-error-index/w/W0048.md @@ -9,7 +9,7 @@ sidebar_position: 48 ```erlang -module(main). -dialyzer({nowarn_function, foo/0}). -%% ^^^^^^^^^ 💡 warning: Avoid -dialyzer attribute. +%% ^^^^^^^^^ 💡 warning: Avoid using the -dialyzer attribute. ``` ## Explanation diff --git a/website/docs/erlang-error-index/w/W0050.md b/website/docs/erlang-error-index/w/W0050.md index a0d43d7171..8c8210dcca 100644 --- a/website/docs/erlang-error-index/w/W0050.md +++ b/website/docs/erlang-error-index/w/W0050.md @@ -20,40 +20,26 @@ size(Input) -> ## Explanation -The [`size/1`](https://www.erlang.org/doc/apps/erts/erlang#size/1) BIF returns -the size for both tuples and binaries. +The [`size/1`](https://www.erlang.org/doc/apps/erts/erlang#size/1) BIF can be +used to return the size tuples, binaries and bitstrings. -The BIF is not optimized by the -[JIT](https://www.erlang.org/doc/apps/erts/beamasm.html) though, and its use can -result in worse types for -[dialyzer](https://www.erlang.org/doc/apps/dialyzer/dialyzer.html) and -[eqWAlizer](https://github.com/WhatsApp/eqwalizer). +However, this BIF is not optimized by the +[JIT](https://www.erlang.org/doc/apps/erts/beamasm.html); it may report +inaccurate values for bitstrings, and its use can result in worse types for +[dialyzer](https://www.erlang.org/doc/apps/dialyzer/dialyzer.html). -When one knows that the value being tested must be a tuple, `tuple_size/1` -should always be preferred. +What to do instead: -When one knows that the value being tested must be a binary, `byte_size/1` -should be preferred. However, `byte_size/1` also accepts a bitstring (rounding -up size to a whole number of bytes), so one must make sure that the call to -`byte_size/1` is preceded by a call to `is_binary/1` to ensure that bitstrings -are rejected. Note that the compiler removes redundant calls to `is_binary/1`, -so if one is not sure whether previous code had made sure that the argument is a -binary, it does not harm to add an `is_binary/1` test immediately before the -call to `byte_size/1`. +- For tuples, use `tuple_size/1`. -For the above use case, a better approach would be: +- For binaries, use `byte_size/1`. + +- For bitstrings, use `byte_size/1`, but please notice that while `byte_size/1` + rounds _up_ to the nearest byte, `size/1` would round _down_: ```erlang --module(main). --export([size/1]). - --type input() :: tuple() | binary(). - --spec size(input()) -> non_neg_integer(). -size(Input) when is_tuple(Input) -> - erlang:tuple_size(Input); -size(Input) when is_binary(Input) -> - erlang:byte_size(Input). +4 = byte_size(<<0:25>>). +3 = size(<<0:25>>). ``` For more information, see the Erlang diff --git a/website/docs/erlang-error-index/w/W0056.md b/website/docs/erlang-error-index/w/W0056.md new file mode 100644 index 0000000000..14848f5898 --- /dev/null +++ b/website/docs/erlang-error-index/w/W0056.md @@ -0,0 +1,45 @@ +--- +sidebar_position: 56 +--- + +# W0056 - Use `lists:reverse/2` instead of `lists:reverse/1 ++ Tail` + +## Warning + +```erlang +reverse_and_append(List, Tail) -> + lists:reverse(List) ++ Tail. +%% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0056: Use `lists:reverse/2` instead of `lists:reverse/1 ++ Tail` for better performance. +``` + +## Explanation + +The warning indicates that you are using the pattern `lists:reverse(List) ++ Tail`, which is inefficient. This pattern first reverses the list, creating an intermediate reversed list, and then appends the tail to it using the `++` operator. + +The Erlang standard library provides a more efficient function `lists:reverse/2` that performs both operations in a single pass without creating the intermediate reversed list. + +### Why This Matters + +- **Performance**: Using `lists:reverse/1` followed by `++` creates an unnecessary intermediate data structure +- **Memory**: The intermediate reversed list consumes additional memory that is immediately discarded +- **Idiomatic Erlang**: `lists:reverse/2` is the idiomatic way to reverse and prepend in Erlang + +### Fix + +Replace the pattern with `lists:reverse/2`: + +```erlang +reverse_and_append(List, Tail) -> + lists:reverse(List, Tail). +``` + +### How lists:reverse/2 Works + +The second argument to `lists:reverse/2` acts as an accumulator that gets prepended to the reversed list: + +```erlang +lists:reverse([1, 2, 3], [4, 5, 6]). +% Returns: [3, 2, 1, 4, 5, 6] +``` + +This is equivalent to `lists:reverse([1, 2, 3]) ++ [4, 5, 6]` but significantly more efficient. diff --git a/website/docs/erlang-error-index/w/W0057.md b/website/docs/erlang-error-index/w/W0057.md new file mode 100644 index 0000000000..990c4648b6 --- /dev/null +++ b/website/docs/erlang-error-index/w/W0057.md @@ -0,0 +1,106 @@ +--- +sidebar_position: 57 +--- + +# W0057 - Undefined macro + +## Error + +```erlang +-module(example). + +foo() -> + ?UNDEFINED_MACRO. +%% ^^^^^^^^^^^^^^^^ error: W0057: undefined macro 'UNDEFINED_MACRO' + +bar(X) -> + ?UNDEFINED_WITH_ARGS(X, 42). +%% ^^^^^^^^^^^^^^^^^^^^ error: W0057: undefined macro 'UNDEFINED_WITH_ARGS/2' +``` + +This diagnostic is triggered when a macro usage cannot be resolved in ELP native +diagnostic processing. The macro is referenced in the code but has not been defined +in the current file or any included header files. + +**Important:** This diagnostic corresponds to Erlang service diagnostics +**E1507** (for macros without arguments) and **E1508** (for macros with +arguments). W0057 only appears when the corresponding Erlang service diagnostic +does not appear. If you see W0057 in a Buck project, it is likely a sign that +the project is not defined properly in your TARGETS/BUCK file. + +## Common Causes + +1. **Missing macro definition**: The macro has not been defined with + `-define()`. +2. **Missing include file**: The macro is defined in a header file that has not + been included. +3. **Typo in macro name**: The macro name is misspelled. +4. **Conditional compilation**: The macro is defined within a conditional block + (e.g., `-ifdef()`) that is not active. + +## Fix + +### Define the macro + +If the macro is not defined anywhere, add a `-define()` directive: + +```erlang +-module(example). + +-define(UNDEFINED_MACRO, 42). +-define(UNDEFINED_WITH_ARGS(X, Y), X + Y). + +foo() -> + ?UNDEFINED_MACRO. + +bar(X) -> + ?UNDEFINED_WITH_ARGS(X, 42). +``` + +### Include the header file + +If the macro is defined in a header file, add the appropriate `-include()` or +`-include_lib()` directive: + +```erlang +-module(example). + +-include("macros.hrl"). + +foo() -> + ?UNDEFINED_MACRO. +``` + +### Fix typos + +If the macro name is misspelled, correct it to match the definition: + +```erlang +-module(example). + +-define(DEFINED_MACRO, 42). + +foo() -> + ?DEFINED_MACRO. % Fixed typo +``` + +## Relationship to Erlang Service Diagnostics + +W0057 is filtered out when the Erlang service reports the corresponding +diagnostics: + +- **E1507**: undefined macro (without arguments) +- **E1508**: undefined macro (with arguments) + +When both diagnostics detect the same issue, only the Erlang service diagnostic +will be shown to avoid duplication. + +## Buck Project Configuration + +If you see W0057 in a Buck project, this typically indicates that your TARGETS +file is not properly configured. The Erlang service relies on Buck to understand +the project structure, dependencies, and include paths. Check that: + +- All application dependencies are declared in the `deps` attribute +- Include paths are properly configured +- The macro definition is in an accessible dependency diff --git a/website/docs/erlang-error-index/w/W0058.md b/website/docs/erlang-error-index/w/W0058.md new file mode 100644 index 0000000000..acad4c2d79 --- /dev/null +++ b/website/docs/erlang-error-index/w/W0058.md @@ -0,0 +1,108 @@ +--- +sidebar_position: 58 +--- + +# W0058 - Can't find include file + +## Error + +```erlang +-module(example). + +-include("missing_header.hrl"). +%% error: W0058: can't find include file "missing_header.hrl" + +-include_lib("missing_app/include/header.hrl"). +%% error: W0058: can't find include lib "missing_app/include/header.hrl" +``` + +This diagnostic is triggered when an include file referenced by `-include()` or +`-include_lib()` cannot be resolved by the ELP native diagnostic processing. +The file path cannot be found in the search paths available to ELP. + +**Important:** This diagnostic corresponds to Erlang service diagnostic +**E1516** (can't find include file/lib). W0058 only appears when the +corresponding Erlang service diagnostic does not appear. If you see W0058 in a +Buck project, it is likely a sign that the project is not defined properly in +your TARGETS file. + +## Common Causes + +1. **Missing file**: The include file does not exist at the specified path. +2. **Incorrect path**: The path to the include file is incorrect or misspelled. +3. **Missing application dependency**: For `-include_lib()`, the referenced + application is not listed as a dependency. +4. **Incorrect include path configuration**: The directory containing the header + file is not in the configured include paths. + +## Fix + +### Verify the file exists + +Make sure the include file exists at the expected location. + +### Fix the path + +Correct the path to match the actual file location: + +```erlang +-module(example). + +% Correct path to the include file +-include("correct_header.hrl"). + +% Correct application name and path +-include_lib("correct_app/include/header.hrl"). +``` + +### Add missing dependency + +For `-include_lib()`, ensure the application is listed as a dependency in your +build configuration: + +**rebar.config:** + +```erlang +{deps, [ + missing_app +]}. +``` + +**Buck TARGETS file:** + +```python +erlang_app( + name = "my_app", + deps = [ + "//path/to:missing_app", + ], +) +``` + +### Configure include paths + +Ensure the directory containing the header file is in your project's include +paths configuration. + +## Relationship to Erlang Service Diagnostics + +W0058 is filtered out when the Erlang service reports the corresponding +diagnostic: + +- **E1516**: can't find include file/lib + +When both diagnostics detect the same issue, only the Erlang service diagnostic +will be shown to avoid duplication. + +## Buck Project Configuration + +If you see W0058 in a Buck project, this typically indicates that your TARGETS +file is not properly configured. The Erlang service relies on Buck to understand +the project structure, dependencies, and include paths. Check that: + +- All application dependencies are declared in the `deps` attribute +- For `-include_lib()` directives, the referenced application is included as a + dependency +- Include paths are properly configured with the `include` or `include_lib` + attributes +- The header file is defined in a dependency of the buck target holding the current file diff --git a/website/docs/erlang-error-index/w/W0059.md b/website/docs/erlang-error-index/w/W0059.md new file mode 100644 index 0000000000..fd2e990cb3 --- /dev/null +++ b/website/docs/erlang-error-index/w/W0059.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 59 +--- + +# W0059: Unavailable Type + +## Error + +```erlang +-module(main). +-spec foo() -> dep:nonexistent_type(). +%% ^^^^^^^^^^^^^^^^^^^^^^ warning: W0059: Type 'dep:nonexistent_type/0' is not available through dependencies. +foo() -> ok. +``` + +## Explanation + +This diagnostic warns you when your code references a type that either: + +1. Does not exist (is undefined) +2. Is not accessible because it's from a module outside your application's dependencies +3. Exists but is not exported from the defining module + + +### What This Diagnostic Checks + +The diagnostic examines: + +- Type specifications (`-spec` attributes) +- Type definitions (`-type` and `-opaque` attributes) +- Callback specifications (`-callback` attributes) + +## How to Fix + +* Check if the type actually exists +* Verify the name and arity of the type +* Add the application that defines the type to your application's dependencies or move the type definition to an application that is already a dependency diff --git a/website/docs/erlang-error-index/w/W0060.md b/website/docs/erlang-error-index/w/W0060.md new file mode 100644 index 0000000000..91b972dacf --- /dev/null +++ b/website/docs/erlang-error-index/w/W0060.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 60 +--- + +# W0060 - Bound Variable in LHS + +## Error + +```erlang +handle_request(Message) -> + Message = next_action(). +%% ^^^^^^^ 💡 warning: W0060: Match on a bound variable +``` + +## Explanation + +This diagnostic flags cases where a variable that is already bound appears on the left-hand side (LHS) of a match expression. This can be problematic if the binding is not intentional and can lead to subtle bugs. + +Consider the following code snippet: + +```erlang showLineNumbers +foo() -> + AA = foo(), + AA = bar(). +``` + +The pattern on line `3` will only match if and only if the result of the call to `bar/0` is the same as the call to `foo/0`. This behaviour could be intentional or not. If not, it can easily lead to bugs. + +An alternative, more explicit, way to express that behaviour - when intentional - could be: + +```erlang showLineNumbers +foo() -> + AA = foo(), + BB = bar(), + AA = BB. +``` + +Or using an assertion: + +```erlang showLineNumbers +foo() -> + AA = foo(), + ?assertEqual(AA, bar()). +``` + +## Semantic highlighting + +Note that we also have semantic highlighting of the more general case, where a bound variable appears in any pattern. diff --git a/website/docs/get-started/configure-project/elp-toml.md b/website/docs/get-started/configure-project/elp-toml.md index 931d207989..90a9eb5f1e 100644 --- a/website/docs/get-started/configure-project/elp-toml.md +++ b/website/docs/get-started/configure-project/elp-toml.md @@ -111,9 +111,21 @@ Configure the interaction between ELP and the [Buck2](https://buck2.build/) build tool. See [this presentation](https://youtu.be/4ALgsBqNBhQ) for details about Erlang support for Buck2. -| Key | Type | Description | -| ------- | ------- | -------------------------------- | -| enabled | Boolean | Discover the project using Buck2 | +| Key | Type | Description | Default | +| ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------- | +| enabled | Boolean | Discover the project using Buck2 | | +| test_application_labels | Array of Strings | Buck2 labels that identify test application targets. Targets with any of these labels will be treated as test utilities. | `["test_application"]` | + +The `test_application_labels` setting allows you to customize which Buck2 labels +indicate test applications. This is useful for: + +Example usage: + +```toml +[buck] +enabled = true +test_application_labels = ["test_application", "integration_test", "e2e_test"] +``` :::warning diff --git a/website/docs/get-started/editors/emacs.md b/website/docs/get-started/editors/emacs.md index 5111167df6..fab357f4a4 100644 --- a/website/docs/get-started/editors/emacs.md +++ b/website/docs/get-started/editors/emacs.md @@ -4,17 +4,22 @@ sidebar_position: 2 # Emacs -The ELP project can be used as a [language server](https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/) in the Emacs text editor via the [eglot](https://github.com/joaotavora/eglot) or [lsp-mode](https://emacs-lsp.github.io/lsp-mode/) LSP clients. +The ELP project can be used as a +[language server](https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/) +in the Emacs text editor via the [eglot](https://github.com/joaotavora/eglot) or +[lsp-mode](https://emacs-lsp.github.io/lsp-mode/) LSP clients. ## Eglot -Eglot is part of Emacs core since Emacs 29. -For earlier versions it can be installed with the `eglot` package. +Eglot is part of Emacs core since Emacs 29. For earlier versions it can be +installed with the `eglot` package. ### Configuration ```elisp (use-package eglot + :ensure t + :hook ((erlang-mode . eglot-ensure)) :config ;; Remove default LSP server @@ -23,14 +28,81 @@ For earlier versions it can be installed with the `eglot` package. ;; Enable ELP (add-to-list 'eglot-server-programs - '(erlang-mode . ("elp" "server")) + '(erlang-mode . ("elp" "server")))) ``` -Refer to the [manual](https://elpa.gnu.org/devel/doc/eglot.html#Customization-Variables) for additional configuration options. +Refer to the +[manual](https://elpa.gnu.org/devel/doc/eglot.html#Customization-Variables) for +additional configuration options. + +### Semantic tokens + +Semantic token support has been added to eglot, but is not yet in the released +version. But it is possible to install the updated version of eglot. + +To do so, add + +```elisp +(add-to-list 'package-archives '("gnu-devel" . "https://elpa.gnu.org/devel/")) +``` + +to your `init.el`, then run `M-x eglot-upgrade-eglot` + +Once upgraded, add the following to the `(use-package` entry for `eglot` + +```elisp +(setq-default eglot-workspace-configuration + ;; Run `elp config` to see that options can be used here + ;; Use `eglot-show-workspace-configuration` to see what is sent + '(:elp (:highlightDynamic (:enable t) + :typesOnHover (:enable t) )) + + eglot-semantic-token-modifiers + '("bound" "exported_function" "exported_type" "deprecated_function" "type_dynamic")) + + ;; Each face name arises as a template from the modifiers as + ;; "eglot-semantic-%s-face" + (defface eglot-semantic-bound-face + '((t :underline t)) + "The face modification to use for bound variables in patterns." + :group 'eglot-semantic-semantic-tokens) + + (defface eglot-semantic-exported_function-face + '((t :underline t)) + "The face modification to use for exported functions." + :group 'eglot-semantic-semantic-tokens) + + (defface eglot-semantic-exported_type-face + '((t :underline t)) + "The face modification to use for exported types." + :group 'eglot-semantic-semantic-tokens) + + (defface eglot-semantic-deprecated_function-face + '((t :strike-through t)) + "The face modification to use for deprecated functions." + :group 'eglot-semantic-semantic-tokens) + + (defface eglot-semantic-type_dynamic-face + '((t (:weight bold))) + "The face modification to use for dynamic types." + :group 'eglot-semantic-semantic-tokens) + + ;; Bare eglot makes the refresh a no-op. Provide our own version for + ;; when Eqwalizer gets its results. + (cl-defmethod eglot-handle-request + (server (_method (eql workspace/semanticTokens/refresh)) &rest args) + "Handle workspace/semanticTokens/refresh by refreshing font-lock." + (dolist (buffer (eglot--managed-buffers server)) + (eglot--when-live-buffer buffer + (eglot--widening (font-lock-flush))))) +``` ## lsp-mode -Install the `lsp-mode` package, which is a generic Emacs client for LSP servers. You can follow [these instructions](https://emacs-lsp.github.io/lsp-mode/page/installation/) to install it. +Install the `lsp-mode` package, which is a generic Emacs client for LSP servers. +You can follow +[these instructions](https://emacs-lsp.github.io/lsp-mode/page/installation/) to +install it. ### Configuration @@ -52,13 +124,21 @@ Add the following to your emacs `.emacs` file or equivalent. ) ``` -For a list of available configuration option, please refer to [this page](https://emacs-lsp.github.io/lsp-mode/page/lsp-erlang-elp/) and to the [`lsp-mode` settings documentation](https://emacs-lsp.github.io/lsp-mode/page/settings/mode/). +For a list of available configuration option, please refer to +[this page](https://emacs-lsp.github.io/lsp-mode/page/lsp-erlang-elp/) and to +the +[`lsp-mode` settings documentation](https://emacs-lsp.github.io/lsp-mode/page/settings/mode/). + +There is also a +[`.dotemacs`](https://github.com/WhatsApp/erlang-language-platform/blob/main/editors/emacs/dotemacs.el) +file in the ELP repository that you can use as a reference. ## Troubleshooting #### The following servers support current file but do not have automatic installation -Ensure that the `elp` executable is available in your `PATH` via Emacs. A workaround is: +Ensure that the `elp` executable is available in your `PATH` via Emacs. A +workaround is: ```elisp ;; Ensure your Emacs environment looks like your user's shell one diff --git a/website/docs/get-started/install.md b/website/docs/get-started/install.md index 4c8adb7bb1..74cc3b9159 100644 --- a/website/docs/get-started/install.md +++ b/website/docs/get-started/install.md @@ -4,9 +4,26 @@ sidebar_position: 2 # Install ELP -The easiest way to install to ELP is [from binary](#from-binary). It is also +The easiest way to install ELP on Mac is [using Homebrew](#using-homebrew-mac). +For other platforms, you can install [from binary](#from-binary). It is also possible to compile it [from source](#from-source). +## Using Homebrew (Mac) + +Mac users can install ELP using the dedicated Homebrew formula: + +``` +brew install erlang-language-platform +``` + +This will install the latest version of ELP and make it available in your PATH. + +For more information about the Homebrew formula, visit: +https://formulae.brew.sh/formula/erlang-language-platform + +Follow [these steps](cli.md#verify-elp-is-correctly-installed) to verify ELP is +correctly installed. + ## From Binary Visit our diff --git a/website/package.json b/website/package.json index 4053a7477f..49a34973ca 100644 --- a/website/package.json +++ b/website/package.json @@ -16,12 +16,12 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.6.3", - "@docusaurus/preset-classic": "3.6.3", + "@docusaurus/core": "3.9.2", + "@docusaurus/preset-classic": "3.9.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", - "prism-react-renderer": "^2.3.0", "docusaurus-plugin-internaldocs-fb": "^1.18.4", + "prism-react-renderer": "^2.3.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, @@ -62,6 +62,11 @@ "brace-expansion": ">=1.1.12", "webpack-dev-server": ">=5.2.1", "on-headers": ">=1.1.0", - "mermaid": ">=11.10.0" + "mermaid": ">=11.10.0", + "glob": ">=10.5.0", + "node-forge": ">=1.3.2", + "mdast-util-to-hast": ">=13.2.1", + "remark-rehype": ">=11.0.0", + "express": ">=4.22.0" } } diff --git a/website/yarn.lock b/website/yarn.lock index c07cd453c7..1ac9f2033a 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2,274 +2,191 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz#2c410baa94a47c5c5f56ed712bb4a00ebe24088b" - integrity sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q== +"@ai-sdk/gateway@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-2.0.9.tgz#33b77dfee9a068df7bd8e11b4e10b9f1ee4228dd" + integrity sha512-E6x4h5CPPPJ0za1r5HsLtHbeI+Tp3H+YFtcH8G3dSSPFE6w+PZINzB4NxLZmg1QqSeA5HTP3ZEzzsohp0o2GEw== dependencies: - "@algolia/autocomplete-plugin-algolia-insights" "1.17.7" - "@algolia/autocomplete-shared" "1.17.7" + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.17" + "@vercel/oidc" "3.0.3" -"@algolia/autocomplete-plugin-algolia-insights@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz#7d2b105f84e7dd8f0370aa4c4ab3b704e6760d82" - integrity sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A== +"@ai-sdk/provider-utils@3.0.17": + version "3.0.17" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz#2f3d0be398d3f165efe8dd252b63aea6ac3896d1" + integrity sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw== dependencies: - "@algolia/autocomplete-shared" "1.17.7" + "@ai-sdk/provider" "2.0.0" + "@standard-schema/spec" "^1.0.0" + eventsource-parser "^3.0.6" -"@algolia/autocomplete-preset-algolia@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz#c9badc0d73d62db5bf565d839d94ec0034680ae9" - integrity sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA== +"@ai-sdk/provider@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-2.0.0.tgz#b853c739d523b33675bc74b6c506b2c690bc602b" + integrity sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA== dependencies: - "@algolia/autocomplete-shared" "1.17.7" + json-schema "^0.4.0" -"@algolia/autocomplete-shared@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz#105e84ad9d1a31d3fb86ba20dc890eefe1a313a0" - integrity sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg== - -"@algolia/cache-browser-local-storage@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz#97bc6d067a9fd932b9c922faa6b7fd6e546e1348" - integrity sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww== +"@ai-sdk/react@^2.0.30": + version "2.0.93" + resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-2.0.93.tgz#c81f8550a2ac44799c7527a75be19b1c3cf60dac" + integrity sha512-2TzhpQr10HuWxpqyHpSAUMRUqD1G2O73J2sAaJChomVDbjr7BwpM0mdR3aRamCXNtuLiJmTFQhbNzw8fXMBdYw== dependencies: - "@algolia/cache-common" "4.24.0" + "@ai-sdk/provider-utils" "3.0.17" + ai "5.0.93" + swr "^2.2.5" + throttleit "2.1.0" -"@algolia/cache-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.24.0.tgz#81a8d3a82ceb75302abb9b150a52eba9960c9744" - integrity sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g== - -"@algolia/cache-in-memory@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz#ffcf8872f3a10cb85c4f4641bdffd307933a6e44" - integrity sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w== +"@algolia/abtesting@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.10.0.tgz#7f4c915d3e3188e6af101e6f4e829cda795d3caf" + integrity sha512-mQT3jwuTgX8QMoqbIR7mPlWkqQqBPQaPabQzm37xg2txMlaMogK/4hCiiESGdg39MlHZOVHeV+0VJuE7f5UK8A== dependencies: - "@algolia/cache-common" "4.24.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-abtesting@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.15.0.tgz#6414895e2246dc7b7facd97bd98c3abe13cabe59" - integrity sha512-FaEM40iuiv1mAipYyiptP4EyxkJ8qHfowCpEeusdHUC4C7spATJYArD2rX3AxkVeREkDIgYEOuXcwKUbDCr7Nw== +"@algolia/autocomplete-core@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz#702df67a08cb3cfe8c33ee1111ef136ec1a9e232" + integrity sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/autocomplete-plugin-algolia-insights" "1.19.2" + "@algolia/autocomplete-shared" "1.19.2" -"@algolia/client-account@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.24.0.tgz#eba7a921d828e7c8c40a32d4add21206c7fe12f1" - integrity sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA== +"@algolia/autocomplete-plugin-algolia-insights@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz#3584b625b9317e333d1ae43664d02358e175c52d" + integrity sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/autocomplete-shared" "1.19.2" -"@algolia/client-analytics@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.24.0.tgz#9d2576c46a9093a14e668833c505ea697a1a3e30" - integrity sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg== +"@algolia/autocomplete-shared@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz#c0b7b8dc30a5c65b70501640e62b009535e4578f" + integrity sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w== + +"@algolia/client-abtesting@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.44.0.tgz#33e35fb59bfdb5bef26eb38902de5bdae3766e1e" + integrity sha512-KY5CcrWhRTUo/lV7KcyjrZkPOOF9bjgWpMj9z98VA+sXzVpZtkuskBLCKsWYFp2sbwchZFTd3wJM48H0IGgF7g== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-analytics@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.15.0.tgz#7ca1043cba7ac225d30e8bb52579504946b95f58" - integrity sha512-lho0gTFsQDIdCwyUKTtMuf9nCLwq9jOGlLGIeQGKDxXF7HbiAysFIu5QW/iQr1LzMgDyM9NH7K98KY+BiIFriQ== +"@algolia/client-analytics@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.44.0.tgz#2fdf0d41ac39fd071a9cde7c9a118b2ffb3ce8d9" + integrity sha512-LKOCE8S4ewI9bN3ot9RZoYASPi8b78E918/DVPW3HHjCMUe6i+NjbNG6KotU4RpP6AhRWZjjswbOkWelUO+OoA== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.24.0.tgz#77c46eee42b9444a1d1c1583a83f7df4398a649d" - integrity sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA== +"@algolia/client-common@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.44.0.tgz#aacc0fe3d07afae0d70bf7581b39d377f0bc0b7a" + integrity sha512-1yyJm4OYC2cztbS28XYVWwLXdwpLsMG4LoZLOltVglQ2+hc/i9q9fUDZyjRa2Bqt4DmkIfezagfMrokhyH4uxQ== + +"@algolia/client-insights@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.44.0.tgz#350f93bab703fa102acf82685364732937369025" + integrity sha512-wVQWK6jYYsbEOjIMI+e5voLGPUIbXrvDj392IckXaCPvQ6vCMTXakQqOYCd+znQdL76S+3wHDo77HZWiAYKrtA== dependencies: - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-common@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.15.0.tgz#cd47ae07a3afc7065438a2dab29f8434f848928e" - integrity sha512-IofrVh213VLsDkPoSKMeM9Dshrv28jhDlBDLRcVJQvlL8pzue7PEB1EZ4UoJFYS3NSn7JOcJ/V+olRQzXlJj1w== - -"@algolia/client-insights@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.15.0.tgz#f3bead0edd10e69365895da4a96044064b504f4d" - integrity sha512-bDDEQGfFidDi0UQUCbxXOCdphbVAgbVmxvaV75cypBTQkJ+ABx/Npw7LkFGw1FsoVrttlrrQbwjvUB6mLVKs/w== +"@algolia/client-personalization@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.44.0.tgz#b2c20dc59026babd695c571eb6015a8b46acb892" + integrity sha512-lkgRjOjOkqmIkebHjHpU9rLJcJNUDMm+eVSW/KJQYLjGqykEZxal+nYJJTBbLceEU2roByP/+27ZmgIwCdf0iA== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-personalization@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.24.0.tgz#8b47789fb1cb0f8efbea0f79295b7c5a3850f6ae" - integrity sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w== +"@algolia/client-query-suggestions@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.44.0.tgz#d39d0b21fe6b38f8dab2f1f201db6d6e5225908f" + integrity sha512-sYfhgwKu6NDVmZHL1WEKVLsOx/jUXCY4BHKLUOcYa8k4COCs6USGgz6IjFkUf+niwq8NCECMmTC4o/fVQOalsA== dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/client-personalization@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.15.0.tgz#e962793ebf737a5ffa4867d2dfdfe17924be3833" - integrity sha512-LfaZqLUWxdYFq44QrasCDED5bSYOswpQjSiIL7Q5fYlefAAUO95PzBPKCfUhSwhb4rKxigHfDkd81AvEicIEoA== +"@algolia/client-search@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.44.0.tgz#04ca43ee8181bf16f9df085286214ad3c06de126" + integrity sha512-/FRKUM1G4xn3vV8+9xH1WJ9XknU8rkBGlefruq9jDhYUAvYozKimhrmC2pRqw/RyHhPivmgZCRuC8jHP8piz4Q== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" - -"@algolia/client-query-suggestions@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.15.0.tgz#d9a2d0d0660241bdae5fc36a6f1fcf339abbafeb" - integrity sha512-wu8GVluiZ5+il8WIRsGKu8VxMK9dAlr225h878GGtpTL6VBvwyJvAyLdZsfFIpY0iN++jiNb31q2C1PlPL+n/A== - dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" - -"@algolia/client-search@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.24.0.tgz#75e6c02d33ef3e0f34afd9962c085b856fc4a55f" - integrity sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA== - dependencies: - "@algolia/client-common" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/transporter" "4.24.0" - -"@algolia/client-search@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.15.0.tgz#8645f5bc87a959b8008e021d8b31d55a47920b94" - integrity sha512-Z32gEMrRRpEta5UqVQA612sLdoqY3AovvUPClDfMxYrbdDAebmGDVPtSogUba1FZ4pP5dx20D3OV3reogLKsRA== - dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" "@algolia/events@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== -"@algolia/ingestion@1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.15.0.tgz#a3f3ec2139042f8597c2a975430a6f77cd764db3" - integrity sha512-MkqkAxBQxtQ5if/EX2IPqFA7LothghVyvPoRNA/meS2AW2qkHwcxjuiBxv4H6mnAVEPfJlhu9rkdVz9LgCBgJg== +"@algolia/ingestion@1.44.0": + version "1.44.0" + resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.44.0.tgz#f6bd13216c5a589414f43f9bec78db55cb022d90" + integrity sha512-5+S5ynwMmpTpCLXGjTDpeIa81J+R4BLH0lAojOhmeGSeGEHQTqacl/4sbPyDTcidvnWhaqtyf8m42ue6lvISAw== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/logger-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.24.0.tgz#28d439976019ec0a46ba7a1a739ef493d4ef8123" - integrity sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA== - -"@algolia/logger-console@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.24.0.tgz#c6ff486036cd90b81d07a95aaba04461da7e1c65" - integrity sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg== +"@algolia/monitoring@1.44.0": + version "1.44.0" + resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.44.0.tgz#423fa61d68826a72cd5c468b45b232d8ba379775" + integrity sha512-xhaTN8pXJjR6zkrecg4Cc9YZaQK2LKm2R+LkbAq+AYGBCWJxtSGlNwftozZzkUyq4AXWoyoc0x2SyBtq5LRtqQ== dependencies: - "@algolia/logger-common" "4.24.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/monitoring@1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.15.0.tgz#1eb58722ec9ea6e5de3621150f97a43571bd312e" - integrity sha512-QPrFnnGLMMdRa8t/4bs7XilPYnoUXDY8PMQJ1sf9ZFwhUysYYhQNX34/enoO0LBjpoOY6rLpha39YQEFbzgKyQ== +"@algolia/recommend@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.44.0.tgz#7e52b3e612bfd5bf82d14e05f577888a038279fa" + integrity sha512-GNcite/uOIS7wgRU1MT7SdNIupGSW+vbK9igIzMePvD2Dl8dy0O3urKPKIbTuZQqiVH1Cb84y5cgLvwNrdCj/Q== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" -"@algolia/recommend@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-4.24.0.tgz#8a3f78aea471ee0a4836b78fd2aad4e9abcaaf34" - integrity sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw== +"@algolia/requester-browser-xhr@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.44.0.tgz#a007b2c7b665224b04e61a1246fa015f06bd4100" + integrity sha512-YZHBk72Cd7pcuNHzbhNzF/FbbYszlc7JhZlDyQAchnX5S7tcemSS96F39Sy8t4O4WQLpFvUf1MTNedlitWdOsQ== dependencies: - "@algolia/cache-browser-local-storage" "4.24.0" - "@algolia/cache-common" "4.24.0" - "@algolia/cache-in-memory" "4.24.0" - "@algolia/client-common" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/logger-console" "4.24.0" - "@algolia/requester-browser-xhr" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/requester-node-http" "4.24.0" - "@algolia/transporter" "4.24.0" + "@algolia/client-common" "5.44.0" -"@algolia/recommend@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.15.0.tgz#8f3359ee7e855849ac3872f67c0672f6835c8f79" - integrity sha512-5eupMwSqMLDObgSMF0XG958zR6GJP3f7jHDQ3/WlzCM9/YIJiWIUoJFGsko9GYsA5xbLDHE/PhWtq4chcCdaGQ== +"@algolia/requester-fetch@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.44.0.tgz#055202db6b1ab37e042f207acb9e7f788e710089" + integrity sha512-B9WHl+wQ7uf46t9cq+vVM/ypVbOeuldVDq9OtKsX2ApL2g/htx6ImB9ugDOOJmB5+fE31/XPTuCcYz/j03+idA== dependencies: - "@algolia/client-common" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/client-common" "5.44.0" -"@algolia/requester-browser-xhr@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz#313c5edab4ed73a052e75803855833b62dd19c16" - integrity sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA== +"@algolia/requester-node-http@5.44.0": + version "5.44.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.44.0.tgz#3e2f56491303d1a94d172e98a1560af350950c01" + integrity sha512-MULm0qeAIk4cdzZ/ehJnl1o7uB5NMokg83/3MKhPq0Pk7+I0uELGNbzIfAkvkKKEYcHALemKdArtySF9eKzh/A== dependencies: - "@algolia/requester-common" "4.24.0" - -"@algolia/requester-browser-xhr@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.15.0.tgz#5ffdccdf5cd7814ed3486bed418edb6db25c32a2" - integrity sha512-Po/GNib6QKruC3XE+WKP1HwVSfCDaZcXu48kD+gwmtDlqHWKc7Bq9lrS0sNZ456rfCKhXksOmMfUs4wRM/Y96w== - dependencies: - "@algolia/client-common" "5.15.0" - -"@algolia/requester-common@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.24.0.tgz#1c60c198031f48fcdb9e34c4057a3ea987b9a436" - integrity sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA== - -"@algolia/requester-fetch@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.15.0.tgz#2ce94d4855090fac192b208d95eeea22e1ca4489" - integrity sha512-rOZ+c0P7ajmccAvpeeNrUmEKoliYFL8aOR5qGW5pFq3oj3Iept7Y5mEtEsOBYsRt6qLnaXn4zUKf+N8nvJpcIw== - dependencies: - "@algolia/client-common" "5.15.0" - -"@algolia/requester-node-http@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz#4461593714031d02aa7da221c49df675212f482f" - integrity sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw== - dependencies: - "@algolia/requester-common" "4.24.0" - -"@algolia/requester-node-http@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.15.0.tgz#e2020afcdaea56dc204bc6c82daab41478b32d87" - integrity sha512-b1jTpbFf9LnQHEJP5ddDJKE2sAlhYd7EVSOWgzo/27n/SfCoHfqD0VWntnWYD83PnOKvfe8auZ2+xCb0TXotrQ== - dependencies: - "@algolia/client-common" "5.15.0" - -"@algolia/transporter@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.24.0.tgz#226bb1f8af62430374c1972b2e5c8580ab275102" - integrity sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA== - dependencies: - "@algolia/cache-common" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/requester-common" "4.24.0" + "@algolia/client-common" "5.44.0" "@ampproject/remapping@^2.2.0": version "2.3.0" @@ -292,7 +209,7 @@ resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-8.1.1.tgz#95b1947d292a9a2efffba2081796dcaa05ecedfb" integrity sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -1246,92 +1163,136 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@csstools/cascade-layer-name-parser@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz#64d128529397aa1e1c986f685713363b262b81b1" - integrity sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA== +"@csstools/cascade-layer-name-parser@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz#43f962bebead0052a9fed1a2deeb11f85efcbc72" + integrity sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A== -"@csstools/color-helpers@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.1.tgz#829f1c76f5800b79c51c709e2f36821b728e0e10" - integrity sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA== +"@csstools/color-helpers@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" + integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== -"@csstools/css-calc@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.0.tgz#3f28b8f8f736b8f78abbc75eebd55c756207e773" - integrity sha512-X69PmFOrjTZfN5ijxtI8hZ9kRADFSLrmmQ6hgDJ272Il049WGKpDY64KhrFm/7rbWve0z81QepawzjkKlqkNGw== +"@csstools/css-calc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== -"@csstools/css-color-parser@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.6.tgz#e646838f6aab4618aeea7ba0c4921a254e180276" - integrity sha512-S/IjXqTHdpI4EtzGoNCHfqraXF37x12ZZHA1Lk7zoT5pm2lMjFuqhX/89L7dqX4CcMacKK+6ZCs5TmEGb/+wKw== +"@csstools/css-color-parser@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" + integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== dependencies: - "@csstools/color-helpers" "^5.0.1" - "@csstools/css-calc" "^2.1.0" + "@csstools/color-helpers" "^5.1.0" + "@csstools/css-calc" "^2.1.4" -"@csstools/css-parser-algorithms@^3.0.4": +"@csstools/css-parser-algorithms@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== + +"@csstools/css-tokenizer@^3.0.4": version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" - integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== -"@csstools/css-tokenizer@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" - integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== +"@csstools/media-query-list-parser@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz#7aec77bcb89c2da80ef207e73f474ef9e1b3cdf1" + integrity sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ== -"@csstools/media-query-list-parser@^4.0.2": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz#e80e17eba1693fceafb8d6f2cfc68c0e7a9ab78a" - integrity sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A== +"@csstools/postcss-alpha-function@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz#7989605711de7831bc7cd75b94c9b5bac9c3728e" + integrity sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" -"@csstools/postcss-cascade-layers@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz#9640313e64b5e39133de7e38a5aa7f40dc259597" - integrity sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ== +"@csstools/postcss-cascade-layers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz#dd2c70db3867b88975f2922da3bfbae7d7a2cae7" + integrity sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@csstools/postcss-color-function@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-4.0.6.tgz#dabd1e516ccd4c7bd5803e37075a503b5f7f0ac4" - integrity sha512-EcvXfC60cTIumzpsxWuvVjb7rsJEHPvqn3jeMEBUaE3JSc4FRuP7mEQ+1eicxWmIrs3FtzMH9gR3sgA5TH+ebQ== +"@csstools/postcss-color-function-display-p3-linear@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz#3017ff5e1f65307d6083e58e93d76724fb1ebf9f" + integrity sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-color-mix-function@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.6.tgz#d971832ec30b3b60363bceddfeb4b90c7cc0f4b8" - integrity sha512-jVKdJn4+JkASYGhyPO+Wa5WXSx1+oUgaXb3JsjJn/BlrtFh5zjocCY7pwWi0nuP24V1fY7glQsxEYcYNy0dMFg== +"@csstools/postcss-color-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz#a7c85a98c77b522a194a1bbb00dd207f40c7a771" + integrity sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-content-alt-text@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz#76f4687fb15ed45bc1139bb71e5775779762897a" - integrity sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw== +"@csstools/postcss-color-mix-function@^3.0.12": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz#2f1ee9f8208077af069545c9bd79bb9733382c2a" + integrity sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-exponential-functions@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.5.tgz#0c39f75df3357ee1e444b0aa0ede4e12aafea0e9" - integrity sha512-mi8R6dVfA2nDoKM3wcEi64I8vOYEgQVtVKCfmLHXupeLpACfGAided5ddMt5f+CnEodNu4DifuVwb0I6fQDGGQ== +"@csstools/postcss-color-mix-variadic-function-arguments@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz#b4012b62a4eaa24d694172bb7137f9d2319cb8f2" + integrity sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-content-alt-text@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz#1d52da1762893c32999ff76839e48d6ec7c7a4cb" + integrity sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q== + dependencies: + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-contrast-color-function@^2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz#ca46986d095c60f208d9e3f24704d199c9172637" + integrity sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-exponential-functions@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz#fc03d1272888cb77e64cc1a7d8a33016e4f05c69" + integrity sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw== + dependencies: + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/postcss-font-format-keywords@^4.0.0": version "4.0.0" @@ -1341,67 +1302,67 @@ "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-gamut-mapping@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.6.tgz#04ec6a50fdbca2a30dec56e6bb780c79621e47a7" - integrity sha512-0ke7fmXfc8H+kysZz246yjirAH6JFhyX9GTlyRnM0exHO80XcA9zeJpy5pOp5zo/AZiC/q5Pf+Hw7Pd6/uAoYA== +"@csstools/postcss-gamut-mapping@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz#be0e34c9f0142852cccfc02b917511f0d677db8b" + integrity sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-gradients-interpolation-method@^5.0.6": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.6.tgz#67fa61ada95e4534687fa76cd2d15ac74386560e" - integrity sha512-Itrbx6SLUzsZ6Mz3VuOlxhbfuyLTogG5DwEF1V8dAi24iMuvQPIHd7Ti+pNDp7j6WixndJGZaoNR0f9VSzwuTg== +"@csstools/postcss-gradients-interpolation-method@^5.0.12": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz#0955cce4d97203b861bf66742bbec611b2f3661c" + integrity sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-hwb-function@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.6.tgz#c40f557a54ed45e75c601a9ba7a08d315f64dbd7" - integrity sha512-927Pqy3a1uBP7U8sTfaNdZVB0mNXzIrJO/GZ8us9219q9n06gOqCdfZ0E6d1P66Fm0fYHvxfDbfcUuwAn5UwhQ== +"@csstools/postcss-hwb-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz#07f7ecb08c50e094673bd20eaf7757db0162beee" + integrity sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-ic-unit@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz#b60ec06500717c337447c39ae7fe7952eeb9d48f" - integrity sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA== +"@csstools/postcss-ic-unit@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz#2ee2da0690db7edfbc469279711b9e69495659d2" + integrity sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-initial@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-initial/-/postcss-initial-2.0.0.tgz#a86f5fc59ab9f16f1422dade4c58bd941af5df22" - integrity sha512-dv2lNUKR+JV+OOhZm9paWzYBXOCi+rJPqJ2cJuhh9xd8USVrd0cBEPczla81HNOyThMQWeCcdln3gZkQV2kYxA== +"@csstools/postcss-initial@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz#c385bd9d8ad31ad159edd7992069e97ceea4d09a" + integrity sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg== -"@csstools/postcss-is-pseudo-class@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz#12041448fedf01090dd4626022c28b7f7623f58e" - integrity sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ== +"@csstools/postcss-is-pseudo-class@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz#d34e850bcad4013c2ed7abe948bfa0448aa8eb74" + integrity sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@csstools/postcss-light-dark-function@^2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz#807c170cd28eebb0c00e64dfc6ab0bf418f19209" - integrity sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw== +"@csstools/postcss-light-dark-function@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz#0df448aab9a33cb9a085264ff1f396fb80c4437d" + integrity sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" "@csstools/postcss-logical-float-and-clear@^3.0.0": @@ -1426,32 +1387,32 @@ dependencies: postcss-value-parser "^4.2.0" -"@csstools/postcss-logical-viewport-units@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz#f6cc63520ca2a6eb76b9cd946070c38dda66d733" - integrity sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw== +"@csstools/postcss-logical-viewport-units@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz#016d98a8b7b5f969e58eb8413447eb801add16fc" + integrity sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ== dependencies: - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-media-minmax@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.5.tgz#66970aa8d8057f84b88aff21f385194fbe03eb11" - integrity sha512-sdh5i5GToZOIAiwhdntRWv77QDtsxP2r2gXW/WbLSCoLr00KTq/yiF1qlQ5XX2+lmiFa8rATKMcbwl3oXDMNew== +"@csstools/postcss-media-minmax@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz#184252d5b93155ae526689328af6bdf3fc113987" + integrity sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" -"@csstools/postcss-media-queries-aspect-ratio-number-values@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz#d71102172c74baf3f892fac88cf1ea46a961600d" - integrity sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ== +"@csstools/postcss-media-queries-aspect-ratio-number-values@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz#f485c31ec13d6b0fb5c528a3474334a40eff5f11" + integrity sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" "@csstools/postcss-nested-calc@^4.0.0": version "4.0.0" @@ -1468,42 +1429,42 @@ dependencies: postcss-value-parser "^4.2.0" -"@csstools/postcss-oklab-function@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.6.tgz#17e8dfb6422dfd8d77256def5d5be8335ea7af34" - integrity sha512-Hptoa0uX+XsNacFBCIQKTUBrFKDiplHan42X73EklG6XmQLG7/aIvxoNhvZ7PvOWMt67Pw3bIlUY2nD6p5vL8A== +"@csstools/postcss-oklab-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz#416640ef10227eea1375b47b72d141495950971d" + integrity sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-progressive-custom-properties@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz#ecdb85bcdb1852d73970a214a376684a91f82bdc" - integrity sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q== +"@csstools/postcss-progressive-custom-properties@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz#c39780b9ff0d554efb842b6bd75276aa6f1705db" + integrity sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw== dependencies: postcss-value-parser "^4.2.0" -"@csstools/postcss-random-function@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-random-function/-/postcss-random-function-1.0.1.tgz#73a0b62b5dbbc03c25a28f085235eb61b09a2fb0" - integrity sha512-Ab/tF8/RXktQlFwVhiC70UNfpFQRhtE5fQQoP2pO+KCPGLsLdWFiOuHgSRtBOqEshCVAzR4H6o38nhvRZq8deA== +"@csstools/postcss-random-function@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz#3191f32fe72936e361dadf7dbfb55a0209e2691e" + integrity sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-relative-color-syntax@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.6.tgz#4b8bc219b34b16f5abdbbcf09ac13e65bff6ef16" - integrity sha512-yxP618Xb+ji1I624jILaYM62uEmZcmbdmFoZHoaThw896sq0vU39kqTTF+ZNic9XyPtPMvq0vyvbgmHaszq8xg== +"@csstools/postcss-relative-color-syntax@^3.0.12": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz#ced792450102441f7c160e1d106f33e4b44181f8" + integrity sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" "@csstools/postcss-scope-pseudo-class@^4.0.1": @@ -1513,50 +1474,50 @@ dependencies: postcss-selector-parser "^7.0.0" -"@csstools/postcss-sign-functions@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.0.tgz#a524fae1374b0e167729f612ca875d7b1b334262" - integrity sha512-SLcc20Nujx/kqbSwDmj6oaXgpy3UjFhBy1sfcqPgDkHfOIfUtUVH7OXO+j7BU4v/At5s61N5ZX6shvgPwluhsA== +"@csstools/postcss-sign-functions@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz#a9ac56954014ae4c513475b3f1b3e3424a1e0c12" + integrity sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-stepped-value-functions@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.5.tgz#4d68633d502fbe2b6ef3898e368e3540488a0d8a" - integrity sha512-G6SJ6hZJkhxo6UZojVlLo14MohH4J5J7z8CRBrxxUYy9JuZiIqUo5TBYyDGcE0PLdzpg63a7mHSJz3VD+gMwqw== +"@csstools/postcss-stepped-value-functions@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz#36036f1a0e5e5ee2308e72f3c9cb433567c387b9" + integrity sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-text-decoration-shorthand@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.1.tgz#251fab0939d50c6fd73bb2b830b2574188efa087" - integrity sha512-xPZIikbx6jyzWvhms27uugIc0I4ykH4keRvoa3rxX5K7lEhkbd54rjj/dv60qOCTisoS+3bmwJTeyV1VNBrXaw== +"@csstools/postcss-text-decoration-shorthand@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz#fae1b70f07d1b7beb4c841c86d69e41ecc6f743c" + integrity sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA== dependencies: - "@csstools/color-helpers" "^5.0.1" + "@csstools/color-helpers" "^5.1.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-trigonometric-functions@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.5.tgz#267b95a8bd45536e0360596b6da660a9eb6aac83" - integrity sha512-/YQThYkt5MLvAmVu7zxjhceCYlKrYddK6LEmK5I4ojlS6BmO9u2yO4+xjXzu2+NPYmHSTtP4NFSamBCMmJ1NJA== +"@csstools/postcss-trigonometric-functions@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz#3f94ed2e319b57f2c59720b64e4d0a8a6fb8c3b2" + integrity sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/postcss-unset-value@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz#7caa981a34196d06a737754864baf77d64de4bba" integrity sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA== -"@csstools/selector-resolve-nested@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz#704a9b637975680e025e069a4c58b3beb3e2752a" - integrity sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ== +"@csstools/selector-resolve-nested@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz#848c6f44cb65e3733e478319b9342b7aa436fac7" + integrity sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g== "@csstools/selector-specificity@^5.0.0": version "5.0.0" @@ -1573,25 +1534,34 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@docsearch/css@3.8.0": - version "3.8.0" - resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.8.0.tgz#c70a1a326249d878ab7c630d7a908c6769a38db3" - integrity sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA== +"@docsearch/core@4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@docsearch/core/-/core-4.3.1.tgz#88a97a6fe4d4025269b6dee8b9d070b76758ad82" + integrity sha512-ktVbkePE+2h9RwqCUMbWXOoebFyDOxHqImAqfs+lC8yOU+XwEW4jgvHGJK079deTeHtdhUNj0PXHSnhJINvHzQ== -"@docsearch/react@^3.5.2": - version "3.8.0" - resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.8.0.tgz#c32165e34fadea8a0283c8b61cd73e6e1844797d" - integrity sha512-WnFK720+iwTVt94CxY3u+FgX6exb3BfN5kE9xUY6uuAH/9W/UFboBZFLlrw/zxFRHoHZCOXRtOylsXF+6LHI+Q== +"@docsearch/css@4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-4.3.2.tgz#d47d25336c9516b419245fa74e8dd5ae84a17492" + integrity sha512-K3Yhay9MgkBjJJ0WEL5MxnACModX9xuNt3UlQQkDEDZJZ0+aeWKtOkxHNndMRkMBnHdYvQjxkm6mdlneOtU1IQ== + +"@docsearch/react@^3.9.0 || ^4.1.0": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-4.3.2.tgz#450b8341cb5cca03737a00075d4dfd3a904a3e3e" + integrity sha512-74SFD6WluwvgsOPqifYOviEEVwDxslxfhakTlra+JviaNcs7KK/rjsPj89kVEoQc9FUxRkAofaJnHIR7pb4TSQ== dependencies: - "@algolia/autocomplete-core" "1.17.7" - "@algolia/autocomplete-preset-algolia" "1.17.7" - "@docsearch/css" "3.8.0" - algoliasearch "^5.12.0" + "@ai-sdk/react" "^2.0.30" + "@algolia/autocomplete-core" "1.19.2" + "@docsearch/core" "4.3.1" + "@docsearch/css" "4.3.2" + ai "^5.0.30" + algoliasearch "^5.28.0" + marked "^16.3.0" + zod "^4.1.8" -"@docusaurus/babel@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.6.3.tgz#016714fe7a8807d0fc2f7180eace5e82bebbb8a6" - integrity sha512-7dW9Hat9EHYCVicFXYA4hjxBY38+hPuCURL8oRF9fySRm7vzNWuEOghA1TXcykuXZp0HLG2td4RhDxCvGG7tNw== +"@docusaurus/babel@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.9.2.tgz#f956c638baeccf2040e482c71a742bc7e35fdb22" + integrity sha512-GEANdi/SgER+L7Japs25YiGil/AUDnFFHaCGPBbundxoWtCkA2lmy7/tFmgED4y1htAy6Oi4wkJEQdGssnw9MA== dependencies: "@babel/core" "^7.25.9" "@babel/generator" "^7.25.9" @@ -1603,55 +1573,54 @@ "@babel/runtime" "^7.25.9" "@babel/runtime-corejs3" "^7.25.9" "@babel/traverse" "^7.25.9" - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" babel-plugin-dynamic-import-node "^2.3.3" fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/bundler@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.6.3.tgz#f09c2e29613f988b874a4be2247708e121b7fc5c" - integrity sha512-47JLuc8D4wA+6VOvmMd5fUC9rFppBQpQOnxDYiVXffm/DeV/wmm3sbpNd5Y+O+G2+nevLTRnvCm/qyancv0Y3A== +"@docusaurus/bundler@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.9.2.tgz#0ca82cda4acf13a493e3f66061aea351e9d356cf" + integrity sha512-ZOVi6GYgTcsZcUzjblpzk3wH1Fya2VNpd5jtHoCCFcJlMQ1EYXZetfAnRHLcyiFeBABaI1ltTYbOBtH/gahGVA== dependencies: "@babel/core" "^7.25.9" - "@docusaurus/babel" "3.6.3" - "@docusaurus/cssnano-preset" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/babel" "3.9.2" + "@docusaurus/cssnano-preset" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" babel-loader "^9.2.1" - clean-css "^5.3.2" + clean-css "^5.3.3" copy-webpack-plugin "^11.0.0" - css-loader "^6.8.1" + css-loader "^6.11.0" css-minimizer-webpack-plugin "^5.0.1" cssnano "^6.1.2" file-loader "^6.2.0" html-minifier-terser "^7.2.0" - mini-css-extract-plugin "^2.9.1" + mini-css-extract-plugin "^2.9.2" null-loader "^4.0.1" - postcss "^8.4.26" - postcss-loader "^7.3.3" - postcss-preset-env "^10.1.0" - react-dev-utils "^12.0.1" + postcss "^8.5.4" + postcss-loader "^7.3.4" + postcss-preset-env "^10.2.1" terser-webpack-plugin "^5.3.9" tslib "^2.6.0" url-loader "^4.1.1" webpack "^5.95.0" webpackbar "^6.0.1" -"@docusaurus/core@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.6.3.tgz#6bf968ee26a36d71387bab293f27ccffc0e428b6" - integrity sha512-xL7FRY9Jr5DWqB6pEnqgKqcMPJOX5V0pgWXi5lCiih11sUBmcFKM7c3+GyxcVeeWFxyYSDP3grLTWqJoP4P9Vw== +"@docusaurus/core@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.9.2.tgz#cc970f29b85a8926d63c84f8cffdcda43ed266ff" + integrity sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw== dependencies: - "@docusaurus/babel" "3.6.3" - "@docusaurus/bundler" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/babel" "3.9.2" + "@docusaurus/bundler" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" boxen "^6.2.1" chalk "^4.1.2" chokidar "^3.5.3" @@ -1659,69 +1628,68 @@ combine-promises "^1.1.0" commander "^5.1.0" core-js "^3.31.1" - del "^6.1.1" detect-port "^1.5.1" escape-html "^1.0.3" eta "^2.2.0" eval "^0.1.8" + execa "5.1.1" fs-extra "^11.1.1" html-tags "^3.3.1" html-webpack-plugin "^5.6.0" leven "^3.1.0" lodash "^4.17.21" + open "^8.4.0" p-map "^4.0.0" prompts "^2.4.2" - react-dev-utils "^12.0.1" - react-helmet-async "^1.3.0" + react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" react-loadable "npm:@docusaurus/react-loadable@6.0.0" react-loadable-ssr-addon-v5-slorber "^1.0.1" react-router "^5.3.4" react-router-config "^5.1.1" react-router-dom "^5.3.4" - rtl-detect "^1.0.4" semver "^7.5.4" serve-handler "^6.1.6" - shelljs "^0.8.5" + tinypool "^1.0.2" tslib "^2.6.0" update-notifier "^6.0.2" webpack "^5.95.0" webpack-bundle-analyzer "^4.10.2" - webpack-dev-server "^4.15.2" + webpack-dev-server "^5.2.2" webpack-merge "^6.0.1" -"@docusaurus/cssnano-preset@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.3.tgz#ea19b307183ec20dea4927efc4ddf249150b8c6a" - integrity sha512-qP7SXrwZ+23GFJdPN4aIHQrZW+oH/7tzwEuc/RNL0+BdZdmIjYQqUxdXsjE4lFxLNZjj0eUrSNYIS6xwfij+5Q== +"@docusaurus/cssnano-preset@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.2.tgz#523aab65349db3c51a77f2489048d28527759428" + integrity sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ== dependencies: cssnano-preset-advanced "^6.1.2" - postcss "^8.4.38" + postcss "^8.5.4" postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@docusaurus/logger@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.6.3.tgz#c6e514c9429487ef38be2f2129b2b842740d92fd" - integrity sha512-xSubJixcNyMV9wMV4q0s47CBz3Rlc5jbcCCuij8pfQP8qn/DIpt0ks8W6hQWzHAedg/J/EwxxUOUrnEoKzJo8g== +"@docusaurus/logger@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.9.2.tgz#6ec6364b90f5a618a438cc9fd01ac7376869f92a" + integrity sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA== dependencies: chalk "^4.1.2" tslib "^2.6.0" -"@docusaurus/mdx-loader@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.6.3.tgz#127babc7cdb26d37c723bc3ae518bda17ce40160" - integrity sha512-3iJdiDz9540ppBseeI93tWTDtUGVkxzh59nMq4ignylxMuXBLK8dFqVeaEor23v1vx6TrGKZ2FuLaTB+U7C0QQ== +"@docusaurus/mdx-loader@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.9.2.tgz#78d238de6c6203fa811cc2a7e90b9b79e111408c" + integrity sha512-wiYoGwF9gdd6rev62xDU8AAM8JuLI/hlwOtCzMmYcspEkzecKrP8J8X+KpYnTlACBUUtXNJpSoCwFWJhLRevzQ== dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@mdx-js/mdx" "^3.0.0" "@slorber/remark-comment" "^1.0.0" escape-html "^1.0.3" estree-util-value-to-estree "^3.0.1" file-loader "^6.2.0" fs-extra "^11.1.1" - image-size "^1.0.2" + image-size "^2.0.2" mdast-util-mdx "^3.0.0" mdast-util-to-string "^4.0.0" rehype-raw "^7.0.0" @@ -1737,182 +1705,209 @@ vfile "^6.0.1" webpack "^5.88.1" -"@docusaurus/module-type-aliases@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.3.tgz#1f7030b1cf1f658cf664d41b6eadba93bbe51d87" - integrity sha512-MjaXX9PN/k5ugNvfRZdWyKWq4FsrhN4LEXaj0pEmMebJuBNlFeGyKQUa9DRhJHpadNaiMLrbo9m3U7Ig5YlsZg== +"@docusaurus/module-type-aliases@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.2.tgz#993c7cb0114363dea5ef6855e989b3ad4b843a34" + integrity sha512-8qVe2QA9hVLzvnxP46ysuofJUIc/yYQ82tvA/rBTrnpXtCjNSFLxEZfd5U8cYZuJIVlkPxamsIgwd5tGZXfvew== dependencies: - "@docusaurus/types" "3.6.3" + "@docusaurus/types" "3.9.2" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" "@types/react-router-dom" "*" - react-helmet-async "*" + react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@docusaurus/plugin-content-blog@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.3.tgz#d6a597e4bfdeb3f1f6ce06d2ac86207296988cc9" - integrity sha512-k0ogWwwJU3pFRFfvW1kRVHxzf2DutLGaaLjAnHVEU6ju+aRP0Z5ap/13DHyPOfHeE4WKpn/M0TqjdwZAcY3kAw== +"@docusaurus/plugin-content-blog@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz#d5ce51eb7757bdab0515e2dd26a793ed4e119df9" + integrity sha512-3I2HXy3L1QcjLJLGAoTvoBnpOwa6DPUa3Q0dMK19UTY9mhPkKQg/DYhAGTiBUKcTR0f08iw7kLPqOhIgdV3eVQ== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" lodash "^4.17.21" - reading-time "^1.5.0" + schema-dts "^1.1.2" srcset "^4.0.0" tslib "^2.6.0" unist-util-visit "^5.0.0" utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.3.tgz#aae044d2af6996d1a6de8d815aca8a83b485e0a5" - integrity sha512-r2wS8y/fsaDcxkm20W5bbYJFPzdWdEaTWVYjNxlHlcmX086eqQR1Fomlg9BHTJ0dLXPzAlbC8EN4XqMr3QzNCQ== +"@docusaurus/plugin-content-docs@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz#cd8f2d1c06e53c3fa3d24bdfcb48d237bf2d6b2e" + integrity sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@types/react-router-config" "^5.0.7" combine-promises "^1.1.0" fs-extra "^11.1.1" js-yaml "^4.1.0" lodash "^4.17.21" + schema-dts "^1.1.2" tslib "^2.6.0" utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-pages@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.3.tgz#0a5a43d1677ee519f63a54634653c54ddf41f475" - integrity sha512-eHrmTgjgLZsuqfsYr5X2xEwyIcck0wseSofWrjTwT9FLOWp+KDmMAuVK+wRo7sFImWXZk3oV/xX/g9aZrhD7OA== +"@docusaurus/plugin-content-pages@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.2.tgz#22db6c88ade91cec0a9e87a00b8089898051b08d" + integrity sha512-s4849w/p4noXUrGpPUF0BPqIAfdAe76BLaRGAGKZ1gTDNiGxGcpsLcwJ9OTi1/V8A+AzvsmI9pkjie2zjIQZKA== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-debug@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.6.3.tgz#4e62ddfbae4d597b073f8e3c632cc12d012339e3" - integrity sha512-zB9GXfIZNPRfzKnNjU6xGVrqn9bPXuGhpjgsuc/YtcTDjnjhasg38NdYd5LEqXex5G/zIorQgWB3n6x/Ut62vQ== +"@docusaurus/plugin-css-cascade-layers@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.2.tgz#358c85f63f1c6a11f611f1b8889d9435c11b22f8" + integrity sha512-w1s3+Ss+eOQbscGM4cfIFBlVg/QKxyYgj26k5AnakuHkKxH6004ZtuLe5awMBotIYF2bbGDoDhpgQ4r/kcj4rQ== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + tslib "^2.6.0" + +"@docusaurus/plugin-debug@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.9.2.tgz#b5df4db115583f5404a252dbf66f379ff933e53c" + integrity sha512-j7a5hWuAFxyQAkilZwhsQ/b3T7FfHZ+0dub6j/GxKNFJp2h9qk/P1Bp7vrGASnvA9KNQBBL1ZXTe7jlh4VdPdA== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" fs-extra "^11.1.1" - react-json-view-lite "^1.2.0" + react-json-view-lite "^2.3.0" tslib "^2.6.0" -"@docusaurus/plugin-google-analytics@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.3.tgz#63648d469b1e3c50fad8878e7a7db9856e503d5f" - integrity sha512-rCDNy1QW8Dag7nZq67pcum0bpFLrwvxJhYuVprhFh8BMBDxV0bY+bAkGHbSf68P3Bk9C3hNOAXX1srGLIDvcTA== +"@docusaurus/plugin-google-analytics@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.2.tgz#857fe075fdeccdf6959e62954d9efe39769fa247" + integrity sha512-mAwwQJ1Us9jL/lVjXtErXto4p4/iaLlweC54yDUK1a97WfkC6Z2k5/769JsFgwOwOP+n5mUQGACXOEQ0XDuVUw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" tslib "^2.6.0" -"@docusaurus/plugin-google-gtag@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.3.tgz#8a1388b4123904be17e661ea7aa71d798d0c046e" - integrity sha512-+OyDvhM6rqVkQOmLVkQWVJAizEEfkPzVWtIHXlWPOCFGK9X4/AWeBSrU0WG4iMg9Z4zD4YDRrU+lvI4s6DSC+w== +"@docusaurus/plugin-google-gtag@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.2.tgz#df75b1a90ae9266b0471909ba0265f46d5dcae62" + integrity sha512-YJ4lDCphabBtw19ooSlc1MnxtYGpjFV9rEdzjLsUnBCeis2djUyCozZaFhCg6NGEwOn7HDDyMh0yzcdRpnuIvA== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@docusaurus/plugin-google-tag-manager@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.3.tgz#38cbe416803f29782807cebf3ebf240cb47c3c74" - integrity sha512-1M6UPB13gWUtN2UHX083/beTn85PlRI9ABItTl/JL1FJ5dJTWWFXXsHf9WW/6hrVwthwTeV/AGbGKvLKV+IlCA== +"@docusaurus/plugin-google-tag-manager@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.2.tgz#d1a3cf935acb7d31b84685e92d70a1d342946677" + integrity sha512-LJtIrkZN/tuHD8NqDAW1Tnw0ekOwRTfobWPsdO15YxcicBo2ykKF0/D6n0vVBfd3srwr9Z6rzrIWYrMzBGrvNw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" tslib "^2.6.0" -"@docusaurus/plugin-sitemap@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.3.tgz#0458e6f7476ab6fd1466e01b153a3211d3223c53" - integrity sha512-94qOO4M9Fwv9KfVQJsgbe91k+fPJ4byf1L3Ez8TUa6TAFPo/BrLwQ80zclHkENlL1824TuxkcMKv33u6eydQCg== +"@docusaurus/plugin-sitemap@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.2.tgz#e1d9f7012942562cc0c6543d3cb2cdc4ae713dc4" + integrity sha512-WLh7ymgDXjG8oPoM/T4/zUP7KcSuFYRZAUTl8vR6VzYkfc18GBM4xLhcT+AKOwun6kBivYKUJf+vlqYJkm+RHw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@docusaurus/preset-classic@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.6.3.tgz#072298b5b6d0de7d0346b1e9b550a30ef2add56d" - integrity sha512-VHSYWROT3flvNNI1SrnMOtW1EsjeHNK9dhU6s9eY5hryZe79lUqnZJyze/ymDe2LXAqzyj6y5oYvyBoZZk6ErA== +"@docusaurus/plugin-svgr@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.2.tgz#62857ed79d97c0150d25f7e7380fdee65671163a" + integrity sha512-n+1DE+5b3Lnf27TgVU5jM1d4x5tUh2oW5LTsBxJX4PsAPV0JGcmI6p3yLYtEY0LRVEIJh+8RsdQmRE66wSV8mw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/plugin-content-blog" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/plugin-content-pages" "3.6.3" - "@docusaurus/plugin-debug" "3.6.3" - "@docusaurus/plugin-google-analytics" "3.6.3" - "@docusaurus/plugin-google-gtag" "3.6.3" - "@docusaurus/plugin-google-tag-manager" "3.6.3" - "@docusaurus/plugin-sitemap" "3.6.3" - "@docusaurus/theme-classic" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-search-algolia" "3.6.3" - "@docusaurus/types" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + "@svgr/core" "8.1.0" + "@svgr/webpack" "^8.1.0" + tslib "^2.6.0" + webpack "^5.88.1" -"@docusaurus/theme-classic@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.6.3.tgz#00599a9de5fd5c122fd1b8c59d3b755878f2a72c" - integrity sha512-1RRLK1tSArI2c00qugWYO3jRocjOZwGF1mBzPPylDVRwWCS/rnWWR91ChdbbaxIupRJ+hX8ZBYrwr5bbU0oztQ== +"@docusaurus/preset-classic@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.9.2.tgz#85cc4f91baf177f8146c9ce896dfa1f0fd377050" + integrity sha512-IgyYO2Gvaigi21LuDIe+nvmN/dfGXAiMcV/murFqcpjnZc7jxFAxW+9LEjdPt61uZLxG4ByW/oUmX/DDK9t/8w== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/plugin-content-blog" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/plugin-content-pages" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-translations" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/plugin-content-blog" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/plugin-content-pages" "3.9.2" + "@docusaurus/plugin-css-cascade-layers" "3.9.2" + "@docusaurus/plugin-debug" "3.9.2" + "@docusaurus/plugin-google-analytics" "3.9.2" + "@docusaurus/plugin-google-gtag" "3.9.2" + "@docusaurus/plugin-google-tag-manager" "3.9.2" + "@docusaurus/plugin-sitemap" "3.9.2" + "@docusaurus/plugin-svgr" "3.9.2" + "@docusaurus/theme-classic" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-search-algolia" "3.9.2" + "@docusaurus/types" "3.9.2" + +"@docusaurus/theme-classic@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz#6e514f99a0ff42b80afcf42d5e5d042618311ce0" + integrity sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/plugin-content-blog" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/plugin-content-pages" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-translations" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@mdx-js/react" "^3.0.0" clsx "^2.0.0" - copy-text-to-clipboard "^3.2.0" infima "0.2.0-alpha.45" lodash "^4.17.21" nprogress "^0.2.0" - postcss "^8.4.26" + postcss "^8.5.4" prism-react-renderer "^2.3.0" prismjs "^1.29.0" react-router-dom "^5.3.4" @@ -1920,15 +1915,15 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.6.3.tgz#a8a6ebd2b0fd7a5cca4d0c6a2f9ccff905fa7438" - integrity sha512-b8ZkhczXHDxWWyvz+YJy4t/PlPbEogTTbgnHoflYnH7rmRtyoodTsu8WVM12la5LmlMJBclBXFl29OH8kPE7gg== +"@docusaurus/theme-common@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.9.2.tgz#487172c6fef9815c2746ef62a71e4f5b326f9ba5" + integrity sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag== dependencies: - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -1938,21 +1933,21 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.3.tgz#1a3331a489f392f5b032c4efc5f431e57eddf7ce" - integrity sha512-rt+MGCCpYgPyWCGXtbxlwFbTSobu15jWBTPI2LHsHNa5B0zSmOISX6FWYAPt5X1rNDOqMGM0FATnh7TBHRohVA== +"@docusaurus/theme-search-algolia@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz#420fd5b27fc1673b48151fdc9fe7167ba135ed50" + integrity sha512-GBDSFNwjnh5/LdkxCKQHkgO2pIMX1447BxYUBG2wBiajS21uj64a+gH/qlbQjDLxmGrbrllBrtJkUHxIsiwRnw== dependencies: - "@docsearch/react" "^3.5.2" - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-translations" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" - algoliasearch "^4.18.0" - algoliasearch-helper "^3.13.3" + "@docsearch/react" "^3.9.0 || ^4.1.0" + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-translations" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + algoliasearch "^5.37.0" + algoliasearch-helper "^3.26.0" clsx "^2.0.0" eta "^2.2.0" fs-extra "^11.1.1" @@ -1960,61 +1955,62 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.6.3.tgz#6e473835ea016ce4acd7d2997f411811db8c4f6b" - integrity sha512-Gb0regclToVlngSIIwUCtBMQBq48qVUaN1XQNKW4XwlsgUyk0vP01LULdqbem7czSwIeBAFXFoORJ0RPX7ht/w== +"@docusaurus/theme-translations@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.9.2.tgz#238cd69c2da92d612be3d3b4f95944c1d0f1e041" + integrity sha512-vIryvpP18ON9T9rjgMRFLr2xJVDpw1rtagEGf8Ccce4CkTrvM/fRB8N2nyWYOW5u3DdjkwKw5fBa+3tbn9P4PA== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/types@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.6.3.tgz#e87592e31616da1b8dc473e4c8205c61885a1518" - integrity sha512-xD9oTGDrouWzefkhe9ogB2fDV96/82cRpNGx2HIvI5L87JHNhQVIWimQ/3JIiiX/TEd5S9s+VO6FFguwKNRVow== +"@docusaurus/types@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.9.2.tgz#e482cf18faea0d1fa5ce0e3f1e28e0f32d2593eb" + integrity sha512-Ux1JUNswg+EfUEmajJjyhIohKceitY/yzjRUpu04WXgvVz+fbhVC0p+R0JhvEu4ytw8zIAys2hrdpQPBHRIa8Q== dependencies: "@mdx-js/mdx" "^3.0.0" "@types/history" "^4.7.11" + "@types/mdast" "^4.0.2" "@types/react" "*" commander "^5.1.0" joi "^17.9.2" - react-helmet-async "^1.3.0" + react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" utility-types "^3.10.0" webpack "^5.95.0" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.6.3.tgz#57f840bd6f0928cf10060198cb421f1b9212c8f5" - integrity sha512-v4nKDaANLgT3pMBewHYEMAl/ufY0LkXao1QkFWzI5huWFOmNQ2UFzv2BiKeHX5Ownis0/w6cAyoxPhVdDonlSQ== +"@docusaurus/utils-common@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.9.2.tgz#e89bfcf43d66359f43df45293fcdf22814847460" + integrity sha512-I53UC1QctruA6SWLvbjbhCpAw7+X7PePoe5pYcwTOEXD/PxeP8LnECAhTHHwWCblyUX5bMi4QLRkxvyZ+IT8Aw== dependencies: - "@docusaurus/types" "3.6.3" + "@docusaurus/types" "3.9.2" tslib "^2.6.0" -"@docusaurus/utils-validation@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.6.3.tgz#3eca7125235eb90983ff660b97a71f331e331f57" - integrity sha512-bhEGGiN5BE38h21vjqD70Gxg++j+PfYVddDUE5UFvLDup68QOcpD33CLr+2knPorlxRbEaNfz6HQDUMQ3HuqKw== +"@docusaurus/utils-validation@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz#04aec285604790806e2fc5aa90aa950dc7ba75ae" + integrity sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A== dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" fs-extra "^11.2.0" joi "^17.9.2" js-yaml "^4.1.0" lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils@3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.6.3.tgz#8dcb1969e4011a84dfb0a031da806dadddebf0ea" - integrity sha512-0R/FR3bKVl4yl8QwbL4TYFfR+OXBRpVUaTJdENapBGR3YMwfM6/JnhGilWQO8AOwPJGtGoDK7ib8+8UF9f3OZQ== +"@docusaurus/utils@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.9.2.tgz#ffab7922631c7e0febcb54e6d499f648bf8a89eb" + integrity sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ== dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@svgr/webpack" "^8.1.0" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-common" "3.9.2" escape-string-regexp "^4.0.0" + execa "5.1.1" file-loader "^6.2.0" fs-extra "^11.1.1" github-slugger "^1.5.0" @@ -2024,9 +2020,9 @@ js-yaml "^4.1.0" lodash "^4.17.21" micromatch "^4.0.5" + p-queue "^6.6.2" prompts "^2.4.2" resolve-pathname "^3.0.0" - shelljs "^0.8.5" tslib "^2.6.0" url-loader "^4.1.1" utility-types "^3.10.0" @@ -2063,6 +2059,18 @@ local-pkg "^1.0.0" mlly "^1.7.4" +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -2240,6 +2248,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@opentelemetry/api@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -2312,6 +2325,11 @@ micromark-util-character "^1.1.0" micromark-util-symbol "^1.0.1" +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" @@ -2835,7 +2853,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2921,11 +2939,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - "@types/parse5@^5.0.0": version "5.0.3" resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" @@ -3092,6 +3105,11 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vercel/oidc@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@vercel/oidc/-/oidc-3.0.3.tgz#82c2b6dd4d5c3b37dcb1189718cdeb9db402d052" + integrity sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg== + "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" @@ -3233,7 +3251,15 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.4, accepts@~1.3.8: +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + +accepts@~1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -3258,7 +3284,7 @@ acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -address@^1.0.1, address@^1.1.2: +address@^1.0.1: version "1.2.2" resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== @@ -3271,6 +3297,16 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ai@5.0.93, ai@^5.0.30: + version "5.0.93" + resolved "https://registry.yarnpkg.com/ai/-/ai-5.0.93.tgz#040fff47ce6603b36ed9b8b7ca228c2400d94be3" + integrity sha512-9eGcu+1PJgPg4pRNV4L7tLjRR3wdJC9CXQoNMvtqvYNOLZHFCzjHtVIOr2SIkoJJeu2+sOy3hyiSuTmy2MA40g== + dependencies: + "@ai-sdk/gateway" "2.0.9" + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.17" + "@opentelemetry/api" "1.9.0" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -3278,7 +3314,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== @@ -3290,7 +3326,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.5: +ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3310,52 +3346,32 @@ ajv@^8.0.0, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -algoliasearch-helper@^3.13.3: - version "3.22.5" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.22.5.tgz#2fcc26814e10a121a2c2526a1b05c754061c56c0" - integrity sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw== +algoliasearch-helper@^3.26.0: + version "3.26.1" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.26.1.tgz#5b7f0874a2751c3d6de675d5403d8fa2f015023f" + integrity sha512-CAlCxm4fYBXtvc5MamDzP6Svu8rW4z9me4DCBY1rQ2UDJ0u0flWmusQ8M3nOExZsLLRcUwUPoRAPMrhzOG3erw== dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.18.0: - version "4.24.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.24.0.tgz#b953b3e2309ef8f25da9de311b95b994ac918275" - integrity sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g== +algoliasearch@^5.28.0, algoliasearch@^5.37.0: + version "5.44.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.44.0.tgz#25017f7ea7afcd35b35fb5a790a78694e5dddf4b" + integrity sha512-f8IpsbdQjzTjr/4mJ/jv5UplrtyMnnciGax6/B0OnLCs2/GJTK13O4Y7Ff1AvJVAaztanH+m5nzPoUq6EAy+aA== dependencies: - "@algolia/cache-browser-local-storage" "4.24.0" - "@algolia/cache-common" "4.24.0" - "@algolia/cache-in-memory" "4.24.0" - "@algolia/client-account" "4.24.0" - "@algolia/client-analytics" "4.24.0" - "@algolia/client-common" "4.24.0" - "@algolia/client-personalization" "4.24.0" - "@algolia/client-search" "4.24.0" - "@algolia/logger-common" "4.24.0" - "@algolia/logger-console" "4.24.0" - "@algolia/recommend" "4.24.0" - "@algolia/requester-browser-xhr" "4.24.0" - "@algolia/requester-common" "4.24.0" - "@algolia/requester-node-http" "4.24.0" - "@algolia/transporter" "4.24.0" - -algoliasearch@^5.12.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.15.0.tgz#09cef5a2555c4554b37a99f0488ea6ab2347e625" - integrity sha512-Yf3Swz1s63hjvBVZ/9f2P1Uu48GjmjCN+Esxb6MAONMGtZB1fRX8/S1AhUTtsuTlcGovbYLxpHgc7wEzstDZBw== - dependencies: - "@algolia/client-abtesting" "5.15.0" - "@algolia/client-analytics" "5.15.0" - "@algolia/client-common" "5.15.0" - "@algolia/client-insights" "5.15.0" - "@algolia/client-personalization" "5.15.0" - "@algolia/client-query-suggestions" "5.15.0" - "@algolia/client-search" "5.15.0" - "@algolia/ingestion" "1.15.0" - "@algolia/monitoring" "1.15.0" - "@algolia/recommend" "5.15.0" - "@algolia/requester-browser-xhr" "5.15.0" - "@algolia/requester-fetch" "5.15.0" - "@algolia/requester-node-http" "5.15.0" + "@algolia/abtesting" "1.10.0" + "@algolia/client-abtesting" "5.44.0" + "@algolia/client-analytics" "5.44.0" + "@algolia/client-common" "5.44.0" + "@algolia/client-insights" "5.44.0" + "@algolia/client-personalization" "5.44.0" + "@algolia/client-query-suggestions" "5.44.0" + "@algolia/client-search" "5.44.0" + "@algolia/ingestion" "1.44.0" + "@algolia/monitoring" "1.44.0" + "@algolia/recommend" "5.44.0" + "@algolia/requester-browser-xhr" "5.44.0" + "@algolia/requester-fetch" "5.44.0" + "@algolia/requester-node-http" "5.44.0" ansi-align@^3.0.1: version "3.0.1" @@ -3433,11 +3449,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -3459,11 +3470,6 @@ astring@^1.8.0: resolved "https://registry.yarnpkg.com/astring/-/astring-1.9.0.tgz#cc73e6062a7eb03e7d19c22d8b0b3451fd9bfeef" integrity sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - autocomplete.js@^0.37.0: version "0.37.1" resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.37.1.tgz#a29a048d827e7d2bf8f7df8b831766e5cc97df01" @@ -3483,6 +3489,18 @@ autoprefixer@^10.4.19: picocolors "^1.0.1" postcss-value-parser "^4.2.0" +autoprefixer@^10.4.21: + version "10.4.22" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.22.tgz#90b27ab55ec0cf0684210d1f056f7d65dac55f16" + integrity sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg== + dependencies: + browserslist "^4.27.0" + caniuse-lite "^1.0.30001754" + fraction.js "^5.3.4" + normalize-range "^0.1.2" + picocolors "^1.1.1" + postcss-value-parser "^4.2.0" + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -3549,6 +3567,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.8.25: + version "2.8.28" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.28.tgz#9ef511f5a7c19d74a94cafcbf951608398e9bdb3" + integrity sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ== + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -3569,23 +3592,20 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== +body-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.1.tgz#6df606b0eb0a6e3f783dde91dde182c24c82438c" + integrity sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw== dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.1" + type-is "^2.0.1" bonjour-service@^1.2.1: version "1.3.0" @@ -3628,7 +3648,7 @@ boxen@^7.0.0: widest-line "^4.0.1" wrap-ansi "^8.1.0" -brace-expansion@>=1.1.12, brace-expansion@^1.1.7, brace-expansion@^2.0.1: +brace-expansion@>=1.1.12, brace-expansion@^1.1.7: version "4.0.1" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-4.0.1.tgz#3387e13eaa2992025d05ea47308f77e4a8dedd1e" integrity sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA== @@ -3642,7 +3662,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2: +browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2: version "4.24.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== @@ -3652,6 +3672,17 @@ browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4 node-releases "^2.0.18" update-browserslist-db "^1.1.1" +browserslist@^4.26.0, browserslist@^4.27.0: + version "4.28.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" + integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== + dependencies: + baseline-browser-mapping "^2.8.25" + caniuse-lite "^1.0.30001754" + electron-to-chromium "^1.5.249" + node-releases "^2.0.27" + update-browserslist-db "^1.1.4" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -3677,7 +3708,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== -bytes@3.1.2: +bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -3708,6 +3739,14 @@ call-bind-apply-helpers@^1.0.0: es-errors "^1.3.0" function-bind "^1.1.2" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" @@ -3718,6 +3757,14 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.2" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -3751,17 +3798,17 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: - version "1.0.30001687" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz#d0ac634d043648498eedf7a3932836beba90ebae" - integrity sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669, caniuse-lite@^1.0.30001754: + version "1.0.30001757" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz" + integrity sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ== ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3843,7 +3890,7 @@ chevrotain@~11.0.3: "@chevrotain/utils" "11.0.3" lodash-es "4.17.21" -chokidar@^3.4.2, chokidar@^3.5.3, chokidar@^3.6.0: +chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -3868,7 +3915,7 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -clean-css@^5.2.2, clean-css@^5.3.2, clean-css@~5.3.2: +clean-css@^5.2.2, clean-css@^5.3.3, clean-css@~5.3.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== @@ -4078,14 +4125,12 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.1.tgz#a8b7bbeb2904befdfb6787e5c0c086959f605f9b" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== -content-type@~1.0.4, content-type@~1.0.5: +content-type@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -4095,21 +4140,16 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== -cookie@0.7.1, cookie@>=0.7.0: +cookie@>=0.7.0, cookie@^0.7.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== -copy-text-to-clipboard@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz#0202b2d9bdae30a49a53f898626dcc3b49ad960b" - integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== - copy-webpack-plugin@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" @@ -4158,17 +4198,6 @@ cose-base@^2.2.0: dependencies: layout-base "^2.0.0" -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" - cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" @@ -4179,7 +4208,7 @@ cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: parse-json "^5.2.0" path-type "^4.0.0" -cross-spawn@>=6.0.6, cross-spawn@^7.0.0, cross-spawn@^7.0.3: +cross-spawn@>=6.0.6, cross-spawn@^7.0.3: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -4207,16 +4236,16 @@ css-declaration-sorter@^7.2.0: resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== -css-has-pseudo@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-7.0.1.tgz#adbb51821e51f7a7c1d2df4d12827870cc311137" - integrity sha512-EOcoyJt+OsuKfCADgLT7gADZI5jMzIe/AeI6MeAYKiFBDmNmM7kk46DtSfMj5AohUJisqVzopBpnQTlvbyaBWg== +css-has-pseudo@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz#a5ee2daf5f70a2032f3cefdf1e36e7f52a243873" + integrity sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" postcss-value-parser "^4.2.0" -css-loader@^6.8.1: +css-loader@^6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== @@ -4295,10 +4324,10 @@ css-what@^6.0.1, css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -cssdb@^8.2.1: - version "8.2.2" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.2.2.tgz#0a5bcbc47a297e6b0296e6082f60363e17b337d4" - integrity sha512-Z3kpWyvN68aKyeMxOUGmffQeHjvrzDxbre2B2ikr/WqQ4ZMkhHu2nOD6uwSeq3TpuOYU7ckvmJRAUIt6orkYUg== +cssdb@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.4.2.tgz#1a367ab1904c97af0bb2c7ae179764deae7b078b" + integrity sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg== cssesc@^3.0.0: version "3.0.0" @@ -4687,7 +4716,7 @@ debounce@^1.2.1: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.6.0: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -4701,6 +4730,13 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "^2.1.3" +debug@^4.3.5, debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + debug@^4.3.6: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -4734,7 +4770,7 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deepmerge@^4.2.2, deepmerge@^4.3.1: +deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -4785,20 +4821,6 @@ define-properties@^1.1.3, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -del@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" - integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== - dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" - delaunator@5: version "5.0.1" resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278" @@ -4806,7 +4828,7 @@ delaunator@5: dependencies: robust-predicates "^3.0.2" -depd@2.0.0: +depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -4816,29 +4838,16 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -dequal@^2.0.0: +dequal@^2.0.0, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -detect-port-alt@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" - integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== - dependencies: - address "^1.0.1" - debug "^2.6.0" - detect-port@^1.5.1: version "1.6.1" resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.6.1.tgz#45e4073997c5f292b957cb678fb0bb8ed4250a67" @@ -5013,6 +5022,15 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5028,6 +5046,11 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +electron-to-chromium@^1.5.249: + version "1.5.254" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.254.tgz#94b84c0a5faff94b334536090a9dec1c74b10130" + integrity sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg== + electron-to-chromium@^1.5.41: version "1.5.71" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz#d8b5dba1e55b320f2f4e9b1ca80738f53fcfec2b" @@ -5058,12 +5081,7 @@ emoticon@^4.0.1: resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-4.1.0.tgz#d5a156868ee173095627a33de3f1e914c3dde79e" integrity sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ== -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: +encodeurl@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== @@ -5105,6 +5123,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" @@ -5115,6 +5138,13 @@ es-module-lexer@^1.2.1: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + esast-util-from-estree@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz#8d1cfb51ad534d2f159dc250e604f3478a79f1ad" @@ -5304,7 +5334,7 @@ eta@^2.2.0: resolved "https://registry.yarnpkg.com/eta/-/eta-2.2.0.tgz#eb8b5f8c4e8b6306561a455e62cd7492fe3a9b8a" integrity sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g== -etag@~1.8.1: +etag@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== @@ -5317,7 +5347,7 @@ eval@^0.1.8: "@types/node" "*" require-like ">= 0.1.1" -eventemitter3@^4.0.0: +eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -5327,47 +5357,64 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource-parser@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +execa@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exenv@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== -express@^4.21.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== +express@>=4.22.0, express@^4.21.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.3" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.12" - proxy-addr "~2.0.7" - qs "6.13.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" exsolve@^1.0.7: version "1.0.7" @@ -5455,11 +5502,6 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -filesize@^8.0.6: - version "8.0.7" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" - integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -5467,18 +5509,17 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" find-cache-dir@^4.0.0: version "4.0.0" @@ -5488,21 +5529,6 @@ find-cache-dir@^4.0.0: common-path-prefix "^3.0.0" pkg-dir "^7.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-up@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" @@ -5528,33 +5554,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -foreground-child@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" - integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - -fork-ts-checker-webpack-plugin@^6.5.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" - integrity sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@types/json-schema" "^7.0.5" - chalk "^4.1.0" - chokidar "^3.4.2" - cosmiconfig "^6.0.0" - deepmerge "^4.2.2" - fs-extra "^9.0.0" - glob "^7.1.6" - memfs "^3.1.2" - minimatch "^3.0.4" - schema-utils "2.7.0" - semver "^7.3.2" - tapable "^1.0.0" - form-data-encoder@^2.1.2: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" @@ -5575,10 +5574,15 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fraction.js@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a" + integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ== + +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== fs-extra@^10.1.0: version "10.1.0" @@ -5598,26 +5602,6 @@ fs-extra@^11.1.1, fs-extra@^11.2.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-monkey@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" - integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -5659,12 +5643,36 @@ get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== -get-stream@^6.0.1: +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5693,29 +5701,14 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.3.10: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== +glob@>=10.5.0, glob@^10.3.10: + version "13.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.0.tgz#9d9233a4a274fc28ef7adce5508b7ef6237a1be3" + integrity sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA== dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" + minimatch "^10.1.1" minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" + path-scurry "^2.0.0" global-dirs@^3.0.0: version "3.0.1" @@ -5724,22 +5717,6 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5750,7 +5727,7 @@ globals@^15.14.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== -globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5773,7 +5750,7 @@ globby@^13.1.1, globby@^13.1.4: merge2 "^1.4.1" slash "^4.0.0" -gopd@^1.0.1: +gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== @@ -5851,7 +5828,7 @@ has-proto@^1.0.1: dependencies: call-bind "^1.0.7" -has-symbols@^1.0.3: +has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== @@ -6221,16 +6198,16 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== +http-errors@^2.0.0, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" http-errors@~1.6.2: version "1.6.3" @@ -6276,18 +6253,16 @@ http2-wrapper@^2.1.10: quick-lru "^5.1.1" resolve-alpn "^1.2.0" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + hyperdyperid@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - iconv-lite@0.6: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -6295,6 +6270,13 @@ iconv-lite@0.6: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" + integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -6310,7 +6292,7 @@ ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -image-size@>=1.2.1, image-size@^1.0.2: +image-size@>=1.2.1, image-size@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/image-size/-/image-size-2.0.2.tgz#84a7b43704db5736f364bf0d1b029821299b4bdc" integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w== @@ -6320,12 +6302,7 @@ immediate@^3.2.3: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== -immer@^9.0.7: - version "9.0.21" - resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" - integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== - -import-fresh@^3.1.0, import-fresh@^3.3.0: +import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -6353,30 +6330,22 @@ infima@0.2.0-alpha.45: resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.45.tgz#542aab5a249274d81679631b492973dd2c1e7466" integrity sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw== -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + ini@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -6401,11 +6370,6 @@ internmap@^1.0.0: resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -6577,11 +6541,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -6609,6 +6568,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-reference@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.3.tgz#9ef7bf9029c70a67b2152da4adf57c23d718910f" @@ -6621,10 +6585,10 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== -is-root@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" - integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-typed-array@^1.1.3: version "1.1.13" @@ -6682,7 +6646,7 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -jackspeak@2.1.1, jackspeak@^3.1.2: +jackspeak@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.1.1.tgz#2a42db4cfbb7e55433c28b6f75d8b796af9669cd" integrity sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw== @@ -6744,17 +6708,17 @@ joi@^17.9.2: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + version "3.14.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" esprima "^4.0.0" js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" @@ -6783,6 +6747,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -6901,11 +6870,6 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5" - integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg== - local-pkg@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-1.1.2.tgz#c03d208787126445303f8161619dc701afa4abb5" @@ -6915,21 +6879,6 @@ local-pkg@^1.0.0: pkg-types "^2.3.0" quansync "^0.2.11" -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - locate-path@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" @@ -6991,10 +6940,10 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" + integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== lru-cache@^5.1.1: version "5.1.1" @@ -7040,14 +6989,15 @@ marked@^16.0.0: resolved "https://registry.yarnpkg.com/marked/-/marked-16.2.0.tgz#c407a4f7ed3acc1110812525cfd1b0ed8502792c" integrity sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg== -mdast-util-definitions@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7" - integrity sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - unist-util-visit "^4.0.0" +marked@^16.3.0: + version "16.4.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-16.4.2.tgz#4959a64be6c486f0db7467ead7ce288de54290a3" + integrity sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== mdast-util-directive@^3.0.0: version "3.0.0" @@ -7374,24 +7324,10 @@ mdast-util-phrasing@^4.0.0: "@types/mdast" "^4.0.0" unist-util-is "^6.0.0" -mdast-util-to-hast@^12.1.0: - version "12.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz#045d2825fb04374e59970f5b3f279b5700f6fb49" - integrity sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw== - dependencies: - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-definitions "^5.0.0" - micromark-util-sanitize-uri "^1.1.0" - trim-lines "^3.0.0" - unist-util-generated "^2.0.0" - unist-util-position "^4.0.0" - unist-util-visit "^4.0.0" - -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== +mdast-util-to-hast@>=13.2.1, mdast-util-to-hast@^13.0.0: + version "13.2.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz#d7ff84ca499a57e2c060ae67548ad950e689a053" + integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -7456,17 +7392,10 @@ mdn-data@2.0.30: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memfs@^3.1.2: - version "3.6.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== - dependencies: - fs-monkey "^1.0.4" +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== memfs@^4.6.0: version "4.17.2" @@ -7478,10 +7407,10 @@ memfs@^4.6.0: tree-dump "^1.0.1" tslib "^2.0.0" -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== merge-stream@^2.0.0: version "2.0.0" @@ -7519,11 +7448,6 @@ mermaid@>=11.10.0, mermaid@^10.9.0: ts-dedent "^2.2.0" uuid "^11.1.0" -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz#1386628df59946b2d39fb2edfd10f3e8e0a75bb8" @@ -8179,7 +8103,7 @@ micromark-util-resolve-all@^2.0.0: dependencies: micromark-util-types "^2.0.0" -micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0: +micromark-util-sanitize-uri@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz#613f738e4400c6eedbc53590c67b197e30d7f90d" integrity sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A== @@ -8301,6 +8225,11 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" @@ -8313,17 +8242,24 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime-types@^3.0.0, mime-types@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^3.1.0: version "3.1.0" @@ -8335,10 +8271,10 @@ mimic-response@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== -mini-css-extract-plugin@^2.9.1: - version "2.9.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz#966031b468917a5446f4c24a80854b2947503c5b" - integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w== +mini-css-extract-plugin@^2.9.2: + version "2.9.4" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz#cafa1a42f8c71357f49cd1566810d74ff1cb0200" + integrity sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ== dependencies: schema-utils "^4.0.0" tapable "^2.2.1" @@ -8348,26 +8284,26 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: +minimatch@3.1.2, minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== +minimatch@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55" + integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ== dependencies: - brace-expansion "^2.0.1" + "@isaacs/brace-expansion" "^5.0.0" minimist@^1.2.0: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -8402,7 +8338,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.3, ms@^2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -8424,6 +8360,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -8434,6 +8375,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + negotiator@~0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" @@ -8476,16 +8422,21 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-forge@>=1.3.2, node-forge@^1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.2.tgz#d0d2659a26eef778bf84d73e7f55c08144ee7750" + integrity sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw== node-releases@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + nopt@1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -8513,6 +8464,13 @@ not@^0.1.0: resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" integrity sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nprogress@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -8538,10 +8496,10 @@ object-assign@^4.0.1, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" - integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-is@^1.1.5: version "1.1.6" @@ -8571,7 +8529,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1, on-finished@^2.4.1: +on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -8583,13 +8541,20 @@ on-headers@>=1.1.0, on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== -once@^1.3.0: +once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + open@^10.0.3: version "10.1.2" resolved "https://registry.yarnpkg.com/open/-/open-10.1.2.tgz#d5df40984755c9a9c3c93df8156a12467e882925" @@ -8619,19 +8584,10 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-limit@^4.0.0: version "4.0.0" @@ -8640,20 +8596,6 @@ p-limit@^4.0.0: dependencies: yocto-queue "^1.0.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - p-locate@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" @@ -8668,6 +8610,14 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + p-retry@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" @@ -8677,15 +8627,12 @@ p-retry@^6.2.0: is-network-error "^1.0.0" retry "^0.13.1" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" package-json@^8.1.0: version "8.1.1" @@ -8731,7 +8678,7 @@ parse-entities@^4.0.0: is-decimal "^2.0.0" is-hexadecimal "^2.0.0" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -8766,7 +8713,7 @@ parse5@^7.0.0: dependencies: entities "^4.5.0" -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@^1.3.3, parseurl@~1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -8789,32 +8736,17 @@ path-data-parser@0.1.0, path-data-parser@^0.1.0: resolved "https://registry.yarnpkg.com/path-data-parser/-/path-data-parser-0.1.0.tgz#8f5ba5cc70fc7becb3dcefaea08e2659aba60b8c" integrity sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-exists@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-is-inside@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -8836,18 +8768,13 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== +path-scurry@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.1.tgz#4b6572376cfd8b811fca9cd1f5c24b3cbac0fe10" + integrity sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA== dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + lru-cache "^11.0.0" + minipass "^7.1.2" path-to-regexp@3.3.0: version "3.3.0" @@ -8861,6 +8788,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -8920,13 +8852,6 @@ pkg-types@^2.3.0: exsolve "^1.0.7" pathe "^2.0.3" -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== - dependencies: - find-up "^3.0.0" - points-on-curve@0.2.0, points-on-curve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/points-on-curve/-/points-on-curve-0.2.0.tgz#7dbb98c43791859434284761330fa893cb81b4d1" @@ -8967,15 +8892,15 @@ postcss-clamp@^4.1.0: dependencies: postcss-value-parser "^4.2.0" -postcss-color-functional-notation@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.6.tgz#d74c1e2294b72287eb9af079c04b7ddeff7ec5b3" - integrity sha512-wLXvm8RmLs14Z2nVpB4CWlnvaWPRcOZFltJSlcbYwSJ1EDZKsKDhPKIMecCnuU054KSmlmubkqczmm6qBPCBhA== +postcss-color-functional-notation@^7.0.12: + version "7.0.12" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz#9a3df2296889e629fde18b873bb1f50a4ecf4b83" + integrity sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-color-hex-alpha@^10.0.0: @@ -9012,35 +8937,35 @@ postcss-convert-values@^6.1.0: browserslist "^4.23.0" postcss-value-parser "^4.2.0" -postcss-custom-media@^11.0.5: - version "11.0.5" - resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz#2fcd88a9b1d4da41c67dac6f2def903063a3377d" - integrity sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ== +postcss-custom-media@^11.0.6: + version "11.0.6" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz#6b450e5bfa209efb736830066682e6567bd04967" + integrity sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" -postcss-custom-properties@^14.0.4: - version "14.0.4" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz#de9c663285a98833a946d7003a34369d3ce373a9" - integrity sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A== +postcss-custom-properties@^14.0.6: + version "14.0.6" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz#1af73a650bf115ba052cf915287c9982825fc90e" + integrity sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -postcss-custom-selectors@^8.0.4: - version "8.0.4" - resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz#95ef8268fdbbbd84f34cf84a4517c9d99d419c5a" - integrity sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg== +postcss-custom-selectors@^8.0.5: + version "8.0.5" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz#9448ed37a12271d7ab6cb364b6f76a46a4a323e8" + integrity sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" postcss-selector-parser "^7.0.0" postcss-dir-pseudo-class@^9.0.1: @@ -9077,12 +9002,12 @@ postcss-discard-unused@^6.0.5: dependencies: postcss-selector-parser "^6.0.16" -postcss-double-position-gradients@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz#eddd424ec754bb543d057d4d2180b1848095d4d2" - integrity sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg== +postcss-double-position-gradients@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz#b482d08b5ced092b393eb297d07976ab482d4cad" + integrity sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" @@ -9118,18 +9043,18 @@ postcss-image-set-function@^7.0.0: "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -postcss-lab-function@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-7.0.6.tgz#3121800fc7939ed1d9a1e87abeb33c407151252c" - integrity sha512-HPwvsoK7C949vBZ+eMyvH2cQeMr3UREoHvbtra76/UhDuiViZH6pir+z71UaJQohd7VDSVUdR6TkWYKExEc9aQ== +postcss-lab-function@^7.0.12: + version "7.0.12" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz#eb555ac542607730eb0a87555074e4a5c6eef6e4" + integrity sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -postcss-loader@^7.3.3: +postcss-loader@^7.3.4: version "7.3.4" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== @@ -9138,10 +9063,10 @@ postcss-loader@^7.3.3: jiti "^1.20.0" semver "^7.5.4" -postcss-logical@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-8.0.0.tgz#0db0b90c2dc53b485a8074a4b7a906297544f58d" - integrity sha512-HpIdsdieClTjXLOyYdUPAX/XQASNIwdKt5hoZW08ZOAiI+tbV0ta1oclkpVkW5ANU+xJvk3KkA0FejkjGLXUkg== +postcss-logical@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-8.1.0.tgz#4092b16b49e3ecda70c4d8945257da403d167228" + integrity sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA== dependencies: postcss-value-parser "^4.2.0" @@ -9231,12 +9156,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nesting@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-13.0.1.tgz#c405796d7245a3e4c267a9956cacfe9670b5d43e" - integrity sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ== +postcss-nesting@^13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-13.0.2.tgz#fde0d4df772b76d03b52eccc84372e8d1ca1402e" + integrity sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ== dependencies: - "@csstools/selector-resolve-nested" "^3.0.0" + "@csstools/selector-resolve-nested" "^3.1.0" "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" @@ -9334,67 +9259,71 @@ postcss-place@^10.0.0: dependencies: postcss-value-parser "^4.2.0" -postcss-preset-env@^10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.1.1.tgz#6ee631272353fb1c4a9711943e9b80a178ffce44" - integrity sha512-wqqsnBFD6VIwcHHRbhjTOcOi4qRVlB26RwSr0ordPj7OubRRxdWebv/aLjKLRR8zkZrbxZyuus03nOIgC5elMQ== +postcss-preset-env@^10.2.1: + version "10.4.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.4.0.tgz#fa6167a307f337b2bcdd1d125604ff97cdeb5142" + integrity sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw== dependencies: - "@csstools/postcss-cascade-layers" "^5.0.1" - "@csstools/postcss-color-function" "^4.0.6" - "@csstools/postcss-color-mix-function" "^3.0.6" - "@csstools/postcss-content-alt-text" "^2.0.4" - "@csstools/postcss-exponential-functions" "^2.0.5" + "@csstools/postcss-alpha-function" "^1.0.1" + "@csstools/postcss-cascade-layers" "^5.0.2" + "@csstools/postcss-color-function" "^4.0.12" + "@csstools/postcss-color-function-display-p3-linear" "^1.0.1" + "@csstools/postcss-color-mix-function" "^3.0.12" + "@csstools/postcss-color-mix-variadic-function-arguments" "^1.0.2" + "@csstools/postcss-content-alt-text" "^2.0.8" + "@csstools/postcss-contrast-color-function" "^2.0.12" + "@csstools/postcss-exponential-functions" "^2.0.9" "@csstools/postcss-font-format-keywords" "^4.0.0" - "@csstools/postcss-gamut-mapping" "^2.0.6" - "@csstools/postcss-gradients-interpolation-method" "^5.0.6" - "@csstools/postcss-hwb-function" "^4.0.6" - "@csstools/postcss-ic-unit" "^4.0.0" - "@csstools/postcss-initial" "^2.0.0" - "@csstools/postcss-is-pseudo-class" "^5.0.1" - "@csstools/postcss-light-dark-function" "^2.0.7" + "@csstools/postcss-gamut-mapping" "^2.0.11" + "@csstools/postcss-gradients-interpolation-method" "^5.0.12" + "@csstools/postcss-hwb-function" "^4.0.12" + "@csstools/postcss-ic-unit" "^4.0.4" + "@csstools/postcss-initial" "^2.0.1" + "@csstools/postcss-is-pseudo-class" "^5.0.3" + "@csstools/postcss-light-dark-function" "^2.0.11" "@csstools/postcss-logical-float-and-clear" "^3.0.0" "@csstools/postcss-logical-overflow" "^2.0.0" "@csstools/postcss-logical-overscroll-behavior" "^2.0.0" "@csstools/postcss-logical-resize" "^3.0.0" - "@csstools/postcss-logical-viewport-units" "^3.0.3" - "@csstools/postcss-media-minmax" "^2.0.5" - "@csstools/postcss-media-queries-aspect-ratio-number-values" "^3.0.4" + "@csstools/postcss-logical-viewport-units" "^3.0.4" + "@csstools/postcss-media-minmax" "^2.0.9" + "@csstools/postcss-media-queries-aspect-ratio-number-values" "^3.0.5" "@csstools/postcss-nested-calc" "^4.0.0" "@csstools/postcss-normalize-display-values" "^4.0.0" - "@csstools/postcss-oklab-function" "^4.0.6" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" - "@csstools/postcss-random-function" "^1.0.1" - "@csstools/postcss-relative-color-syntax" "^3.0.6" + "@csstools/postcss-oklab-function" "^4.0.12" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/postcss-random-function" "^2.0.1" + "@csstools/postcss-relative-color-syntax" "^3.0.12" "@csstools/postcss-scope-pseudo-class" "^4.0.1" - "@csstools/postcss-sign-functions" "^1.1.0" - "@csstools/postcss-stepped-value-functions" "^4.0.5" - "@csstools/postcss-text-decoration-shorthand" "^4.0.1" - "@csstools/postcss-trigonometric-functions" "^4.0.5" + "@csstools/postcss-sign-functions" "^1.1.4" + "@csstools/postcss-stepped-value-functions" "^4.0.9" + "@csstools/postcss-text-decoration-shorthand" "^4.0.3" + "@csstools/postcss-trigonometric-functions" "^4.0.9" "@csstools/postcss-unset-value" "^4.0.0" - autoprefixer "^10.4.19" - browserslist "^4.23.1" + autoprefixer "^10.4.21" + browserslist "^4.26.0" css-blank-pseudo "^7.0.1" - css-has-pseudo "^7.0.1" + css-has-pseudo "^7.0.3" css-prefers-color-scheme "^10.0.0" - cssdb "^8.2.1" + cssdb "^8.4.2" postcss-attribute-case-insensitive "^7.0.1" postcss-clamp "^4.1.0" - postcss-color-functional-notation "^7.0.6" + postcss-color-functional-notation "^7.0.12" postcss-color-hex-alpha "^10.0.0" postcss-color-rebeccapurple "^10.0.0" - postcss-custom-media "^11.0.5" - postcss-custom-properties "^14.0.4" - postcss-custom-selectors "^8.0.4" + postcss-custom-media "^11.0.6" + postcss-custom-properties "^14.0.6" + postcss-custom-selectors "^8.0.5" postcss-dir-pseudo-class "^9.0.1" - postcss-double-position-gradients "^6.0.0" + postcss-double-position-gradients "^6.0.4" postcss-focus-visible "^10.0.1" postcss-focus-within "^9.0.1" postcss-font-variant "^5.0.0" postcss-gap-properties "^6.0.0" postcss-image-set-function "^7.0.0" - postcss-lab-function "^7.0.6" - postcss-logical "^8.0.0" - postcss-nesting "^13.0.1" + postcss-lab-function "^7.0.12" + postcss-logical "^8.1.0" + postcss-nesting "^13.0.2" postcss-opacity-percentage "^3.0.0" postcss-overflow-shorthand "^6.0.0" postcss-page-break "^3.0.4" @@ -9492,7 +9421,7 @@ postcss-zindex@^6.0.2: resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-6.0.2.tgz#e498304b83a8b165755f53db40e2ea65a99b56e1" integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg== -postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: +postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.33: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -9501,6 +9430,15 @@ postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4 picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.5.4: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -9566,7 +9504,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -proxy-addr@~2.0.7: +proxy-addr@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -9586,12 +9524,12 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== +qs@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" quansync@^0.2.11: version "0.2.11" @@ -9620,20 +9558,20 @@ range-parser@1.2.0: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" rc@1.2.8: version "1.2.8" @@ -9645,36 +9583,6 @@ rc@1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dev-utils@^12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" - integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== - dependencies: - "@babel/code-frame" "^7.16.0" - address "^1.1.2" - browserslist "^4.18.1" - chalk "^4.1.2" - cross-spawn "^7.0.3" - detect-port-alt "^1.1.6" - escape-string-regexp "^4.0.0" - filesize "^8.0.6" - find-up "^5.0.0" - fork-ts-checker-webpack-plugin "^6.5.0" - global-modules "^2.0.0" - globby "^11.0.4" - gzip-size "^6.0.0" - immer "^9.0.7" - is-root "^2.1.0" - loader-utils "^3.2.0" - open "^8.4.0" - pkg-up "^3.1.0" - prompts "^2.4.2" - react-error-overlay "^6.0.11" - recursive-readdir "^2.2.2" - shell-quote "^1.7.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - react-dom@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -9683,29 +9591,15 @@ react-dom@^18.0.0: loose-envify "^1.1.0" scheduler "^0.23.2" -react-error-overlay@^6.0.11: - version "6.0.11" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" - integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== - -react-fast-compare@^3.2.0, react-fast-compare@^3.2.2: +react-fast-compare@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== -react-helmet-async@*: - version "2.0.5" - resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-2.0.5.tgz#cfc70cd7bb32df7883a8ed55502a1513747223ec" - integrity sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg== - dependencies: - invariant "^2.2.4" - react-fast-compare "^3.2.2" - shallowequal "^1.1.0" - -react-helmet-async@^1.3.0: +"react-helmet-async@npm:@slorber/react-helmet-async@1.3.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" - integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== + resolved "https://registry.yarnpkg.com/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz#11fbc6094605cf60aa04a28c17e0aab894b4ecff" + integrity sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A== dependencies: "@babel/runtime" "^7.12.5" invariant "^2.2.4" @@ -9718,10 +9612,10 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-json-view-lite@^1.2.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz#377cc302821717ac79a1b6d099e1891df54c8662" - integrity sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw== +react-json-view-lite@^2.3.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/react-json-view-lite/-/react-json-view-lite-2.5.0.tgz#c7ff011c7cc80e9900abc7aa4916c6a5c6d6c1c6" + integrity sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g== react-lifecycles-compat@^3.0.0: version "3.0.4" @@ -9832,18 +9726,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -reading-time@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" - integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== - dependencies: - resolve "^1.1.6" - recma-build-jsx@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz#c02f29e047e103d2fab2054954e1761b8ea253c4" @@ -9884,13 +9766,6 @@ recma-stringify@^1.0.0: unified "^11.0.0" vfile "^6.0.0" -recursive-readdir@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" - integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== - dependencies: - minimatch "^3.0.5" - regenerate-unicode-properties@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" @@ -10081,20 +9956,10 @@ remark-parse@^11.0.0: micromark-util-types "^2.0.0" unified "^11.0.0" -remark-rehype@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" - integrity sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw== - dependencies: - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-to-hast "^12.1.0" - unified "^10.0.0" - -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== +remark-rehype@>=11.0.0, remark-rehype@^10.0.0, remark-rehype@^11.0.0: + version "11.1.2" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.2.tgz#2addaadda80ca9bd9aa0da763e74d16327683b37" + integrity sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -10164,7 +10029,7 @@ resolve-pathname@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== -resolve@^1.1.6, resolve@^1.14.2: +resolve@^1.14.2: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -10190,13 +10055,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - robust-predicates@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" @@ -10212,10 +10070,16 @@ roughjs@^4.6.6: points-on-curve "^0.2.0" points-on-path "^0.2.1" -rtl-detect@^1.0.4: - version "1.1.2" - resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.1.2.tgz#ca7f0330af5c6bb626c15675c642ba85ad6273c6" - integrity sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ== +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" rtlcss@^4.1.0: version "4.3.0" @@ -10261,7 +10125,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -10278,14 +10142,10 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" +schema-dts@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/schema-dts/-/schema-dts-1.1.5.tgz#9237725d305bac3469f02b292a035107595dc324" + integrity sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg== schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" @@ -10349,29 +10209,27 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4, semver@^7.6.3: +semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -send@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== +send@^1.1.0, send@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" + integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" + debug "^4.3.5" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.0" + mime-types "^3.0.1" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.1" serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" @@ -10406,15 +10264,15 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== +serve-static@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" + integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" set-function-length@^1.2.2: version "1.2.2" @@ -10433,7 +10291,7 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: +setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -10462,40 +10320,56 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3, shell-quote@^1.8.1: +shell-quote@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.2.tgz#d2d83e057959d53ec261311e9e9b8f51dcb2934a" integrity sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA== -shelljs@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" + object-inspect "^1.13.3" -signal-exit@^3.0.0, signal-exit@^3.0.2: +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - sirv@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" @@ -10630,16 +10504,16 @@ srcset@^4.0.0: resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@^2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + std-env@^3.7.0: version "3.8.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" @@ -10713,6 +10587,11 @@ strip-bom-string@^1.0.0: resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -10800,6 +10679,14 @@ svgo@^3.0.2, svgo@^3.2.0: csso "^5.0.5" picocolors "^1.0.0" +swr@^2.2.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.6.tgz#5fee0ee8a0762a16871ee371075cb09422b64f50" + integrity sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw== + dependencies: + dequal "^2.0.3" + use-sync-external-store "^1.4.0" + synp@^1.9.10: version "1.9.14" resolved "https://registry.yarnpkg.com/synp/-/synp-1.9.14.tgz#1feb222d273f6092c6c264746277e95655c60717" @@ -10815,11 +10702,6 @@ synp@^1.9.10: semver "^7.6.3" sort-object-keys "^1.1.3" -tapable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -10846,11 +10728,6 @@ terser@^5.10.0, terser@^5.15.1, terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -10870,6 +10747,11 @@ thingies@^1.20.0: resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== +throttleit@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" + integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -10890,6 +10772,11 @@ tinyexec@^1.0.1: resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.1.tgz#70c31ab7abbb4aea0a24f55d120e5990bfa1e0b1" integrity sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw== +tinypool@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -10905,7 +10792,7 @@ to-vfile@^6.1.0: is-buffer "^2.0.0" vfile "^4.0.0" -toidentifier@1.0.1: +toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -10970,13 +10857,14 @@ type-fest@^2.13.0, type-fest@^2.5.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -11075,11 +10963,6 @@ unist-util-find-after@^3.0.0: dependencies: unist-util-is "^4.0.0" -unist-util-generated@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.1.tgz#e37c50af35d3ed185ac6ceacb6ca0afb28a85cae" - integrity sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== - unist-util-is@^4.0.0, unist-util-is@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" @@ -11221,7 +11104,7 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -11234,6 +11117,14 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.0" +update-browserslist-db@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-6.0.2.tgz#a6990253dfe6d5a02bd04fbb6a61543f55026b60" @@ -11275,6 +11166,11 @@ use-editable@^2.3.3: resolved "https://registry.yarnpkg.com/use-editable/-/use-editable-2.3.3.tgz#a292fe9ba4c291cd28d1cc2728c75a5fc8d9a33f" integrity sha512-7wVD2JbfAFJ3DK0vITvXBdpd9JAz5BcKAAolsnLBuBn6UDDwBGuCIAGvR3yA2BNKm578vAMVHFCWaOcA+BhhiA== +use-sync-external-store@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -11301,11 +11197,6 @@ utility-types@^3.10.0: resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c" integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw== -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - uuid@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" @@ -11339,7 +11230,7 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -vary@~1.1.2: +vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -11511,7 +11402,7 @@ webpack-dev-middleware@^7.4.2: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@>=5.2.1, webpack-dev-server@^4.15.2: +webpack-dev-server@>=5.2.1, webpack-dev-server@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz#96a143d50c58fef0c79107e61df911728d7ceb39" integrity sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg== @@ -11651,13 +11542,6 @@ which@4.0.0: dependencies: isexe "^3.1.1" -which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -11749,11 +11633,6 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.7.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yarn-audit-fix@^9.3.10: version "9.3.12" resolved "https://registry.yarnpkg.com/yarn-audit-fix/-/yarn-audit-fix-9.3.12.tgz#cc34e87aa080bace32f2f105be6b581a3cb6eb24" @@ -11778,16 +11657,16 @@ yarn-audit-fix@^9.3.10: synp "^1.9.10" tslib "^2.5.3" -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - yocto-queue@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== +zod@^4.1.8: + version "4.1.12" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" + integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"