diff --git a/.cargo/config.toml b/.cargo/config.toml index eb89fa1e55..7e4e7a0f90 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,8 @@ [alias] xtask = "run --package xtask --" -# @fb-only: [build] -# @fb-only: target-dir = "../../../buck-out/elp" +# @fb-only +# @fb-only [profile.release] codegen-units = 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9980283f2..471b9d2418 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-15-x64, macos-latest-arm, windows-2022-x64] + platform-arch: [ubuntu-22.04-x64, ubuntu-22.04-arm, macos-13-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-15-x64 - platform: macos-15-intel + - platform-arch: macos-13-x64 + platform: macos-13 os: macos target: x86_64-apple-darwin vscode-target: darwin-x64 @@ -97,8 +97,6 @@ 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: @@ -137,7 +135,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 --workspace --target ${{ matrix.target }}' + run: 'cargo test --no-default-features --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\"' @@ -202,8 +200,6 @@ 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 @@ -289,7 +285,3 @@ 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 efd132ac4b..d6462ed7e3 100644 --- a/.llms/rules/elp_development.md +++ b/.llms/rules/elp_development.md @@ -3,34 +3,13 @@ llms-gk: 'devmate_elp_development_md' apply_to_regex: '^(.*\.rs|.*\.md)$' oncalls: ['vscode_erlang'] --- -# ELP Development Rules for LLMs (OSS) + +# ELP Development Rules for LLMs + ## 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. - -## 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 -``` +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. ## Diagnostic Code Management @@ -38,13 +17,13 @@ cargo xtask codegen 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 @@ -58,8 +37,7 @@ 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 @@ -73,19 +51,16 @@ 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 @@ -94,8 +69,7 @@ when and how the diagnostic runs: - 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 @@ -104,6 +78,12 @@ when and how the diagnostic runs: - 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 @@ -138,96 +118,12 @@ when and how the diagnostic runs: - Group related tests in the same module - Use descriptive test names that explain the scenario -### 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 `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 -3. **Annotations**: Mark expected diagnostics or ranges using `%% ^^^` syntax -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: - -- **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 - -```rust -let fixture = r#" -//- /src/main.erl --module(main). - -foo( -> ok. %% -%% ^ error: W0004: Missing ')'~ -"#; -``` - ### Test Data - Create minimal test cases that focus on specific functionality - Use realistic Erlang code examples in tests - Test both positive and negative cases -### 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: - -| 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_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` | - -Example: To run tests for the `elp_ide` crate: - -```bash -cargo test -p elp_ide -``` - -Or to run tests in a specific directory: - -```bash -cargo test --manifest-path crates/ide/Cargo.toml -``` - ### Existing tests - Do not change existing tests without asking @@ -315,8 +211,14 @@ cargo test --manifest-path crates/ide/Cargo.toml - 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 `cargo clippy --tests` before submitting PRs -- Use `cargo fmt` for code formatting +- Always run tests before finishing. +- Always run `./meta/cargo.sh clippy --tests` before submitting a diff diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7572a84e98..51f0340659 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ { "label": "ELP: build (debug)", "type": "shell", - // @fb-only: "command": "./meta/cargo.sh build", + // @fb-only "command": "cargo build", // @oss-only "group": { "kind": "build", @@ -19,7 +19,7 @@ { "label": "ELP: build (release)", "type": "shell", - // @fb-only: "command": "./meta/cargo.sh build --release", + // @fb-only "command": "cargo build --release", // @oss-only "group": { "kind": "build", @@ -34,7 +34,7 @@ { "label": "ELP: build (release-thin)", "type": "shell", - // @fb-only: "command": "./meta/cargo.sh build --profile release-thin --bins", + // @fb-only "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: "command": "./meta/clippy.sh --workspace --tests", + // @fb-only "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: "command": "./meta/clippy.sh --workspace --tests --fix", + // @fb-only "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: "command": "./meta/cargo.sh test --workspace", + // @fb-only "command": "cargo test --workspace", // @oss-only "group": { "kind": "build", diff --git a/Cargo.lock b/Cargo.lock index 2da9907eca..03f23763eb 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,6 +572,7 @@ dependencies = [ "elp_ide_ssr", "elp_project_model", "elp_syntax", + "elp_text_edit", "elp_types_db", "env_logger", "expect-test", @@ -603,6 +604,7 @@ dependencies = [ "cov-mark", "elp_ide_db", "elp_syntax", + "elp_text_edit", "expect-test", "fxhash", "hir", @@ -635,7 +637,6 @@ name = "elp_ide_db" version = "1.1.0" dependencies = [ "anyhow", - "cov-mark", "eetf", "either", "elp_base_db", @@ -643,12 +644,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", @@ -663,7 +664,6 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "text-size", "toml", "tracing", ] @@ -734,8 +734,10 @@ 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", @@ -755,6 +757,14 @@ 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 825530fe4c..5814517745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,13 @@ 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 new file mode 100644 index 0000000000..6b2733b5b9 --- /dev/null +++ b/bench_runner/example_bench/benches/main.rs @@ -0,0 +1,60 @@ +/* + * 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 new file mode 100644 index 0000000000..f895c08c52 --- /dev/null +++ b/bench_runner/runner/main.rs @@ -0,0 +1,16 @@ +/* + * 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 cd1328d14d..e94e180032 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -87,7 +87,6 @@ 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) } } @@ -102,7 +101,6 @@ pub struct ChangeFixture { pub diagnostics_enabled: DiagnosticsEnabled, pub tags: FxHashMap)>>, pub annotations: FxHashMap>, - pub expect_parse_errors: bool, } struct Builder { @@ -174,7 +172,6 @@ impl ChangeFixture { let FixtureWithProjectMeta { fixture, mut diagnostics_enabled, - expect_parse_errors, } = fixture_with_meta.clone(); let builder = Builder::new(diagnostics_enabled.clone()); @@ -298,7 +295,7 @@ impl ChangeFixture { ProjectManifest::discover(&AbsPathBuf::assert(json_config_file.into())).unwrap(); let loaded_project = Project::load( &manifest, - &elp_config, + elp_config.eqwalizer, &BuckQueryConfig::BuildGeneratedCode, &|_| {}, ) @@ -347,7 +344,6 @@ impl ChangeFixture { diagnostics_enabled, tags, annotations, - expect_parse_errors, }, change, project, @@ -409,64 +405,6 @@ 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 0cd8df74c9..6b3757ff43 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: mod meta_only; +// @fb-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: meta_only::ignored_sources_regexes() + // @fb-only ]; regexes.into_iter().flatten().collect::>() }; diff --git a/crates/elp/Cargo.toml b/crates/elp/Cargo.toml index 2a1b6e8b65..66552ada1d 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 374dcdba2f..c113311661 100644 --- a/crates/elp/src/arc_types.rs +++ b/crates/elp/src/arc_types.rs @@ -8,8 +8,8 @@ * above-listed licenses. */ -// @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 +// @fb-only +// @fb-only use std::path::Path; use serde::Serialize; diff --git a/crates/elp/src/bin/args.rs b/crates/elp/src/bin/args.rs index c9790e9314..961d3216f7 100644 --- a/crates/elp/src/bin/args.rs +++ b/crates/elp/src/bin/args.rs @@ -11,20 +11,14 @@ use std::cmp::Ordering; use std::env; use std::fs; -use std::io::IsTerminal; use std::path::PathBuf; -use anyhow::Result; -use anyhow::bail; use bpaf::Bpaf; use bpaf::Parser; use bpaf::construct; use bpaf::long; use elp_ide::elp_ide_db::DiagnosticCode; use elp_project_model::buck::BuckQueryConfig; -use hir::fold::MacroStrategy; -use hir::fold::ParenStrategy; -use hir::fold::Strategy; use itertools::Itertools; use serde::Deserialize; @@ -72,17 +66,6 @@ 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)] @@ -155,6 +138,8 @@ 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 @@ -171,6 +156,8 @@ 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/... @@ -189,6 +176,8 @@ 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 @@ -211,6 +200,8 @@ 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, } @@ -278,6 +269,8 @@ 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, @@ -334,102 +327,11 @@ 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, } -#[derive(Clone, Debug, Bpaf)] -pub struct Ssr { - /// Path to directory with project, or to a JSON file (defaults to `.`) - #[bpaf(argument("PROJECT"), fallback(PathBuf::from(".")))] - pub project: PathBuf, - /// Parse a single module from the project, not the entire project. - #[bpaf(argument("MODULE"))] - pub module: Option, - /// Parse a single application from the project, not the entire project. - #[bpaf(long("app"), long("application"), argument("APP"))] - pub app: Option, - /// Parse a single file from the project, not the entire project. This can be an include file or escript, etc. - #[bpaf(argument("FILE"))] - pub file: Option, - - /// Run with rebar - pub rebar: bool, - /// Rebar3 profile to pickup (default is test) - #[bpaf(long("as"), argument("PROFILE"), fallback("test".to_string()))] - pub profile: String, - - /// Also generate diagnostics for generated files - pub include_generated: bool, - /// Also generate diagnostics for test files - pub include_tests: bool, - - /// Show diagnostics in JSON format - #[bpaf( - argument("FORMAT"), - complete(format_completer), - fallback(None), - guard(format_guard, "Please use json") - )] - pub format: Option, - - /// Macro expansion strategy: expand | no-expand | visible-expand (default expand) - #[bpaf( - long("macros"), - argument("STRATEGY"), - complete(macros_completer), - fallback(None), - guard(macros_guard, "Please supply a valid macro expansion value") - )] - pub macro_strategy: Option, - - /// Explicitly match parentheses. If omitted, they are ignored. - #[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, - - /// SSR specs to use - #[bpaf( - positional("SSR_SPECS"), - guard(at_least_1, "there should be at least one spec") - )] - pub ssr_specs: Vec, -} - #[derive(Clone, Debug, Bpaf)] pub struct Explain { /// Error code to explain @@ -454,8 +356,6 @@ pub struct ProjectInfo { pub to: Option, /// Include the buck uquery results in the output pub buck_query: bool, - /// Dump a list of targets and their types - pub target_types: bool, } #[derive(Clone, Debug, Bpaf)] @@ -474,6 +374,8 @@ 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)] @@ -493,7 +395,6 @@ pub enum Command { GenerateCompletions(GenerateCompletions), RunServer(RunServer), Lint(Lint), - Ssr(Ssr), Version(Version), Shell(Shell), Explain(Explain), @@ -517,47 +418,18 @@ pub struct Args { /// When using buck, do not invoke a build step for generated files. pub no_buck_generated: bool, - /// 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, } impl Args { pub fn query_config(&self) -> BuckQueryConfig { - if self.buck_quick_start { - BuckQueryConfig::BuckTargetsOnly - } else if self.no_buck_generated { + if self.no_buck_generated { BuckQueryConfig::NoBuildGeneratedCode } else { 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 { @@ -601,8 +473,7 @@ pub fn command() -> impl Parser { .map(Command::EqwalizeStats) .to_options() .command("eqwalize-stats") - .help("Return statistics about code quality for eqWAlizer") - .hide(); + .help("Return statistics about code quality for eqWAlizer"); let dialyze_all = dialyze_all() .map(Command::DialyzeAll) @@ -629,18 +500,6 @@ 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() - .command("ssr") - .help("Run SSR (Structural Search and Replace) pattern matching on project files."); - let run_server = run_server() .map(Command::RunServer) .to_options() @@ -684,26 +543,23 @@ pub fn command() -> impl Parser { .help("Dump a JSON config stanza suitable for use in VS Code project.json"); construct!([ - // Note: The order here is what is used for `elp --help` output - version, - run_server, - shell, eqwalize, eqwalize_all, eqwalize_app, eqwalize_target, - eqwalize_stats, dialyze_all, lint, - ssr, - search, + run_server, + generate_completions, parse_all, parse_elp, - explain, build_info, + version, + shell, + eqwalize_stats, + explain, project_info, glean, - generate_completions, config_stanza, ]) .fallback(Help()) @@ -786,48 +642,6 @@ 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), - ("no-expand".to_string(), None), - ("visible-expand".to_string(), None), - ] -} - -fn macros_guard(format: &Option) -> bool { - match format { - None => true, - Some(_) => parse_macro_strategy(format).is_ok(), - } -} - -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() @@ -908,44 +722,6 @@ 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 { - match macro_strategy.as_deref() { - Some("no-expand") => Ok(MacroStrategy::DoNotExpand), - Some("expand") => Ok(MacroStrategy::Expand), - Some("visible-expand") => Ok(MacroStrategy::ExpandButIncludeMacroCall), - None => Ok(MacroStrategy::Expand), - Some(s) => bail!( - "Invalid macro strategy '{}'. Valid options are: expand, no-expand, visible-expand", - s - ), - } -} - -impl Ssr { - pub fn is_format_normal(&self) -> bool { - self.format.is_none() - } - - pub fn is_format_json(&self) -> bool { - self.format == Some("json".to_string()) - } - - pub fn parse_strategy(&self) -> Result { - let macros = parse_macro_strategy(&self.macro_strategy)?; - let parens = if self.paren_strategy { - ParenStrategy::VisibleParens - } else { - ParenStrategy::InvisibleParens - }; - Ok(Strategy { macros, parens }) - } } impl ParseAllElp { diff --git a/crates/elp/src/bin/build_info_cli.rs b/crates/elp/src/bin/build_info_cli.rs index 2308acb2a7..d091fc8e90 100644 --- a/crates/elp/src/bin/build_info_cli.rs +++ b/crates/elp/src/bin/build_info_cli.rs @@ -15,18 +15,15 @@ use std::io::Write; use anyhow::Result; use elp_ide::elp_ide_db::elp_base_db::AbsPath; use elp_ide::elp_ide_db::elp_base_db::AbsPathBuf; -use elp_project_model::AppType; use elp_project_model::ElpConfig; +use elp_project_model::EqwalizerConfig; use elp_project_model::IncludeParentDirs; use elp_project_model::Project; -use elp_project_model::ProjectAppData; use elp_project_model::ProjectBuildData; use elp_project_model::ProjectManifest; use elp_project_model::buck::BuckQueryConfig; -use elp_project_model::buck::BuckTarget; -use elp_project_model::buck::query_buck_targets; +use elp_project_model::buck::query_buck_targets_bxl; use elp_project_model::json::JsonConfig; -use fxhash::FxHashMap; use crate::args::BuildInfo; use crate::args::ProjectInfo; @@ -34,8 +31,8 @@ use crate::args::ProjectInfo; pub(crate) fn save_build_info(args: BuildInfo, query_config: &BuckQueryConfig) -> Result<()> { let root = fs::canonicalize(&args.project)?; let root = AbsPathBuf::assert_utf8(root); - let (elp_config, manifest) = ProjectManifest::discover(&root)?; - let project = Project::load(&manifest, &elp_config, query_config, &|_| {})?; + let (_elp_config, manifest) = ProjectManifest::discover(&root)?; + let project = Project::load(&manifest, EqwalizerConfig::default(), query_config, &|_| {})?; let mut writer = File::create(&args.to)?; let json_str = serde_json::to_string_pretty::(&project.as_json(root))?; writer.write_all(json_str.as_bytes())?; @@ -69,60 +66,17 @@ pub(crate) fn save_project_info(args: ProjectInfo, query_config: &BuckQueryConfi if args.buck_query && let ProjectBuildData::Buck(buck) = &project.project_build_data { - let buck_targets_query = query_buck_targets(&buck.buck_conf, query_config); - if let Ok(targets) = &buck_targets_query { - writer.write_all(format!("{:#?}\n", sort_buck_targets(targets)).as_bytes())?; - } else { - writer.write_all(format!("{:#?}\n", &buck_targets_query).as_bytes())?; - } - } else if args.target_types { - writer.write_all(b"================target types================\n")?; - for line in buck_targets_and_types(&project.project_apps) { - writer.write_all(format!("{}\n", line).as_bytes())?; - } - } else { - writer.write_all(b"================manifest================\n")?; - writer.write_all(format!("{:#?}\n", &manifest).as_bytes())?; - writer.write_all(b"================project_build_data================\n")?; - writer.write_all(format!("{:#?}\n", &project.project_build_data).as_bytes())?; - writer.write_all(b"================project_app_data================\n")?; - writer.write_all(format!("{:#?}\n", &project.project_apps).as_bytes())?; - } - Ok(()) -} - -fn sort_buck_targets(hash_map: &FxHashMap) -> Vec<(String, &BuckTarget)> { - let mut vec = hash_map - .iter() - .map(|(n, t)| (format!("target_name:{}", n), t)) - .collect::>(); - vec.sort_by(|a, b| a.0.cmp(&b.0)); - vec -} - -fn buck_targets_and_types(apps: &[ProjectAppData]) -> Vec { - let tn = |tn| -> String { - if let Some(tn) = tn { - tn - } else { - "".to_string() - } + let buck_targets_query = query_buck_targets_bxl(&buck.buck_conf, query_config); + writer.write_all(b"================buck targets query raw================\n")?; + writer.write_all(format!("{:#?}\n", &buck_targets_query).as_bytes())?; }; - let mut vec = apps - .iter() - .filter(|app| app.app_type != AppType::Otp) - .filter(|app| app.is_buck_generated != Some(true)) - .map(|app| { - format!( - "{:?} {:<30} {}", - app.app_type, - app.name, - tn(app.buck_target_name.clone()) - ) - }) - .collect::>(); - vec.sort(); - vec + writer.write_all(b"================manifest================\n")?; + writer.write_all(format!("{:#?}\n", &manifest).as_bytes())?; + writer.write_all(b"================project_build_data================\n")?; + writer.write_all(format!("{:#?}\n", &project.project_build_data).as_bytes())?; + writer.write_all(b"================project_app_data================\n")?; + writer.write_all(format!("{:#?}\n", &project.project_apps).as_bytes())?; + Ok(()) } fn load_project( @@ -130,7 +84,7 @@ fn load_project( query_config: &BuckQueryConfig, ) -> Result<(ProjectManifest, Project)> { let (elp_config, manifest) = ProjectManifest::discover(root)?; - let project = Project::load(&manifest, &elp_config, query_config, &|_| {})?; + let project = Project::load(&manifest, elp_config.eqwalizer, query_config, &|_| {})?; Ok((manifest, project)) } @@ -140,6 +94,6 @@ fn load_fallback( ) -> Result<(ProjectManifest, Project)> { let manifest = ProjectManifest::discover_no_manifest(root, IncludeParentDirs::Yes); let elp_config = ElpConfig::default(); - let project = Project::load(&manifest, &elp_config, query_config, &|_| {})?; + let project = Project::load(&manifest, elp_config.eqwalizer, query_config, &|_| {})?; Ok((manifest, project)) } diff --git a/crates/elp/src/bin/elp_parse_cli.rs b/crates/elp/src/bin/elp_parse_cli.rs index 770ab60456..c826c0a927 100644 --- a/crates/elp/src/bin/elp_parse_cli.rs +++ b/crates/elp/src/bin/elp_parse_cli.rs @@ -14,7 +14,6 @@ use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::str; -use std::time::SystemTime; use anyhow::Result; use anyhow::bail; @@ -24,6 +23,7 @@ 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; @@ -40,7 +40,6 @@ use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::ModuleName; use elp_ide::elp_ide_db::elp_base_db::Vfs; use elp_ide::elp_ide_db::elp_base_db::VfsPath; -use elp_log::telemetry; use elp_project_model::AppType; use elp_project_model::DiscoverConfig; use elp_project_model::buck::BuckQueryConfig; @@ -57,35 +56,6 @@ 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, @@ -100,7 +70,6 @@ pub fn parse_all( ) -> Result<()> { log::info!("Loading project at: {:?}", args.project); - let start_time = SystemTime::now(); // Track memory usage at the start let memory_start = MemoryUsage::now(); @@ -160,7 +129,8 @@ 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, &cfg, &args.to, file_id, &name)?.map_or(vec![], |x| vec![x]) + do_parse_one(&analysis, &loaded.vfs, &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:?}"), }; @@ -171,24 +141,13 @@ pub fn parse_all( let db = loaded.analysis_host.raw_database(); - telemetry::report_elapsed_time("parse-elp operational", start_time); + // 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(); 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")?; @@ -207,7 +166,6 @@ 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())?; } @@ -234,14 +192,12 @@ pub fn parse_all( cli, )?; } else { - print_diagnostic(&diag, &line_index, &mut err_in_diag, cli)?; + print_diagnostic(&diag, &line_index, &url, &mut err_in_diag, cli)?; } } } } - telemetry::report_elapsed_time("parse-elp done", start_time); - if args.is_format_normal() && args.report_system_stats { print_memory_usage(loaded.analysis_host, loaded.vfs, cli)?; writeln!(cli, "{}", memory_used)?; @@ -279,10 +235,11 @@ 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, diag, |_file_id| None); + let diag = convert::ide_to_lsp_diagnostic(line_index, url, diag); let severity = match diag.severity { None => DiagnosticSeverity::ERROR, Some(sev) => { @@ -325,6 +282,7 @@ 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) @@ -335,7 +293,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, config, to, file_id, module_name.as_str()).unwrap() + do_parse_one(db, vfs, config, to, file_id, module_name.as_str()).unwrap() } else { None } @@ -356,6 +314,7 @@ 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) @@ -364,7 +323,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, config, to, file_id, module_name.as_str()).unwrap() + do_parse_one(&db, vfs, config, to, file_id, module_name.as_str()).unwrap() } else { None } @@ -374,11 +333,13 @@ 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)?; @@ -396,13 +357,11 @@ 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, diagnostic, |_file_id| None); + let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); writeln!(output, "{diagnostic:?}")?; } for diagnostic in erlang_service.iter() { - let diagnostic = - convert::ide_to_lsp_diagnostic(&line_index, diagnostic, |_file_id| None); + let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic); writeln!(output, "{diagnostic:?}")?; } } diff --git a/crates/elp/src/bin/eqwalizer_cli.rs b/crates/elp/src/bin/eqwalizer_cli.rs index 141b2157d0..e9159372be 100644 --- a/crates/elp/src/bin/eqwalizer_cli.rs +++ b/crates/elp/src/bin/eqwalizer_cli.rs @@ -10,7 +10,6 @@ use std::path::Path; use std::sync::Arc; -use std::time::SystemTime; use anyhow::Context; use anyhow::Result; @@ -39,7 +38,6 @@ use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::elp_ide_db::elp_base_db::ModuleName; use elp_ide::elp_ide_db::elp_base_db::VfsPath; -use elp_log::telemetry; use elp_project_model::AppName; use elp_project_model::DiscoverConfig; use elp_project_model::ProjectBuildData; @@ -78,7 +76,6 @@ pub fn eqwalize_module( cli: &mut dyn Cli, query_config: &BuckQueryConfig, ) -> Result<()> { - let start_time = SystemTime::now(); let config = DiscoverConfig::new(args.rebar, &args.profile); let mut loaded = load::load_project_at( cli, @@ -89,10 +86,7 @@ pub fn eqwalize_module( query_config, )?; build::compile_deps(&loaded, cli)?; - telemetry::report_elapsed_time("eqwalize operational", start_time); - let r = do_eqwalize_module(args, &mut loaded, cli); - telemetry::report_elapsed_time("eqwalize done", start_time); - r + do_eqwalize_module(args, &mut loaded, cli) } pub fn do_eqwalize_module( @@ -149,7 +143,6 @@ pub fn eqwalize_all( cli: &mut dyn Cli, query_config: &BuckQueryConfig, ) -> Result<()> { - let start_time = SystemTime::now(); // Hack to avoid hint appearing in tests cli.spinner(SHELL_HINT).finish(); let config = DiscoverConfig::new(args.rebar, &args.profile); @@ -162,10 +155,7 @@ pub fn eqwalize_all( query_config, )?; build::compile_deps(&loaded, cli)?; - telemetry::report_elapsed_time("eqwalize-all operational", start_time); - let r = do_eqwalize_all(args, &mut loaded, cli); - telemetry::report_elapsed_time("eqwalize-all done", start_time); - r + do_eqwalize_all(args, &mut loaded, cli) } pub fn do_eqwalize_all( @@ -186,7 +176,10 @@ 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).unwrap() && !otp_file_to_ignore(analysis, file_id) + if analysis + .should_eqwalize(file_id, args.include_tests) + .unwrap() + && !otp_file_to_ignore(analysis, file_id) { if args.stats { add_stat(name.to_string()); @@ -233,7 +226,6 @@ pub fn eqwalize_app( cli: &mut dyn Cli, query_config: &BuckQueryConfig, ) -> Result<()> { - let start_time = SystemTime::now(); let config = DiscoverConfig::new(args.rebar, &args.profile); let mut loaded = load::load_project_at( cli, @@ -244,10 +236,7 @@ pub fn eqwalize_app( query_config, )?; build::compile_deps(&loaded, cli)?; - telemetry::report_elapsed_time("eqwalize-app operational", start_time); - let r = do_eqwalize_app(args, &mut loaded, cli); - telemetry::report_elapsed_time("eqwalize-app done", start_time); - r + do_eqwalize_app(args, &mut loaded, cli) } pub fn do_eqwalize_app( @@ -266,7 +255,9 @@ 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).unwrap() + && analysis + .should_eqwalize(file_id, args.include_tests) + .unwrap() && !otp_file_to_ignore(analysis, file_id) { Some(file_id) @@ -292,7 +283,6 @@ pub fn eqwalize_target( cli: &mut dyn Cli, query_config: &BuckQueryConfig, ) -> Result<()> { - let start_time = SystemTime::now(); let config = DiscoverConfig::buck(); let mut loaded = load::load_project_at( cli, @@ -304,7 +294,6 @@ pub fn eqwalize_target( )?; set_eqwalizer_config(&mut loaded); - telemetry::report_elapsed_time("eqwalize-target operational", start_time); let buck = match &loaded.project.project_build_data { ProjectBuildData::Buck(buck) => buck, @@ -334,7 +323,9 @@ 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).unwrap() + if analysis + .should_eqwalize(file_id, args.include_tests) + .unwrap() && !otp_file_to_ignore(analysis, file_id) { file_ids.push(file_id); @@ -362,15 +353,13 @@ elp eqwalize-target erl/chatd #same as //erl/chatd/... but enables shell complet let mut reporter = reporting::PrettyReporter::new(analysis, &loaded, cli); let bail_on_error = args.bail_on_error; - let r = eqwalize(EqwalizerInternalArgs { + eqwalize(EqwalizerInternalArgs { analysis, loaded: &loaded, file_ids, reporter: &mut reporter, bail_on_error, - }); - telemetry::report_elapsed_time("eqwalize-target done", start_time); - r + }) } pub fn eqwalize_stats( @@ -401,7 +390,9 @@ 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).expect("cancelled") + if analysis + .should_eqwalize(file_id, args.include_tests) + .expect("cancelled") && !otp_file_to_ignore(analysis, file_id) { analysis @@ -473,6 +464,8 @@ 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| { @@ -591,6 +584,17 @@ 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 5b39a4aa06..0c1946b329 100644 --- a/crates/elp/src/bin/erlang_service_cli.rs +++ b/crates/elp/src/bin/erlang_service_cli.rs @@ -11,7 +11,6 @@ use std::fs; use std::path::Path; use std::str; -use std::time::SystemTime; use anyhow::Context; use anyhow::Error; @@ -27,7 +26,6 @@ use elp_ide::Analysis; use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::IncludeOtp; use elp_ide::erlang_service::DiagnosticLocation; -use elp_log::telemetry; use elp_log::timeit; use elp_project_model::AppType; use elp_project_model::DiscoverConfig; @@ -42,7 +40,6 @@ use crate::reporting::add_stat; use crate::reporting::dump_stats; pub fn parse_all(args: &ParseAll, cli: &mut dyn Cli, query_config: &BuckQueryConfig) -> Result<()> { - let start_time = SystemTime::now(); let config = DiscoverConfig::new(!args.buck, &args.profile); let loaded = load::load_project_at( cli, @@ -55,15 +52,10 @@ pub fn parse_all(args: &ParseAll, cli: &mut dyn Cli, query_config: &BuckQueryCon build::compile_deps(&loaded, cli)?; fs::create_dir_all(&args.to)?; - telemetry::report_elapsed_time("parse-all operational", start_time); - let parse_diagnostics = do_parse_all(cli, &loaded, &args.to, &args.module, args.buck)?; if args.stats { dump_stats(cli, args.list_modules); } - - telemetry::report_elapsed_time("parse-all done", start_time); - if !parse_diagnostics.is_empty() { writeln!( cli, @@ -150,15 +142,14 @@ 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 { - file_attribute_location: directive_location, - error_path: _, + directive_location, error_location: _, }) => ( Some(directive_location), @@ -170,7 +161,7 @@ pub fn do_parse_one( relative_path: relative_path.to_owned(), line_num, msg: err.msg.to_owned(), - range: range.copied(), + range, } }) .collect(); diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index cb420261d2..dad833e0c1 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -11,6 +11,7 @@ use core::option::Option::None; use std::io::Write; use std::mem; +use std::path::Path; use anyhow::Result; use elp::build::load; @@ -84,7 +85,7 @@ const REC_ARITY: u32 = 99; const HEADER_ARITY: u32 = 100; const FACTS_FILE: &str = "facts.json"; -// @fb-only: mod meta_only; +// @fb-only #[derive(Serialize, Debug, Eq, Hash, PartialEq, Clone)] struct GleanFileId(u32); @@ -92,6 +93,7 @@ struct GleanFileId(u32); #[derive(Clone, Debug, Default)] struct IndexConfig { pub multi: bool, + pub prefix: Option, } impl From for FileId { @@ -767,7 +769,10 @@ 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 }; + let config = IndexConfig { + multi: args.multi, + prefix: args.prefix.clone(), + }; let (facts, module_index) = indexer.index(config)?; write_results(facts, module_index, cli, args) } @@ -856,7 +861,14 @@ 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) { + match Self::index_file( + db, + file_id, + path, + project_id, + &module_index, + config.prefix.as_ref(), + ) { Some((file, line, decl, xref, facts, module_fact)) => { let mut result = FxHashMap::default(); result.insert( @@ -872,7 +884,14 @@ 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) + Self::index_file( + db, + file_id, + &path, + project_id, + &module_index, + config.prefix.as_ref(), + ) }) }) .flatten() @@ -929,6 +948,7 @@ impl GleanIndexer { path: &VfsPath, project_id: ProjectId, module_index: &FxHashMap, + prefix: Option<&String>, ) -> Option<( FileFact, FileLinesFact, @@ -937,7 +957,7 @@ impl GleanIndexer { Option<(Vec, XRefFact)>, Option, )> { - let file_fact = Self::file_fact(db, file_id, path, project_id)?; + let file_fact = Self::file_fact(db, file_id, path, project_id, prefix)?; 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)?; @@ -994,7 +1014,7 @@ impl GleanIndexer { .filter(|text| !text.is_empty()) }); - // @fb-only: let exdoc_link = elp_ide::meta_only::exdoc_links::module_exdoc_link(&module, &sema); + // @fb-only let exdoc_link: Option = None; // @oss-only ModuleFact::new( @@ -1153,12 +1173,16 @@ 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 = file_path.as_str().to_string(); + let file_path = match prefix { + Some(prefix) => Path::new(&prefix).join(file_path).to_str()?.into(), + None => file_path.as_str().to_string(), + }; Some(FileFact::new(file_id, file_path)) } @@ -1532,7 +1556,7 @@ impl GleanIndexer { }) => { let def = macro_def.as_ref()?; let mut resolved = Self::resolve_macro_v2(sema, def, source_file, ctx)?; - // @fb-only: meta_only::resolve_macro_expansion(sema, *expansion, ctx, &mut resolved); + // @fb-only Some(resolved) } hir::AnyExpr::Pat(Pat::MacroCall { macro_def, .. }) @@ -1560,7 +1584,7 @@ impl GleanIndexer { vars: FxHashMap<&Location, &String>, ) -> Vec { let mut result = vec![]; - if !db.is_eqwalizer_enabled(file_id) { + if !db.is_eqwalizer_enabled(file_id, false) { return result; } let module_diagnostics = db.eqwalizer_diagnostics_by_project(project_id, vec![file_id]); @@ -1875,9 +1899,9 @@ impl GleanIndexer { let source_file = sema.parse(file_id); let range = Self::find_range(sema, ctx, &source_file, &expr_source)?; - // @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()); + // @fb-only + // @fb-only + // @fb-only let wam_url = None; // @oss-only Some(XRef { @@ -2039,6 +2063,7 @@ 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()); @@ -2065,6 +2090,25 @@ 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#" @@ -2335,10 +2379,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 @@ -2393,10 +2437,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 241a3d4481..aeb6fe4871 100644 --- a/crates/elp/src/bin/lint_cli.rs +++ b/crates/elp/src/bin/lint_cli.rs @@ -13,14 +13,12 @@ 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; @@ -53,16 +51,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; @@ -77,7 +75,6 @@ pub fn run_lint_command( cli: &mut dyn Cli, query_config: &BuckQueryConfig, ) -> Result<()> { - let start_time = SystemTime::now(); let memory_start = MemoryUsage::now(); if let Some(to) = &args.to { @@ -89,12 +86,9 @@ pub fn run_lint_command( // We load the project after loading config, in case it bails with // errors. No point wasting time if the config is wrong. let mut loaded = load_project(args, cli, query_config)?; - telemetry::report_elapsed_time("lint operational", start_time); let result = do_codemod(cli, &mut loaded, &diagnostics_config, args); - telemetry::report_elapsed_time("lint done", start_time); - let memory_end = MemoryUsage::now(); let memory_used = memory_end - memory_start; @@ -115,7 +109,7 @@ fn get_and_report_diagnostics_config(args: &Lint, cli: &mut dyn Cli) -> Result, -) -> Result<(Vec<(String, FileId, DiagnosticCollection)>, bool, bool)> { +) -> Result> { 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())); - // 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)) + 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()) } -fn do_diagnostics_one( +fn do_parse_one( db: &Analysis, config: &DiagnosticsConfig, file_id: FileId, @@ -293,8 +233,6 @@ 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 @@ -335,18 +273,7 @@ pub fn do_codemod( res = match (file_id, name) { (None, _) => { - 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 + do_parse_all(cli, &analysis, &loaded.project_id, diagnostics_config, args)? } (Some(file_id), Some(name)) => { if let Some(app) = &args.app @@ -355,124 +282,87 @@ pub fn do_codemod( { panic!("Module {} does not belong to app {}", name.as_str(), app) } - 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 + do_parse_one(&analysis, diagnostics_config, file_id, &name, args)? + .map_or(vec![], |x| vec![x]) } (Some(file_id), _) => { panic!("Could not get name from file_id for {file_id:?}") } }; - res + filter_diagnostics( + &analysis, + &args.module, + Some(&diagnostics_config.enabled), + &res, + &FxHashSet::default(), + )? }; - let mut err_in_diag = streamed_err_in_diag; - // At this point, the analysis variable from above is dropped - - // 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 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() + )?; - // Handle apply_fix case separately since it needs to filter diagnostics anyway - if args.apply_fix { - if diagnostics_config.enabled.all_enabled() { + 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, + )?; + } + } + } + } + if args.apply_fix && diagnostics_config.enabled.all_enabled() { bail!( "We cannot apply fixes if all diagnostics enabled. Perhaps provide --diagnostic-filter" ); } - - 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 - } - } - } - + if args.apply_fix && !diagnostics_config.enabled.all_enabled() { let mut changed_files = FxHashSet::default(); let mut lints = Lints::new( &mut loaded.analysis_host, @@ -480,7 +370,7 @@ pub fn do_codemod( &mut loaded.vfs, args, &mut changed_files, - filtered_diags, + initial_diags, ); // We handle the fix application result here, so // the overall status of whether error-severity @@ -492,232 +382,20 @@ pub fn do_codemod( writeln!(cli, "Apply fix failed: {err:#}").ok(); } }; - - if err_in_diag { - bail!("Errors found") - } } - } 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 { + 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)? } else { LintConfig::default() }; - let cfg = DiagnosticsConfig::default() .configure_diagnostics( &cfg_from_file, @@ -736,82 +414,12 @@ 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)?; - 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 - )?; - } - } - } - + writeln!(cli, " {}", diag.print(&line_index, use_cli_severity))?; Ok(()) } @@ -996,10 +604,13 @@ impl<'a> Lints<'a> { if self.args.check_eqwalize_all { writeln!(cli, "Running eqwalize-all to check for knock-on problems.")?; } - let diags = { - let analysis = self.analysis_host.analysis(); - do_diagnostics_one(&analysis, self.cfg, file_id, &name, self.args)? - }; + let diags = do_parse_one( + &self.analysis_host.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 @@ -1010,15 +621,14 @@ impl<'a> Lints<'a> { bail!("Applying change introduces an error diagnostic"); } else { self.changed_files.insert((file_id, name.clone())); - let changed_forms = { - let analysis = self.analysis_host.analysis(); - changes - .iter() - .filter_map(|d| form_from_diff(&analysis, file_id, d)) - .collect::>() - }; + let changes = changes + .iter() + .filter_map(|d| { + form_from_diff(&self.analysis_host.analysis(), file_id, d) + }) + .collect::>(); - for form_id in &changed_forms { + for form_id in &changes { self.changed_forms.insert(InFile::new(file_id, *form_id)); } @@ -1031,24 +641,24 @@ impl<'a> Lints<'a> { .flatten() .collect::>(); - let new_diagnostics = { - let analysis = self.analysis_host.analysis(); - filter_diagnostics(&analysis, &None, None, &new_diags, &self.changed_forms)? - }; + let new_diagnostics = filter_diagnostics( + &self.analysis_host.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, - &analysis, - self.vfs, + &self.analysis_host.analysis(), *file_id, - None, self.args.use_cli_severity, cli, )?; @@ -1120,13 +730,10 @@ 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, - &analysis, - self.vfs, + &self.analysis_host.analysis(), file_id, - None, self.args.use_cli_severity, cli, )?; @@ -1193,9 +800,7 @@ impl<'a> Lints<'a> { print_diagnostic( &diagnostic, &self.analysis_host.analysis(), - self.vfs, file_id, - None, self.args.use_cli_severity, cli, )?; @@ -1330,6 +935,13 @@ 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; @@ -1381,7 +993,7 @@ mod tests { expect![[r#" enabled_lints = ["P1700"] disabled_lints = [] - + [erlang_service] warnings_as_errors = true [[ad_hoc_lints.lints]] @@ -1469,11 +1081,11 @@ mod tests { head_mismatcX(0) -> 0. "#, expect![[r#" - 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 - "#]], + module specified: lints + Diagnostics reported in 1 modules: + lints: 1 + 4:2-4:15::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + "#]], expect![""], ); } @@ -1490,8 +1102,9 @@ mod tests { "#, expect![[r#" module specified: lints - Diagnostics reported: - app_a/src/lints.erl:3:3-3:6::[Warning] [L1230] function foo/0 is unused + Diagnostics reported in 1 modules: + lints: 1 + 2:2-2:5::[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 56535e4492..e40d3b5b05 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -40,10 +40,8 @@ mod erlang_service_cli; mod explain_cli; mod glean; mod lint_cli; -// @fb-only: mod meta_only; mod reporting; mod shell; -mod ssr_cli; // Use jemalloc as the global allocator #[cfg(not(any(target_env = "msvc", target_os = "openbsd")))] @@ -65,14 +63,9 @@ 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 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 res = try_main(&mut cli, args); let code = handle_res(res, cli.err()); process::exit(code); } @@ -103,28 +96,14 @@ fn setup_static(args: &Args) { } } -fn setup_cli_telemetry(args: &Args) { - match &args.command { - args::Command::RunServer(_) => { - // Do nothing, we have server telemetry - } - _ => { - // Initialize CLI telemetry, if used - // @fb-only: meta_only::initialize_telemetry(); - } - } -} - fn try_main(cli: &mut dyn Cli, args: Args) -> Result<()> { let logger = setup_logging(&args.log_file, args.no_log_buffering)?; - setup_cli_telemetry(&args); INIT.call_once(|| { setup_static(&args); 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)?, @@ -142,9 +121,6 @@ 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(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}")? @@ -288,7 +264,7 @@ mod tests { let (_stdout, stderr, code) = elp(args_vec![ "parse-all", "--project", - "../../test/test_projects/standard", + "../../test_projects/standard", "--to", tmp.path(), ]); @@ -306,7 +282,7 @@ mod tests { fn parse_all_complete(project: &str) -> Result { // Just check the command returns. - let project_path = format!("../../test/test_projects/{project}"); + let project_path = format!("../../test_projects/{project}"); let tmp = Builder::new().prefix("elp_parse_all_").tempdir().unwrap(); let (_stdout, _stderr, code) = elp(args_vec![ "parse-all", @@ -443,34 +419,16 @@ mod tests { }) .unwrap(); + let otp_version = OTP_VERSION.as_ref().expect("MISSING OTP VERSION"); let exp_path = expect_file!(format!( - "../resources/test/{}/{}/{}.pretty", + "../resources/test/{}/{}/{}-OTP-{}.pretty", project, app, module.as_str(), + otp_version, )); 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!("{}OTP([0-9]+)Only", "@")).unwrap(); - let contents = analysis.file_text(file_id).unwrap(); - 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, - ); - } - } else { - assert_normalised_file(exp_path, &stdout, project_path.into(), false); - } + assert_normalised_file(exp_path, &stdout, project_path.into(), false); } } EqwalizerDiagnostics::NoAst { module } => { @@ -605,7 +563,10 @@ mod tests { fn eqwalize_target_diagnostics_match_snapshot_pretty() { if cfg!(feature = "buck") { simple_snapshot( - args_vec!["eqwalize-target", "//standard:app_a",], + args_vec![ + "eqwalize-target", + "//whatsapp/elp/test_projects/standard:app_a", + ], "standard", expect_file!("../resources/test/standard/eqwalize_target_diagnostics.pretty"), true, @@ -669,24 +630,6 @@ 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) { @@ -970,9 +913,7 @@ 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().join(path_str.clone()), - )); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); 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]/"); @@ -984,13 +925,38 @@ 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": [ "" ], @@ -998,88 +964,61 @@ mod tests { "macros": { "COMMON_TEST": "true", "TEST": "true" - }, - "name": "app_a_SUITE", - "src_dirs": [] + } }, { - "dir": "/[prelude]//erlang/common_test/test_exec/src", - "extra_src_dirs": [], - "include_dirs": [], - "macros": {}, - "name": "test_exec", - "src_dirs": [ - "" - ] - }, - { - "dir": "/[prelude]//erlang/common_test/common", - "extra_src_dirs": [], - "include_dirs": [ - "include" - ], - "macros": {}, "name": "common", + "dir": "/[prelude]//erlang/common_test/common", "src_dirs": [ "src" - ] - }, - { - "dir": "/[prelude]//erlang/common_test/cth_hooks/src", - "extra_src_dirs": [], - "include_dirs": [ - "" ], - "macros": {}, - "name": "cth_hooks", - "src_dirs": [ - "" - ] - }, - { - "dir": "/[prelude]//erlang/shell/src", - "extra_src_dirs": [], - "include_dirs": [], - "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" - ] + "macros": {} }, { - "dir": "/[prelude]//erlang/common_test/test_binary/src", + "name": "cth_hooks", + "dir": "/[prelude]//erlang/common_test/cth_hooks/src", + "src_dirs": [ + "" + ], + "extra_src_dirs": [], + "include_dirs": [ + "" + ], + "macros": {} + }, + { + "name": "buck2_shell_utils", + "dir": "/[prelude]//erlang/shell/src", + "src_dirs": [ + "" + ], "extra_src_dirs": [], "include_dirs": [], - "macros": {}, + "macros": {} + }, + { "name": "test_binary", + "dir": "/[prelude]//erlang/common_test/test_binary/src", "src_dirs": [ "" - ] - }, - { - "dir": "/[prelude]//erlang/common_test/test_cli_lib/src", + ], "extra_src_dirs": [], "include_dirs": [], - "macros": {}, + "macros": {} + }, + { "name": "test_cli_lib", + "dir": "/[prelude]//erlang/common_test/test_cli_lib/src", "src_dirs": [ "" - ] + ], + "extra_src_dirs": [], + "include_dirs": [], + "macros": {} } ], "deps": [] @@ -1094,12 +1033,6 @@ 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() { @@ -1113,7 +1046,7 @@ mod tests { "--to", tmp_file.clone(), "--project", - path_str.clone() + path_str ]; let (stdout, stderr, code) = elp(args); assert_eq!( @@ -1128,9 +1061,7 @@ 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().join(path_str.clone()), - )); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); 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]/"); @@ -1366,7 +1297,6 @@ mod tests { check_lint_fix( args_vec![ "lint", - "--no-stream", "--diagnostic-filter", "W0010", "--experimental", @@ -1394,7 +1324,6 @@ mod tests { check_lint_fix( args_vec![ "lint", - "--no-stream", "--diagnostic-filter", "W0010", "--experimental", @@ -1420,7 +1349,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_sorted( + check_lint_fix_stderr( args_vec![ "lint", "--diagnostic-filter", @@ -1454,7 +1383,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test/test_projects/linter/does_not_exist.toml" + "../../test_projects/linter/does_not_exist.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_invalid_output.stdout"), @@ -1466,7 +1395,7 @@ mod tests { &[], false, Some(expect![[r#" - unable to read "../../test/test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) + unable to read "../../test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) "#]]), ) .expect("bad test"); @@ -1477,12 +1406,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_stderr_sorted( + check_lint_fix( args_vec![ "lint", "--experimental", "--config-file", - "../../test/test_projects/linter/elp_lint_test1.toml" + "../../test_projects/linter/elp_lint_test1.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_output.stdout"), @@ -1493,7 +1422,6 @@ mod tests { Path::new("../resources/test/lint/lint_recursive"), &[], false, - None, ) .expect("bad test"); } @@ -1508,7 +1436,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test/test_projects/linter/elp_lint_adhoc.toml", + "../../test_projects/linter/elp_lint_adhoc.toml", "--module", "app_b", "--apply-fix", @@ -1532,14 +1460,14 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_diagnostic_ignore(buck: bool) { - simple_snapshot_sorted( + simple_snapshot( args_vec![ "lint", "--experimental", "--diagnostic-ignore", "W0011", "--config-file", - "../../test/test_projects/linter/elp_lint_test_ignore.toml" + "../../test_projects/linter/elp_lint_test_ignore.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_ignore.stdout"), @@ -1583,7 +1511,7 @@ mod tests { &[], false, Some(expect![[r#" - failed to read "../../test/test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 + failed to read "../../test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 "#]]), ) .expect("bad test"); @@ -1592,8 +1520,8 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_diagnostics_filter_all_enabled(buck: bool) { - simple_snapshot_expect_error_sorted( - args_vec!["lint"], + simple_snapshot_expect_error( + args_vec!["lint",], "linter", expect_file!("../resources/test/linter/parse_elp_no_lint_specified_output.stdout"), buck, @@ -1601,24 +1529,10 @@ 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_sorted( + simple_snapshot_expect_error( args_vec!["lint", "--format", "json"], "linter", expect_file!("../resources/test/linter/parse_elp_no_lint_specified_json_output.stdout"), @@ -1645,11 +1559,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_sorted( + check_lint_fix_stderr( args_vec![ "lint", "--config-file", - "../../test/test_projects/linter/elp_lint_test2.toml" + "../../test_projects/linter/elp_lint_test2.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_explicit_enable_output.stdout"), @@ -1672,7 +1586,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_sorted( + check_lint_fix_stderr( args_vec![ "lint", "--diagnostic-filter", @@ -1697,6 +1611,38 @@ 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) { @@ -1884,7 +1830,7 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_edoc(buck: bool) { - simple_snapshot_sorted( + simple_snapshot( args_vec![ "lint", "--include-edoc-diagnostics", @@ -1918,7 +1864,7 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_ct_include_tests(buck: bool) { - simple_snapshot_expect_error_sorted( + simple_snapshot_expect_error( args_vec![ "lint", "--include-ct-diagnostics", @@ -1936,8 +1882,8 @@ mod tests { #[test] fn lint_resolves_generated_includes() { if cfg!(feature = "buck") { - simple_snapshot_expect_error_sorted( - args_vec!["lint", "--module", "top_includer",], + simple_snapshot_expect_error( + args_vec!["lint"], "buck_tests_2", expect_file!("../resources/test/buck_tests_2/resolves_generated_includes.stdout"), true, @@ -1952,8 +1898,7 @@ mod tests { simple_snapshot_expect_stderror( args_vec!["lint",], "buck_bad_config", - // @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 + expect_file!("../resources/test/buck_bad_config/bxl_error_message.stdout"), true, None, true, @@ -1963,12 +1908,11 @@ mod tests { #[test] fn lint_warnings_as_errors() { - simple_snapshot_expect_error_sorted( + simple_snapshot_expect_error( args_vec![ "lint", - "--no-stream" "--config-file", - "../../test/test_projects/linter/elp_lint_warnings_as_errors.toml" + "../../test_projects/linter/elp_lint_warnings_as_errors.toml" ], "linter", expect_file!("../resources/test/linter/warnings_as_errors.stdout"), @@ -1983,7 +1927,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test/test_projects/linter/elp_lint_custom_function_matches.toml", + "../../test_projects/linter/elp_lint_custom_function_matches.toml", "--module", "custom_function_matches" ], @@ -1994,287 +1938,6 @@ mod tests { ) } - #[test] - fn lint_unavailable_type() { - simple_snapshot( - args_vec![ - "lint", - "--config-file", - "../../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"), - true, - None, - ) - } - - #[test] - fn lint_ssr_from_bad_config() { - simple_snapshot_expect_stderror( - args_vec![ - "lint", - "--config-file", - "../../test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", - ], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_parse_fail.stdout"), - true, - None, - false, - ) - } - - #[test] - fn lint_ssr_as_cli_arg() { - simple_snapshot( - args_vec!["ssr", "ssr: {_@A, _@B}.",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_arg_without_prefix() { - simple_snapshot( - args_vec!["ssr", "{_@A, _@B}",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli.stdout"), - true, - None, - ) - } - - #[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( - args_vec!["ssr", "3" "{4}",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_multiple.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_arg_malformed() { - simple_snapshot_expect_stderror( - args_vec!["ssr", "ssr: {_@A, = _@B}.",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_parse_error.stdout"), - true, - None, - false, - ) - } - - #[test] - fn lint_ssr_as_cli_parens_visible() { - simple_snapshot( - args_vec!["ssr", "--parens", "(_@A)",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_parens_invisible() { - // Invisible parens are the default - simple_snapshot( - args_vec!["ssr", "(((3)))",], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_macros_expand() { - simple_snapshot( - args_vec!["ssr", "--macros", "expand", "?BAR(_@AA)", "{4}"], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_macros_expand_is_default() { - simple_snapshot( - args_vec!["ssr", "?BAR(_@AA)", "{4}"], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_macros_visible_expand() { - simple_snapshot( - args_vec!["ssr", "--macros", "visible-expand", "?BAR(_@AA)", "{4}"], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout"), - true, - None, - ) - } - - #[test] - fn lint_ssr_as_cli_macros_no_expand() { - simple_snapshot( - args_vec!["ssr", "--macros", "no-expand", "?BAR(_@AA)", "{4}"], - "linter", - expect_file!("../resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout"), - true, - None, - ) - } - - #[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) { @@ -2406,117 +2069,6 @@ 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] @@ -2607,26 +2159,6 @@ mod tests { expected.assert_eq(&stdout); } - #[test] - fn ssr_help() { - let args = args::args() - .run_inner(Args::from(&["ssr", "--help"])) - .unwrap_err(); - let expected = expect_file!["../resources/test/ssr_help.stdout"]; - let stdout = args.unwrap_stdout(); - 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() @@ -2845,61 +2377,6 @@ 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, @@ -3033,55 +2510,6 @@ 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, @@ -3136,14 +2564,7 @@ mod tests { } fn project_path(project: &str) -> String { - 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() + format!("../../test_projects/{project}") } struct BackupFiles { diff --git a/crates/elp/src/bin/reporting.rs b/crates/elp/src/bin/reporting.rs index 7ab10459fe..fe38287629 100644 --- a/crates/elp/src/bin/reporting.rs +++ b/crates/elp/src/bin/reporting.rs @@ -227,6 +227,9 @@ 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 @@ -235,8 +238,12 @@ 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); + let diagnostic = convert::eqwalizer_to_arc_diagnostic( + diagnostic, + &line_index, + relative_path, + eqwalizer_enabled, + ); 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 13ff79ed36..490c852893 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -15,7 +15,6 @@ use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::sync::Arc; -use std::time::SystemTime; use anyhow::Result; use elp::build::load; @@ -30,7 +29,6 @@ use elp_ide::elp_ide_db::elp_base_db::SourceDatabaseExt; use elp_ide::elp_ide_db::elp_base_db::SourceRoot; use elp_ide::elp_ide_db::elp_base_db::SourceRootId; use elp_ide::elp_ide_db::elp_base_db::VfsPath; -use elp_log::telemetry; use elp_project_model::DiscoverConfig; use elp_project_model::buck::BuckQueryConfig; use paths::Utf8PathBuf; @@ -157,9 +155,10 @@ 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") + .find(|&opt| opt != "--include-generated" && opt != "--include-tests") { return Err(ShellError::UnexpectedOption( "eqwalize-app".into(), @@ -176,6 +175,7 @@ impl ShellCommand { rebar, app: app.into(), include_generated, + include_tests, bail_on_error: false, }))); } @@ -183,9 +183,10 @@ 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") + .find(|&opt| opt != "--include-generated" && opt != "--include-tests") { return Err(ShellError::UnexpectedOption( "eqwalize-all".into(), @@ -201,6 +202,7 @@ impl ShellCommand { rebar, format: None, include_generated, + include_tests, bail_on_error: false, stats: false, list_modules: false, @@ -222,8 +224,10 @@ 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 "; @@ -327,7 +331,6 @@ fn update_changes( } pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfig) -> Result<()> { - let start_time = SystemTime::now(); let mut cmd = Command::new("watchman"); let _ = cmd.arg("--version").output().map_err(|_| { anyhow::Error::msg("`watchman` command not found. install it from https://facebook.github.io/watchman/ to use `elp shell`.") @@ -346,7 +349,6 @@ pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfi Mode::Shell, query_config, )?; - telemetry::report_elapsed_time("shell operational", start_time); let mut rl = rustyline::DefaultEditor::new()?; let mut last_read = watchman.get_clock()?; write!(cli, "{WELCOME}")?; @@ -401,6 +403,5 @@ pub fn run_shell(shell: &Shell, cli: &mut dyn Cli, query_config: &BuckQueryConfi } } } - telemetry::report_elapsed_time("shell done", start_time); Ok(()) } diff --git a/crates/elp/src/bin/ssr_cli.rs b/crates/elp/src/bin/ssr_cli.rs deleted file mode 100644 index 36f26d4554..0000000000 --- a/crates/elp/src/bin/ssr_cli.rs +++ /dev/null @@ -1,717 +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::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; -use elp::convert; -use elp::memory_usage::MemoryUsage; -use elp::otp_file_to_ignore; -use elp_eqwalizer::Mode; -use elp_ide::Analysis; -use elp_ide::AnalysisHost; -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; -use elp_ide::elp_ide_db::elp_base_db::ModuleName; -use elp_ide::elp_ide_db::elp_base_db::ProjectId; -use elp_ide::elp_ide_db::elp_base_db::VfsPath; -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 hir::Semantic; -use paths::Utf8PathBuf; -use rayon::prelude::ParallelBridge; -use rayon::prelude::ParallelIterator; - -use crate::args::Ssr; -use crate::reporting; -use crate::reporting::print_memory_usage; - -fn normalize_ssr_pattern(pattern: &str) -> String { - if pattern.starts_with("ssr:") { - pattern.to_string() - } else { - format!("ssr: {}.", pattern) - } -} - -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(); - - // Validate all SSR patterns early - let analysis_host = AnalysisHost::default(); - let analysis = analysis_host.analysis(); - for pattern in &args.ssr_specs { - let normalized_pattern = normalize_ssr_pattern(pattern); - match analysis.validate_ssr_pattern(&normalized_pattern) { - Ok(Ok(())) => {} - Ok(Err(e)) => bail!("invalid SSR pattern '{}': {}", pattern, e), - Err(_cancelled) => bail!("SSR pattern validation was cancelled"), - } - } - - // Parse the strategy from CLI arguments - let strategy = args.parse_strategy()?; - - // Create the lint config with all SSR patterns - 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); - } - - // Build the diagnostics config - let diagnostics_config = DiagnosticsConfig::default() - .configure_diagnostics( - &lint_config, - &Some("ad-hoc: ssr-match".to_string()), - &None, - FallBackToAll::Yes, - )? - .set_include_generated(args.include_generated) - .set_experimental(false) - .set_use_cli_severity(false); - - 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, use_color); - - telemetry::report_elapsed_time("ssr done", start_time); - - let memory_end = MemoryUsage::now(); - let memory_used = memory_end - memory_start; - - // Print memory usage at the end if requested and format is normal - if args.is_format_normal() && args.report_system_stats { - print_memory_usage(loaded.analysis_host, loaded.vfs, cli)?; - writeln!(cli, "{}", memory_used)?; - } - r -} - -pub fn run_ssr( - cli: &mut dyn Cli, - loaded: &mut LoadResult, - diagnostics_config: &DiagnosticsConfig, - args: &Ssr, - use_color: bool, -) -> Result<()> { - let analysis = loaded.analysis(); - let (file_id, name) = match &args.module { - Some(module) => match analysis.module_file_id(loaded.project_id, module)? { - Some(file_id) => { - if args.is_format_normal() { - writeln!(cli, "module specified: {module}")?; - } - (Some(file_id), analysis.module_name(file_id)?) - } - None => panic!("Module not found: {module}"), - }, - None => match &args.file { - Some(file_name) => { - if args.is_format_normal() { - writeln!(cli, "file specified: {file_name}")?; - } - let path_buf = Utf8PathBuf::from_path_buf(fs::canonicalize(file_name).unwrap()) - .expect("UTF8 conversion failed"); - let path = AbsPath::assert(&path_buf); - let path = path.as_os_str().to_str().unwrap(); - ( - loaded - .vfs - .file_id(&VfsPath::new_real_path(path.to_string())) - .map(|(id, _)| id), - path_buf.as_path().file_name().map(ModuleName::new), - ) - } - None => (None, None), - }, - }; - - 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) - && file_app != AppName(app.to_string()) - { - panic!("Module {} does not belong to app {}", name.as_str(), app) - } - 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 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 { - 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)?; - } - // 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)?; - } - } - } - Ok(()) -} - -fn load_project( - args: &Ssr, - cli: &mut dyn Cli, - query_config: &BuckQueryConfig, -) -> Result { - log::info!("Loading project at: {:?}", args.project); - let config = DiscoverConfig::new(args.rebar, &args.profile); - load::load_project_at( - cli, - &args.project, - config, - IncludeOtp::Yes, - Mode::Server, - query_config, - ) -} -fn do_parse_one( - db: &Analysis, - config: &DiagnosticsConfig, - file_id: FileId, - 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); - } - - // Run only the SSR lint configured in lints_from_config - let diagnostics = db.with_db(|database| { - let sema = Semantic::new(database); - let mut diags = Vec::new(); - config - .lints_from_config - .get_diagnostics(&mut diags, &sema, file_id); - diags - })?; - - if !diagnostics.is_empty() { - let res = (name.to_string(), file_id, diagnostics); - Ok(Some(res)) - } else { - Ok(None) - } -} - -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)?; - 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(()) -} - -fn print_diagnostic_json( - diagnostic: &diagnostics::Diagnostic, - analysis: &Analysis, - file_id: FileId, - path: &Path, - use_cli_severity: bool, - cli: &mut dyn Cli, -) -> Result<(), anyhow::Error> { - let line_index = analysis.line_index(file_id)?; - let converted_diagnostic = - convert::ide_to_arc_diagnostic(&line_index, path, diagnostic, use_cli_severity); - writeln!( - cli, - "{}", - serde_json::to_string(&converted_diagnostic).unwrap_or_else(|err| panic!( - "print_diagnostics_json failed for '{converted_diagnostic:?}': {err}" - )) - )?; - 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/build/load.rs b/crates/elp/src/build/load.rs index 67457e049f..cb9cb6570f 100644 --- a/crates/elp/src/build/load.rs +++ b/crates/elp/src/build/load.rs @@ -80,7 +80,12 @@ pub fn load_project_at( log::info!("Discovered project: {manifest:?}"); let pb = cli.spinner("Loading build info"); - let project = Project::load(&manifest, &elp_config, query_config, &|_progress| {})?; + let project = Project::load( + &manifest, + elp_config.eqwalizer.clone(), + query_config, + &|_progress| {}, + )?; pb.finish(); load_project(cli, project, include_otp, eqwalizer_mode) diff --git a/crates/elp/src/cli.rs b/crates/elp/src/cli.rs index 0678aaebc1..b3070768d6 100644 --- a/crates/elp/src/cli.rs +++ b/crates/elp/src/cli.rs @@ -30,13 +30,18 @@ pub trait Cli: Write + WriteColor { fn err(&mut self) -> &mut dyn Write; } -pub struct StandardCli(StandardStream, Stderr); +pub struct Real(StandardStream, Stderr); -impl StandardCli { - fn new(color_choice: ColorChoice) -> Self { - Self(StandardStream::stdout(color_choice), std::io::stderr()) +impl Default for Real { + fn default() -> Self { + Self( + StandardStream::stdout(ColorChoice::Always), + std::io::stderr(), + ) } +} +impl Real { fn progress_with_style( &self, len: u64, @@ -54,7 +59,7 @@ impl StandardCli { } } -impl Cli for StandardCli { +impl Cli for Real { fn progress(&self, len: u64, prefix: &'static str) -> ProgressBar { self.progress_with_style(len, prefix, " {prefix:25!} {bar} {pos}/{len} {wide_msg}") } @@ -79,63 +84,6 @@ impl Cli for StandardCli { } } -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) @@ -160,48 +108,6 @@ 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 681a022261..432382a0c0 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: use crate::meta_only; +// @fb-only // Defines the server-side configuration of ELP. We generate *parts* // of VS Code's `package.json` config from this. @@ -42,8 +42,6 @@ use crate::from_json; // `new_name | `old_name` so that we keep parsing the old name. config_data! { struct ConfigData { - /// Whether to use the expermintal `buck2 targets` quick start process. - buck_quickStart: bool = json! { false }, /// Whether to show experimental ELP diagnostics that might /// have more false positives than usual. diagnostics_enableExperimental: bool = json! { false }, @@ -180,7 +178,7 @@ impl Config { return; } self.data = ConfigData::from_json(json); - // @fb-only: meta_only::harmonise_gks(self); + // @fb-only } pub fn update_gks(&mut self, json: serde_json::Value) { @@ -381,20 +379,8 @@ impl Config { try_or!(self.caps.window.as_ref()?.work_done_progress?, false) } - pub fn set_buck_quick_start(&mut self, value: bool) { - self.data.buck_quickStart = value; - } - - pub fn buck_quick_start(&self) -> bool { - self.data.buck_quickStart - } - pub fn buck_query(&self) -> BuckQueryConfig { - if self.buck_quick_start() { - BuckQueryConfig::BuckTargetsOnly - } else { - BuckQueryConfig::BuildGeneratedCode - } + BuckQueryConfig::BuildGeneratedCode } pub fn set_eqwalizer_all(&mut self, value: bool) { @@ -618,15 +604,10 @@ mod tests { let s = remove_ws(&schema); - expect![[r#""elp.buck.quickStart":{"default":false,"markdownDescription":"Whethertousetheexpermintal`buck2targets`quickstartprocess.","type":"boolean"},"elp.diagnostics.disabled":{"default":[],"items":{"type":"string"},"markdownDescription":"ListofELPdiagnosticstodisable.","type":"array","uniqueItems":true},"elp.diagnostics.enableExperimental":{"default":false,"markdownDescription":"WhethertoshowexperimentalELPdiagnosticsthatmight\nhavemorefalsepositivesthanusual.","type":"boolean"},"elp.diagnostics.enableOtp":{"default":false,"markdownDescription":"WhethertoreportdiagnosticsforOTPfiles.","type":"boolean"},"elp.diagnostics.onSave.enable":{"default":false,"markdownDescription":"Updatenativediagnosticsonlywhenthefileissaved.","type":"boolean"},"elp.edoc.enable":{"default":false,"markdownDescription":"WhethertoreportEDocdiagnostics.","type":"boolean"},"elp.eqwalizer.all":{"default":false,"markdownDescription":"WhethertoreportEqwalizerdiagnosticsforthewholeprojectandnotonlyforopenedfiles.","type":"boolean"},"elp.eqwalizer.chunkSize":{"default":100,"markdownDescription":"Chunksizetouseforproject-wideeqwalization.","minimum":0,"type":"integer"},"elp.eqwalizer.maxTasks":{"default":32,"markdownDescription":"Maximumnumberoftaskstoruninparallelforproject-wideeqwalization.","minimum":0,"type":"integer"},"elp.highlightDynamic.enable":{"default":false,"markdownDescription":"Ifenabled,highlightvariableswithtype`dynamic()`whenEqwalizerresultsareavailable.","type":"boolean"},"elp.hoverActions.docLinks.enable":{"default":false,"markdownDescription":"WhethertoshowHoverActionsoftype`docs`.Onlyapplieswhen\n`#elp.hoverActions.enable#`isset.","type":"boolean"},"elp.hoverActions.enable":{"default":false,"markdownDescription":"WhethertoshowHoverActions.","type":"boolean"},"elp.inlayHints.parameterHints.enable":{"default":true,"markdownDescription":"Whethertoshowfunctionparameternameinlayhintsatthecall\nsite.","type":"boolean"},"elp.lens.buck2.mode":{"default":null,"markdownDescription":"Thebuck2modetouseforrunningtestsviathecodelenses.","type":["null","string"]},"elp.lens.debug.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Debug`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.enable":{"default":false,"markdownDescription":"WhethertoshowCodeLensesinErlangfiles.","type":"boolean"},"elp.lens.links.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Link`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.logview.links":{"default":false,"markdownDescription":"WhethertoenableLogViewlenslinks.","type":"boolean"},"elp.lens.run.coverage.enable":{"default":true,"markdownDescription":"Displaycodecoverageinformationwhenrunningtestsviathe\nCodeLenses.Onlyapplieswhen`#elp.lens.enabled`and\n`#elp.lens.run.enable#`areset.","type":"boolean"},"elp.lens.run.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Run`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.run.interactive.enable":{"default":false,"markdownDescription":"Whethertoshowthe`RunInteractive`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.scuba.links":{"default":false,"markdownDescription":"WhethertoenableScubalenslinks.","type":"boolean"},"elp.lens.wam.links":{"default":false,"markdownDescription":"WhethertoenableWAMlenslinks.","type":"boolean"},"elp.log":{"default":"error","markdownDescription":"ConfigureLSP-basedloggingusingenv_loggersyntax.","type":"string"},"elp.signatureHelp.enable":{"default":true,"markdownDescription":"WhethertoshowSignatureHelp.","type":"boolean"},"elp.typesOnHover.enable":{"default":false,"markdownDescription":"Displaytypeswhenhoveringoverexpressions.","type":"boolean"},"#]] + expect![[r#""elp.diagnostics.disabled":{"default":[],"items":{"type":"string"},"markdownDescription":"ListofELPdiagnosticstodisable.","type":"array","uniqueItems":true},"elp.diagnostics.enableExperimental":{"default":false,"markdownDescription":"WhethertoshowexperimentalELPdiagnosticsthatmight\nhavemorefalsepositivesthanusual.","type":"boolean"},"elp.diagnostics.enableOtp":{"default":false,"markdownDescription":"WhethertoreportdiagnosticsforOTPfiles.","type":"boolean"},"elp.diagnostics.onSave.enable":{"default":false,"markdownDescription":"Updatenativediagnosticsonlywhenthefileissaved.","type":"boolean"},"elp.edoc.enable":{"default":false,"markdownDescription":"WhethertoreportEDocdiagnostics.","type":"boolean"},"elp.eqwalizer.all":{"default":false,"markdownDescription":"WhethertoreportEqwalizerdiagnosticsforthewholeprojectandnotonlyforopenedfiles.","type":"boolean"},"elp.eqwalizer.chunkSize":{"default":100,"markdownDescription":"Chunksizetouseforproject-wideeqwalization.","minimum":0,"type":"integer"},"elp.eqwalizer.maxTasks":{"default":32,"markdownDescription":"Maximumnumberoftaskstoruninparallelforproject-wideeqwalization.","minimum":0,"type":"integer"},"elp.highlightDynamic.enable":{"default":false,"markdownDescription":"Ifenabled,highlightvariableswithtype`dynamic()`whenEqwalizerresultsareavailable.","type":"boolean"},"elp.hoverActions.docLinks.enable":{"default":false,"markdownDescription":"WhethertoshowHoverActionsoftype`docs`.Onlyapplieswhen\n`#elp.hoverActions.enable#`isset.","type":"boolean"},"elp.hoverActions.enable":{"default":false,"markdownDescription":"WhethertoshowHoverActions.","type":"boolean"},"elp.inlayHints.parameterHints.enable":{"default":true,"markdownDescription":"Whethertoshowfunctionparameternameinlayhintsatthecall\nsite.","type":"boolean"},"elp.lens.buck2.mode":{"default":null,"markdownDescription":"Thebuck2modetouseforrunningtestsviathecodelenses.","type":["null","string"]},"elp.lens.debug.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Debug`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.enable":{"default":false,"markdownDescription":"WhethertoshowCodeLensesinErlangfiles.","type":"boolean"},"elp.lens.links.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Link`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.logview.links":{"default":false,"markdownDescription":"WhethertoenableLogViewlenslinks.","type":"boolean"},"elp.lens.run.coverage.enable":{"default":true,"markdownDescription":"Displaycodecoverageinformationwhenrunningtestsviathe\nCodeLenses.Onlyapplieswhen`#elp.lens.enabled`and\n`#elp.lens.run.enable#`areset.","type":"boolean"},"elp.lens.run.enable":{"default":false,"markdownDescription":"Whethertoshowthe`Run`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.run.interactive.enable":{"default":false,"markdownDescription":"Whethertoshowthe`RunInteractive`lenses.Onlyapplieswhen\n`#elp.lens.enable#`isset.","type":"boolean"},"elp.lens.scuba.links":{"default":false,"markdownDescription":"WhethertoenableScubalenslinks.","type":"boolean"},"elp.lens.wam.links":{"default":false,"markdownDescription":"WhethertoenableWAMlenslinks.","type":"boolean"},"elp.log":{"default":"error","markdownDescription":"ConfigureLSP-basedloggingusingenv_loggersyntax.","type":"string"},"elp.signatureHelp.enable":{"default":true,"markdownDescription":"WhethertoshowSignatureHelp.","type":"boolean"},"elp.typesOnHover.enable":{"default":false,"markdownDescription":"Displaytypeswhenhoveringoverexpressions.","type":"boolean"},"#]] .assert_eq(s.as_str()); expect![[r#" - "elp.buck.quickStart": { - "default": false, - "markdownDescription": "Whether to use the expermintal `buck2 targets` quick start process.", - "type": "boolean" - }, "elp.diagnostics.disabled": { "default": [], "items": { diff --git a/crates/elp/src/convert.rs b/crates/elp/src/convert.rs index c8db07a5d0..3f68488f7d 100644 --- a/crates/elp/src/convert.rs +++ b/crates/elp/src/convert.rs @@ -26,7 +26,6 @@ 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; @@ -68,14 +67,11 @@ 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, - get_file_info: F, -) -> lsp_types::Diagnostic -where - F: Fn(FileId) -> Option<(LineIndex, Url)>, -{ +) -> lsp_types::Diagnostic { let code_description = match &d.code_doc_uri { Some(uri) => match lsp_types::Url::parse(uri) { Ok(href) => Some(lsp_types::CodeDescription { href }), @@ -94,7 +90,7 @@ where code_description, source, message: d.message.clone(), - related_information: from_related(get_file_info, &d.related_info), + related_information: from_related(line_index, url, &d.related_info), tags: d.tag.as_ref().map(lsp_diagnostic_tags), data: None, } @@ -126,11 +122,18 @@ 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 = arc_types::Severity::Error; + 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 + }; // formatting: https://fburl.com/max_wiki_link_to_phabricator_rich_text let explanation = match &d.explanation { Some(s) => format!("```\n{s}\n```"), @@ -162,26 +165,22 @@ pub fn eqwalizer_to_arc_diagnostic( ) } -fn from_related( - get_file_info: F, +fn from_related( + line_index: &LineIndex, + url: &Url, r: &Option>, -) -> Option> -where - F: Fn(elp_ide::elp_ide_db::elp_base_db::FileId) -> Option<(LineIndex, Url)>, -{ +) -> Option> { r.as_ref().map(|ri| { ri.iter() - .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)?; + .map(|i| { let location = Location { - range: range(&line_index, i.range), - uri, + range: range(line_index, i.range), + uri: url.clone(), }; - Some(DiagnosticRelatedInformation { + DiagnosticRelatedInformation { location, message: i.message.clone(), - }) + } }) .collect() }) diff --git a/crates/elp/src/lib.rs b/crates/elp/src/lib.rs index f462141e39..2ed7fece37 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: mod meta_only; +// @fb-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: .chain(meta_only::FILES_TO_IGNORE.iter()) + // @fb-only .map(SmolStr::new) .collect(); } @@ -162,7 +162,6 @@ mod tests { use elp_ide::diagnostics::ErlangServiceConfig; use elp_ide::diagnostics::Lint; use elp_ide::diagnostics::LintsFromConfig; - use elp_ide::diagnostics::MatchSsr; use elp_ide::diagnostics::ReplaceCall; use elp_ide::diagnostics::ReplaceCallAction; use elp_ide::diagnostics::Replacement; @@ -189,12 +188,6 @@ mod tests { perm: vec![1, 2], }), }), - Lint::LintMatchSsr(MatchSsr { - ssr_pattern: "ssr: _@A = 10.".to_string(), - message: None, - strategy: None, - severity: None, - }), ], }, linters: FxHashMap::default(), @@ -230,10 +223,6 @@ mod tests { type = "ArgsPermutation" perm = [1, 2] - [[ad_hoc_lints.lints]] - type = "LintMatchSsr" - ssr_pattern = "ssr: _@A = 10." - [linters] "#]] .assert_eq(&toml::to_string::(&lint_config).unwrap()); diff --git a/crates/elp/src/project_loader.rs b/crates/elp/src/project_loader.rs index 103353e5db..f092e26242 100644 --- a/crates/elp/src/project_loader.rs +++ b/crates/elp/src/project_loader.rs @@ -18,7 +18,6 @@ use elp_log::telemetry; use elp_project_model::ElpConfig; use elp_project_model::IncludeParentDirs; use elp_project_model::ProjectManifest; -use elp_project_model::buck::BuckQueryConfig; use elp_project_model::otp::Otp; use fxhash::FxHashMap; use fxhash::FxHashSet; @@ -122,33 +121,12 @@ impl ProjectLoader { } } -/// If using buck quick start, it happens in two stages, first to -/// get the basic project config, then to invoke the generation of -/// any artifacts that will become part of the project. -#[derive(Debug, PartialEq, Eq)] -pub enum BuckGenerated { - /// Initial value - NoLoadDone, - /// After first load (buck targets) - NoGenerated, - /// After second load (elp.bxl) - Generated, -} - pub struct ReloadManager { - /// Files that have changed since the last reload. changed_files: FxHashSet, - /// This field is updated when a `changed_files` file is added. - /// It allows us to wait until the last file has been added - /// when a branch switch is done to avoid doing a reload for each. - /// We wait until RELOAD_QUIESCENT_WAIT_TIME has elapsed before doing - /// the reload. last_change: SystemTime, /// ReloadManager clients should ensure this is set when a reload /// task is active, reset when done. reload_in_progress: bool, - buck_generated: BuckGenerated, - buck_quick_start: bool, } /// How long to wait after the last changed file was added before @@ -156,76 +134,24 @@ pub struct ReloadManager { const RELOAD_QUIESCENT_WAIT_TIME: Duration = Duration::from_millis(500); impl ReloadManager { - pub fn new(buck_quick_start: bool) -> ReloadManager { + pub fn new() -> ReloadManager { ReloadManager { changed_files: FxHashSet::default(), last_change: SystemTime::now(), reload_in_progress: false, - buck_generated: BuckGenerated::NoLoadDone, - buck_quick_start, } } /// Used to check if any files are queued, and if so cancel an /// existing reload - pub fn ok_to_switch_workspace(&self) -> bool { - if self.buck_quick_start { - // `BuckGenerated::NoLoadDone` or `BuckGenerated::NoGenerated`. - - if self.buck_generated == BuckGenerated::NoLoadDone { - // We are doing a 2-stage load, and have just completed the `buck targets` step. - // So time to activate the Project, this is the whole point of the two stage process - true - } else { - self.changed_files.is_empty() - } - } else { - // Do not switch if there are files which will trigger a reload. - // This lets us start that process sooner without wasted effort - // switching when it is going to change anyway. - self.changed_files.is_empty() - } + pub fn has_changed_files(&self) -> bool { + !self.changed_files.is_empty() } - pub fn set_reload_active(&mut self) -> BuckQueryConfig { + pub fn set_reload_active(&mut self) { self.reload_in_progress = true; - self.get_query_config() } - - pub fn get_query_config(&self) -> BuckQueryConfig { - if self.buck_quick_start { - match self.buck_generated { - BuckGenerated::NoLoadDone => BuckQueryConfig::BuckTargetsOnly, - BuckGenerated::NoGenerated => BuckQueryConfig::BuildGeneratedCode, - BuckGenerated::Generated => BuckQueryConfig::BuildGeneratedCode, - } - } else { - BuckQueryConfig::BuildGeneratedCode - } - } - - /// This is called when the `Task::FetchProject` is done in `server.rs`, - /// but only after `switch_workspace_ok` has returned true. - pub fn set_reload_done(&mut self, a_file_per_project: FxHashSet) { - if self.buck_quick_start { - match &self.buck_generated { - BuckGenerated::NoLoadDone => { - if self.changed_files.is_empty() && !a_file_per_project.is_empty() { - // We have done the initial "buck targets" query on at least one Project, - // move on to doing `elp.bxl` - self.buck_generated = BuckGenerated::NoGenerated; - self.changed_files = a_file_per_project; - } else { - // We already have changed files from another source, so - // need to repeat this step. Do not change state. - } - } - BuckGenerated::NoGenerated => { - self.buck_generated = BuckGenerated::Generated; - } - BuckGenerated::Generated => {} - }; - } + pub fn set_reload_done(&mut self) { self.reload_in_progress = false; } @@ -255,8 +181,4 @@ impl ReloadManager { self.last_change = SystemTime::now(); } } - - pub fn set_buck_quickstart(&mut self, buck_quick_start: bool) { - self.buck_quick_start = buck_quick_start; - } } 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 deleted file mode 100644 index f5225605a5..0000000000 --- a/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout +++ /dev/null @@ -1 +0,0 @@ -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 d4954b84ee..7c9890aa86 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,7 +1,18 @@ - [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 -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 +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. + 49:18-49:31::[WeakWarning] [W0051] Binary string can be written using sigil syntax. + 51:18-51:28::[WeakWarning] [W0051] Binary string can be written using sigil syntax. + 51:38-51:51::[WeakWarning] [W0051] Binary string can be written using sigil syntax. + 55:30-55:40::[WeakWarning] [W0051] Binary string can be written using sigil syntax. diff --git a/crates/elp/src/resources/test/config_stanza.stdout b/crates/elp/src/resources/test/config_stanza.stdout index 02e3da475a..bdd5c8b487 100644 --- a/crates/elp/src/resources/test/config_stanza.stdout +++ b/crates/elp/src/resources/test/config_stanza.stdout @@ -1,9 +1,4 @@ { - "elp.buck.quickStart": { - "default": false, - "markdownDescription": "Whether to use the expermintal `buck2 targets` quick start process.", - "type": "boolean" - }, "elp.diagnostics.disabled": { "default": [], "items": { diff --git a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout deleted file mode 100644 index eef1c92c6d..0000000000 --- a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout +++ /dev/null @@ -1,138 +0,0 @@ -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 523b8d4bc7..c8cdedb3ea 100644 --- a/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout +++ b/crates/elp/src/resources/test/diagnostics/lint_report_suppressed.stdout @@ -1,3 +1,4 @@ module specified: suppressed -Diagnostics reported: -app_a/src/suppressed.erl:8:5-8:9::[Warning] [W0007] match is redundant +Diagnostics reported in 1 modules: + suppressed: 1 + 7:4-7:8::[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 4b0a155055..a1db95ccbf 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout @@ -1,10 +1,9 @@ module specified: diagnostics Diagnostics reported in 1 modules: - diagnostics: 7 + diagnostics: 6 2:9-2:26::[Hint] [W0037] Unspecific include. - 3:0-3:35::[Error] [L0000] Issue in included file + 3:0-3:0::[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 deleted file mode 100644 index 3af8bfb086..0000000000 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout +++ /dev/null @@ -1,5 +0,0 @@ -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 ede82c7c5f..0c3e5ae9a3 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,4 +4,3 @@ {"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 f040cee98e..b3a5f365b7 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,12 +1,11 @@ module specified: lints -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 +Diagnostics reported in 1 modules: + lints: 1 + 4:0-4:13::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' --------------------------------------------- Applying fix in module 'lints' for - 5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' - 4:1-4:14: Mismatched clause name + 4:0-4:13::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' @@ -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 a88f567370..6bcfecb8a6 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,11 +1,12 @@ module specified: lint_recursive -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 +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 --------------------------------------------- Applying fix in module 'lint_recursive' for - 19:5-19:12::[Warning] [W0007] match is redundant + 18:4-18:11::[Warning] [W0007] match is redundant @@ -16,7 +16,7 @@ test_foo2(Config) -> @@ -20,12 +21,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 14:5-14:12::[Warning] [W0007] match is redundant - 19:5-19:11::[Warning] [W0006] this statement has no effect + 13:4-13:11::[Warning] [W0007] match is redundant + 18:4-18:10::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 14:5-14:12::[Warning] [W0007] match is redundant + 13:4-13:11::[Warning] [W0007] match is redundant @@ -11,7 +11,7 @@ %% something/0. test_foo(Config) -> @@ -40,12 +41,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 19:5-19:11::[Warning] [W0006] this statement has no effect - 14:5-14:11::[Warning] [W0006] this statement has no effect + 18:4-18:10::[Warning] [W0006] this statement has no effect + 13:4-13:10::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 19:5-19:11::[Warning] [W0006] this statement has no effect + 18:4-18:10::[Warning] [W0006] this statement has no effect @@ -16,7 +16,6 @@ test_foo2(Config) -> @@ -59,12 +60,12 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 2 - 17:11-17:17::[Warning] [W0010] this variable is unused - 14:5-14:11::[Warning] [W0006] this statement has no effect + 16:10-16:16::[Warning] [W0010] this variable is unused + 13:4-13:10::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 17:11-17:17::[Warning] [W0010] this variable is unused + 16:10-16:16::[Warning] [W0010] this variable is unused @@ -14,7 +14,7 @@ Config, clean_mocks(). @@ -79,11 +80,11 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 1 - 14:5-14:11::[Warning] [W0006] this statement has no effect + 13:4-13:10::[Warning] [W0006] this statement has no effect --------------------------------------------- Applying fix in module 'lint_recursive' for - 14:5-14:11::[Warning] [W0006] this statement has no effect + 13:4-13:10::[Warning] [W0006] this statement has no effect @@ -11,7 +11,6 @@ %% something/0. test_foo(Config) -> @@ -97,11 +98,11 @@ Applying fix in module 'lint_recursive' for New filtered diagnostics lint_recursive: 1 - 12:10-12:16::[Warning] [W0010] this variable is unused + 11:9-11:15::[Warning] [W0010] this variable is unused --------------------------------------------- Applying fix in module 'lint_recursive' for - 12:10-12:16::[Warning] [W0010] this variable is unused + 11:9-11:15::[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 c34c9037db..5e286c673f 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl +++ b/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl @@ -1,6 +1,4 @@ module specified: otp27_docstrings Diagnostics reported in 1 modules: - 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 + otp27_docstrings: 1 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 deleted file mode 100644 index 1cef26124c..0000000000 --- a/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 2b88b6e10c..0000000000 --- a/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout +++ /dev/null @@ -1,5 +0,0 @@ -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 67497c4000..43f66a0a3a 100644 --- a/crates/elp/src/resources/test/eqwalize_all_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_all_help.stdout @@ -1,10 +1,11 @@ -Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--bail-on-error] [--stats] [--list-modules] +Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--include-tests] [--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 bb128f249a..eaf1d3126a 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] [--rebar] [--bail-on-error] +Usage: [--project PROJECT] [--as PROFILE] [--include-tests] [--rebar] [--bail-on-error] Available positional items: app name @@ -6,6 +6,7 @@ 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 e9a01166dd..eec74e4938 100644 --- a/crates/elp/src/resources/test/eqwalize_target_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_target_help.stdout @@ -1,9 +1,10 @@ -Usage: [--project PROJECT] [--bail-on-error] +Usage: [--project PROJECT] [--include-tests] [--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-OTP-26.pretty similarity index 77% rename from crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type-OTP-26.pretty index 69251d1a75..c2a5c42406 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-OTP-26.pretty @@ -37,6 +37,12 @@ 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 │ @@ -61,6 +67,16 @@ 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 │ @@ -87,4 +103,14 @@ 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/any_fun_type-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type-OTP-27.pretty new file mode 100644 index 0000000000..c2a5c42406 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type-OTP-27.pretty @@ -0,0 +1,116 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:46:21 + │ +46 │ to_f_any_neg2(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: 'f0' | fun((atom()) -> pid()) | 'f1' +Context expected type: fun() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: fun((atom()) -> pid()) + However the following candidate: 'f0' + Differs from the expected type: fun() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:49:23 + │ +49 │ f_any_to_f0_neg(F) -> F. + │ ^ F. +Expression has type: fun() +Context expected type: 'f0' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:61:31 + │ +61 │ when is_function(F, 1) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> term()) +Context expected type: f2() + │ + +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 + │ +64 │ a_to_a(a) -> a. + │ ^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:92:24 + │ +92 │ f5_to_f4_cov_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f5('a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +98 │ apply_f4_neg(F) -> F(a). + │ ^^^^ F('a'). +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:113:22 + │ +113 │ fun3_to_f4_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> 'a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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/any_fun_type-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type-OTP-28.pretty new file mode 100644 index 0000000000..c2a5c42406 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/any_fun_type-OTP-28.pretty @@ -0,0 +1,116 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:46:21 + │ +46 │ to_f_any_neg2(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: 'f0' | fun((atom()) -> pid()) | 'f1' +Context expected type: fun() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: fun((atom()) -> pid()) + However the following candidate: 'f0' + Differs from the expected type: fun() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:49:23 + │ +49 │ f_any_to_f0_neg(F) -> F. + │ ^ F. +Expression has type: fun() +Context expected type: 'f0' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:61:31 + │ +61 │ when is_function(F, 1) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> term()) +Context expected type: f2() + │ + +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 + │ +64 │ a_to_a(a) -> a. + │ ^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:92:24 + │ +92 │ f5_to_f4_cov_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f5('a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +98 │ apply_f4_neg(F) -> F(a). + │ ^^^^ F('a'). +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/any_fun_type.erl:113:22 + │ +113 │ fun3_to_f4_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> 'a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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/apply_none.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/apply_none.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-27.pretty new file mode 100644 index 0000000000..60ad8dc0ca --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-27.pretty @@ -0,0 +1,24 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:26:1 + │ +26 │ ╭ apply_none1(F) +27 │ │ when is_function(F, 1), +28 │ │ is_function(F, 2) -> +29 │ │ F(a). + │ ╰────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:32:1 + │ +32 │ apply_none2(F) -> F(ok). + │ ^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:35:1 + │ +35 │ ╭ apply_none3(F) -> +36 │ │ Res = F(ok), +37 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-28.pretty new file mode 100644 index 0000000000..60ad8dc0ca --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/apply_none-OTP-28.pretty @@ -0,0 +1,24 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:26:1 + │ +26 │ ╭ apply_none1(F) +27 │ │ when is_function(F, 1), +28 │ │ is_function(F, 2) -> +29 │ │ F(a). + │ ╰────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:32:1 + │ +32 │ apply_none2(F) -> F(ok). + │ ^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/apply_none.erl:35:1 + │ +35 │ ╭ apply_none3(F) -> +36 │ │ Res = F(ok), +37 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-26.pretty similarity index 94% rename from crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-26.pretty index 15655d9cac..0fe7555553 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/approx.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-26.pretty @@ -60,6 +60,12 @@ 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/approx-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-27.pretty new file mode 100644 index 0000000000..0fe7555553 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-27.pretty @@ -0,0 +1,105 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/approx.erl:36:5 + │ +36 │ {X, X}. + │ ^^^^^^ {X, X}. +Expression has type: {dynamic(), dynamic()} +Context expected type: 'ok' + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/approx.erl:39:1 + │ +39 │ generics_with_unions:type_var_from_nowhere(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unknown id: generics_with_unions:type_var_from_nowhere/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:41:1 + │ +41 │ ╭ -spec test_trans_invalid1_neg( +42 │ │ trans_invalid() +43 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid1_neg/1 references type with invalid definition: trans_invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:47:1 + │ +47 │ ╭ -spec test_trans_invalid2_neg( +48 │ │ trans_invalid() +49 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid2_neg/1 references type with invalid definition: trans_invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:53:1 + │ +53 │ ╭ -spec test_trans_invalid3_neg( +54 │ │ trans_invalid() +55 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid3_neg/1 references type with invalid definition: trans_invalid/0 + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/approx.erl:62:30 + │ +62 │ test_opaque_as_fun_neg(X) -> X(ok). + │ ^ X. +Expected fun type with arity 1 +Got: misc:o() | fun((term()) -> none()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/approx.erl:69:3 + │ +69 │ Dyn2. + │ ^^^^ + │ │ + │ Dyn2. +Expression has type: string() | dynamic() +Context expected type: 'anything' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +74 │ ╭ dyn_union_1(N, D) -> +75 │ │ Res = case D of +76 │ │ 1 -> N; +77 │ │ 2 -> D +78 │ │ end, +79 │ │ eqwalizer:reveal_type(Res), +80 │ │ Res. + │ ╰─────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/approx.erl:79:25 + │ +79 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic(number()) + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/approx.erl:85:1 + │ +85 │ ╭ dyn_union_2(N, D) -> +86 │ │ Res = case D of +87 │ │ 2 -> D; +88 │ │ 1 -> N +89 │ │ end, +90 │ │ eqwalizer:reveal_type(Res), +91 │ │ Res. + │ ╰─────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/approx.erl:90:25 + │ +90 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic(number()) + +11 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-28.pretty new file mode 100644 index 0000000000..0fe7555553 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/approx-OTP-28.pretty @@ -0,0 +1,105 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/approx.erl:36:5 + │ +36 │ {X, X}. + │ ^^^^^^ {X, X}. +Expression has type: {dynamic(), dynamic()} +Context expected type: 'ok' + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/approx.erl:39:1 + │ +39 │ generics_with_unions:type_var_from_nowhere(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unknown id: generics_with_unions:type_var_from_nowhere/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:41:1 + │ +41 │ ╭ -spec test_trans_invalid1_neg( +42 │ │ trans_invalid() +43 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid1_neg/1 references type with invalid definition: trans_invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:47:1 + │ +47 │ ╭ -spec test_trans_invalid2_neg( +48 │ │ trans_invalid() +49 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid2_neg/1 references type with invalid definition: trans_invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/approx.erl:53:1 + │ +53 │ ╭ -spec test_trans_invalid3_neg( +54 │ │ trans_invalid() +55 │ │ ) -> nok. + │ ╰────────^ test_trans_invalid3_neg/1 references type with invalid definition: trans_invalid/0 + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/approx.erl:62:30 + │ +62 │ test_opaque_as_fun_neg(X) -> X(ok). + │ ^ X. +Expected fun type with arity 1 +Got: misc:o() | fun((term()) -> none()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/approx.erl:69:3 + │ +69 │ Dyn2. + │ ^^^^ + │ │ + │ Dyn2. +Expression has type: string() | dynamic() +Context expected type: 'anything' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +74 │ ╭ dyn_union_1(N, D) -> +75 │ │ Res = case D of +76 │ │ 1 -> N; +77 │ │ 2 -> D +78 │ │ end, +79 │ │ eqwalizer:reveal_type(Res), +80 │ │ Res. + │ ╰─────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/approx.erl:79:25 + │ +79 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic(number()) + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/approx.erl:85:1 + │ +85 │ ╭ dyn_union_2(N, D) -> +86 │ │ Res = case D of +87 │ │ 2 -> D; +88 │ │ 1 -> N +89 │ │ end, +90 │ │ eqwalizer:reveal_type(Res), +91 │ │ Res. + │ ╰─────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/approx.erl:90:25 + │ +90 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic(number()) + +11 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/as_pat.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-27.pretty new file mode 100644 index 0000000000..da41873f62 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-27.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/as_pat.erl:51:38 + │ +51 │ unboxL_neg(BN = #box_n{}) -> unbox_a(BN). + │ ^^ BN. +Expression has type: #box_n{} +Context expected type: #box_a{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/as_pat.erl:61:38 + │ +61 │ unboxR_neg(#box_n{} = BN) -> unbox_b(BN). + │ ^^ BN. +Expression has type: #box_n{} +Context expected type: #box_b{} + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-28.pretty new file mode 100644 index 0000000000..da41873f62 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/as_pat-OTP-28.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/as_pat.erl:51:38 + │ +51 │ unboxL_neg(BN = #box_n{}) -> unbox_a(BN). + │ ^^ BN. +Expression has type: #box_n{} +Context expected type: #box_a{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/as_pat.erl:61:38 + │ +61 │ unboxR_neg(#box_n{} = BN) -> unbox_b(BN). + │ ^^ BN. +Expression has type: #box_n{} +Context expected type: #box_b{} + +2 ERRORS 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-OTP-26.pretty similarity index 52% rename from crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-26.pretty index 2c9ecb3a1f..f1d2ca6927 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-26.pretty @@ -2,8 +2,15 @@ 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/auto_imports-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-27.pretty new file mode 100644 index 0000000000..f1d2ca6927 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-27.pretty @@ -0,0 +1,16 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/auto_imports.erl:22:20 + │ +22 │ erlang:error(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/auto_imports-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-28.pretty new file mode 100644 index 0000000000..f1d2ca6927 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/auto_imports-OTP-28.pretty @@ -0,0 +1,16 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/auto_imports.erl:22:20 + │ +22 │ erlang:error(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/behave.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/behave.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-27.pretty new file mode 100644 index 0000000000..df46470af5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-27.pretty @@ -0,0 +1,13 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/behave.erl:8:20 + │ +8 │ -callback foo() -> behave1:test(). + │ ^^^^^^^^^^^^^^ Unknown id: behave1:test/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/behave.erl:10:1 + │ +10 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-28.pretty new file mode 100644 index 0000000000..df46470af5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/behave-OTP-28.pretty @@ -0,0 +1,13 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/behave.erl:8:20 + │ +8 │ -callback foo() -> behave1:test(). + │ ^^^^^^^^^^^^^^ Unknown id: behave1:test/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/behave.erl:10:1 + │ +10 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/binaries.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/binaries.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-27.pretty new file mode 100644 index 0000000000..123c9c69cc --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-27.pretty @@ -0,0 +1,65 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:24:17 + │ +24 │ test04_neg() -> <<>>. + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:32:20 + │ +32 │ test05_neg(A) -> <>. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:35:23 + │ +35 │ test06_neg(A, S) -> <>. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:38:18 + │ +38 │ test07_neg(A) -> [A]. + │ ^^^ [A]. +Expression has type: [atom()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:55:7 + │ +55 │ <<"binary"/binary>>. + │ ^^^^^^^^ string_lit. +Expression has type: string() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:74:9 + │ +74 │ <> = Bits, + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:80:9 + │ +80 │ <> = Bits, + │ ^^^^^^^^ erlang:self(). +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:89:7 + │ +89 │ <<[]>>. + │ ^^ []. +Expression has type: [] +Context expected type: number() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-28.pretty new file mode 100644 index 0000000000..123c9c69cc --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/binaries-OTP-28.pretty @@ -0,0 +1,65 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:24:17 + │ +24 │ test04_neg() -> <<>>. + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:32:20 + │ +32 │ test05_neg(A) -> <>. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:35:23 + │ +35 │ test06_neg(A, S) -> <>. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:38:18 + │ +38 │ test07_neg(A) -> [A]. + │ ^^^ [A]. +Expression has type: [atom()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:55:7 + │ +55 │ <<"binary"/binary>>. + │ ^^^^^^^^ string_lit. +Expression has type: string() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:74:9 + │ +74 │ <> = Bits, + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:80:9 + │ +80 │ <> = Bits, + │ ^^^^^^^^ erlang:self(). +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/binaries.erl:89:7 + │ +89 │ <<[]>>. + │ ^^ []. +Expression has type: [] +Context expected type: number() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-26.pretty similarity index 80% rename from crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-26.pretty index 7227163b41..4407c43d42 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/booleans.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-26.pretty @@ -22,4 +22,10 @@ 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/booleans-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-27.pretty new file mode 100644 index 0000000000..4407c43d42 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-27.pretty @@ -0,0 +1,31 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/booleans.erl:47:3 + │ +47 │ 1 andalso b(). + │ ^ 1. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/booleans.erl:59:3 + │ +59 │ dyn() andalso only_true(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ _ andalso _. +Expression has type: 'false' | 'true' +Context expected type: 'true' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'true' + 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/booleans-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-28.pretty new file mode 100644 index 0000000000..4407c43d42 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/booleans-OTP-28.pretty @@ -0,0 +1,31 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/booleans.erl:47:3 + │ +47 │ 1 andalso b(). + │ ^ 1. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/booleans.erl:59:3 + │ +59 │ dyn() andalso only_true(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ _ andalso _. +Expression has type: 'false' | 'true' +Context expected type: 'true' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'true' + 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/callbacks1_pos.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks1_pos-OTP-28.pretty 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 new file mode 100644 index 0000000000..63be36e638 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000000..63be36e638 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000000..a24c7a6a48 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty @@ -0,0 +1,14 @@ +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 deleted file mode 100644 index 6cb2012096..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty +++ /dev/null @@ -1,27 +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: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/callbacks4_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-27.pretty new file mode 100644 index 0000000000..b25bd83371 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-27.pretty @@ -0,0 +1,7 @@ +error: behaviour_does_not_exist (See https://fb.me/eqwalizer_errors#behaviour_does_not_exist) + ┌─ check/src/callbacks4_neg.erl:9:1 + │ +9 │ -behaviour(nonexistent). + │ ^^^^^^^^^^^^^^^^^^^^^^^ Behaviour does not exist: nonexistent + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-28.pretty new file mode 100644 index 0000000000..b25bd83371 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks4_neg-OTP-28.pretty @@ -0,0 +1,7 @@ +error: behaviour_does_not_exist (See https://fb.me/eqwalizer_errors#behaviour_does_not_exist) + ┌─ check/src/callbacks4_neg.erl:9:1 + │ +9 │ -behaviour(nonexistent). + │ ^^^^^^^^^^^^^^^^^^^^^^^ Behaviour does not exist: nonexistent + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-27.pretty new file mode 100644 index 0000000000..82ba8217dd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-27.pretty @@ -0,0 +1,7 @@ +error: incorrect_param_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_param_type_in_cb_implementation) + ┌─ check/src/callbacks5_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ Parameter 1 in implementation of gen_server:format_status/2 has no overlap with expected parameter type. Expected: 'normal' | 'terminate', Got: 'bad'. + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-28.pretty new file mode 100644 index 0000000000..82ba8217dd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks5_neg-OTP-28.pretty @@ -0,0 +1,7 @@ +error: incorrect_param_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_param_type_in_cb_implementation) + ┌─ check/src/callbacks5_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ Parameter 1 in implementation of gen_server:format_status/2 has no overlap with expected parameter type. Expected: 'normal' | 'terminate', Got: 'bad'. + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-27.pretty new file mode 100644 index 0000000000..a3c0bd398b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-27.pretty @@ -0,0 +1,13 @@ +error: missing_cb_implementation (See https://fb.me/eqwalizer_errors#missing_cb_implementation) + ┌─ check/src/callbacks6_neg.erl:8:1 + │ +8 │ -behavior(behave). + │ ^^^^^^^^^^^^^^^^^ Missing implementation for behave callback use_invalid/0 + +error: missing_cb_implementation (See https://fb.me/eqwalizer_errors#missing_cb_implementation) + ┌─ check/src/callbacks6_neg.erl:8:1 + │ +8 │ -behavior(behave). + │ ^^^^^^^^^^^^^^^^^ Missing implementation for behave callback use_invalid2/0 + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-28.pretty new file mode 100644 index 0000000000..a3c0bd398b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks6_neg-OTP-28.pretty @@ -0,0 +1,13 @@ +error: missing_cb_implementation (See https://fb.me/eqwalizer_errors#missing_cb_implementation) + ┌─ check/src/callbacks6_neg.erl:8:1 + │ +8 │ -behavior(behave). + │ ^^^^^^^^^^^^^^^^^ Missing implementation for behave callback use_invalid/0 + +error: missing_cb_implementation (See https://fb.me/eqwalizer_errors#missing_cb_implementation) + ┌─ check/src/callbacks6_neg.erl:8:1 + │ +8 │ -behavior(behave). + │ ^^^^^^^^^^^^^^^^^ Missing implementation for behave callback use_invalid2/0 + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks7_overload_pos-OTP-28.pretty 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-26.pretty index a3bc7c96f9..30ef1a7f38 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-26.pretty @@ -61,7 +61,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^^^ │ │ │ {'p', X}. -Expression has type: {'p', pid() | reference()} +Expression has type: {'p', none() | pid() | reference()} Context expected type: {'a', atom()} | {'p', pid()} │ @@ -72,6 +72,17 @@ Because in the expression's type: Differs from the expected type: pid() } +------------------------------ Detailed message ------------------------------ + + {'p', none() | pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()} + because + at tuple index 2: + {'p', none() | pid() | reference()} is not compatible with {'p', pid()} + because + none() | 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 │ @@ -79,7 +90,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ X. -Expression has type: 'undefined' | 'restarting' +Expression has type: 'undefined' | none() | 'restarting' Context expected type: {'p', pid()} | 'undefined' │ @@ -88,20 +99,42 @@ Because in the expression's type: However the following candidate: 'restarting' Differs from the expected type: {'p', pid()} | 'undefined' -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/case_predicates.erl:174:16 - │ -174 │ lists:nth(1, L). - │ ^ L. -Expression has type: #{dynamic() => dynamic()} -Context expected type: [T] +------------------------------ Detailed message ------------------------------ + + 'undefined' | none() | '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 │ 174 │ lists:nth(1, L). - │ ^ L. -Expression has type: #{dynamic() => dynamic()} + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() +Context expected type: [T] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:174:16 + │ +174 │ lists:nth(1, L). + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() Context expected type: [dynamic()] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [dynamic()] 11 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-27.pretty new file mode 100644 index 0000000000..30ef1a7f38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-27.pretty @@ -0,0 +1,140 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:13:24 + │ +13 │ {true, true} -> aa(X, Y); + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:13:27 + │ +13 │ {true, true} -> aa(X, Y); + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:14:20 + │ +14 │ {true, _} -> a(X); + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:15:20 + │ +15 │ {_, true} -> a(Y); + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:10 + │ +16 │ _ -> not-ok + │ ^^^^^^ not _. +Expression has type: boolean() +Context expected type: 'ok' | 'not_ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:13 + │ +16 │ _ -> not-ok + │ ^^^ - _. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:14 + │ +16 │ _ -> not-ok + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:124:14 + │ +124 │ false -> {p, X} + │ ^^^^^^ + │ │ + │ {'p', X}. +Expression has type: {'p', none() | pid() | reference()} +Context expected type: {'a', atom()} | {'p', pid()} + │ + +Because in the expression's type: + { 'p', + Here the type is a union type with some valid candidates: pid() + However the following candidate: reference() + Differs from the expected type: pid() + } + +------------------------------ Detailed message ------------------------------ + + {'p', none() | pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()} + because + at tuple index 2: + {'p', none() | pid() | reference()} is not compatible with {'p', pid()} + because + none() | 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 + │ +144 │ _ -> X + │ ^ + │ │ + │ X. +Expression has type: 'undefined' | none() | 'restarting' +Context expected type: {'p', pid()} | 'undefined' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + However the following candidate: 'restarting' + Differs from the expected type: {'p', pid()} | 'undefined' + +------------------------------ Detailed message ------------------------------ + + 'undefined' | none() | '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 + │ +174 │ lists:nth(1, L). + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() +Context expected type: [T] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:174:16 + │ +174 │ lists:nth(1, L). + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() +Context expected type: [dynamic()] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [dynamic()] + +11 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-28.pretty new file mode 100644 index 0000000000..30ef1a7f38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/case_predicates-OTP-28.pretty @@ -0,0 +1,140 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:13:24 + │ +13 │ {true, true} -> aa(X, Y); + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:13:27 + │ +13 │ {true, true} -> aa(X, Y); + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:14:20 + │ +14 │ {true, _} -> a(X); + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:15:20 + │ +15 │ {_, true} -> a(Y); + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:10 + │ +16 │ _ -> not-ok + │ ^^^^^^ not _. +Expression has type: boolean() +Context expected type: 'ok' | 'not_ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:13 + │ +16 │ _ -> not-ok + │ ^^^ - _. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:16:14 + │ +16 │ _ -> not-ok + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:124:14 + │ +124 │ false -> {p, X} + │ ^^^^^^ + │ │ + │ {'p', X}. +Expression has type: {'p', none() | pid() | reference()} +Context expected type: {'a', atom()} | {'p', pid()} + │ + +Because in the expression's type: + { 'p', + Here the type is a union type with some valid candidates: pid() + However the following candidate: reference() + Differs from the expected type: pid() + } + +------------------------------ Detailed message ------------------------------ + + {'p', none() | pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()} + because + at tuple index 2: + {'p', none() | pid() | reference()} is not compatible with {'p', pid()} + because + none() | 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 + │ +144 │ _ -> X + │ ^ + │ │ + │ X. +Expression has type: 'undefined' | none() | 'restarting' +Context expected type: {'p', pid()} | 'undefined' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + However the following candidate: 'restarting' + Differs from the expected type: {'p', pid()} | 'undefined' + +------------------------------ Detailed message ------------------------------ + + 'undefined' | none() | '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 + │ +174 │ lists:nth(1, L). + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() +Context expected type: [T] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/case_predicates.erl:174:16 + │ +174 │ lists:nth(1, L). + │ ^ + │ │ + │ L. +Expression has type: #{dynamic() => dynamic()} | none() +Context expected type: [dynamic()] + │ + +Because in the expression's type: + Here the type is: #{dynamic() => dynamic()} + Context expects type: [dynamic()] + +11 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-27.pretty new file mode 100644 index 0000000000..9e9f88bb38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-27.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/test/check_SUITE.erl:13:11 + │ +13 │ fail() -> wrong_ret. + │ ^^^^^^^^^ 'wrong_ret'. +Expression has type: 'wrong_ret' +Context expected type: pid() + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-28.pretty new file mode 100644 index 0000000000..9e9f88bb38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/check_SUITE-OTP-28.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/test/check_SUITE.erl:13:11 + │ +13 │ fail() -> wrong_ret. + │ ^^^^^^^^^ 'wrong_ret'. +Expression has type: 'wrong_ret' +Context expected type: pid() + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/compiler_macro-OTP-28.pretty 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-26.pretty index aae5348c54..3143803509 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-26.pretty @@ -14,6 +14,10 @@ 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 │ @@ -31,6 +35,15 @@ 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 │ @@ -47,6 +60,13 @@ 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 │ @@ -65,6 +85,13 @@ 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 │ @@ -81,6 +108,13 @@ 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 │ @@ -97,6 +131,13 @@ 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 │ @@ -113,6 +154,13 @@ 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 │ @@ -170,6 +218,13 @@ 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 │ @@ -187,6 +242,13 @@ 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 │ @@ -204,6 +266,13 @@ 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 │ @@ -221,4 +290,11 @@ 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/complex_maps-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-27.pretty new file mode 100644 index 0000000000..3143803509 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-27.pretty @@ -0,0 +1,300 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:11:21 + │ +11 │ downcast_neg1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', c => 'd'} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{c => ...} + 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 + │ +17 │ downcast_neg2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', c => 'd'} +Context expected type: #{a => 'b', atom() => number()} + │ + +Because in the expression's type: + #{ c => + Here the type is: 'd' + 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 + │ +20 │ upcast_neg1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => atom()} +Context expected type: #{a => 'b', c => 'd'} + │ + +Because in the expression's type: + Here the type is: #{atom() => atom()} + 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 + │ +23 │ upcast_neg2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => number()} +Context expected type: #{a => 'b', c => 'd', atom() => number()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: number() + Context expects type: 'd' + , ... } + 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 + │ +38 │ downcast_neg3(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => atom()} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{atom() => atom()} + 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 + │ +44 │ downcast_dyn_neg_1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', dynamic(atom()) => atom()} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{dynamic(atom()) => atom()} + 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 + │ +47 │ downcast_dyn_neg_2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => dynamic(atom())} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{atom() => dynamic(atom())} + 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 + │ +56 │ tuple_key_get_neg(#{{ok, {a, b}} := V}) -> V. + │ ^ V. +Expression has type: 'v' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:62:30 + │ +62 │ tuple_key_maps_get_neg(M) -> maps:get({ok, {a, b}}, M). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ maps:get({'ok', {'a', 'b'}}, M). +Expression has type: 'v' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:71:32 + │ +71 │ get_default_neg1(#{a := V}) -> V. + │ ^ V. +Expression has type: 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:74:34 + │ +74 │ get_default_neg2(#{def := V}) -> V. + │ ^ V. +Expression has type: number() +Context expected type: 'b' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:77:41 + │ +77 │ match_required_1_neg(#{b := _V} = M) -> M. + │ ^ M. +Expression has type: #{a := number(), b := dynamic(), atom() => dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:80:28 + │ +80 │ compat_default_1_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{atom() => binary()} +Context expected type: #{a => binary(), 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 #{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 + │ +83 │ compat_default_2_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a := 'b', atom() => binary()} +Context expected type: #{a => 'b', {c, d} => atom() | binary(), term() => atom()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: binary() + 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 + │ +86 │ compat_default_3_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a := 'b', atom() => binary()} +Context expected type: #{a => 'b', {c, d} => atom() | binary(), atom() => atom()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: binary() + 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 + │ +89 │ dyn_domain_coerce_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{dynamic() => atom()} +Context expected type: #{dynamic() => binary()} + │ + +Because in the expression's type: + #{ dynamic() => + Here the type is: atom() + 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/complex_maps-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-28.pretty new file mode 100644 index 0000000000..3143803509 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/complex_maps-OTP-28.pretty @@ -0,0 +1,300 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:11:21 + │ +11 │ downcast_neg1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', c => 'd'} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{c => ...} + 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 + │ +17 │ downcast_neg2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', c => 'd'} +Context expected type: #{a => 'b', atom() => number()} + │ + +Because in the expression's type: + #{ c => + Here the type is: 'd' + 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 + │ +20 │ upcast_neg1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => atom()} +Context expected type: #{a => 'b', c => 'd'} + │ + +Because in the expression's type: + Here the type is: #{atom() => atom()} + 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 + │ +23 │ upcast_neg2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => number()} +Context expected type: #{a => 'b', c => 'd', atom() => number()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: number() + Context expects type: 'd' + , ... } + 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 + │ +38 │ downcast_neg3(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => atom()} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{atom() => atom()} + 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 + │ +44 │ downcast_dyn_neg_1(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', dynamic(atom()) => atom()} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{dynamic(atom()) => atom()} + 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 + │ +47 │ downcast_dyn_neg_2(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'b', atom() => dynamic(atom())} +Context expected type: #{a => 'b'} + │ + +Because in the expression's type: + Here the type is: #{atom() => dynamic(atom())} + 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 + │ +56 │ tuple_key_get_neg(#{{ok, {a, b}} := V}) -> V. + │ ^ V. +Expression has type: 'v' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:62:30 + │ +62 │ tuple_key_maps_get_neg(M) -> maps:get({ok, {a, b}}, M). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ maps:get({'ok', {'a', 'b'}}, M). +Expression has type: 'v' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:71:32 + │ +71 │ get_default_neg1(#{a := V}) -> V. + │ ^ V. +Expression has type: 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:74:34 + │ +74 │ get_default_neg2(#{def := V}) -> V. + │ ^ V. +Expression has type: number() +Context expected type: 'b' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:77:41 + │ +77 │ match_required_1_neg(#{b := _V} = M) -> M. + │ ^ M. +Expression has type: #{a := number(), b := dynamic(), atom() => dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/complex_maps.erl:80:28 + │ +80 │ compat_default_1_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{atom() => binary()} +Context expected type: #{a => binary(), 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 #{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 + │ +83 │ compat_default_2_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a := 'b', atom() => binary()} +Context expected type: #{a => 'b', {c, d} => atom() | binary(), term() => atom()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: binary() + 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 + │ +86 │ compat_default_3_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{a := 'b', atom() => binary()} +Context expected type: #{a => 'b', {c, d} => atom() | binary(), atom() => atom()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: binary() + 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 + │ +89 │ dyn_domain_coerce_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #{dynamic() => atom()} +Context expected type: #{dynamic() => binary()} + │ + +Because in the expression's type: + #{ dynamic() => + Here the type is: atom() + 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-OTP-26.pretty similarity index 88% rename from crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-26.pretty index 2b387ca9b9..396ce7fc52 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-26.pretty @@ -15,6 +15,12 @@ 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 │ @@ -80,6 +86,12 @@ 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 │ @@ -258,6 +270,12 @@ 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 │ @@ -275,6 +293,12 @@ 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 │ @@ -292,6 +316,12 @@ 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 │ @@ -309,6 +339,12 @@ 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 │ @@ -327,6 +363,14 @@ 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/comprehensions-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-27.pretty new file mode 100644 index 0000000000..396ce7fc52 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-27.pretty @@ -0,0 +1,382 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:28:5 + │ +28 │ [N || {_, N} <- L]. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +40 │ [X || X <- L]. + │ ^ L. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:48:5 + │ +48 │ << Y || <> <= B >>. + │ ^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:48:8 + │ +48 │ << Y || <> <= B >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:56:18 + │ +56 │ << Y || Y <- B >>. + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:60:8 + │ +60 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:60:22 + │ +60 │ << Y || <> <= LB >>. + │ ^^ LB. +Expression has type: [binary()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:68:5 + │ +68 │ [ Y || <> <= LB ]. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [binary()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +72 │ << Y || <> <= LB >>. + │ ^^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: [binary()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:72:8 + │ +72 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:76:8 + │ +76 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:90:22 + │ +90 │ Res = [X || X <- L], + │ ^ L. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:100:22 + │ +100 │ Res = [X || X <- L, X], + │ ^ L. +Expression has type: boolean() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:116:14 + │ +116 │ Res = << Y || <> <= LB >>, + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:116:28 + │ +116 │ Res = << Y || <> <= LB >>, + │ ^^ LB. +Expression has type: [binary()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:121:24 + │ +121 │ Res = << Y || Y <- B >>, + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:147:1 + │ +147 │ test26(X) -> [Y || Y <- X]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:150:1 + │ +150 │ ╭ test27(X) -> +151 │ │ Res = [Y || Y <- X], +152 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:155:1 + │ +155 │ ╭ test28(X) -> +156 │ │ << Y || +157 │ │ <> <= X >>. + │ ╰─────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:156:5 + │ +156 │ ╭ << Y || +157 │ │ <> <= X >>. + │ ╰─────────────────────^ << || >>. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:156:8 + │ +156 │ << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:160:1 + │ +160 │ ╭ test29(X) -> +161 │ │ Res = << Y || +162 │ │ <> <= X >>, +163 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:161:14 + │ +161 │ Res = << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:163:5 + │ +163 │ Res. + │ ^^^ Res. +Expression has type: binary() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:166:1 + │ +166 │ ╭ test30(X) -> +167 │ │ << Y || +168 │ │ <> <= X >>. + │ ╰─────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:167:8 + │ +167 │ << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:171:1 + │ +171 │ ╭ test31(X) -> +172 │ │ Res = << Y || +173 │ │ <> <= X >>, +174 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:172:14 + │ +172 │ Res = << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:184:5 + │ +184 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [atom()] +Context expected type: [binary()] + │ + +Because in the expression's type: + [ + Here the type is: atom() + 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 + │ +226 │ [X || X <- L, X]. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: ['true'] +Context expected type: ['false'] + │ + +Because in the expression's type: + [ + Here the type is: 'true' + 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 + │ +231 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: ['true'] +Context expected type: ['false'] + │ + +Because in the expression's type: + [ + Here the type is: 'true' + 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 + │ +239 │ [Y || <> <= B, is_integer(Y) ]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +267 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [binary() | 'undefined'] +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: 'undefined' + 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 + │ +386 │ F() || F <- Fs, + │ ^ F. +Expected fun type with arity 0 +Got: fun(() -> atom()) | fun((number()) -> atom()) + +34 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-28.pretty new file mode 100644 index 0000000000..396ce7fc52 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/comprehensions-OTP-28.pretty @@ -0,0 +1,382 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:28:5 + │ +28 │ [N || {_, N} <- L]. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +40 │ [X || X <- L]. + │ ^ L. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:48:5 + │ +48 │ << Y || <> <= B >>. + │ ^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:48:8 + │ +48 │ << Y || <> <= B >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:56:18 + │ +56 │ << Y || Y <- B >>. + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:60:8 + │ +60 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:60:22 + │ +60 │ << Y || <> <= LB >>. + │ ^^ LB. +Expression has type: [binary()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:68:5 + │ +68 │ [ Y || <> <= LB ]. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [binary()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +72 │ << Y || <> <= LB >>. + │ ^^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: [binary()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:72:8 + │ +72 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:76:8 + │ +76 │ << Y || <> <= LB >>. + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:90:22 + │ +90 │ Res = [X || X <- L], + │ ^ L. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:100:22 + │ +100 │ Res = [X || X <- L, X], + │ ^ L. +Expression has type: boolean() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:116:14 + │ +116 │ Res = << Y || <> <= LB >>, + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:116:28 + │ +116 │ Res = << Y || <> <= LB >>, + │ ^^ LB. +Expression has type: [binary()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:121:24 + │ +121 │ Res = << Y || Y <- B >>, + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:147:1 + │ +147 │ test26(X) -> [Y || Y <- X]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:150:1 + │ +150 │ ╭ test27(X) -> +151 │ │ Res = [Y || Y <- X], +152 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:155:1 + │ +155 │ ╭ test28(X) -> +156 │ │ << Y || +157 │ │ <> <= X >>. + │ ╰─────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:156:5 + │ +156 │ ╭ << Y || +157 │ │ <> <= X >>. + │ ╰─────────────────────^ << || >>. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:156:8 + │ +156 │ << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:160:1 + │ +160 │ ╭ test29(X) -> +161 │ │ Res = << Y || +162 │ │ <> <= X >>, +163 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:161:14 + │ +161 │ Res = << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:163:5 + │ +163 │ Res. + │ ^^^ Res. +Expression has type: binary() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:166:1 + │ +166 │ ╭ test30(X) -> +167 │ │ << Y || +168 │ │ <> <= X >>. + │ ╰─────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:167:8 + │ +167 │ << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/comprehensions.erl:171:1 + │ +171 │ ╭ test31(X) -> +172 │ │ Res = << Y || +173 │ │ <> <= X >>, +174 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:172:14 + │ +172 │ Res = << Y || + │ ^ Y. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/comprehensions.erl:184:5 + │ +184 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [atom()] +Context expected type: [binary()] + │ + +Because in the expression's type: + [ + Here the type is: atom() + 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 + │ +226 │ [X || X <- L, X]. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: ['true'] +Context expected type: ['false'] + │ + +Because in the expression's type: + [ + Here the type is: 'true' + 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 + │ +231 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: ['true'] +Context expected type: ['false'] + │ + +Because in the expression's type: + [ + Here the type is: 'true' + 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 + │ +239 │ [Y || <> <= B, is_integer(Y) ]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +267 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [binary() | 'undefined'] +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: 'undefined' + 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 + │ +386 │ F() || F <- Fs, + │ ^ F. +Expected fun type with arity 0 +Got: fun(() -> atom()) | fun((number()) -> atom()) + +34 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-26.pretty similarity index 62% rename from crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-26.pretty index f228f5cd50..69c58c0e77 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-26.pretty @@ -16,4 +16,14 @@ 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/contravariant-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-27.pretty new file mode 100644 index 0000000000..69c58c0e77 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-27.pretty @@ -0,0 +1,29 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/contravariant.erl:54:33 + │ +54 │ contravariant_subtype_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: ref_contravariant('a') +Context expected type: ref_contravariant_ab() + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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/contravariant-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-28.pretty new file mode 100644 index 0000000000..69c58c0e77 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/contravariant-OTP-28.pretty @@ -0,0 +1,29 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/contravariant.erl:54:33 + │ +54 │ contravariant_subtype_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: ref_contravariant('a') +Context expected type: ref_contravariant_ab() + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty similarity index 75% rename from crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty index efc8c47aa0..92e20b1f1a 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty @@ -37,6 +37,12 @@ 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 │ @@ -68,6 +74,12 @@ 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 │ @@ -84,6 +96,12 @@ 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 │ @@ -100,6 +118,12 @@ 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 │ @@ -178,6 +202,12 @@ 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 │ @@ -194,6 +224,12 @@ 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 │ @@ -213,6 +249,15 @@ 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 │ @@ -229,6 +274,14 @@ 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 │ @@ -245,6 +298,14 @@ 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 │ @@ -301,13 +362,28 @@ 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 @@ -327,6 +403,13 @@ 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 │ @@ -368,13 +451,29 @@ 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 @@ -403,6 +502,14 @@ 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 │ @@ -424,6 +531,14 @@ 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 │ @@ -440,9 +555,16 @@ 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 @@ -484,6 +606,12 @@ 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 │ @@ -554,13 +682,30 @@ 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 @@ -578,6 +723,13 @@ 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 │ @@ -594,6 +746,13 @@ 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 │ @@ -610,6 +769,10 @@ 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 │ @@ -629,10 +792,10 @@ 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. + #{'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 @@ -648,10 +811,18 @@ 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()} + Context expects type: boolean() | {'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 boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:739:20 │ @@ -663,10 +834,9 @@ 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. + 'wrong_ret' is not compatible with boolean() | {'true', term()} + because + 'wrong_ret' is not compatible with boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:749:9 @@ -690,7 +860,15 @@ 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()} + Differs from the expected type: boolean() | {'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 boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:827:9 @@ -707,9 +885,19 @@ 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'] + Differs from the expected type: boolean() | ['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 boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:829:20 │ @@ -721,10 +909,9 @@ 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. + 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] + because + 'wrong_ret' is not compatible with boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:837:9 @@ -740,9 +927,19 @@ 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] + Differs from the expected type: boolean() | [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] + because + {'true', 'a'} is not compatible with boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:837:20 │ @@ -754,10 +951,9 @@ 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. + {'true', 'a'} is not compatible with boolean() | [dynamic()] + because + {'true', 'a'} is not compatible with boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:839:9 @@ -774,6 +970,12 @@ 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 │ @@ -789,6 +991,12 @@ 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 │ @@ -817,9 +1025,19 @@ 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'] + Differs from the expected type: boolean() | ['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'] + because + {'true', 'a'} is not compatible with boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:857:20 │ @@ -831,10 +1049,9 @@ 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. + {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] + because + {'true', 'a'} is not compatible with boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:873:9 @@ -855,9 +1072,19 @@ 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'] + Differs from the expected type: boolean() | ['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 boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:881:17 │ @@ -872,7 +1099,15 @@ 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'] + Differs from the expected type: boolean() | ['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 boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:891:9 @@ -888,10 +1123,18 @@ 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()] + Context expects type: boolean() | [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 boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:891:9 │ @@ -906,10 +1149,18 @@ 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'] + Context expects type: boolean() | ['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 boolean() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:900:9 │ @@ -927,6 +1178,12 @@ 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 │ @@ -944,6 +1201,12 @@ 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 │ @@ -961,6 +1224,12 @@ 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 │ @@ -978,6 +1247,12 @@ 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 │ @@ -995,6 +1270,12 @@ 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 │ @@ -1012,6 +1293,12 @@ 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 │ @@ -1040,6 +1327,12 @@ 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 │ @@ -1057,6 +1350,12 @@ 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 │ @@ -1106,6 +1405,12 @@ 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 │ @@ -1206,6 +1511,14 @@ 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 │ @@ -1250,17 +1563,31 @@ 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 @@ -1274,17 +1601,31 @@ 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 @@ -1352,6 +1693,14 @@ 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 │ @@ -1370,6 +1719,14 @@ 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 │ @@ -1387,6 +1744,12 @@ 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 │ @@ -1454,6 +1817,12 @@ 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 │ @@ -1472,6 +1841,14 @@ 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 │ @@ -1497,21 +1874,41 @@ 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 @@ -1561,6 +1958,12 @@ 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 │ @@ -1633,6 +2036,17 @@ 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 │ @@ -1677,25 +2091,46 @@ 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 @@ -1730,6 +2165,12 @@ 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 │ @@ -1752,6 +2193,14 @@ 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 │ @@ -1895,6 +2344,17 @@ 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 │ @@ -1915,6 +2375,10 @@ 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 │ @@ -1958,6 +2422,10 @@ 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 │ @@ -1975,6 +2443,10 @@ 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 │ @@ -2000,6 +2472,10 @@ 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 │ @@ -2020,9 +2496,16 @@ 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 @@ -2042,6 +2525,15 @@ 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 │ @@ -2057,6 +2549,14 @@ 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 │ @@ -2072,6 +2572,14 @@ 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 │ @@ -2087,6 +2595,14 @@ 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 │ @@ -2103,6 +2619,14 @@ 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 │ @@ -2121,6 +2645,16 @@ 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 │ @@ -2136,6 +2670,14 @@ 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 │ @@ -2151,6 +2693,14 @@ 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 │ @@ -2166,6 +2716,14 @@ 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 │ @@ -2182,6 +2740,14 @@ 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 │ @@ -2198,6 +2764,14 @@ 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 │ @@ -2219,6 +2793,17 @@ 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 │ @@ -2237,6 +2822,15 @@ 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 │ @@ -2261,6 +2855,10 @@ 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 │ @@ -2281,6 +2879,10 @@ 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 │ @@ -2301,6 +2903,13 @@ 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 │ @@ -2312,10 +2921,9 @@ 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. + 'err' is not compatible with boolean() | {'true', term()} + because + 'err' is not compatible with boolean() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2362:5 @@ -2335,6 +2943,13 @@ 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 │ @@ -2372,8 +2987,13 @@ 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. + Context expects type: iodata() + +------------------------------ Detailed message ------------------------------ + + atom() is not compatible with iodata() | unicode:charlist() + because + atom() is not compatible with iodata() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/custom.erl:2386:5 @@ -2403,6 +3023,17 @@ 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 │ @@ -2410,7 +3041,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ │ │ lists:partition(fun, L). -Expression has type: {[number()], [atom()]} +Expression has type: {[number()], [atom() | none()]} Context expected type: {[atom()], [number()]} │ @@ -2420,7 +3051,16 @@ Because in the expression's type: Here the type is: number() Context expects type: atom() ] - , [atom()]} + , [atom() | none()]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[number()], [atom() | none()]} 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 @@ -2429,7 +3069,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ │ │ lists:partition(fun, L). -Expression has type: {[{term(), number()}], [{term(), atom()}]} +Expression has type: {[{term(), number()}], [{term(), atom() | none()}]} Context expected type: {[{term(), atom()}], [{term(), number()}]} │ @@ -2441,7 +3081,19 @@ Because in the expression's type: Context expects type: atom() } ] - , [{term(), atom()}]} + , [{term(), atom() | none()}]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[{term(), number()}], [{term(), atom() | none()}]} 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 @@ -2464,6 +3116,18 @@ 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 │ @@ -2480,6 +3144,10 @@ 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 │ @@ -2497,6 +3165,15 @@ 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 │ @@ -2513,28 +3190,53 @@ 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 │ 2709 │ (foo, A) -> binary_to_atom(A); - │ ^ A. + │ ^ + │ │ + │ A. Expression has type: atom() Context expected type: binary() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects 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. + │ ^ + │ │ + │ B. Expression has type: binary() Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: binary() + Context expects 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. + │ ^ + │ │ + │ I. Expression has type: number() Context expected type: binary() + │ + +Because in the expression's type: + Here the type is: number() + Context expects type: binary() 202 ERRORS 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 new file mode 100644 index 0000000000..92e20b1f1a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty @@ -0,0 +1,3242 @@ +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()} + │ + + #{'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: boolean() | {'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 boolean() + +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()} + │ + + 'wrong_ret' is not compatible with boolean() | {'true', term()} + because + 'wrong_ret' is not compatible with boolean() + +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: boolean() | {'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 boolean() + +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: boolean() | ['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 boolean() + +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'] + │ + + 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] + because + 'wrong_ret' is not compatible with boolean() + +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: boolean() | [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] + because + {'true', 'a'} is not compatible with boolean() + +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()] + │ + + {'true', 'a'} is not compatible with boolean() | [dynamic()] + because + {'true', 'a'} is not compatible with boolean() + +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: boolean() | ['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'] + because + {'true', 'a'} is not compatible with boolean() + +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'] + │ + + {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] + because + {'true', 'a'} is not compatible with boolean() + +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: boolean() | ['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 boolean() + +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: boolean() | ['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 boolean() + +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: boolean() | [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 boolean() + +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: boolean() | ['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 boolean() + +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()} + │ + + 'err' is not compatible with boolean() | {'true', term()} + because + 'err' is not compatible with boolean() + +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: iodata() + +------------------------------ Detailed message ------------------------------ + + atom() is not compatible with iodata() | unicode:charlist() + because + atom() is not compatible with iodata() + +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() | none()]} +Context expected type: {[atom()], [number()]} + │ + +Because in the expression's type: + { + [ + Here the type is: number() + Context expects type: atom() + ] + , [atom() | none()]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[number()], [atom() | none()]} 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() | none()}]} +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() | none()}]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[{term(), number()}], [{term(), atom() | none()}]} 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() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects 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() + │ + +Because in the expression's type: + Here the type is: binary() + Context expects 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() + │ + +Because in the expression's type: + Here the type is: number() + Context expects 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 new file mode 100644 index 0000000000..6eb40f681a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty @@ -0,0 +1,3242 @@ +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()} + │ + + #{'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: boolean() | {'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 boolean() + +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()} + │ + + 'wrong_ret' is not compatible with boolean() | {'true', term()} + because + 'wrong_ret' is not compatible with boolean() + +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: boolean() | {'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 boolean() + +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: boolean() | ['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 boolean() + +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'] + │ + + 'wrong_ret' is not compatible with boolean() | ['a' | 'b'] + because + 'wrong_ret' is not compatible with boolean() + +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: boolean() | [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] + because + {'true', 'a'} is not compatible with boolean() + +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()] + │ + + {'true', 'a'} is not compatible with boolean() | [dynamic()] + because + {'true', 'a'} is not compatible with boolean() + +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: boolean() | ['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'] + because + {'true', 'a'} is not compatible with boolean() + +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'] + │ + + {'true', 'a'} is not compatible with boolean() | ['a' | 'b'] + because + {'true', 'a'} is not compatible with boolean() + +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: boolean() | ['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 boolean() + +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: boolean() | ['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 boolean() + +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: boolean() | [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 boolean() + +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: boolean() | ['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 boolean() + +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()} + │ + + 'err' is not compatible with boolean() | {'true', term()} + because + 'err' is not compatible with boolean() + +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: iodata() + +------------------------------ Detailed message ------------------------------ + + atom() is not compatible with iodata() | unicode:charlist() + because + atom() is not compatible with iodata() + +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() | none()]} +Context expected type: {[atom()], [number()]} + │ + +Because in the expression's type: + { + [ + Here the type is: number() + Context expects type: atom() + ] + , [atom() | none()]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[number()], [atom() | none()]} 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() | none()}]} +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() | none()}]} + +------------------------------ Detailed message ------------------------------ + + at tuple index 1: + {[{term(), number()}], [{term(), atom() | none()}]} 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() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects 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() + │ + +Because in the expression's type: + Here the type is: binary() + Context expects 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() + │ + +Because in the expression's type: + Here the type is: number() + Context expects type: binary() + +202 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/kp_01.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/kp_01.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs1-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/detached_specs2-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-27.pretty new file mode 100644 index 0000000000..4871b7bbeb --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-27.pretty @@ -0,0 +1,8 @@ +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/dyn_calls.erl:49:3 + │ +49 │ (fun() -> res end)(arg), + │ ^^^^^^^^^^^^^^^^^^ fun. +fun with arity 0 used as fun with 1 arguments + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-28.pretty new file mode 100644 index 0000000000..4871b7bbeb --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_calls-OTP-28.pretty @@ -0,0 +1,8 @@ +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/dyn_calls.erl:49:3 + │ +49 │ (fun() -> res end)(arg), + │ ^^^^^^^^^^^^^^^^^^ fun. +fun with arity 0 used as fun with 1 arguments + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-27.pretty new file mode 100644 index 0000000000..9c82c19829 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-27.pretty @@ -0,0 +1,41 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:69:12 + │ +69 │ -> Res; + │ ^^^ Res. +Expression has type: fun((term(), term()) -> term()) +Context expected type: fun((none()) -> term()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:80:5 + │ +80 │ fun M:F/2. + │ ^^^^^^^^^ M:F/2. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:85:9 + │ +85 │ fun M:F/A. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:90:1 + │ +90 │ fun M:F/A. + │ ^^^^^^^^^ M:F/A. +Expression has type: fun() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:90:9 + │ +90 │ fun M:F/A. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-28.pretty new file mode 100644 index 0000000000..9c82c19829 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dyn_remote_funs-OTP-28.pretty @@ -0,0 +1,41 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:69:12 + │ +69 │ -> Res; + │ ^^^ Res. +Expression has type: fun((term(), term()) -> term()) +Context expected type: fun((none()) -> term()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:80:5 + │ +80 │ fun M:F/2. + │ ^^^^^^^^^ M:F/2. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:85:9 + │ +85 │ fun M:F/A. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:90:1 + │ +90 │ fun M:F/A. + │ ^^^^^^^^^ M:F/A. +Expression has type: fun() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dyn_remote_funs.erl:90:9 + │ +90 │ fun M:F/A. + │ ^ A. +Expression has type: atom() +Context expected type: number() + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_header.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/my_header.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_1-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pinned.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/pinned.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/scoping.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/scoping.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/skip.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/skip.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_callbacks_2-OTP-28.pretty 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-OTP-26.pretty similarity index 82% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-26.pretty index bc6265b8c1..a8dc692a9b 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-26.pretty @@ -66,33 +66,61 @@ 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 @@ -118,6 +146,12 @@ 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 │ @@ -142,6 +176,12 @@ 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_calls-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-27.pretty new file mode 100644 index 0000000000..a8dc692a9b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-27.pretty @@ -0,0 +1,217 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:22:5 + │ +22 │ F(X). + │ ^^^^ F(X). +Expression has type: 'b' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:38:5 + │ +38 │ (Rec#rec.method)(atom). + │ ^^^^^^^^^^^^^^^^^^^^^^ (...#rec.method)('atom'). +Expression has type: pid() +Context expected type: number() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:47:19 + │ +47 │ test_06_neg(F) -> F(1, 2). + │ ^ F. +Expected fun type with arity 2 +Got: fun(('a') -> 'b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:57:5 + │ +57 │ F(). + │ ^^^ F(). +Expression has type: 'ok' +Context expected type: pid() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:62:5 + │ +62 │ F(1). + │ ^ F. +Expected fun type with arity 1 +Got: fun(() -> 'ok') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:71:5 + │ +71 │ (fun ret_ok/0)(1, 2). + │ ^^^^^^^^^^^^^^ ret_ok/0. +Expected fun type with arity 2 +Got: fun(() -> 'ok') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:5 + │ +78 │ (FUnion)(false). + │ ^^^^^^^^^^^^^^^ FUnion('false'). +Expression has type: 'r1' | 'r2' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:5 + │ +78 │ (FUnion)(false). + │ ^^^^^^^^^^^^^^^ FUnion('false'). +Expression has type: 'r2' | 'r3' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:14 + │ +78 │ (FUnion)(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'. +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'. +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'. +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 + │ +87 │ Res. + │ ^^^ Res. +Expression has type: 'r1' | 'r2' | 'r3' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:94:5 + │ +94 │ (FUnion)(a2). + │ ^^^^^^^^^^^^ + │ │ + │ FUnion('a2'). +Expression has type: 'r1' | 'r2' +Context expected type: 'r1' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'r1' + 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 + │ +94 │ (FUnion)(a2). + │ ^^^^^^^^^^^^ FUnion('a2'). +Expression has type: 'r2' | 'r3' +Context expected type: 'r1' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:102:5 + │ +102 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: 'r1' | 'r2' | 'r3' +Context expected type: 'r1' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'r1' + 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 + │ +108 │ (F)(a). + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | 'z' + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:114:11 + │ +114 │ Res = (F)(a), + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | 'z' + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:138:5 + │ +138 │ (F)(a). + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | fun(() -> 'b') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:144:11 + │ +144 │ Res = (F)(a), + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | fun(() -> 'b') + +20 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-28.pretty new file mode 100644 index 0000000000..a8dc692a9b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_calls-OTP-28.pretty @@ -0,0 +1,217 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:22:5 + │ +22 │ F(X). + │ ^^^^ F(X). +Expression has type: 'b' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:38:5 + │ +38 │ (Rec#rec.method)(atom). + │ ^^^^^^^^^^^^^^^^^^^^^^ (...#rec.method)('atom'). +Expression has type: pid() +Context expected type: number() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:47:19 + │ +47 │ test_06_neg(F) -> F(1, 2). + │ ^ F. +Expected fun type with arity 2 +Got: fun(('a') -> 'b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:57:5 + │ +57 │ F(). + │ ^^^ F(). +Expression has type: 'ok' +Context expected type: pid() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:62:5 + │ +62 │ F(1). + │ ^ F. +Expected fun type with arity 1 +Got: fun(() -> 'ok') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:71:5 + │ +71 │ (fun ret_ok/0)(1, 2). + │ ^^^^^^^^^^^^^^ ret_ok/0. +Expected fun type with arity 2 +Got: fun(() -> 'ok') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:5 + │ +78 │ (FUnion)(false). + │ ^^^^^^^^^^^^^^^ FUnion('false'). +Expression has type: 'r1' | 'r2' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:5 + │ +78 │ (FUnion)(false). + │ ^^^^^^^^^^^^^^^ FUnion('false'). +Expression has type: 'r2' | 'r3' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:78:14 + │ +78 │ (FUnion)(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'. +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'. +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'. +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 + │ +87 │ Res. + │ ^^^ Res. +Expression has type: 'r1' | 'r2' | 'r3' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:94:5 + │ +94 │ (FUnion)(a2). + │ ^^^^^^^^^^^^ + │ │ + │ FUnion('a2'). +Expression has type: 'r1' | 'r2' +Context expected type: 'r1' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'r1' + 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 + │ +94 │ (FUnion)(a2). + │ ^^^^^^^^^^^^ FUnion('a2'). +Expression has type: 'r2' | 'r3' +Context expected type: 'r1' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_calls.erl:102:5 + │ +102 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: 'r1' | 'r2' | 'r3' +Context expected type: 'r1' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'r1' + 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 + │ +108 │ (F)(a). + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | 'z' + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:114:11 + │ +114 │ Res = (F)(a), + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | 'z' + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:138:5 + │ +138 │ (F)(a). + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | fun(() -> 'b') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/dynamic_calls.erl:144:11 + │ +144 │ Res = (F)(a), + │ ^^^ F. +Expected fun type with arity 1 +Got: fun(('a') -> 'b') | fun(() -> 'b') + +20 ERRORS 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-OTP-26.pretty similarity index 74% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-26.pretty index 78fd9f16f2..a8135429d1 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-26.pretty @@ -14,4 +14,10 @@ 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_catch-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-27.pretty new file mode 100644 index 0000000000..a8135429d1 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-27.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_catch.erl:22:16 + │ +22 │ {ok, A} -> A; + │ ^ + │ │ + │ A. +Expression has type: atom() | dynamic() +Context expected type: binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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_catch-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-28.pretty new file mode 100644 index 0000000000..a8135429d1 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_catch-OTP-28.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_catch.erl:22:16 + │ +22 │ {ok, A} -> A; + │ ^ + │ │ + │ A. +Expression has type: atom() | dynamic() +Context expected type: binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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-OTP-26.pretty similarity index 83% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-26.pretty index 7f7add29a8..1f55b0f88a 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-26.pretty @@ -72,6 +72,16 @@ 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 │ @@ -98,6 +108,16 @@ 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 │ @@ -116,6 +136,16 @@ 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_fun-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-27.pretty new file mode 100644 index 0000000000..1f55b0f88a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-27.pretty @@ -0,0 +1,181 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:65:3 + │ +65 │ fun M:id/1. + │ ^^^^^^^^^^ M:'id'/1. +Expression has type: fun((dynamic()) -> dynamic()) +Context expected type: fun(() -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:71:3 + │ +71 │ Res. + │ ^^^ Res. +Expression has type: fun((dynamic()) -> dynamic()) +Context expected type: fun(() -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:87:3 + │ +87 │ fun M:F/2. + │ ^^^^^^^^^ M:F/2. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun((pid()) -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:93:3 + │ +93 │ Res. + │ ^^^ Res. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun((pid()) -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:98:7 + │ +98 │ fun M:F/1. + │ ^ M. +Expression has type: {atom()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:103:9 + │ +103 │ fun M:F/1. + │ ^ F. +Expression has type: {atom()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:109:11 + │ +109 │ fun M:F/A. + │ ^ A. +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:146:24 + │ +146 │ f5_to_f4_cov_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f5('a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +155 │ apply_f4_neg(F) -> F(a). + │ ^^^^ F('a'). +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:170:22 + │ +170 │ fun3_to_f4_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> 'a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +176 │ f4_to_fun_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f4('a' | 'b') +Context expected type: fun((term()) -> 'a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +208 │ apply_by_name_neg1(Mod, Name) -> Mod:Name(). + │ ^^^ Mod. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:208:38 + │ +208 │ apply_by_name_neg1(Mod, Name) -> Mod:Name(). + │ ^^^^ Name. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:212:9 + │ +212 │ Res = Mod:Name(), + │ ^^^ Mod. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:212:13 + │ +212 │ Res = Mod:Name(), + │ ^^^^ Name. +Expression has type: term() +Context expected type: atom() + +15 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-28.pretty new file mode 100644 index 0000000000..1f55b0f88a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_fun-OTP-28.pretty @@ -0,0 +1,181 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:65:3 + │ +65 │ fun M:id/1. + │ ^^^^^^^^^^ M:'id'/1. +Expression has type: fun((dynamic()) -> dynamic()) +Context expected type: fun(() -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:71:3 + │ +71 │ Res. + │ ^^^ Res. +Expression has type: fun((dynamic()) -> dynamic()) +Context expected type: fun(() -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:87:3 + │ +87 │ fun M:F/2. + │ ^^^^^^^^^ M:F/2. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun((pid()) -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:93:3 + │ +93 │ Res. + │ ^^^ Res. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun((pid()) -> pid()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:98:7 + │ +98 │ fun M:F/1. + │ ^ M. +Expression has type: {atom()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:103:9 + │ +103 │ fun M:F/1. + │ ^ F. +Expression has type: {atom()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:109:11 + │ +109 │ fun M:F/A. + │ ^ A. +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:146:24 + │ +146 │ f5_to_f4_cov_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f5('a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +155 │ apply_f4_neg(F) -> F(a). + │ ^^^^ F('a'). +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:170:22 + │ +170 │ fun3_to_f4_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((term()) -> 'a' | 'b') +Context expected type: f4('a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +176 │ f4_to_fun_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: f4('a' | 'b') +Context expected type: fun((term()) -> 'a') + │ + +Because in the expression's type: + fun((term()) -> + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +208 │ apply_by_name_neg1(Mod, Name) -> Mod:Name(). + │ ^^^ Mod. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:208:38 + │ +208 │ apply_by_name_neg1(Mod, Name) -> Mod:Name(). + │ ^^^^ Name. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:212:9 + │ +212 │ Res = Mod:Name(), + │ ^^^ Mod. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_fun.erl:212:13 + │ +212 │ Res = Mod:Name(), + │ ^^^^ Name. +Expression has type: term() +Context expected type: atom() + +15 ERRORS 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-OTP-26.pretty similarity index 83% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-26.pretty index 78c9a41f3f..cd54f2d176 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-26.pretty @@ -38,6 +38,12 @@ 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 │ @@ -62,6 +68,12 @@ 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 │ @@ -100,6 +112,14 @@ 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 │ @@ -116,6 +136,12 @@ 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 │ @@ -132,6 +158,12 @@ 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_generics-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-27.pretty new file mode 100644 index 0000000000..cd54f2d176 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-27.pretty @@ -0,0 +1,181 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:33:3 + │ +33 │ lists:map(fun inc/1, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ lists:map(inc/1, Ns). +Expression has type: [number()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:43:3 + │ +43 │ lists:map(fun dyn_fun/1, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:map(dyn_fun/1, Ns). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:53:3 + │ +53 │ lists:foldl(fun add/2, 0, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:foldl(add/2, 0, Ns). +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:65:3 + │ +65 │ lists:foldl(fun add/2, Acc, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(add/2, Acc, Ns). +Expression has type: number() | dynamic() +Context expected type: none() + │ + +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: 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 + │ +72 │ {Res}. + │ ^^^^^ {Res}. +Expression has type: {number() | dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:78:3 + │ +78 │ lists:foldl(fun dyn_fun/2, Acc, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(dyn_fun/2, Acc, Ns). +Expression has type: dynamic() | string() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +87 │ fapply(fun inc/1, dyn_val()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fapply(inc/1, dyn_val()). +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:92:3 + │ +92 │ {Res}. + │ ^^^^^ {Res}. +Expression has type: {dynamic()} +Context expected type: X + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:108:3 + │ +108 │ ╭ ╭ lists:map( +109 │ │ │ fun +110 │ │ │ Fib(N) when N < 2 +111 │ │ │ -> 1; + · │ │ +117 │ │ │ [1, 2, 3] +118 │ │ │ ). + │ ╰─│───^ lists:map(fun, [1, 2, 3]). +Expression has type: [number() | 'three'] +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: 'three' + 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 + │ +115 │ Fib(N - 2) + Fib(N - 1) + │ ^^^^^^^^^^ + │ │ + │ Fib(_ - _). +Expression has type: number() | 'three' +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: '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 + │ +115 │ Fib(N - 2) + Fib(N - 1) + │ ^^^^^^^^^^ + │ │ + │ Fib(_ - _). +Expression has type: number() | 'three' +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: '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 + │ +127 │ Fib(undefined) + Fib(N - 1) + │ ^^^^^^^^^ 'undefined'. +Expression has type: 'undefined' +Context expected type: number() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/dynamic_generics.erl:171:47 + │ +171 │ unify_dyn_atom(D, A) -> eqwalizer:reveal_type(either(D, A)). + │ ^^^^^^^^^^^^ dynamic() | 'undef' + +13 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-28.pretty new file mode 100644 index 0000000000..cd54f2d176 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_generics-OTP-28.pretty @@ -0,0 +1,181 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:33:3 + │ +33 │ lists:map(fun inc/1, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ lists:map(inc/1, Ns). +Expression has type: [number()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:43:3 + │ +43 │ lists:map(fun dyn_fun/1, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:map(dyn_fun/1, Ns). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:53:3 + │ +53 │ lists:foldl(fun add/2, 0, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:foldl(add/2, 0, Ns). +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:65:3 + │ +65 │ lists:foldl(fun add/2, Acc, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(add/2, Acc, Ns). +Expression has type: number() | dynamic() +Context expected type: none() + │ + +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: 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 + │ +72 │ {Res}. + │ ^^^^^ {Res}. +Expression has type: {number() | dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:78:3 + │ +78 │ lists:foldl(fun dyn_fun/2, Acc, Ns). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(dyn_fun/2, Acc, Ns). +Expression has type: dynamic() | string() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +87 │ fapply(fun inc/1, dyn_val()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fapply(inc/1, dyn_val()). +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:92:3 + │ +92 │ {Res}. + │ ^^^^^ {Res}. +Expression has type: {dynamic()} +Context expected type: X + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_generics.erl:108:3 + │ +108 │ ╭ ╭ lists:map( +109 │ │ │ fun +110 │ │ │ Fib(N) when N < 2 +111 │ │ │ -> 1; + · │ │ +117 │ │ │ [1, 2, 3] +118 │ │ │ ). + │ ╰─│───^ lists:map(fun, [1, 2, 3]). +Expression has type: [number() | 'three'] +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: 'three' + 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 + │ +115 │ Fib(N - 2) + Fib(N - 1) + │ ^^^^^^^^^^ + │ │ + │ Fib(_ - _). +Expression has type: number() | 'three' +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: '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 + │ +115 │ Fib(N - 2) + Fib(N - 1) + │ ^^^^^^^^^^ + │ │ + │ Fib(_ - _). +Expression has type: number() | 'three' +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: '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 + │ +127 │ Fib(undefined) + Fib(N - 1) + │ ^^^^^^^^^ 'undefined'. +Expression has type: 'undefined' +Context expected type: number() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/dynamic_generics.erl:171:47 + │ +171 │ unify_dyn_atom(D, A) -> eqwalizer:reveal_type(either(D, A)). + │ ^^^^^^^^^^^^ dynamic() | 'undef' + +13 ERRORS 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-OTP-26.pretty similarity index 87% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-26.pretty index 6e82aa3702..3ca5a136e6 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-OTP-26.pretty @@ -14,6 +14,12 @@ 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_local_funs-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-27.pretty new file mode 100644 index 0000000000..3ca5a136e6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-27.pretty @@ -0,0 +1,47 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:30:3 + │ +30 │ lists:foldl(F, 0, L). + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(F, 0, L). +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/dynamic_local_funs.erl:36:15 + │ +36 │ N1 + N2 + binary_to_atom(Acc) + │ ^^^^^^^^^^^^^^^^^^^ erlang:binary_to_atom(Acc). +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:48:3 + │ +48 │ X. + │ ^ X. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun(('a') -> 'b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:111:3 + │ +111 │ ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: pid() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-28.pretty new file mode 100644 index 0000000000..3ca5a136e6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_local_funs-OTP-28.pretty @@ -0,0 +1,47 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:30:3 + │ +30 │ lists:foldl(F, 0, L). + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ lists:foldl(F, 0, L). +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/dynamic_local_funs.erl:36:15 + │ +36 │ N1 + N2 + binary_to_atom(Acc) + │ ^^^^^^^^^^^^^^^^^^^ erlang:binary_to_atom(Acc). +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:48:3 + │ +48 │ X. + │ ^ X. +Expression has type: fun((dynamic(), dynamic()) -> dynamic()) +Context expected type: fun(('a') -> 'b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_local_funs.erl:111:3 + │ +111 │ ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: pid() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/test.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/test.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_receive-OTP-28.pretty 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-OTP-26.pretty similarity index 87% rename from crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-26.pretty index ce03971816..16ed8f0a82 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-26.pretty @@ -91,7 +91,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ D. -Expression has type: dynamic() | 'error' +Expression has type: dynamic() | 'error' | none() Context expected type: 'ok' │ @@ -100,6 +100,12 @@ Because in the expression's type: However the following candidate: 'error' Differs from the expected type: 'ok' +------------------------------ Detailed message ------------------------------ + + dynamic() | 'error' | none() 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 │ @@ -132,6 +138,12 @@ 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 │ @@ -150,4 +162,15 @@ 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/dynamic_refine-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-27.pretty new file mode 100644 index 0000000000..16ed8f0a82 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-27.pretty @@ -0,0 +1,176 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:86:21 + │ +86 │ is_list(Arg) -> {Arg} + │ ^^^^^ {Arg}. +Expression has type: {[term()]} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:95:21 + │ +95 │ is_list(Dyn) -> {Dyn} + │ ^^^^^ {Dyn}. +Expression has type: {dynamic([term()])} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:102:23 + │ +102 │ is_binary(Arg) -> {Arg} + │ ^^^^^ {Arg}. +Expression has type: {binary()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:111:23 + │ +111 │ is_binary(Dyn) -> {Dyn}; + │ ^^^^^ {Dyn}. +Expression has type: {dynamic(binary())} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:123:3 + │ +123 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:133:3 + │ +133 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:143:3 + │ +143 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {boolean() | dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:153:3 + │ +153 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {dynamic() | binary()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:168:3 + │ +168 │ {Mixed2}. + │ ^^^^^^^^ {Mixed2}. +Expression has type: {binary() | atom() | dynamic()} +Context expected type: 'ok' + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/dynamic_refine.erl:180:25 + │ +180 │ eqwalizer:reveal_type(Name), + │ ^^^^ atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:181:3 + │ +181 │ Name. + │ ^^^^ Name. +Expression has type: atom() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:221:28 + │ +221 │ dyn_refine_union_neg(D) -> D. + │ ^ + │ │ + │ D. +Expression has type: dynamic() | 'error' | none() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: 'error' + Differs from the expected type: 'ok' + +------------------------------ Detailed message ------------------------------ + + dynamic() | 'error' | none() 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 + │ +236 │ atom_to_binary(Id). + │ ^^^^^^^^^^^^^^^^^^ erlang:atom_to_binary(Id). +Expression has type: binary() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:236:18 + │ +236 │ atom_to_binary(Id). + │ ^^ Id. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:247:32 + │ +247 │ refine_union_alias_neg({X}) -> X. + │ ^ + │ │ + │ X. +Expression has type: dyn_alias() | 'err' +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn_alias() + 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 + │ +260 │ refine_in_tuple_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: union() +Context expected type: {'ok'} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: dyn_alias() + However the following candidate: 'err' + 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/dynamic_refine-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-28.pretty new file mode 100644 index 0000000000..16ed8f0a82 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_refine-OTP-28.pretty @@ -0,0 +1,176 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:86:21 + │ +86 │ is_list(Arg) -> {Arg} + │ ^^^^^ {Arg}. +Expression has type: {[term()]} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:95:21 + │ +95 │ is_list(Dyn) -> {Dyn} + │ ^^^^^ {Dyn}. +Expression has type: {dynamic([term()])} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:102:23 + │ +102 │ is_binary(Arg) -> {Arg} + │ ^^^^^ {Arg}. +Expression has type: {binary()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:111:23 + │ +111 │ is_binary(Dyn) -> {Dyn}; + │ ^^^^^ {Dyn}. +Expression has type: {dynamic(binary())} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:123:3 + │ +123 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:133:3 + │ +133 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:143:3 + │ +143 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {boolean() | dynamic()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:153:3 + │ +153 │ {Mixed}. + │ ^^^^^^^ {Mixed}. +Expression has type: {dynamic() | binary()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:168:3 + │ +168 │ {Mixed2}. + │ ^^^^^^^^ {Mixed2}. +Expression has type: {binary() | atom() | dynamic()} +Context expected type: 'ok' + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/dynamic_refine.erl:180:25 + │ +180 │ eqwalizer:reveal_type(Name), + │ ^^^^ atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:181:3 + │ +181 │ Name. + │ ^^^^ Name. +Expression has type: atom() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:221:28 + │ +221 │ dyn_refine_union_neg(D) -> D. + │ ^ + │ │ + │ D. +Expression has type: dynamic() | 'error' | none() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: 'error' + Differs from the expected type: 'ok' + +------------------------------ Detailed message ------------------------------ + + dynamic() | 'error' | none() 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 + │ +236 │ atom_to_binary(Id). + │ ^^^^^^^^^^^^^^^^^^ erlang:atom_to_binary(Id). +Expression has type: binary() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:236:18 + │ +236 │ atom_to_binary(Id). + │ ^^ Id. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/dynamic_refine.erl:247:32 + │ +247 │ refine_union_alias_neg({X}) -> X. + │ ^ + │ │ + │ X. +Expression has type: dyn_alias() | 'err' +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn_alias() + 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 + │ +260 │ refine_in_tuple_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: union() +Context expected type: {'ok'} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: dyn_alias() + However the following candidate: 'err' + 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/united_fun.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/united_fun.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/vars1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/vars2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/dynamic_try_catch-OTP-28.pretty 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-OTP-26.pretty similarity index 85% rename from crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-26.pretty index e02928cc14..b9fdc2fbce 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-26.pretty @@ -38,6 +38,12 @@ 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 │ @@ -54,6 +60,12 @@ 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/elab_clause-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-27.pretty new file mode 100644 index 0000000000..b9fdc2fbce --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-27.pretty @@ -0,0 +1,85 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:30:5 + │ +30 │ Res. + │ ^^^ Res. +Expression has type: atom() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:48:5 + │ +48 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: atom() | {number()} +Context expected type: {atom()} | number() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects type: {atom()} | number() + No candidate matches in the expected union. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:76:5 + │ +76 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: 'error' | 'exit' | 'throw' | 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: '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 + │ +86 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: number() | [dynamic()] +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: [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 + │ +93 │ A:_ -> A + │ ^ A. +Expression has type: 'error' | 'exit' | 'throw' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:101:27 + │ +101 │ Class:_:Stack -> {Class, Stack} + │ ^^^^^^^^^^^^^^ {Class, Stack}. +Expression has type: {'error' | 'exit' | 'throw', [dynamic()]} +Context expected type: number() + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-28.pretty new file mode 100644 index 0000000000..b9fdc2fbce --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/elab_clause-OTP-28.pretty @@ -0,0 +1,85 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:30:5 + │ +30 │ Res. + │ ^^^ Res. +Expression has type: atom() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:48:5 + │ +48 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: atom() | {number()} +Context expected type: {atom()} | number() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects type: {atom()} | number() + No candidate matches in the expected union. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:76:5 + │ +76 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: 'error' | 'exit' | 'throw' | 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: '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 + │ +86 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: number() | [dynamic()] +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: [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 + │ +93 │ A:_ -> A + │ ^ A. +Expression has type: 'error' | 'exit' | 'throw' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/elab_clause.erl:101:27 + │ +101 │ Class:_:Stack -> {Class, Stack} + │ ^^^^^^^^^^^^^^ {Class, Stack}. +Expression has type: {'error' | 'exit' | 'throw', [dynamic()]} +Context expected type: number() + +6 ERRORS 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-OTP-26.pretty similarity index 61% rename from crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-26.pretty index fab550d5e6..ee63204870 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-26.pretty @@ -15,6 +15,17 @@ 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 │ @@ -33,6 +44,12 @@ 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 │ @@ -51,6 +68,15 @@ 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 │ @@ -63,9 +89,17 @@ 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 @@ -84,40 +118,11 @@ Because in the expression's type: Context expects type: binary() , atom()} -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/error_messages.erl:37:31 - │ -37 │ no_good_map_candidate_1(M) -> M. - │ ^ - │ │ - │ M. -Expression has type: #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} -Context expected type: #{foo => #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}} - │ +------------------------------ Detailed message ------------------------------ -Because in the expression's type: - #{ foo => - Here the type is: #{large_map_key_a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: large_map_key_a. - , ... } + 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:41:31 - │ -41 │ no_good_map_candidate_2(M) -> M. - │ ^ - │ │ - │ M. -Expression has type: #{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} -Context expected type: #{foo => #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'} - │ - -Because in the expression's type: - #{ foo => - Here the type is: #{large_map_key_a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: large_map_key_a. - , ... } - -8 ERRORS +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-27.pretty new file mode 100644 index 0000000000..ee63204870 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-27.pretty @@ -0,0 +1,128 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/error_messages.erl:16:22 + │ +16 │ map_candidates(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{bar := atom(), baz := atom()} +Context expected type: foo_map() | #{foo => atom()} + │ + +Because in the expression's type: + #{ baz => + Here the type is: atom() + 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 + │ +19 │ map_candidates_2(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: [#{bar => 'a' | 'b'} | #{baz => 'a' | 'b'}] +Context expected type: [#{bar => 'a'}] + │ + +Because in the expression's type: + [ + Here the type is: #{baz => ...} + Context expects 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 + │ +22 │ no_map_rewrite(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'undefined' | binary() => atom()} +Context expected type: #{binary() => atom()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is a union type with some valid candidates: binary() + However the following candidate: 'undefined' + 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 + │ +27 │ no_record_conversion_1(Foo) -> Foo. + │ ^^^ Foo. +Expression has type: #foo{} +Context expected type: {'foobar', atom()} + +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. +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 + │ +33 │ record_conversion(Foo) -> Foo. + │ ^^^ + │ │ + │ Foo. +Expression has type: #foo{} +Context expected type: {'foo', binary(), atom()} + │ + +Because in the expression's type: + { 'foo', + Here the type is: atom() + 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() + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-28.pretty new file mode 100644 index 0000000000..ee63204870 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/error_messages-OTP-28.pretty @@ -0,0 +1,128 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/error_messages.erl:16:22 + │ +16 │ map_candidates(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{bar := atom(), baz := atom()} +Context expected type: foo_map() | #{foo => atom()} + │ + +Because in the expression's type: + #{ baz => + Here the type is: atom() + 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 + │ +19 │ map_candidates_2(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: [#{bar => 'a' | 'b'} | #{baz => 'a' | 'b'}] +Context expected type: [#{bar => 'a'}] + │ + +Because in the expression's type: + [ + Here the type is: #{baz => ...} + Context expects 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 + │ +22 │ no_map_rewrite(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'undefined' | binary() => atom()} +Context expected type: #{binary() => atom()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is a union type with some valid candidates: binary() + However the following candidate: 'undefined' + 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 + │ +27 │ no_record_conversion_1(Foo) -> Foo. + │ ^^^ Foo. +Expression has type: #foo{} +Context expected type: {'foobar', atom()} + +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. +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 + │ +33 │ record_conversion(Foo) -> Foo. + │ ^^^ + │ │ + │ Foo. +Expression has type: #foo{} +Context expected type: {'foo', binary(), atom()} + │ + +Because in the expression's type: + { 'foo', + Here the type is: atom() + 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() + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/attributes.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/expr2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/fancy_generics-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-26.pretty similarity index 69% rename from crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-26.pretty index 1a6ae465e8..f6d7701c2e 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/format.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-26.pretty @@ -13,4 +13,10 @@ 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/format-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-27.pretty new file mode 100644 index 0000000000..f6d7701c2e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-27.pretty @@ -0,0 +1,22 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/format.erl:13:3 + │ +13 │ io_lib:format("~p", [X]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ io_lib:format(string_lit, [X]). +Expression has type: io_lib:chars() +Context expected type: string() + │ + +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/format-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-28.pretty new file mode 100644 index 0000000000..f6d7701c2e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/format-OTP-28.pretty @@ -0,0 +1,22 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/format.erl:13:3 + │ +13 │ io_lib:format("~p", [X]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ io_lib:format(string_lit, [X]). +Expression has type: io_lib:chars() +Context expected type: string() + │ + +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/fun_stats.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-27.pretty new file mode 100644 index 0000000000..a8ac75c742 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-27.pretty @@ -0,0 +1,41 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:36:3 + │ +36 │ wn_ey_sy() + 3. + │ ^^^^^^^^^^ wn_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:36:3 + │ +36 │ wn_ey_sy() + 3. + │ ^^^^^^^^^^^^^^ _ + _. +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:43:3 + │ +43 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:46:3 + │ +46 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:55:3 + │ +55 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-28.pretty new file mode 100644 index 0000000000..a8ac75c742 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats-OTP-28.pretty @@ -0,0 +1,41 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:36:3 + │ +36 │ wn_ey_sy() + 3. + │ ^^^^^^^^^^ wn_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:36:3 + │ +36 │ wn_ey_sy() + 3. + │ ^^^^^^^^^^^^^^ _ + _. +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:43:3 + │ +43 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:46:3 + │ +46 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats.erl:55:3 + │ +55 │ wy_ey_sy() + 3. + │ ^^^^^^^^^^ wy_ey_sy(). +Expression has type: 'ok' +Context expected type: number() + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-27.pretty new file mode 100644 index 0000000000..0fdf42f94d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-27.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:15:11 + │ +15 │ _ = 1 + Atom, + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:16:22 + │ +16 │ _ = binary_to_atom(Atom), + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:17:13 + │ +17 │ _ = "" ++ Atom, + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: [term()] + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-28.pretty new file mode 100644 index 0000000000..0fdf42f94d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/fun_stats2-OTP-28.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:15:11 + │ +15 │ _ = 1 + Atom, + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:16:22 + │ +16 │ _ = binary_to_atom(Atom), + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/fun_stats2.erl:17:13 + │ +17 │ _ = "" ++ Atom, + │ ^^^^ Atom. +Expression has type: atom() +Context expected type: [term()] + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-26.pretty similarity index 81% rename from crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-26.pretty index 1e2c339ecc..589448836f 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/funs.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-26.pretty @@ -29,6 +29,12 @@ 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 │ @@ -44,6 +50,12 @@ 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 │ @@ -59,6 +71,12 @@ 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 │ @@ -74,6 +92,12 @@ 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 │ @@ -92,6 +116,14 @@ 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 │ @@ -107,6 +139,12 @@ 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 │ @@ -122,6 +160,12 @@ 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 │ @@ -137,6 +181,12 @@ 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 │ @@ -152,6 +202,12 @@ 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 │ @@ -167,6 +223,12 @@ 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 │ @@ -182,6 +244,12 @@ 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 │ @@ -197,6 +265,12 @@ 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/funs-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-27.pretty new file mode 100644 index 0000000000..589448836f --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-27.pretty @@ -0,0 +1,373 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:23:20 + │ +23 │ check_fun_neg() -> fun() -> ok end. + │ ^^^^^^^^^^^^^^^ fun. +Expression has type: fun(() -> 'ok') +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:28:27 + │ +28 │ fun() -> list_to_atom(Tup) end. + │ ^^^ Tup. +Expression has type: {} +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:63:9 + │ +63 │ X ++ [] end, + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +63 │ X ++ [] end, + │ ^^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +70 │ erlang:atom_to_binary(X) + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_binary(X). +Expression has type: binary() +Context expected type: n() + │ + +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 + │ +70 │ erlang:atom_to_binary(X) + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: atom() + │ + +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 + │ +77 │ ╭ ╭ lmap_n_to_n( +78 │ │ │ fun(X) -> X end, +79 │ │ │ [1,2,3] +80 │ │ │ ). + │ ╰─│─────^ lmap_n_to_n(fun, [1, 2, 3]). +Expression has type: [n()] +Context expected type: ['a'] + │ ╰─────' + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^ + │ │ + │ Y. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +96 │ erlang:atom_to_binary(X), + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: atom() + │ + +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 + │ +97 │ X ++ Y + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +97 │ X ++ Y + │ ^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +97 │ X ++ Y + │ ^ + │ │ + │ Y. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +104 │ erlang:is_number(X) + │ ^^^^^^^^^^^^^^^^^^^ erlang:is_number(X). +Expression has type: boolean() +Context expected type: n() + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:127:9 + │ +127 │ fun (X, _) -> X 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/funs.erl:134:24 + │ +134 │ fun (Y) -> Y + N end, + │ ^ N. +Expression has type: atom() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/funs.erl:155:1 + │ +155 │ ╭ x6(L, B) -> +156 │ │ xmap(fun (_) -> B end, L). + │ ╰─────────────────────────────^ Clause is not covered by spec + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:195:5 + │ +195 │ (fun () -> ok end)(1, 2, 3). + │ ^^^^^^^^^^^^^^^^^^ fun. +fun with arity 0 used as fun with 3 arguments + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:199:5 + │ +199 │ (fun (_X) -> ok end)(1, 2, 3). + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 1 used as fun with 3 arguments + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:203:5 + │ +203 │ (fun (_X) -> ok end)(). + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 1 used as fun with 0 arguments + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:224:16 + │ +224 │ fun F() -> F end. + │ ^ F. +Expression has type: fun(() -> 'a') +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:329:9 + │ +329 │ F(A) + │ ^^^^ F(A). +Expression has type: term() +Context expected type: 'other' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:336:11 + │ +336 │ F(A) + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:363:5 + │ +363 │ Fun(). + │ ^^^^^ Fun(). +Expression has type: 'no_ok' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:368:5 + │ +368 │ Fun(). + │ ^^^^^ Fun(). +Expression has type: 'no_ok' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:374:54 + │ +374 │ use_callback_test_neg() -> callback_test(fun (_T) -> ok end). + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: none() + +27 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-28.pretty new file mode 100644 index 0000000000..589448836f --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs-OTP-28.pretty @@ -0,0 +1,373 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:23:20 + │ +23 │ check_fun_neg() -> fun() -> ok end. + │ ^^^^^^^^^^^^^^^ fun. +Expression has type: fun(() -> 'ok') +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:28:27 + │ +28 │ fun() -> list_to_atom(Tup) end. + │ ^^^ Tup. +Expression has type: {} +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:63:9 + │ +63 │ X ++ [] end, + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +63 │ X ++ [] end, + │ ^^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +70 │ erlang:atom_to_binary(X) + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_binary(X). +Expression has type: binary() +Context expected type: n() + │ + +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 + │ +70 │ erlang:atom_to_binary(X) + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: atom() + │ + +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 + │ +77 │ ╭ ╭ lmap_n_to_n( +78 │ │ │ fun(X) -> X end, +79 │ │ │ [1,2,3] +80 │ │ │ ). + │ ╰─│─────^ lmap_n_to_n(fun, [1, 2, 3]). +Expression has type: [n()] +Context expected type: ['a'] + │ ╰─────' + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +90 │ fun(X, Y) -> X ++ Y end. + │ ^ + │ │ + │ Y. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +96 │ erlang:atom_to_binary(X), + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: atom() + │ + +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 + │ +97 │ X ++ Y + │ ^ + │ │ + │ X. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +97 │ X ++ Y + │ ^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [dynamic()] +Context expected type: n() + │ + +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 + │ +97 │ X ++ Y + │ ^ + │ │ + │ Y. +Expression has type: n() +Context expected type: [term()] + │ + +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 + │ +104 │ erlang:is_number(X) + │ ^^^^^^^^^^^^^^^^^^^ erlang:is_number(X). +Expression has type: boolean() +Context expected type: n() + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:127:9 + │ +127 │ fun (X, _) -> X 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/funs.erl:134:24 + │ +134 │ fun (Y) -> Y + N end, + │ ^ N. +Expression has type: atom() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/funs.erl:155:1 + │ +155 │ ╭ x6(L, B) -> +156 │ │ xmap(fun (_) -> B end, L). + │ ╰─────────────────────────────^ Clause is not covered by spec + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:195:5 + │ +195 │ (fun () -> ok end)(1, 2, 3). + │ ^^^^^^^^^^^^^^^^^^ fun. +fun with arity 0 used as fun with 3 arguments + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:199:5 + │ +199 │ (fun (_X) -> ok end)(1, 2, 3). + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 1 used as fun with 3 arguments + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ check/src/funs.erl:203:5 + │ +203 │ (fun (_X) -> ok end)(). + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 1 used as fun with 0 arguments + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:224:16 + │ +224 │ fun F() -> F end. + │ ^ F. +Expression has type: fun(() -> 'a') +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:329:9 + │ +329 │ F(A) + │ ^^^^ F(A). +Expression has type: term() +Context expected type: 'other' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:336:11 + │ +336 │ F(A) + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:363:5 + │ +363 │ Fun(). + │ ^^^^^ Fun(). +Expression has type: 'no_ok' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:368:5 + │ +368 │ Fun(). + │ ^^^^^ Fun(). +Expression has type: 'no_ok' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs.erl:374:54 + │ +374 │ use_callback_test_neg() -> callback_test(fun (_T) -> ok end). + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: none() + +27 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-26.pretty similarity index 74% rename from crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-26.pretty index f3a82abfd6..7b20cba52c 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/funs2.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-26.pretty @@ -19,6 +19,14 @@ 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 │ @@ -58,6 +66,12 @@ 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 │ @@ -76,6 +90,14 @@ 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 │ @@ -94,6 +116,14 @@ 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/funs2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-27.pretty new file mode 100644 index 0000000000..7b20cba52c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-27.pretty @@ -0,0 +1,135 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:32:20 + │ +32 │ 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/funs2.erl:54:5 + │ +54 │ X. + │ ^ X. +Expression has type: [dynamic()] +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/funs2.erl:95:1 + │ +95 │ generic({}, {}, {}) -> nok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:95:24 + │ +95 │ generic({}, {}, {}) -> nok. + │ ^^^ 'nok'. +Expression has type: 'nok' +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:101:9 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((atom()) -> atom()) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: string() + 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 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((dynamic() | fun((string()) -> atom())) -> dynamic() | fun((string()) -> atom())) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: fun((string()) -> atom()) + 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 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((dynamic() | string() | fun((string()) -> atom())) -> dynamic() | string() | fun((string()) -> atom())) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: string() + 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 + │ +152 │ (X, X) -> X + A + │ ^ A. +Expression has type: atom() +Context expected type: number() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-28.pretty new file mode 100644 index 0000000000..7b20cba52c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs2-OTP-28.pretty @@ -0,0 +1,135 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:32:20 + │ +32 │ 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/funs2.erl:54:5 + │ +54 │ X. + │ ^ X. +Expression has type: [dynamic()] +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/funs2.erl:95:1 + │ +95 │ generic({}, {}, {}) -> nok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:95:24 + │ +95 │ generic({}, {}, {}) -> nok. + │ ^^^ 'nok'. +Expression has type: 'nok' +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs2.erl:101:9 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((atom()) -> atom()) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: string() + 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 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((dynamic() | fun((string()) -> atom())) -> dynamic() | fun((string()) -> atom())) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: fun((string()) -> atom()) + 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 + │ +101 │ fun erlang:atom_to_list/1, + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ erlang:atom_to_list/1. +Expression has type: fun((atom()) -> string()) +Context expected type: fun((dynamic() | string() | fun((string()) -> atom())) -> dynamic() | string() | fun((string()) -> atom())) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: string() + 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 + │ +152 │ (X, X) -> X + A + │ ^ A. +Expression has type: atom() +Context expected type: number() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-27.pretty new file mode 100644 index 0000000000..269917ecb4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-27.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs_uncommon.erl:38:13 + │ +38 │ field = {fun(X) -> X end, an_atom} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ {fun, 'an_atom'}. +Expression has type: {fun((dynamic()) -> dynamic()), 'an_atom'} +Context expected type: fun(('a') -> 'a') + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-28.pretty new file mode 100644 index 0000000000..269917ecb4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/funs_uncommon-OTP-28.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/funs_uncommon.erl:38:13 + │ +38 │ field = {fun(X) -> X end, an_atom} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ {fun, 'an_atom'}. +Expression has type: {fun((dynamic()) -> dynamic()), 'an_atom'} +Context expected type: fun(('a') -> 'a') + +1 ERROR 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-OTP-26.pretty similarity index 80% rename from crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-26.pretty index ae03bc49c8..c10af08c9d 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-OTP-26.pretty @@ -31,6 +31,12 @@ 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 │ @@ -48,6 +54,14 @@ 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 │ @@ -73,6 +87,12 @@ 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 │ @@ -91,6 +111,14 @@ 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 │ @@ -108,6 +136,12 @@ 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 │ @@ -125,6 +159,14 @@ 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 │ @@ -150,6 +192,12 @@ 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 │ @@ -168,6 +216,14 @@ 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 │ @@ -216,6 +272,10 @@ 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 │ @@ -305,6 +365,15 @@ 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 │ @@ -345,6 +414,12 @@ 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 │ @@ -369,6 +444,12 @@ 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 │ @@ -385,6 +466,12 @@ 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 │ @@ -434,6 +521,12 @@ 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 │ @@ -474,6 +567,12 @@ 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 │ @@ -490,6 +589,12 @@ 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 │ @@ -507,6 +612,13 @@ 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 │ @@ -565,6 +677,12 @@ 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 │ @@ -583,6 +701,12 @@ 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 │ @@ -601,6 +725,12 @@ 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 │ @@ -651,6 +781,12 @@ 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 │ @@ -669,6 +805,12 @@ 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 │ @@ -687,6 +829,12 @@ 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 │ @@ -727,6 +875,10 @@ 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 │ @@ -743,6 +895,10 @@ 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 │ @@ -760,6 +916,12 @@ 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 │ @@ -778,6 +940,15 @@ 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 │ @@ -795,6 +966,12 @@ 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 │ @@ -811,6 +988,10 @@ 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 │ @@ -827,6 +1008,10 @@ 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 │ @@ -863,6 +1048,16 @@ 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 │ @@ -880,6 +1075,14 @@ 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 │ @@ -897,6 +1100,16 @@ 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 │ @@ -912,6 +1125,12 @@ 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 │ @@ -956,6 +1175,10 @@ 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 │ @@ -982,6 +1205,14 @@ 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/generic_fun_application-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-27.pretty new file mode 100644 index 0000000000..c10af08c9d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-27.pretty @@ -0,0 +1,1232 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:27:18 + │ +27 │ test01_neg(X) -> fapply(fun id/1, X). + │ ^^^^^^^^^^^^^^^^^^^ fapply(id/1, X). +Expression has type: atom() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:38:5 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lmap(atom_to_pid/1, [2, 4]). +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:38:29 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [dynamic(atom())] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mapl([2, 4], atom_to_pid/1). +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:43:18 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((number()) -> B) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: pid() + 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 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((dynamic() | number()) -> pid()) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: number() + 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 + │ +47 │ X = lmap(fun atom_to_pid/1, [2, 4]), + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +47 │ X = lmap(fun atom_to_pid/1, [2, 4]), + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [dynamic(atom())] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +48 │ X. + │ ^ X. +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:52:22 + │ +52 │ X = mapl([2, 4], fun atom_to_pid/1), + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((number()) -> B) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: pid() + 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 + │ +52 │ X = mapl([2, 4], fun atom_to_pid/1), + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((dynamic() | number()) -> pid()) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: number() + 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 + │ +53 │ X. + │ ^ X. +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:67:23 + │ +67 │ test08_neg() -> lhead(h). + │ ^ 'h'. +Expression has type: 'h' +Context expected type: [LH] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:67:23 + │ +67 │ test08_neg() -> lhead(h). + │ ^ 'h'. +Expression has type: 'h' +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:80:18 + │ +80 │ test09_neg(M) -> maptt(M). + │ ^^^^^^^^ maptt(M). +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:95:13 + │ +95 │ map_att(M). + │ ^ + │ │ + │ M. +Expression has type: #{true => pid()} +Context expected type: #{a => dynamic()} + │ + +Because in the expression's type: + Here the type is: #{true => ...} + 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 + │ +99 │ map_att(3). + │ ^ 3. +Expression has type: number() +Context expected type: #{a => T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:99:13 + │ +99 │ map_att(3). + │ ^ 3. +Expression has type: number() +Context expected type: #{a => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:17 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^^^^^^^^^^^^^^^ lmap({}, [2, 4]). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:22 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:22 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:18 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^^^^^^^^^^^^^^^ mapl([2, 4], {}). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:31 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:31 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:119:5 + │ +119 │ F(Shape). + │ ^^^^^^^^ F(Shape). +Expression has type: T +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:119:7 + │ +119 │ F(Shape). + │ ^^^^^ + │ │ + │ Shape. +Expression has type: #{a => number()} +Context expected type: #{atom() => T} + │ + +Because in the expression's type: + #{ a => + Here the type is: number() + 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 + │ +126 │ F(Tuple). + │ ^^^^^^^^ F(Tuple). +Expression has type: X +Context expected type: 'false' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:130:5 + │ +130 │ lmap(F, [2, 4]). + │ ^^^^^^^^^^^^^^^ lmap(F, [2, 4]). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:134:5 + │ +134 │ mapl([2, 4], F). + │ ^^^^^^^^^^^^^^^ mapl([2, 4], F). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:144:5 + │ +144 │ ttt(AB, a). + │ ^^^^^^^^^^ + │ │ + │ ttt(AB, 'a'). +Expression has type: 'a' | 'b' +Context expected type: 'a' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' + 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 + │ +157 │ ttt(a, b). + │ ^^^^^^^^^ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:167:5 + │ +167 │ ttt(a, b). + │ ^^^^^^^^^ + │ │ + │ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'a' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' + 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 + │ +172 │ ttt(a, b). + │ ^^^^^^^^^ + │ │ + │ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' + 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 + │ +226 │ ╭ test31_pos(Any, None) -> +227 │ │ X = ttt(Any, None), +228 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:242:1 + │ +242 │ ╭ test33_pos(None, Pid) -> +243 │ │ X = ttt(None, Pid), +244 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:249:1 + │ +249 │ ╭ test34_pos(None) -> +250 │ │ X=lmap(fun atom_to_pid/1, [None, a]), +251 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:258:23 + │ +258 │ test36_neg(F, X) -> F(X). + │ ^ X. +Expression has type: T +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:270:5 + │ +270 │ fun0_id(F). + │ ^^^^^^^^^^ + │ │ + │ fun0_id(F). +Expression has type: fun(() -> A) +Context expected type: fun(() -> X) + │ + +Because in the expression's type: + fun(() -> + Here the type is: A + 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 + │ +276 │ fun0_id(F). + │ ^^^^^^^^^^ fun0_id(F). +Expression has type: fun(() -> dynamic()) +Context expected type: fun((X) -> Y) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:276:13 + │ +276 │ fun0_id(F). + │ ^ F. +Expression has type: fun((A) -> B) +Context expected type: fun(() -> X) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:276:13 + │ +276 │ fun0_id(F). + │ ^ F. +Expression has type: fun((A) -> B) +Context expected type: fun(() -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:288:5 + │ +288 │ X. + │ ^ + │ │ + │ X. +Expression has type: 'first' | Last +Context expected type: 'first' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'first' + 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 + │ +296 │ X. + │ ^ + │ │ + │ X. +Expression has type: atom() | T +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: 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 + │ +322 │ ╭ ╭ expect_shape( +323 │ │ │ #{t => 1, u => Pid} +324 │ │ │ ). + │ ╰─│─────^ expect_shape(#{..}). +Expression has type: {number(), pid()} +Context expected type: {pid(), number()} + │ ╰─────' + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +330 │ F(A). + │ ^^^^ F(A). +Expression has type: B +Context expected type: C + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((A) -> A) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((A) -> A) 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/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((dynamic()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((dynamic()) -> dynamic()) 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/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((A) -> A) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((dynamic()) -> dynamic()) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((A) -> A) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((Z) -> Z) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((Z) -> Z) 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/generic_fun_application.erl:358:9 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((dynamic()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((dynamic()) -> dynamic()) 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/generic_fun_application.erl:358:9 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((Z) -> Z) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((dynamic()) -> dynamic()) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((Z) -> Z) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +432 │ test_dict1(D) -> num_and_t(D, D). + │ ^^^^^^^^^^^^^^^ num_and_t(D, D). +Expression has type: #{atom() => pid()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:432:28 + │ +432 │ test_dict1(D) -> num_and_t(D, D). + │ ^ D. +Expression has type: #{atom() => pid()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:441:5 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ shape_vars(#{..}, 3). +Expression has type: 'ok' +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:441:16 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{T => number()} +Context expected type: #{a := T} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{T => number()} +Context expected type: #{a := term()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +446 │ dict_vars(F, N). + │ ^ + │ │ + │ F. +Expression has type: fun((#{a => number()}) -> number()) +Context expected type: fun((#{{T} => T}) -> T) + │ + +Because in the expression's type: + fun((#{a => number()}) -> + Here the type is: number() + 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 + │ +446 │ dict_vars(F, N). + │ ^ + │ │ + │ F. +Expression has type: fun((#{a => number()}) -> number()) +Context expected type: fun((#{{number()} => number()}) -> number()) + │ + +Because in the expression's type: + fun(( + Here the type is: #{{number()} => number()} + Context expects type: #{...} (no default association) + 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 + │ +471 │ test_aliases_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun(('a') -> 'b') +Context expected type: fun(('a') -> 'c') + │ + +Because in the expression's type: + fun(('a') -> + Here the type is: 'b' + 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 + │ +478 │ takesShape1(#{}). + │ ^^^ + │ │ + │ #{..}. +Expression has type: #{} +Context expected type: #{a := T} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +478 │ takesShape1(#{}). + │ ^^^ + │ │ + │ #{..}. +Expression has type: #{} +Context expected type: #{a := dynamic()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +500 │ test_quanitifer_scope_neg(F, X) -> F(X). + │ ^^^^ F(X). +Expression has type: T +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:500:38 + │ +500 │ test_quanitifer_scope_neg(F, X) -> F(X). + │ ^ X. +Expression has type: number() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:551:5 + │ +551 │ X. + │ ^ + │ │ + │ X. +Expression has type: contravar(contravar('a' | 'b')) +Context expected type: contravar(contravar('a')) + │ + +Because in the expression's type: + fun(( + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + Differs from the expected type: 'a' + ) -> '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 + │ +563 │ X. + │ ^ + │ │ + │ X. +Expression has type: invar('a') +Context expected type: fun((atom()) -> atom()) + │ + +Because in the expression's type: + fun(( + Here the type is: atom() + 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 + │ +569 │ X. + │ ^ + │ │ + │ X. +Expression has type: invar('a') +Context expected type: invar(atom()) + │ + +Because in the expression's type: + fun(( + Here the type is: atom() + 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 + │ +585 │ a. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: invar('a') + │ + +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 + │ +625 │ stuff_1(0) -> erlang:self(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:633:1 + │ +633 │ stuff_2(0) -> erlang:self(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/generic_fun_application.erl:645:5 + │ +645 │ F(1, 2), + │ ^ F. +Expected fun type with arity 2 +Got: fun((Z) -> Z) + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/generic_fun_application.erl:651:5 + │ +651 │ F(1, 2). + │ ^ F. +Expected fun type with arity 2 +Got: fun((Z) -> Z) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:684:9 + │ +684 │ #{u => 1, t => Pid, extra => 3} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{extra := number(), t := pid(), u := number()} +Context expected type: #{t := pid(), u := number()} + │ + +Because in the expression's type: + Here the type is: #{extra := ...} + 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 + │ +721 │ test_apply2() -> apply2(fun tat/2, 1, 1). + │ ^^^^^^^^^ Arg 2 of 'tat/2'. +Expression has type: number() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:739:5 + │ +739 │ invar(a, a). + │ ^^^^^^^^^^^ + │ │ + │ invar('a', 'a'). +Expression has type: fun(('a') -> 'a') +Context expected type: fun(('a' | 'b') -> 'a' | 'b') + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +752 │ _ = fun_with_tuple({a, b}, {}), + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:752:32 + │ +752 │ _ = fun_with_tuple({a, b}, {}), + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +81 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-28.pretty new file mode 100644 index 0000000000..c10af08c9d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generic_fun_application-OTP-28.pretty @@ -0,0 +1,1232 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:27:18 + │ +27 │ test01_neg(X) -> fapply(fun id/1, X). + │ ^^^^^^^^^^^^^^^^^^^ fapply(id/1, X). +Expression has type: atom() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:38:5 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lmap(atom_to_pid/1, [2, 4]). +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:38:29 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +38 │ lmap(fun atom_to_pid/1, [2, 4]). + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [dynamic(atom())] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mapl([2, 4], atom_to_pid/1). +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:43:18 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((number()) -> B) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: pid() + 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 + │ +43 │ mapl([2, 4], fun atom_to_pid/1). + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((dynamic() | number()) -> pid()) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: number() + 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 + │ +47 │ X = lmap(fun atom_to_pid/1, [2, 4]), + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +47 │ X = lmap(fun atom_to_pid/1, [2, 4]), + │ ^^^^^^ + │ │ + │ [2, 4]. +Expression has type: [number()] +Context expected type: [dynamic(atom())] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +48 │ X. + │ ^ X. +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:52:22 + │ +52 │ X = mapl([2, 4], fun atom_to_pid/1), + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((number()) -> B) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: pid() + 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 + │ +52 │ X = mapl([2, 4], fun atom_to_pid/1), + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ atom_to_pid/1. +Expression has type: fun((atom()) -> pid()) +Context expected type: fun((dynamic() | number()) -> pid()) + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: dynamic() + However the following candidate: number() + 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 + │ +53 │ X. + │ ^ X. +Expression has type: [pid()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:67:23 + │ +67 │ test08_neg() -> lhead(h). + │ ^ 'h'. +Expression has type: 'h' +Context expected type: [LH] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:67:23 + │ +67 │ test08_neg() -> lhead(h). + │ ^ 'h'. +Expression has type: 'h' +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:80:18 + │ +80 │ test09_neg(M) -> maptt(M). + │ ^^^^^^^^ maptt(M). +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:95:13 + │ +95 │ map_att(M). + │ ^ + │ │ + │ M. +Expression has type: #{true => pid()} +Context expected type: #{a => dynamic()} + │ + +Because in the expression's type: + Here the type is: #{true => ...} + 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 + │ +99 │ map_att(3). + │ ^ 3. +Expression has type: number() +Context expected type: #{a => T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:99:13 + │ +99 │ map_att(3). + │ ^ 3. +Expression has type: number() +Context expected type: #{a => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:17 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^^^^^^^^^^^^^^^ lmap({}, [2, 4]). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:22 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:102:22 + │ +102 │ test13_neg() -> lmap({}, [2, 4]). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:18 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^^^^^^^^^^^^^^^ mapl([2, 4], {}). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:31 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:105:31 + │ +105 │ test13b_neg() -> mapl([2, 4], {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((number()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:119:5 + │ +119 │ F(Shape). + │ ^^^^^^^^ F(Shape). +Expression has type: T +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:119:7 + │ +119 │ F(Shape). + │ ^^^^^ + │ │ + │ Shape. +Expression has type: #{a => number()} +Context expected type: #{atom() => T} + │ + +Because in the expression's type: + #{ a => + Here the type is: number() + 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 + │ +126 │ F(Tuple). + │ ^^^^^^^^ F(Tuple). +Expression has type: X +Context expected type: 'false' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:130:5 + │ +130 │ lmap(F, [2, 4]). + │ ^^^^^^^^^^^^^^^ lmap(F, [2, 4]). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:134:5 + │ +134 │ mapl([2, 4], F). + │ ^^^^^^^^^^^^^^^ mapl([2, 4], F). +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:144:5 + │ +144 │ ttt(AB, a). + │ ^^^^^^^^^^ + │ │ + │ ttt(AB, 'a'). +Expression has type: 'a' | 'b' +Context expected type: 'a' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' + 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 + │ +157 │ ttt(a, b). + │ ^^^^^^^^^ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:167:5 + │ +167 │ ttt(a, b). + │ ^^^^^^^^^ + │ │ + │ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'a' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' + 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 + │ +172 │ ttt(a, b). + │ ^^^^^^^^^ + │ │ + │ ttt('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' + 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 + │ +226 │ ╭ test31_pos(Any, None) -> +227 │ │ X = ttt(Any, None), +228 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:242:1 + │ +242 │ ╭ test33_pos(None, Pid) -> +243 │ │ X = ttt(None, Pid), +244 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:249:1 + │ +249 │ ╭ test34_pos(None) -> +250 │ │ X=lmap(fun atom_to_pid/1, [None, a]), +251 │ │ X. + │ ╰─────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:258:23 + │ +258 │ test36_neg(F, X) -> F(X). + │ ^ X. +Expression has type: T +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:270:5 + │ +270 │ fun0_id(F). + │ ^^^^^^^^^^ + │ │ + │ fun0_id(F). +Expression has type: fun(() -> A) +Context expected type: fun(() -> X) + │ + +Because in the expression's type: + fun(() -> + Here the type is: A + 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 + │ +276 │ fun0_id(F). + │ ^^^^^^^^^^ fun0_id(F). +Expression has type: fun(() -> dynamic()) +Context expected type: fun((X) -> Y) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:276:13 + │ +276 │ fun0_id(F). + │ ^ F. +Expression has type: fun((A) -> B) +Context expected type: fun(() -> X) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:276:13 + │ +276 │ fun0_id(F). + │ ^ F. +Expression has type: fun((A) -> B) +Context expected type: fun(() -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:288:5 + │ +288 │ X. + │ ^ + │ │ + │ X. +Expression has type: 'first' | Last +Context expected type: 'first' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'first' + 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 + │ +296 │ X. + │ ^ + │ │ + │ X. +Expression has type: atom() | T +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: 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 + │ +322 │ ╭ ╭ expect_shape( +323 │ │ │ #{t => 1, u => Pid} +324 │ │ │ ). + │ ╰─│─────^ expect_shape(#{..}). +Expression has type: {number(), pid()} +Context expected type: {pid(), number()} + │ ╰─────' + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +330 │ F(A). + │ ^^^^ F(A). +Expression has type: B +Context expected type: C + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((A) -> A) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((A) -> A) 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/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((dynamic()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((dynamic()) -> dynamic()) 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/generic_fun_application.erl:346:9 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((A) -> A) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((dynamic()) -> dynamic()) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +346 │ fun fun1_id_pos_a/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_a/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((A) -> A) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((Z) -> Z) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((Z) -> Z) 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/generic_fun_application.erl:358:9 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) with 1 type parameter +Context expected type: fun((dynamic()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((Z) -> Z) with 1 type parameter + Context expects type: fun((dynamic()) -> dynamic()) 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/generic_fun_application.erl:358:9 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((Z) -> Z) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((dynamic()) -> dynamic()) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +358 │ fun fun1_id_pos_z/1), + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ Arg 1 of 'fun1_id_pos_z/1'. +Expression has type: fun((Z) -> Z) | fun((dynamic()) -> dynamic()) +Context expected type: fun((Z) -> Z) + │ + +Because in the expression's type: + The type is a union type with some valid candidates: fun((dynamic()) -> dynamic()) + However, the following candidate doesn't match: + Here the type is: fun((Z) -> Z) with 1 type parameter + 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 + │ +432 │ test_dict1(D) -> num_and_t(D, D). + │ ^^^^^^^^^^^^^^^ num_and_t(D, D). +Expression has type: #{atom() => pid()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:432:28 + │ +432 │ test_dict1(D) -> num_and_t(D, D). + │ ^ D. +Expression has type: #{atom() => pid()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:441:5 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ shape_vars(#{..}, 3). +Expression has type: 'ok' +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:441:16 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{T => number()} +Context expected type: #{a := T} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +441 │ shape_vars(#{T => 3}, 3). + │ ^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{T => number()} +Context expected type: #{a := term()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +446 │ dict_vars(F, N). + │ ^ + │ │ + │ F. +Expression has type: fun((#{a => number()}) -> number()) +Context expected type: fun((#{{T} => T}) -> T) + │ + +Because in the expression's type: + fun((#{a => number()}) -> + Here the type is: number() + 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 + │ +446 │ dict_vars(F, N). + │ ^ + │ │ + │ F. +Expression has type: fun((#{a => number()}) -> number()) +Context expected type: fun((#{{number()} => number()}) -> number()) + │ + +Because in the expression's type: + fun(( + Here the type is: #{{number()} => number()} + Context expects type: #{...} (no default association) + 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 + │ +471 │ test_aliases_neg(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun(('a') -> 'b') +Context expected type: fun(('a') -> 'c') + │ + +Because in the expression's type: + fun(('a') -> + Here the type is: 'b' + 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 + │ +478 │ takesShape1(#{}). + │ ^^^ + │ │ + │ #{..}. +Expression has type: #{} +Context expected type: #{a := T} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +478 │ takesShape1(#{}). + │ ^^^ + │ │ + │ #{..}. +Expression has type: #{} +Context expected type: #{a := dynamic()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +500 │ test_quanitifer_scope_neg(F, X) -> F(X). + │ ^^^^ F(X). +Expression has type: T +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:500:38 + │ +500 │ test_quanitifer_scope_neg(F, X) -> F(X). + │ ^ X. +Expression has type: number() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:551:5 + │ +551 │ X. + │ ^ + │ │ + │ X. +Expression has type: contravar(contravar('a' | 'b')) +Context expected type: contravar(contravar('a')) + │ + +Because in the expression's type: + fun(( + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + Differs from the expected type: 'a' + ) -> '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 + │ +563 │ X. + │ ^ + │ │ + │ X. +Expression has type: invar('a') +Context expected type: fun((atom()) -> atom()) + │ + +Because in the expression's type: + fun(( + Here the type is: atom() + 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 + │ +569 │ X. + │ ^ + │ │ + │ X. +Expression has type: invar('a') +Context expected type: invar(atom()) + │ + +Because in the expression's type: + fun(( + Here the type is: atom() + 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 + │ +585 │ a. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: invar('a') + │ + +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 + │ +625 │ stuff_1(0) -> erlang:self(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/generic_fun_application.erl:633:1 + │ +633 │ stuff_2(0) -> erlang:self(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/generic_fun_application.erl:645:5 + │ +645 │ F(1, 2), + │ ^ F. +Expected fun type with arity 2 +Got: fun((Z) -> Z) + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/generic_fun_application.erl:651:5 + │ +651 │ F(1, 2). + │ ^ F. +Expected fun type with arity 2 +Got: fun((Z) -> Z) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:684:9 + │ +684 │ #{u => 1, t => Pid, extra => 3} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{extra := number(), t := pid(), u := number()} +Context expected type: #{t := pid(), u := number()} + │ + +Because in the expression's type: + Here the type is: #{extra := ...} + 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 + │ +721 │ test_apply2() -> apply2(fun tat/2, 1, 1). + │ ^^^^^^^^^ Arg 2 of 'tat/2'. +Expression has type: number() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:739:5 + │ +739 │ invar(a, a). + │ ^^^^^^^^^^^ + │ │ + │ invar('a', 'a'). +Expression has type: fun(('a') -> 'a') +Context expected type: fun(('a' | 'b') -> 'a' | 'b') + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +752 │ _ = fun_with_tuple({a, b}, {}), + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generic_fun_application.erl:752:32 + │ +752 │ _ = fun_with_tuple({a, b}, {}), + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +81 ERRORS 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-OTP-26.pretty similarity index 77% rename from crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions-OTP-26.pretty index 9c03aa9a60..53cd1350ec 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-OTP-26.pretty @@ -16,6 +16,15 @@ 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 │ @@ -53,6 +62,12 @@ 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 │ @@ -77,6 +92,12 @@ 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 │ @@ -93,6 +114,12 @@ 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 │ @@ -144,6 +171,14 @@ 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 │ @@ -170,6 +205,17 @@ 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 │ @@ -189,4 +235,15 @@ 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/generics_with_unions-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions-OTP-27.pretty new file mode 100644 index 0000000000..53cd1350ec --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions-OTP-27.pretty @@ -0,0 +1,249 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:17:18 + │ +17 │ test02_neg(X) -> {X, X}. + │ ^^^^^^ + │ │ + │ {X, X}. +Expression has type: {T | U, T | U} +Context expected type: {T, U} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: T + However the following candidate: U + 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 + │ +70 │ test_ret_neg() -> union(a, b). + │ ^^^^^^^^^^^ union('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'ok' + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/generics_with_unions.erl:72:1 + │ +72 │ -type ty_var_from_nowhere() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/generics_with_unions.erl:74:1 + │ +74 │ ╭ -spec test_uses_ty_var_from_nowhere +75 │ │ (pid() | ty_var_from_nowhere()) +76 │ │ -> nok. + │ ╰──────────^ test_uses_ty_var_from_nowhere/1 references type with invalid definition: ty_var_from_nowhere/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:132:5 + │ +132 │ set_list([{a, 5}], [{a, 5}]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ set_list([{'a', 5}], [{'a', 5}]). +Expression has type: props('a', number()) +Context expected type: 'wrong_ret' + │ + +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 + │ +217 │ overlap_1([T]) -> T; + │ ^ T. +Expression has type: term() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:218:17 + │ +218 │ overlap_1(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: [T] | T +Context expected type: T + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: T + 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 + │ +221 │ overlap_2(T) -> T; + │ ^ + │ │ + │ T. +Expression has type: T | [T] +Context expected type: T + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: T + 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 + │ +222 │ overlap_2([T]) -> T. + │ ^ T. +Expression has type: term() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:230:5 + │ +230 │ overlap_2([ok]). + │ ^^^^^^^^^^^^^^^ overlap_2(['ok']). +Expression has type: 'ok' +Context expected type: ['ok'] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:233:18 + │ +233 │ trick(_, {B}) -> B. + │ ^ B. +Expression has type: term() +Context expected type: B + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:256:5 + │ +256 │ Default; + │ ^^^^^^^ Default. +Expression has type: term() +Context expected type: Value + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:328:29 + │ +328 │ overlapping_union_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [K] | [[K]] +Context expected type: [K] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [K] + However, the following candidate doesn't match: + [ + Here the type is: [K] + 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 + │ +332 │ _ = overlapping_union_neg([]), + │ ^^ []. +Expression has type [] which matches multiple generic types in [K] | [[K]] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:379:28 + │ +379 │ Q2 = where1({i, atom}, Q), + │ ^ + │ │ + │ Q. +Expression has type: query() +Context expected type: fun(({'i', 'atom'}) -> 'ok') + │ + +Because in the expression's type: + fun(( + { 'i', + Here the type is: 'atom' + Context expects type: number() + } + ) -> '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 + │ +392 │ Q2 = where2(Q, {a, 123}), + │ ^ + │ │ + │ Q. +Expression has type: query() +Context expected type: fun(({'a', number()}) -> 'ok') + │ + +Because in the expression's type: + fun(( + { 'a', + Here the type is: number() + Context expects type: atom() + } + ) -> '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/generics_with_unions-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions-OTP-28.pretty new file mode 100644 index 0000000000..53cd1350ec --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/generics_with_unions-OTP-28.pretty @@ -0,0 +1,249 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:17:18 + │ +17 │ test02_neg(X) -> {X, X}. + │ ^^^^^^ + │ │ + │ {X, X}. +Expression has type: {T | U, T | U} +Context expected type: {T, U} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: T + However the following candidate: U + 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 + │ +70 │ test_ret_neg() -> union(a, b). + │ ^^^^^^^^^^^ union('a', 'b'). +Expression has type: 'a' | 'b' +Context expected type: 'ok' + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/generics_with_unions.erl:72:1 + │ +72 │ -type ty_var_from_nowhere() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/generics_with_unions.erl:74:1 + │ +74 │ ╭ -spec test_uses_ty_var_from_nowhere +75 │ │ (pid() | ty_var_from_nowhere()) +76 │ │ -> nok. + │ ╰──────────^ test_uses_ty_var_from_nowhere/1 references type with invalid definition: ty_var_from_nowhere/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:132:5 + │ +132 │ set_list([{a, 5}], [{a, 5}]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ set_list([{'a', 5}], [{'a', 5}]). +Expression has type: props('a', number()) +Context expected type: 'wrong_ret' + │ + +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 + │ +217 │ overlap_1([T]) -> T; + │ ^ T. +Expression has type: term() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:218:17 + │ +218 │ overlap_1(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: [T] | T +Context expected type: T + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: T + 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 + │ +221 │ overlap_2(T) -> T; + │ ^ + │ │ + │ T. +Expression has type: T | [T] +Context expected type: T + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: T + 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 + │ +222 │ overlap_2([T]) -> T. + │ ^ T. +Expression has type: term() +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:230:5 + │ +230 │ overlap_2([ok]). + │ ^^^^^^^^^^^^^^^ overlap_2(['ok']). +Expression has type: 'ok' +Context expected type: ['ok'] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:233:18 + │ +233 │ trick(_, {B}) -> B. + │ ^ B. +Expression has type: term() +Context expected type: B + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:256:5 + │ +256 │ Default; + │ ^^^^^^^ Default. +Expression has type: term() +Context expected type: Value + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:328:29 + │ +328 │ overlapping_union_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [K] | [[K]] +Context expected type: [K] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [K] + However, the following candidate doesn't match: + [ + Here the type is: [K] + 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 + │ +332 │ _ = overlapping_union_neg([]), + │ ^^ []. +Expression has type [] which matches multiple generic types in [K] | [[K]] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/generics_with_unions.erl:379:28 + │ +379 │ Q2 = where1({i, atom}, Q), + │ ^ + │ │ + │ Q. +Expression has type: query() +Context expected type: fun(({'i', 'atom'}) -> 'ok') + │ + +Because in the expression's type: + fun(( + { 'i', + Here the type is: 'atom' + Context expects type: number() + } + ) -> '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 + │ +392 │ Q2 = where2(Q, {a, 123}), + │ ^ + │ │ + │ Q. +Expression has type: query() +Context expected type: fun(({'a', number()}) -> 'ok') + │ + +Because in the expression's type: + fun(( + { 'a', + Here the type is: number() + Context expects type: atom() + } + ) -> '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-OTP-26.pretty similarity index 78% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-26.pretty index 1987f06692..bbd9109d68 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-26.pretty @@ -14,6 +14,12 @@ 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 │ @@ -29,6 +35,16 @@ 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 │ @@ -69,6 +85,12 @@ 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 │ @@ -85,4 +107,10 @@ 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_bounded-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-27.pretty new file mode 100644 index 0000000000..bbd9109d68 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-27.pretty @@ -0,0 +1,116 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_bounded.erl:16:21 + │ +16 │ bd_unsafe_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: dyn('a') | 'b' +Context expected type: 'c' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn('a') + 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 + │ +25 │ bd_coerce_safe_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: 'a' +Context expected type: dyn('b') + │ + +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 + │ +44 │ eqwalizer:reveal_type(V). + │ ^ #{b => dynamic('c'), number() => 'a'} + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_bounded.erl:51:27 + │ +51 │ eqwalizer:reveal_type(V). + │ ^ #{a := 'b', b => dynamic('c')} + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_bounded.erl:62:1 + │ +62 │ bd_tuple_02({V, _}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_bounded.erl:66:51 + │ +66 │ bd_distrib_print({a, V}) -> eqwalizer:reveal_type(V). + │ ^ dynamic('b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_bounded.erl:79:5 + │ +79 │ bd_generic_fun(test, 42). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ bd_generic_fun('test', 42). +Expression has type: 'test' | 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: '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 + │ +89 │ bd_generic_fun(test, N). + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ bd_generic_fun('test', N). +Expression has type: 'test' | dyn(number()) +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn(number()) + 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_bounded-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-28.pretty new file mode 100644 index 0000000000..bbd9109d68 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_bounded-OTP-28.pretty @@ -0,0 +1,116 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_bounded.erl:16:21 + │ +16 │ bd_unsafe_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: dyn('a') | 'b' +Context expected type: 'c' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn('a') + 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 + │ +25 │ bd_coerce_safe_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: 'a' +Context expected type: dyn('b') + │ + +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 + │ +44 │ eqwalizer:reveal_type(V). + │ ^ #{b => dynamic('c'), number() => 'a'} + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_bounded.erl:51:27 + │ +51 │ eqwalizer:reveal_type(V). + │ ^ #{a := 'b', b => dynamic('c')} + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_bounded.erl:62:1 + │ +62 │ bd_tuple_02({V, _}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_bounded.erl:66:51 + │ +66 │ bd_distrib_print({a, V}) -> eqwalizer:reveal_type(V). + │ ^ dynamic('b') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_bounded.erl:79:5 + │ +79 │ bd_generic_fun(test, 42). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ bd_generic_fun('test', 42). +Expression has type: 'test' | 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: '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 + │ +89 │ bd_generic_fun(test, N). + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ bd_generic_fun('test', N). +Expression has type: 'test' | dyn(number()) +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dyn(number()) + 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-OTP-26.pretty similarity index 75% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types-OTP-26.pretty index a7a81c8fb4..b7111f9239 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-OTP-26.pretty @@ -13,6 +13,12 @@ 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 │ @@ -40,6 +46,12 @@ 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 │ @@ -55,6 +67,12 @@ 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 │ @@ -92,4 +110,11 @@ 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_complex_types-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types-OTP-27.pretty new file mode 100644 index 0000000000..b7111f9239 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types-OTP-27.pretty @@ -0,0 +1,120 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:24:3 + │ +24 │ undefined. + │ ^^^^^^^^^ + │ │ + │ 'undefined'. +Expression has type: 'undefined' +Context expected type: complex_map() + │ + +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 + │ +29 │ eqwalizer:reveal_type(Map), + │ ^^^ complex_map() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_complex_types.erl:35:25 + │ +35 │ eqwalizer:reveal_type(List), + │ ^^^^ [complex_map()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:48:16 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [T] + │ + +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 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [dynamic()] + │ + +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 + │ +55 │ eqwalizer:reveal_type(M1), + │ ^^ #{a := number(), dynamic() => dynamic()} + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_complex_types.erl:63:25 + │ +63 │ eqwalizer:reveal_type(M1), + │ ^^ #{a := number(), dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:110:3 + │ +110 │ generic(DM, S). + │ ^^^^^^^^^^^^^^ generic(DM, S). +Expression has type: dyn_map() | #{a => atom()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:115:3 + │ +115 │ {DM, ok}. + │ ^^^^^^^^ + │ │ + │ {DM, 'ok'}. +Expression has type: {dyn_map(), 'ok'} +Context expected type: {#{a => 'atom'}, number()} + │ + +Because in the expression's type: + { dyn_map(), + Here the type is: 'ok' + 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_complex_types-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types-OTP-28.pretty new file mode 100644 index 0000000000..b7111f9239 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_complex_types-OTP-28.pretty @@ -0,0 +1,120 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:24:3 + │ +24 │ undefined. + │ ^^^^^^^^^ + │ │ + │ 'undefined'. +Expression has type: 'undefined' +Context expected type: complex_map() + │ + +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 + │ +29 │ eqwalizer:reveal_type(Map), + │ ^^^ complex_map() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_complex_types.erl:35:25 + │ +35 │ eqwalizer:reveal_type(List), + │ ^^^^ [complex_map()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:48:16 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [T] + │ + +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 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [dynamic()] + │ + +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 + │ +55 │ eqwalizer:reveal_type(M1), + │ ^^ #{a := number(), dynamic() => dynamic()} + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_complex_types.erl:63:25 + │ +63 │ eqwalizer:reveal_type(M1), + │ ^^ #{a := number(), dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:110:3 + │ +110 │ generic(DM, S). + │ ^^^^^^^^^^^^^^ generic(DM, S). +Expression has type: dyn_map() | #{a => atom()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_complex_types.erl:115:3 + │ +115 │ {DM, ok}. + │ ^^^^^^^^ + │ │ + │ {DM, 'ok'}. +Expression has type: {dyn_map(), 'ok'} +Context expected type: {#{a => 'atom'}, number()} + │ + +Because in the expression's type: + { dyn_map(), + Here the type is: 'ok' + 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-OTP-26.pretty similarity index 84% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-26.pretty index 065a377d0d..34e16f1479 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-26.pretty @@ -100,6 +100,12 @@ 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 │ @@ -196,6 +202,12 @@ 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 │ @@ -218,6 +230,12 @@ 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 │ @@ -240,6 +258,12 @@ 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 │ @@ -262,6 +286,12 @@ 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 │ @@ -278,6 +308,12 @@ 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 │ @@ -294,6 +330,12 @@ 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 │ @@ -310,6 +352,12 @@ 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 │ @@ -326,6 +374,12 @@ 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 │ @@ -348,6 +402,12 @@ 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 │ @@ -364,6 +424,14 @@ 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 │ @@ -380,4 +448,10 @@ 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_custom-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-27.pretty new file mode 100644 index 0000000000..34e16f1479 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-27.pretty @@ -0,0 +1,457 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:14:27 + │ +14 │ eqwalizer:reveal_type(element(42, Tup)). + │ ^^^^^^^^^^^^^^^^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:18:27 + │ +18 │ eqwalizer:reveal_type(element(2, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(atom()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:22:27 + │ +22 │ eqwalizer:reveal_type(element(1, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(number()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:26:27 + │ +26 │ eqwalizer:reveal_type(element(1, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(none()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:30:27 + │ +30 │ eqwalizer:reveal_type(element(N, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(number() | atom()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:43:3 + │ +43 │ application:get_env(app1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ application:get_env('app1'). +Expression has type: 'undefined' | {'ok', dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:56:3 + │ +56 │ application:get_env(app1, key1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ application:get_env('app1', 'key1'). +Expression has type: 'undefined' | {'ok', dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:73:3 + │ +73 │ lists:flatten(In). + │ ^^^^^^^^^^^^^^^^^ lists:flatten(In). +Expression has type: ['b' | 'c' | 'a'] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:85:3 + │ +85 │ lists:flatten(In, Tail). + │ ^^^^^^^^^^^^^^^^^^^^^^^ lists:flatten(In, Tail). +Expression has type: ['b' | 'a' | 'y' | 'z' | 'c' | 'x'] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:94:3 + │ +94 │ 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/gradual_custom.erl:99:3 + │ +99 │ abs(dynamic()). + │ ^^^^^^^^^^^^^^ erlang:abs(dynamic()). +Expression has type: number() +Context expected type: 'anything' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:108:3 + │ +108 │ abs(N). + │ ^^^^^^ erlang:abs(N). +Expression has type: number() +Context expected type: 'anything' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:108:7 + │ +108 │ abs(N). + │ ^ + │ │ + │ N. +Expression has type: dynamic() | 'a' +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +120 │ 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/gradual_custom.erl:124:3 + │ +124 │ 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/gradual_custom.erl:124:14 + │ +124 │ 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/gradual_custom.erl:143:16 + │ +143 │ 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/gradual_custom.erl:158:3 + │ +158 │ ╭ maps:without( +159 │ │ [a], +160 │ │ Dyn +161 │ │ ). + │ ╰───^ maps:without(['a'], Dyn). +Expression has type: #{dynamic() => dynamic()} +Context expected type: 'wrong_ret' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_custom.erl:166:1 + │ +166 │ ╭ maps_without_3_neg(None) -> +167 │ │ maps:without( +168 │ │ [a, b], +169 │ │ None +170 │ │ ). + │ ╰───^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:185:3 + │ +185 │ ╭ maps:with( +186 │ │ [a], +187 │ │ Dyn +188 │ │ ). + │ ╰───^ maps:with(['a'], Dyn). +Expression has type: #{a := dynamic()} +Context expected type: 'wrong_ret' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_custom.erl:193:1 + │ +193 │ ╭ maps_with_3(None) -> +194 │ │ maps:with( +195 │ │ [a, b], +196 │ │ None +197 │ │ ). + │ ╰───^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:203:25 + │ +203 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:204:3 + │ +204 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +210 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:211:3 + │ +211 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +217 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:218:3 + │ +218 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +224 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:225:3 + │ +225 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +245 │ min3_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | 'undefined' +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: '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 + │ +257 │ min5_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | atom() | binary() +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: 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 + │ +263 │ min6_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | dynamic() | number() | atom() | dynamic() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | dynamic() + 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 + │ +269 │ min7_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | dynamic() | atom() | dynamic() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | dynamic() + 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 + │ +277 │ eqwalizer:reveal_type(Y), + │ ^ dynamic() | {none()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:278:3 + │ +278 │ Y. + │ ^ + │ │ + │ Y. +Expression has type: dynamic() | {none()} +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +325 │ true -> Atom; + │ ^^^^ + │ │ + │ Atom. +Expression has type: 'bar' | 'undefined' | 'foo' +Context expected type: 'foo' | 'bar' | binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'bar' | 'foo' + 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 + │ +343 │ parse_atom(Bin, [foo, bar]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ parse_atom(Bin, ['foo', 'bar']). +Expression has type: 'foo' | 'bar' +Context expected type: 'foo' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'foo' + 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_custom-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-28.pretty new file mode 100644 index 0000000000..34e16f1479 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_custom-OTP-28.pretty @@ -0,0 +1,457 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:14:27 + │ +14 │ eqwalizer:reveal_type(element(42, Tup)). + │ ^^^^^^^^^^^^^^^^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:18:27 + │ +18 │ eqwalizer:reveal_type(element(2, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(atom()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:22:27 + │ +22 │ eqwalizer:reveal_type(element(1, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(number()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:26:27 + │ +26 │ eqwalizer:reveal_type(element(1, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(none()) + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:30:27 + │ +30 │ eqwalizer:reveal_type(element(N, Tup)). + │ ^^^^^^^^^^^^^^^ dynamic(number() | atom()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:43:3 + │ +43 │ application:get_env(app1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ application:get_env('app1'). +Expression has type: 'undefined' | {'ok', dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:56:3 + │ +56 │ application:get_env(app1, key1). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ application:get_env('app1', 'key1'). +Expression has type: 'undefined' | {'ok', dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:73:3 + │ +73 │ lists:flatten(In). + │ ^^^^^^^^^^^^^^^^^ lists:flatten(In). +Expression has type: ['b' | 'c' | 'a'] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:85:3 + │ +85 │ lists:flatten(In, Tail). + │ ^^^^^^^^^^^^^^^^^^^^^^^ lists:flatten(In, Tail). +Expression has type: ['b' | 'a' | 'y' | 'z' | 'c' | 'x'] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:94:3 + │ +94 │ 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/gradual_custom.erl:99:3 + │ +99 │ abs(dynamic()). + │ ^^^^^^^^^^^^^^ erlang:abs(dynamic()). +Expression has type: number() +Context expected type: 'anything' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:108:3 + │ +108 │ abs(N). + │ ^^^^^^ erlang:abs(N). +Expression has type: number() +Context expected type: 'anything' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:108:7 + │ +108 │ abs(N). + │ ^ + │ │ + │ N. +Expression has type: dynamic() | 'a' +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +120 │ 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/gradual_custom.erl:124:3 + │ +124 │ 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/gradual_custom.erl:124:14 + │ +124 │ 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/gradual_custom.erl:143:16 + │ +143 │ 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/gradual_custom.erl:158:3 + │ +158 │ ╭ maps:without( +159 │ │ [a], +160 │ │ Dyn +161 │ │ ). + │ ╰───^ maps:without(['a'], Dyn). +Expression has type: #{dynamic() => dynamic()} +Context expected type: 'wrong_ret' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_custom.erl:166:1 + │ +166 │ ╭ maps_without_3_neg(None) -> +167 │ │ maps:without( +168 │ │ [a, b], +169 │ │ None +170 │ │ ). + │ ╰───^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:185:3 + │ +185 │ ╭ maps:with( +186 │ │ [a], +187 │ │ Dyn +188 │ │ ). + │ ╰───^ maps:with(['a'], Dyn). +Expression has type: #{a := dynamic()} +Context expected type: 'wrong_ret' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_custom.erl:193:1 + │ +193 │ ╭ maps_with_3(None) -> +194 │ │ maps:with( +195 │ │ [a, b], +196 │ │ None +197 │ │ ). + │ ╰───^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_custom.erl:203:25 + │ +203 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:204:3 + │ +204 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +210 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:211:3 + │ +211 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +217 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:218:3 + │ +218 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +224 │ eqwalizer:reveal_type(Res), + │ ^^^ file:filename_all() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:225:3 + │ +225 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: file:filename_all() +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is: string() | binary() + 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 + │ +245 │ min3_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | 'undefined' +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: '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 + │ +257 │ min5_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | atom() | binary() +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: 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 + │ +263 │ min6_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | dynamic() | number() | atom() | dynamic() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | dynamic() + 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 + │ +269 │ min7_neg(X, Y) -> min(X, Y). + │ ^^^^^^^^^ + │ │ + │ erlang:min(X, Y). +Expression has type: number() | dynamic() | atom() | dynamic() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | dynamic() + 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 + │ +277 │ eqwalizer:reveal_type(Y), + │ ^ dynamic() | {none()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_custom.erl:278:3 + │ +278 │ Y. + │ ^ + │ │ + │ Y. +Expression has type: dynamic() | {none()} +Context expected type: number() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: dynamic() + 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 + │ +325 │ true -> Atom; + │ ^^^^ + │ │ + │ Atom. +Expression has type: 'bar' | 'undefined' | 'foo' +Context expected type: 'foo' | 'bar' | binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'bar' | 'foo' + 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 + │ +343 │ parse_atom(Bin, [foo, bar]). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ parse_atom(Bin, ['foo', 'bar']). +Expression has type: 'foo' | 'bar' +Context expected type: 'foo' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'foo' + 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_lambdas.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-27.pretty new file mode 100644 index 0000000000..60301786c5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-27.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:39:17 + │ +39 │ true -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/gradual_lambdas.erl:55:3 + │ +55 │ DoWork(). + │ ^^^^^^ DoWork. +Expected fun type with arity 0 +Got: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:70:13 + │ +70 │ fun(X) -> X + 1 end. + │ ^ X. +Expression has type: string() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:70:13 + │ +70 │ fun(X) -> X + 1 end. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: string() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-28.pretty new file mode 100644 index 0000000000..60301786c5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_lambdas-OTP-28.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:39:17 + │ +39 │ true -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/gradual_lambdas.erl:55:3 + │ +55 │ DoWork(). + │ ^^^^^^ DoWork. +Expected fun type with arity 0 +Got: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:70:13 + │ +70 │ fun(X) -> X + 1 end. + │ ^ X. +Expression has type: string() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_lambdas.erl:70:13 + │ +70 │ fun(X) -> X + 1 end. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: string() + +4 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-OTP-26.pretty similarity index 92% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-26.pretty index 35a509f2f6..5587da0d7e 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-26.pretty @@ -62,6 +62,14 @@ 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_maybe-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-27.pretty new file mode 100644 index 0000000000..5587da0d7e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-27.pretty @@ -0,0 +1,93 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:26:9 + │ +26 │ A + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:50:14 + │ +50 │ _ -> 3 + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:58:21 + │ +58 │ {err, A} ?= T, + │ ^ T. +Expression has type: {'ok', atom()} | {'err', term()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:67:20 + │ +67 │ {ok, A} ?= T, + │ ^ T. +Expression has type: {'ok', atom()} | {'err', term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:91:9 + │ +91 │ B + │ ^ B. +Expression has type: 'ok' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:93:14 + │ +93 │ _ -> ok + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:123:18 + │ +123 │ a = A ?= T, + │ ^ + │ │ + │ T. +Expression has type: 'a' | 'b' | 'c' +Context expected type: 'b' | 'c' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' | 'c' + 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 + │ +147 │ c -> err + │ ^^^ 'err'. +Expression has type: 'err' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:156:13 + │ +156 │ ╭ maybe +157 │ │ B ?= {a, T}, +158 │ │ V = {b, B}, +159 │ │ C ?= V +160 │ │ end + │ ╰───────────────^ maybe ... +Expression has type: {'a', term()} | {'b', {'a', term()}} +Context expected type: 'err' + +9 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-28.pretty new file mode 100644 index 0000000000..5587da0d7e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_maybe-OTP-28.pretty @@ -0,0 +1,93 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:26:9 + │ +26 │ A + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:50:14 + │ +50 │ _ -> 3 + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:58:21 + │ +58 │ {err, A} ?= T, + │ ^ T. +Expression has type: {'ok', atom()} | {'err', term()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:67:20 + │ +67 │ {ok, A} ?= T, + │ ^ T. +Expression has type: {'ok', atom()} | {'err', term()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:91:9 + │ +91 │ B + │ ^ B. +Expression has type: 'ok' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:93:14 + │ +93 │ _ -> ok + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: 'err' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:123:18 + │ +123 │ a = A ?= T, + │ ^ + │ │ + │ T. +Expression has type: 'a' | 'b' | 'c' +Context expected type: 'b' | 'c' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' | 'c' + 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 + │ +147 │ c -> err + │ ^^^ 'err'. +Expression has type: 'err' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_maybe.erl:156:13 + │ +156 │ ╭ maybe +157 │ │ B ?= {a, T}, +158 │ │ V = {b, B}, +159 │ │ C ?= V +160 │ │ end + │ ╰───────────────^ maybe ... +Expression has type: {'a', term()} | {'b', {'a', term()}} +Context expected type: 'err' + +9 ERRORS 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-OTP-26.pretty similarity index 61% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-26.pretty index 28618b5f77..33b6c1bf38 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-26.pretty @@ -16,6 +16,16 @@ 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 │ @@ -35,8 +45,16 @@ 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. -Expression has type: {'b', 'c'} + │ ^ + │ │ + │ T. +Expression has type: none() | {'b', 'c'} Context expected type: 'a' | {none()} + │ + +Because in the expression's type: + Here the type is: {'b', 'c'} + Context expects type: 'a' | {none()} + No candidate matches in the expected union. 4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-27.pretty new file mode 100644 index 0000000000..33b6c1bf38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-27.pretty @@ -0,0 +1,60 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_misc.erl:15:3 + │ +15 │ X. + │ ^ + │ │ + │ X. +Expression has type: opaque:contravariant('a') +Context expected type: opaque:contravariant('a' | 'b') + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +38 │ <>. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: 'ok' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_misc.erl:41:1 + │ +41 │ ╭ fuzz03([_ | {}]) -> +42 │ │ ok. + │ ╰──────^ Clause is not covered by spec + +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. +Expression has type: none() | {'b', 'c'} +Context expected type: 'a' | {none()} + │ + +Because in the expression's type: + Here the type is: {'b', 'c'} + Context expects type: 'a' | {none()} + No candidate matches in the expected union. + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-28.pretty new file mode 100644 index 0000000000..33b6c1bf38 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_misc-OTP-28.pretty @@ -0,0 +1,60 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_misc.erl:15:3 + │ +15 │ X. + │ ^ + │ │ + │ X. +Expression has type: opaque:contravariant('a') +Context expected type: opaque:contravariant('a' | 'b') + │ + +Because in the expression's type: + fun(( + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +38 │ <>. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ << || >>. +Expression has type: binary() +Context expected type: 'ok' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/gradual_misc.erl:41:1 + │ +41 │ ╭ fuzz03([_ | {}]) -> +42 │ │ ok. + │ ╰──────^ Clause is not covered by spec + +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. +Expression has type: none() | {'b', 'c'} +Context expected type: 'a' | {none()} + │ + +Because in the expression's type: + Here the type is: {'b', 'c'} + Context expects type: 'a' | {none()} + No candidate matches in the expected union. + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-27.pretty new file mode 100644 index 0000000000..957e329439 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-27.pretty @@ -0,0 +1,77 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:38:20 + │ +38 │ binary_to_atom(Y). + │ ^ Y. +Expression has type: pid() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:61:5 + │ +61 │ {Y}. + │ ^^^ {Y}. +Expression has type: {dynamic()} +Context expected type: number() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_overloaded.erl:90:27 + │ +90 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:112:5 + │ +112 │ ╭ rec_each( +113 │ │ fun(R) -> R#r{count = 0} end, +114 │ │ Rec +115 │ │ ). + │ ╰─────^ rec_each(fun, Rec). +Expression has type: #r{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:119:5 + │ +119 │ ╭ rec_each( +120 │ │ fun(I) -> I + 1 end, +121 │ │ Rec +122 │ │ ). + │ ╰─────^ rec_each(fun, Rec). +Expression has type: #r{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:120:19 + │ +120 │ fun(I) -> I + 1 end, + │ ^ I. +Expression has type: #r{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:120:19 + │ +120 │ fun(I) -> I + 1 end, + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: #r{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:128:19 + │ +128 │ fun(I) -> I + 1 end, + │ ^ I. +Expression has type: #r{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:128:19 + │ +128 │ fun(I) -> I + 1 end, + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: #r{} + +9 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-28.pretty new file mode 100644 index 0000000000..957e329439 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_overloaded-OTP-28.pretty @@ -0,0 +1,77 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:38:20 + │ +38 │ binary_to_atom(Y). + │ ^ Y. +Expression has type: pid() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:61:5 + │ +61 │ {Y}. + │ ^^^ {Y}. +Expression has type: {dynamic()} +Context expected type: number() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_overloaded.erl:90:27 + │ +90 │ eqwalizer:reveal_type(Res), + │ ^^^ dynamic() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:112:5 + │ +112 │ ╭ rec_each( +113 │ │ fun(R) -> R#r{count = 0} end, +114 │ │ Rec +115 │ │ ). + │ ╰─────^ rec_each(fun, Rec). +Expression has type: #r{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:119:5 + │ +119 │ ╭ rec_each( +120 │ │ fun(I) -> I + 1 end, +121 │ │ Rec +122 │ │ ). + │ ╰─────^ rec_each(fun, Rec). +Expression has type: #r{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:120:19 + │ +120 │ fun(I) -> I + 1 end, + │ ^ I. +Expression has type: #r{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:120:19 + │ +120 │ fun(I) -> I + 1 end, + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: #r{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:128:19 + │ +128 │ fun(I) -> I + 1 end, + │ ^ I. +Expression has type: #r{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/gradual_overloaded.erl:128:19 + │ +128 │ fun(I) -> I + 1 end, + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: #r{} + +9 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/list.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/map.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_regression_01-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-27.pretty new file mode 100644 index 0000000000..e82d8ee3f2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-27.pretty @@ -0,0 +1,25 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:11:25 + │ +11 │ eqwalizer:reveal_type(_L), + │ ^^ [dynamic()] + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:23:25 + │ +23 │ eqwalizer:reveal_type(Field2), + │ ^^^^^^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:28:25 + │ +28 │ eqwalizer:reveal_type(A), + │ ^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:33:25 + │ +33 │ eqwalizer:reveal_type(B), + │ ^ dynamic() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-28.pretty new file mode 100644 index 0000000000..e82d8ee3f2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/gradual_untyped-OTP-28.pretty @@ -0,0 +1,25 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:11:25 + │ +11 │ eqwalizer:reveal_type(_L), + │ ^^ [dynamic()] + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:23:25 + │ +23 │ eqwalizer:reveal_type(Field2), + │ ^^^^^^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:28:25 + │ +28 │ eqwalizer:reveal_type(A), + │ ^ dynamic() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/gradual_untyped.erl:33:25 + │ +33 │ eqwalizer:reveal_type(B), + │ ^ dynamic() + +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-OTP-26.pretty similarity index 66% rename from crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections-OTP-26.pretty index 74cd6b4ca4..88bcfb1267 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-OTP-26.pretty @@ -14,6 +14,14 @@ 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 │ @@ -30,6 +38,14 @@ 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 │ @@ -46,6 +62,14 @@ 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 │ @@ -62,6 +86,14 @@ 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 │ @@ -78,4 +110,12 @@ 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/guard_b_connections-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections-OTP-27.pretty new file mode 100644 index 0000000000..88bcfb1267 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections-OTP-27.pretty @@ -0,0 +1,121 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guard_b_connections.erl:22:3 + │ +22 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +27 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +32 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +38 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +44 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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/guard_b_connections-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections-OTP-28.pretty new file mode 100644 index 0000000000..88bcfb1267 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guard_b_connections-OTP-28.pretty @@ -0,0 +1,121 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guard_b_connections.erl:22:3 + │ +22 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +27 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +32 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +38 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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 + │ +44 │ R. + │ ^ + │ │ + │ R. +Expression has type: #r1{} | #r2{} +Context expected type: r1() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #r1{} + 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.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/guards.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-27.pretty new file mode 100644 index 0000000000..c17a7e6267 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-27.pretty @@ -0,0 +1,37 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:51:5 + │ +51 │ X. + │ ^ X. +Expression has type: term() +Context expected type: #{number() => number()} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/guards.erl:53:1 + │ +53 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/guards.erl:54:1 + │ +54 │ -record(invalid, {field :: loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:89:25 + │ +89 │ guard_element_neg(T) -> T + 1. + │ ^ T. +Expression has type: tuple() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:89:25 + │ +89 │ guard_element_neg(T) -> T + 1. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: tuple() + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-28.pretty new file mode 100644 index 0000000000..c17a7e6267 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards-OTP-28.pretty @@ -0,0 +1,37 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:51:5 + │ +51 │ X. + │ ^ X. +Expression has type: term() +Context expected type: #{number() => number()} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/guards.erl:53:1 + │ +53 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/guards.erl:54:1 + │ +54 │ -record(invalid, {field :: loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:89:25 + │ +89 │ guard_element_neg(T) -> T + 1. + │ ^ T. +Expression has type: tuple() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards.erl:89:25 + │ +89 │ guard_element_neg(T) -> T + 1. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: tuple() + +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-OTP-26.pretty similarity index 80% rename from crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-26.pretty index bc1b2fff4a..c287958c68 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-26.pretty @@ -30,6 +30,14 @@ 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 │ @@ -47,6 +55,13 @@ 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/guards_logic-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-27.pretty new file mode 100644 index 0000000000..c287958c68 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-27.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:32:38 + │ +32 │ test07_neg(X, Y) when X andalso Y -> Y. + │ ^ Y. +Expression has type: 'true' +Context expected type: 'false' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:35:40 + │ +35 │ test08_neg(X) when not is_number(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:57:20 + │ +57 │ is_pid(X)-> X. + │ ^ + │ │ + │ X. +Expression has type: number() | atom() | pid() +Context expected type: number() | atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | atom() + 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 + │ +73 │ {X, Y}. + │ ^^^^^^ + │ │ + │ {X, Y}. +Expression has type: {term(), term()} +Context expected type: {number(), number()} + │ + +Because in the expression's type: + { + Here the type is: term() + 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 + │ +101 │ test21_neg(X) when (X =:= a) or (X =:= b) -> X. + │ ^ X. +Expression has type: 'a' | 'b' +Context expected type: 'c' + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-28.pretty new file mode 100644 index 0000000000..c287958c68 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_logic-OTP-28.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:32:38 + │ +32 │ test07_neg(X, Y) when X andalso Y -> Y. + │ ^ Y. +Expression has type: 'true' +Context expected type: 'false' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:35:40 + │ +35 │ test08_neg(X) when not is_number(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_logic.erl:57:20 + │ +57 │ is_pid(X)-> X. + │ ^ + │ │ + │ X. +Expression has type: number() | atom() | pid() +Context expected type: number() | atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: number() | atom() + 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 + │ +73 │ {X, Y}. + │ ^^^^^^ + │ │ + │ {X, Y}. +Expression has type: {term(), term()} +Context expected type: {number(), number()} + │ + +Because in the expression's type: + { + Here the type is: term() + 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 + │ +101 │ test21_neg(X) when (X =:= a) or (X =:= b) -> X. + │ ^ X. +Expression has type: 'a' | 'b' +Context expected type: 'c' + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-27.pretty new file mode 100644 index 0000000000..7bc05b8ab1 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-27.pretty @@ -0,0 +1,97 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:26:36 + │ +26 │ atom_neg_1(X) when is_number(X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:31:23 + │ +31 │ is_list(X) -> X + │ ^ X. +Expression has type: [term()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:37:31 + │ +37 │ Y when is_tuple(Y) -> Y + │ ^ Y. +Expression has type: tuple() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:56:37 + │ +56 │ boolean_neg_1(X) when is_atom(X) -> X. + │ ^ X. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:61:23 + │ +61 │ is_atom(X) -> X + │ ^ X. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:67:30 + │ +67 │ Y when is_atom(Y) -> Y + │ ^ Y. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:74:36 + │ +74 │ float_neg(X) when is_boolean(X) -> X. + │ ^ X. +Expression has type: 'false' | 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:80:38 + │ +80 │ integer_neg(X) when is_boolean(X) -> X. + │ ^ X. +Expression has type: 'false' | 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:86:33 + │ +86 │ pid_neg(X) when is_number(X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:92:37 + │ +92 │ port_neg(X) when is_reference(X) -> X. + │ ^ X. +Expression has type: reference() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:95:42 + │ +95 │ reference_pos(X) when is_reference(X) -> X. + │ ^ X. +Expression has type: reference() +Context expected type: port() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:98:37 + │ +98 │ reference_neg(X) when is_port(X) -> X. + │ ^ X. +Expression has type: port() +Context expected type: pid() + +12 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-28.pretty new file mode 100644 index 0000000000..7bc05b8ab1 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/guards_simple-OTP-28.pretty @@ -0,0 +1,97 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:26:36 + │ +26 │ atom_neg_1(X) when is_number(X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:31:23 + │ +31 │ is_list(X) -> X + │ ^ X. +Expression has type: [term()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:37:31 + │ +37 │ Y when is_tuple(Y) -> Y + │ ^ Y. +Expression has type: tuple() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:56:37 + │ +56 │ boolean_neg_1(X) when is_atom(X) -> X. + │ ^ X. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:61:23 + │ +61 │ is_atom(X) -> X + │ ^ X. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:67:30 + │ +67 │ Y when is_atom(Y) -> Y + │ ^ Y. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:74:36 + │ +74 │ float_neg(X) when is_boolean(X) -> X. + │ ^ X. +Expression has type: 'false' | 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:80:38 + │ +80 │ integer_neg(X) when is_boolean(X) -> X. + │ ^ X. +Expression has type: 'false' | 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:86:33 + │ +86 │ pid_neg(X) when is_number(X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:92:37 + │ +92 │ port_neg(X) when is_reference(X) -> X. + │ ^ X. +Expression has type: reference() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:95:42 + │ +95 │ reference_pos(X) when is_reference(X) -> X. + │ ^ X. +Expression has type: reference() +Context expected type: port() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/guards_simple.erl:98:37 + │ +98 │ reference_neg(X) when is_port(X) -> X. + │ ^ X. +Expression has type: port() +Context expected type: pid() + +12 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-26.pretty similarity index 81% rename from crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-26.pretty index f990884695..c1dae73601 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/hints.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-26.pretty @@ -14,13 +14,13 @@ error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/hints.erl:32:27 │ 32 │ eqwalizer:reveal_type(B), - │ ^ binary() + │ ^ none() | binary() error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) ┌─ check/src/hints.erl:41:27 │ 41 │ eqwalizer:reveal_type(BT), - │ ^^ binary() | tuple() + │ ^^ none() | binary() | tuple() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/hints.erl:47:5 @@ -38,6 +38,12 @@ 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 │ @@ -54,6 +60,12 @@ 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/hints-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-27.pretty new file mode 100644 index 0000000000..c1dae73601 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-27.pretty @@ -0,0 +1,83 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:13:27 + │ +13 │ eqwalizer:reveal_type(A), + │ ^ atom() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:24:27 + │ +24 │ eqwalizer:reveal_type(_A), + │ ^^ atom() | binary() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:32:27 + │ +32 │ eqwalizer:reveal_type(B), + │ ^ none() | binary() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:41:27 + │ +41 │ eqwalizer:reveal_type(BT), + │ ^^ none() | binary() | tuple() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/hints.erl:47:5 + │ +47 │ Key. + │ ^^^ + │ │ + │ Key. +Expression has type: term() | 'undefined' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + 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 + │ +52 │ Key. + │ ^^^ + │ │ + │ Key. +Expression has type: term() | 'undefined' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + 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 + │ +55 │ ╭ reveal_none(X) -> +56 │ │ eqwalizer:reveal_type(X), +57 │ │ error(none). + │ ╰───────────────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:56:27 + │ +56 │ eqwalizer:reveal_type(X), + │ ^ none() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-28.pretty new file mode 100644 index 0000000000..c1dae73601 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/hints-OTP-28.pretty @@ -0,0 +1,83 @@ +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:13:27 + │ +13 │ eqwalizer:reveal_type(A), + │ ^ atom() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:24:27 + │ +24 │ eqwalizer:reveal_type(_A), + │ ^^ atom() | binary() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:32:27 + │ +32 │ eqwalizer:reveal_type(B), + │ ^ none() | binary() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:41:27 + │ +41 │ eqwalizer:reveal_type(BT), + │ ^^ none() | binary() | tuple() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/hints.erl:47:5 + │ +47 │ Key. + │ ^^^ + │ │ + │ Key. +Expression has type: term() | 'undefined' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + 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 + │ +52 │ Key. + │ ^^^ + │ │ + │ Key. +Expression has type: term() | 'undefined' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'undefined' + 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 + │ +55 │ ╭ reveal_none(X) -> +56 │ │ eqwalizer:reveal_type(X), +57 │ │ error(none). + │ ╰───────────────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/hints.erl:56:27 + │ +56 │ eqwalizer:reveal_type(X), + │ ^ none() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/index1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-27.pretty new file mode 100644 index 0000000000..53b66665b2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-27.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index1.erl:31:21 + │ +31 │ _ = X#rec{field = 3}, + │ ^ 3. +Expression has type: number() +Context expected type: [number()] + +error: undefined_field (See https://fb.me/eqwalizer_errors#undefined_field) + ┌─ check/src/index1.erl:32:7 + │ +32 │ _ = #rec{}, + │ ^^^^^^ #rec{...}: field is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index1.erl:39:5 + │ +39 │ _ = fun index2:foo/1. + │ ^^^^^^^^^^^^^^^^^^ match_expr. +Expression has type: fun((#rec{}) -> {index2:ty_a(), ty_a(), rec()}) +Context expected type: {ty_a(), ty_b(), ty_a(), ty_b(), index2:ty_a(), #rec{}, rec()} + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-28.pretty new file mode 100644 index 0000000000..53b66665b2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/index1-OTP-28.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index1.erl:31:21 + │ +31 │ _ = X#rec{field = 3}, + │ ^ 3. +Expression has type: number() +Context expected type: [number()] + +error: undefined_field (See https://fb.me/eqwalizer_errors#undefined_field) + ┌─ check/src/index1.erl:32:7 + │ +32 │ _ = #rec{}, + │ ^^^^^^ #rec{...}: field is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index1.erl:39:5 + │ +39 │ _ = fun index2:foo/1. + │ ^^^^^^^^^^^^^^^^^^ match_expr. +Expression has type: fun((#rec{}) -> {index2:ty_a(), ty_a(), rec()}) +Context expected type: {ty_a(), ty_b(), ty_a(), ty_b(), index2:ty_a(), #rec{}, rec()} + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/index2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-27.pretty new file mode 100644 index 0000000000..cf62c97c82 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-27.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index2.erl:21:3 + │ +21 │ X#rec.field. + │ ^^^^^^^^^^^ ...#rec.field. +Expression has type: [atom()] +Context expected type: {ty_a(), index1:ty_a(), index1:rec()} + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-28.pretty new file mode 100644 index 0000000000..cf62c97c82 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/index2-OTP-28.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/index2.erl:21:3 + │ +21 │ X#rec.field. + │ ^^^^^^^^^^^ ...#rec.field. +Expression has type: [atom()] +Context expected type: {ty_a(), index1:ty_a(), index1:rec()} + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-26.pretty similarity index 81% rename from crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-26.pretty index 00e34f9cb8..bf9d6e219b 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/iolists.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-26.pretty @@ -23,6 +23,14 @@ 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 │ @@ -41,6 +49,14 @@ 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/iolists-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-27.pretty new file mode 100644 index 0000000000..bf9d6e219b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-27.pretty @@ -0,0 +1,85 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/iolists.erl:18:1 + │ +18 │ ╭ first(IoList) +19 │ │ when is_binary(IoList) -> IoList; + │ ╰────────────────────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/iolists.erl:50:18 + │ +50 │ refine5(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom() | binary()] +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: atom() + 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 + │ +55 │ refine6_neg(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom() | binary()] +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: binary() + 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 + │ +60 │ refine_to_empty1(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom()] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: atom() + Context expects type: none() + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/iolists.erl:83:17 + │ +83 │ test2_neg(X) -> X. + │ ^ X. +Expression has type: iolist() +Context expected type: 'wrong_ret' + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-28.pretty new file mode 100644 index 0000000000..bf9d6e219b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/iolists-OTP-28.pretty @@ -0,0 +1,85 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/iolists.erl:18:1 + │ +18 │ ╭ first(IoList) +19 │ │ when is_binary(IoList) -> IoList; + │ ╰────────────────────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/iolists.erl:50:18 + │ +50 │ refine5(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom() | binary()] +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: atom() + 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 + │ +55 │ refine6_neg(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom() | binary()] +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: binary() + 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 + │ +60 │ refine_to_empty1(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [atom()] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: atom() + Context expects type: none() + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/iolists.erl:83:17 + │ +83 │ test2_neg(X) -> X. + │ ^ X. +Expression has type: iolist() +Context expected type: 'wrong_ret' + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/result.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/kp_01-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/kp_02.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-27.pretty new file mode 100644 index 0000000000..5613ac7817 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-27.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/kp_02.erl:24:21 + │ +24 │ Res = transform(pure_fun(fun id/1), A), + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ pure_fun(id/1). +Expression has type: fun((X) -> X) with 1 type parameter +Context expected type: fun((T1) -> Ret) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((X) -> X) with 1 type parameter + Context expects type: fun((T1) -> Ret) 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/kp_02.erl:24:21 + │ +24 │ Res = transform(pure_fun(fun id/1), A), + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ pure_fun(id/1). +Expression has type: fun((X) -> X) with 1 type parameter +Context expected type: fun((number()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((X) -> X) with 1 type parameter + Context expects type: fun((number()) -> dynamic()) with 0 type parameters + The number of type parameters doesn't match. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-28.pretty new file mode 100644 index 0000000000..5613ac7817 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/kp_02-OTP-28.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/kp_02.erl:24:21 + │ +24 │ Res = transform(pure_fun(fun id/1), A), + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ pure_fun(id/1). +Expression has type: fun((X) -> X) with 1 type parameter +Context expected type: fun((T1) -> Ret) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((X) -> X) with 1 type parameter + Context expects type: fun((T1) -> Ret) 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/kp_02.erl:24:21 + │ +24 │ Res = transform(pure_fun(fun id/1), A), + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ pure_fun(id/1). +Expression has type: fun((X) -> X) with 1 type parameter +Context expected type: fun((number()) -> dynamic()) with 0 type parameters + │ + +Because in the expression's type: + Here the type is: fun((X) -> X) with 1 type parameter + Context expects type: fun((number()) -> dynamic()) with 0 type parameters + The number of type parameters doesn't match. + +2 ERRORS 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-OTP-26.pretty similarity index 66% rename from crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-26.pretty index 4b2ec521dd..d0768e5669 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-26.pretty @@ -16,6 +16,16 @@ 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 │ @@ -34,4 +44,14 @@ 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/lists_tests-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-27.pretty new file mode 100644 index 0000000000..d0768e5669 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-27.pretty @@ -0,0 +1,57 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/lists_tests.erl:12:28 + │ +12 │ lists_union_neg(V1, V2) -> [V1, V2]. + │ ^^^^^^^^ + │ │ + │ [V1, V2]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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 + │ +16 │ lists_union_2_neg(V, L) -> [V | L]. + │ ^^^^^^^ + │ │ + │ [V | L]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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/lists_tests-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-28.pretty new file mode 100644 index 0000000000..d0768e5669 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/lists_tests-OTP-28.pretty @@ -0,0 +1,57 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/lists_tests.erl:12:28 + │ +12 │ lists_union_neg(V1, V2) -> [V1, V2]. + │ ^^^^^^^^ + │ │ + │ [V1, V2]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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 + │ +16 │ lists_union_2_neg(V, L) -> [V | L]. + │ ^^^^^^^ + │ │ + │ [V | L]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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-OTP-26.pretty similarity index 85% rename from crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-26.pretty index ec963a6249..22e373f816 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/misc.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-26.pretty @@ -127,6 +127,12 @@ 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 │ @@ -144,6 +150,12 @@ 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 │ @@ -151,16 +163,22 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^^^^ │ │ │ [not _]. -Expression has type: ['false'] | ['true'] +Expression has type: [boolean()] Context expected type: [number()] │ Because in the expression's type: [ - Here the type is: 'false' + Here the type is: boolean() Context expects type: number() ] +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:124:21 │ @@ -168,16 +186,22 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^^^^^ │ │ │ [_ or _]. -Expression has type: ['false'] | ['true'] +Expression has type: [boolean()] Context expected type: [number()] │ Because in the expression's type: [ - Here the type is: 'false' + Here the type is: boolean() Context expects type: number() ] +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/misc.erl:128:21 │ @@ -195,6 +219,12 @@ 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 │ @@ -305,17 +335,31 @@ 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 @@ -361,6 +405,17 @@ 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 │ @@ -381,6 +436,17 @@ 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 │ @@ -455,6 +521,16 @@ 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 │ @@ -488,6 +564,12 @@ 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 │ @@ -682,6 +764,13 @@ 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 │ @@ -694,9 +783,19 @@ Context expected type: v1_op() │ Because in the expression's type: - Here the type is a union type with some valid candidates: 'stuff1' | 'v0_op2' | 'stuff2' | 'v0_op1' | 'v1_op2' | ... + Here the type is a union type with some valid candidates: v1_op() However the following candidate: 'v2_op' - Differs from the expected type: 'stuff1' | 'v0_op2' | 'stuff2' | 'v0_op1' | 'v1_op2' | ... + Differs from the expected type: 'v1_op1' | 'v1_op2' | stuff() | v0_op() + +------------------------------ 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 @@ -747,6 +846,16 @@ 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 │ @@ -764,6 +873,13 @@ 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 │ @@ -803,6 +919,12 @@ 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 │ @@ -829,6 +951,14 @@ 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 │ @@ -844,6 +974,12 @@ 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/misc-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-27.pretty new file mode 100644 index 0000000000..22e373f816 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-27.pretty @@ -0,0 +1,1011 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:46:23 + │ +46 │ test03_neg(-4 = X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:49:26 + │ +49 │ test04_neg(6 / 3 = Y) -> Y. + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:52:20 + │ +52 │ test05_neg(X) -> + X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:55:36 + │ +55 │ test06_neg(X) when is_number(X) -> + X. + │ ^^^ + _. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:63:5 + │ +63 │ not X. + │ ^^^^^ not _. +Expression has type: boolean() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:73:25 + │ +73 │ is_number(X) -> X + Y. + │ ^ Y. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:77:21 + │ +77 │ test11_neg(X, Y) -> X + Y. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:81:19 + │ +81 │ test12_neg(X) -> -X. + │ ^ X. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:85:21 + │ +85 │ test13_neg(X, Y) -> X or Y. + │ ^ X. +Expression has type: term() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:85:26 + │ +85 │ test13_neg(X, Y) -> X or Y. + │ ^ Y. +Expression has type: term() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:97:21 + │ +97 │ test16_neg(X, Y) -> X or Y. + │ ^^^^^^ _ or _. +Expression has type: boolean() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:101:26 + │ +101 │ test17_neg(X, Y) -> X or Y. + │ ^ Y. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:105:21 + │ +105 │ test18_neg(X, Y) -> X orelse Y. + │ ^^^^^^^^^^ _ orelse _. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:109:21 + │ +109 │ test19_neg(X, Y) -> X andalso Y. + │ ^^^^^^^^^^^ _ andalso _. +Expression has type: 'false' | number() +Context expected type: {number()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:112:18 + │ +112 │ test20_neg(X) -> [- X]. + │ ^^^^^ + │ │ + │ [- _]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +116 │ test21_neg(X, Y) -> [X / Y]. + │ ^^^^^^^ + │ │ + │ [_ / _]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +120 │ test22_neg(X) -> [not X]. + │ ^^^^^^^ + │ │ + │ [not _]. +Expression has type: [boolean()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: boolean() + Context expects type: number() + ] + +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:124:21 + │ +124 │ test23_neg(X, Y) -> [X or Y]. + │ ^^^^^^^^ + │ │ + │ [_ or _]. +Expression has type: [boolean()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: boolean() + Context expects type: number() + ] + +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:128:21 + │ +128 │ test24_neg(X, Y) -> [X orelse Y]. + │ ^^^^^^^^^^^^ + │ │ + │ [_ orelse _]. +Expression has type: [atom()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: atom() + 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 + │ +143 │ test28_neg(B) -> boolean_id(B). + │ ^ B. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:146:17 + │ +146 │ test29_neg() -> [true, false]. + │ ^^^^^^^^^^^^^ + │ │ + │ ['true', 'false']. +Expression has type: ['true' | 'false'] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: 'true' | 'false' + Context expects type: none() + No candidate of the expression's type matches the expected type. + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:149:17 + │ +149 │ test30_neg() -> 1. + │ ^ 1. +Expression has type: number() +Context expected type: 'atom' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:152:17 + │ +152 │ test31_neg() -> fun test30_neg/0. + │ ^^^^^^^^^^^^^^^^ test30_neg/0. +Expression has type: fun(() -> 'atom') +Context expected type: 'atom' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:155:17 + │ +155 │ test32_neg() -> fun misc_lib:boolean_id/1. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ misc_lib:boolean_id/1. +Expression has type: fun((boolean()) -> boolean()) +Context expected type: 'atom' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:192:1 + │ +192 │ test40_pos({X, Y}) -> {X, Y}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:196:1 + │ +196 │ test41_pos([X | Y]) -> {X, Y}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:204:1 + │ +204 │ test43_pos([_ | Y]) when Y + 1 > 0 -> Y. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:221:17 + │ +221 │ test46_neg() -> false. + │ ^^^^^ 'false'. +Expression has type: 'false' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:224:17 + │ +224 │ test47_neg() -> []. + │ ^^ []. +Expression has type: [] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:227:17 + │ +227 │ test48_neg() -> test47_neg(). + │ ^^^^^^^^^^^^ test47_neg(). +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:230:17 + │ +230 │ test49_neg() -> misc:test47_neg(). + │ ^^^^^^^^^^^^^^^^^ misc:test47_neg(). +Expression has type: number() +Context expected type: atom() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:337:1 + │ +337 │ test68_pos({E, _}) -> E. + │ ^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:352:22 + │ +352 │ catch test69_pos(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'. +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 + │ +404 │ after Timeout -> + │ ^^^^^^^ Timeout. +Expression has type: term() +Context expected type: timeout() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:412:11 + │ +412 │ after Timeout -> + │ ^^^^^^^ Timeout. +Expression has type: term() +Context expected type: timeout() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:424:5 + │ +424 │ {Msg}. + │ ^^^^^ {Msg}. +Expression has type: {'default' | dynamic('atom')} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:453:5 + │ +453 │ {[H1|T1], [H2|T2]}. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ {[H1 | T1], [H2 | T2]}. +Expression has type: {[number() | atom()], [atom() | number()]} +Context expected type: {[atom()], [number()]} + │ + +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() + ] + , [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 + │ +461 │ {[H1|T1], [H2|T2]}. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ {[H1 | T1], [H2 | T2]}. +Expression has type: {[atom() | number()], [atom() | number()]} +Context expected type: {[atom()] | [number()], [atom()] | [number()]} + │ + +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() + ] + , [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 + │ +465 │ X = [1 | 2], + │ ^^^^ 2. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:472:21 + │ +472 │ comp01_neg(X, Y) -> X < Y. + │ ^^^^^ _ < _. +Expression has type: boolean() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:482:5 + │ +482 │ Res. + │ ^^^ Res. +Expression has type: boolean() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:497:5 + │ +497 │ Where ! What. + │ ^^^^^^^^^^^^ _ ! _. +Expression has type: term() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:507:20 + │ +507 │ #str_box{str = Str}. + │ ^^^ Str. +Expression has type: atom() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:513:17 + │ +513 │ string_neg() -> "str". + │ ^^^^^ string_lit. +Expression has type: string() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:538:5 + │ +538 │ Atom. + │ ^^^^ Atom. +Expression has type: string() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:555:5 + │ +555 │ Atoms ++ Numbers. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [atom() | binary()] +Context expected type: [atom() | number()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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 + │ +575 │ Atoms -- Anys. + │ ^^^^ Anys. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:581:5 + │ +581 │ Anys ++ Atoms. + │ ^^^^ Anys. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:599:23 + │ +599 │ start_neg([] ++ X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [A] +Context expected type: [[A]] + │ + +Because in the expression's type: + [ + Here the type is: A + 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 + │ +611 │ ss2_neg("" ++ S) -> S. + │ ^ S. +Expression has type: [atom() | number()] +Context expected type: string() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:615:1 + │ +615 │ ss3("b" ++ S) -> S. + │ ^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:632:5 + │ +632 │ X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ check/src/misc.erl:641:5 + │ +641 │ atom() := atom(), + │ ^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:644:15 + │ +644 │ badspec(_) -> ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: #{dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:21 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:21 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:25 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:25 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:26 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:26 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:30 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:30 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:671:5 + │ +671 │ [X, 1, 2, 3 | Y]. + │ ^^^^^^^^^^^^^^^^ [X, 1, 2, 3 | Y]. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:671:17 + │ +671 │ [X, 1, 2, 3 | Y]. + │ ^^^^ Y. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:676:5 + │ +676 │ [X, 1, 2, 3 | an_atom]. + │ ^^^^^^^^^^^^^^^^^^^^^^ [X, 1, 2, 3 | 'an_atom']. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:676:17 + │ +676 │ [X, 1, 2, 3 | an_atom]. + │ ^^^^^^^^^^ 'an_atom'. +Expression has type: 'an_atom' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:681:23 + │ +681 │ Res = [X, 1, 2, 3 | an_atom], + │ ^^^^^^^^^^ 'an_atom'. +Expression has type: 'an_atom' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:682:5 + │ +682 │ Res. + │ ^^^ Res. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:685:1 + │ +685 │ not_list1_neg("atom" ++ A) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:689:1 + │ +689 │ ╭ not_list2_neg +690 │ │ (A, [_ | A]) -> A. + │ ╰─────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:694:1 + │ +694 │ ╭ not_list3_neg +695 │ │ (A, "atom" ++ A) -> A. + │ ╰─────────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:728:1 + │ +728 │ ╭ test91_neg(None) -> +729 │ │ Res = {None, err}, +730 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:730:5 + │ +730 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: {none(), 'err'} +Context expected type: {'ok', 'ok'} + │ + +Because in the expression's type: + { none(), + Here the type is: 'err' + 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 + │ +741 │ X. + │ ^ + │ │ + │ X. +Expression has type: v2_op() +Context expected type: v1_op() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: v1_op() + However the following candidate: 'v2_op' + Differs from the expected type: 'v1_op1' | 'v1_op2' | stuff() | v0_op() + +------------------------------ 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 + │ +760 │ -opaque o_cycle() :: opaque:o_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type o_cycle/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/misc.erl:762:1 + │ +762 │ ╭ -spec use_o_cycle1(misc:o_cycle()) -> +763 │ │ opaque:o_cycle(). + │ ╰────────────────────^ use_o_cycle1/1 references types with invalid definitions: o_cycle/0, opaque:o_cycle/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/misc.erl:766:1 + │ +766 │ ╭ -spec use_o_cycle2(opaque:o_cycle()) -> +767 │ │ misc:o_cycle(). + │ ╰──────────────────^ use_o_cycle2/1 references types with invalid definitions: o_cycle/0, opaque:o_cycle/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/misc.erl:783:1 + │ +783 │ ╭ -type x_cycle() :: +784 │ │ recursive_aliases:x_cycle(). + │ ╰───────────────────────────────^ recursive type x_cycle/0 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/misc.erl:790:21 + │ +790 │ opaque:opair(a, x:y())) -> + │ ^^^^^ Unknown id: x:y/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:797:13 + │ +797 │ names(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:set() +Context expected type: sets:set() + │ + +Because in the expression's type: + Here the type is: [] + 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 + │ +850 │ {ok, lists, 1.0}. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ {'ok', 'lists', float_lit}. +Expression has type: {'ok', 'lists', number()} +Context expected type: {'ok', atom(), pid()} + │ + +Because in the expression's type: + { 'ok', 'lists', + Here the type is: number() + 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 + │ +860 │ Mod. + │ ^^^ Mod. +Expression has type: atom() +Context expected type: 'wrong_ret' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:875:5 + │ +875 │ Arity. + │ ^^^^^ Arity. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:887:5 + │ +887 │ Node. + │ ^^^^ Node. +Expression has type: atom() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:929:16 + │ +929 │ iovec_neg() -> {<<>>}. + │ ^^^^^^ + │ │ + │ {<<..>>}. +Expression has type: {binary()} +Context expected type: erlang:iovec() + │ + +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 + │ +949 │ n_a_neg(N) -> N. + │ ^ N. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:965:5 + │ +965 │ MyPriorities. + │ ^^^^^^^^^^^^ + │ │ + │ MyPriorities. +Expression has type: ['MM' | 'MS' | 'EE' | 'MA' | 'GE'] +Context expected type: [erlang:priority_level()] + │ + +Because in the expression's type: + [ + Here the type is: 'MM' | 'MS' | 'EE' | 'MA' | 'GE' + Context expects type: 'low' | 'normal' | 'high' | 'max' + 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 + │ +971 │ X. + │ ^ + │ │ + │ X. +Expression has type: erlang:timestamp() +Context expected type: atom() + │ + +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 + │ +987 │ Res = (Flag orelse Pid), + │ ^^^^ Flag. +Expression has type: atom() +Context expected type: boolean() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/misc.erl:1005:20 + │ +1005 │ orelse Validator(), + │ ^^^^^^^^^ Validator. +Expected fun type with arity 0 +Got: 'undefined' + +error: non_exported_id (See https://fb.me/eqwalizer_errors#non_exported_id) + ┌─ check/src/misc.erl:1008:23 + │ +1008 │ -spec non_exported_id(any_fun_type:f1()) -> any_fun_type:f1(). + │ ^^^^^^^^^^^^^^^^^ Type exists but is not exported: any_fun_type:f1/0 + +error: non_exported_id (See https://fb.me/eqwalizer_errors#non_exported_id) + ┌─ check/src/misc.erl:1011:30 + │ +1011 │ -type non_exported_id_t() :: any_fun_type:f1(). + │ ^^^^^^^^^^^^^^^^^ Type exists but is not exported: any_fun_type:f1/0 + +93 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-28.pretty new file mode 100644 index 0000000000..22e373f816 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc-OTP-28.pretty @@ -0,0 +1,1011 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:46:23 + │ +46 │ test03_neg(-4 = X) -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:49:26 + │ +49 │ test04_neg(6 / 3 = Y) -> Y. + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:52:20 + │ +52 │ test05_neg(X) -> + X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:55:36 + │ +55 │ test06_neg(X) when is_number(X) -> + X. + │ ^^^ + _. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:63:5 + │ +63 │ not X. + │ ^^^^^ not _. +Expression has type: boolean() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:73:25 + │ +73 │ is_number(X) -> X + Y. + │ ^ Y. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:77:21 + │ +77 │ test11_neg(X, Y) -> X + Y. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:81:19 + │ +81 │ test12_neg(X) -> -X. + │ ^ X. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:85:21 + │ +85 │ test13_neg(X, Y) -> X or Y. + │ ^ X. +Expression has type: term() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:85:26 + │ +85 │ test13_neg(X, Y) -> X or Y. + │ ^ Y. +Expression has type: term() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:97:21 + │ +97 │ test16_neg(X, Y) -> X or Y. + │ ^^^^^^ _ or _. +Expression has type: boolean() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:101:26 + │ +101 │ test17_neg(X, Y) -> X or Y. + │ ^ Y. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:105:21 + │ +105 │ test18_neg(X, Y) -> X orelse Y. + │ ^^^^^^^^^^ _ orelse _. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:109:21 + │ +109 │ test19_neg(X, Y) -> X andalso Y. + │ ^^^^^^^^^^^ _ andalso _. +Expression has type: 'false' | number() +Context expected type: {number()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:112:18 + │ +112 │ test20_neg(X) -> [- X]. + │ ^^^^^ + │ │ + │ [- _]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +116 │ test21_neg(X, Y) -> [X / Y]. + │ ^^^^^^^ + │ │ + │ [_ / _]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +120 │ test22_neg(X) -> [not X]. + │ ^^^^^^^ + │ │ + │ [not _]. +Expression has type: [boolean()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: boolean() + Context expects type: number() + ] + +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:124:21 + │ +124 │ test23_neg(X, Y) -> [X or Y]. + │ ^^^^^^^^ + │ │ + │ [_ or _]. +Expression has type: [boolean()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: boolean() + Context expects type: number() + ] + +------------------------------ Detailed message ------------------------------ + + [boolean()] is not compatible with [number()] + because + boolean() is not compatible with number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:128:21 + │ +128 │ test24_neg(X, Y) -> [X orelse Y]. + │ ^^^^^^^^^^^^ + │ │ + │ [_ orelse _]. +Expression has type: [atom()] +Context expected type: [number()] + │ + +Because in the expression's type: + [ + Here the type is: atom() + 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 + │ +143 │ test28_neg(B) -> boolean_id(B). + │ ^ B. +Expression has type: atom() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:146:17 + │ +146 │ test29_neg() -> [true, false]. + │ ^^^^^^^^^^^^^ + │ │ + │ ['true', 'false']. +Expression has type: ['true' | 'false'] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: 'true' | 'false' + Context expects type: none() + No candidate of the expression's type matches the expected type. + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:149:17 + │ +149 │ test30_neg() -> 1. + │ ^ 1. +Expression has type: number() +Context expected type: 'atom' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:152:17 + │ +152 │ test31_neg() -> fun test30_neg/0. + │ ^^^^^^^^^^^^^^^^ test30_neg/0. +Expression has type: fun(() -> 'atom') +Context expected type: 'atom' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:155:17 + │ +155 │ test32_neg() -> fun misc_lib:boolean_id/1. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ misc_lib:boolean_id/1. +Expression has type: fun((boolean()) -> boolean()) +Context expected type: 'atom' + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:192:1 + │ +192 │ test40_pos({X, Y}) -> {X, Y}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:196:1 + │ +196 │ test41_pos([X | Y]) -> {X, Y}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:204:1 + │ +204 │ test43_pos([_ | Y]) when Y + 1 > 0 -> Y. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:221:17 + │ +221 │ test46_neg() -> false. + │ ^^^^^ 'false'. +Expression has type: 'false' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:224:17 + │ +224 │ test47_neg() -> []. + │ ^^ []. +Expression has type: [] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:227:17 + │ +227 │ test48_neg() -> test47_neg(). + │ ^^^^^^^^^^^^ test47_neg(). +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:230:17 + │ +230 │ test49_neg() -> misc:test47_neg(). + │ ^^^^^^^^^^^^^^^^^ misc:test47_neg(). +Expression has type: number() +Context expected type: atom() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:337:1 + │ +337 │ test68_pos({E, _}) -> E. + │ ^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:352:22 + │ +352 │ catch test69_pos(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'. +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 + │ +404 │ after Timeout -> + │ ^^^^^^^ Timeout. +Expression has type: term() +Context expected type: timeout() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:412:11 + │ +412 │ after Timeout -> + │ ^^^^^^^ Timeout. +Expression has type: term() +Context expected type: timeout() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:424:5 + │ +424 │ {Msg}. + │ ^^^^^ {Msg}. +Expression has type: {'default' | dynamic('atom')} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:453:5 + │ +453 │ {[H1|T1], [H2|T2]}. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ {[H1 | T1], [H2 | T2]}. +Expression has type: {[number() | atom()], [atom() | number()]} +Context expected type: {[atom()], [number()]} + │ + +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() + ] + , [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 + │ +461 │ {[H1|T1], [H2|T2]}. + │ ^^^^^^^^^^^^^^^^^^ + │ │ + │ {[H1 | T1], [H2 | T2]}. +Expression has type: {[atom() | number()], [atom() | number()]} +Context expected type: {[atom()] | [number()], [atom()] | [number()]} + │ + +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() + ] + , [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 + │ +465 │ X = [1 | 2], + │ ^^^^ 2. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:472:21 + │ +472 │ comp01_neg(X, Y) -> X < Y. + │ ^^^^^ _ < _. +Expression has type: boolean() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:482:5 + │ +482 │ Res. + │ ^^^ Res. +Expression has type: boolean() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:497:5 + │ +497 │ Where ! What. + │ ^^^^^^^^^^^^ _ ! _. +Expression has type: term() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:507:20 + │ +507 │ #str_box{str = Str}. + │ ^^^ Str. +Expression has type: atom() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:513:17 + │ +513 │ string_neg() -> "str". + │ ^^^^^ string_lit. +Expression has type: string() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:538:5 + │ +538 │ Atom. + │ ^^^^ Atom. +Expression has type: string() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:555:5 + │ +555 │ Atoms ++ Numbers. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ _ ++ _. +Expression has type: [atom() | binary()] +Context expected type: [atom() | number()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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 + │ +575 │ Atoms -- Anys. + │ ^^^^ Anys. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:581:5 + │ +581 │ Anys ++ Atoms. + │ ^^^^ Anys. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:599:23 + │ +599 │ start_neg([] ++ X) -> X. + │ ^ + │ │ + │ X. +Expression has type: [A] +Context expected type: [[A]] + │ + +Because in the expression's type: + [ + Here the type is: A + 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 + │ +611 │ ss2_neg("" ++ S) -> S. + │ ^ S. +Expression has type: [atom() | number()] +Context expected type: string() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:615:1 + │ +615 │ ss3("b" ++ S) -> S. + │ ^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:632:5 + │ +632 │ X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ check/src/misc.erl:641:5 + │ +641 │ atom() := atom(), + │ ^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:644:15 + │ +644 │ badspec(_) -> ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: #{dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:21 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:21 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:25 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:648:25 + │ +648 │ catch lists:map({}, {}). + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:26 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: fun((A) -> B) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:26 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: fun((dynamic()) -> dynamic()) + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:30 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: [A] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:652:30 + │ +652 │ X = (catch lists:map({}, {})), + │ ^^ {}. +Expression has type: {} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:671:5 + │ +671 │ [X, 1, 2, 3 | Y]. + │ ^^^^^^^^^^^^^^^^ [X, 1, 2, 3 | Y]. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:671:17 + │ +671 │ [X, 1, 2, 3 | Y]. + │ ^^^^ Y. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:676:5 + │ +676 │ [X, 1, 2, 3 | an_atom]. + │ ^^^^^^^^^^^^^^^^^^^^^^ [X, 1, 2, 3 | 'an_atom']. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:676:17 + │ +676 │ [X, 1, 2, 3 | an_atom]. + │ ^^^^^^^^^^ 'an_atom'. +Expression has type: 'an_atom' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:681:23 + │ +681 │ Res = [X, 1, 2, 3 | an_atom], + │ ^^^^^^^^^^ 'an_atom'. +Expression has type: 'an_atom' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:682:5 + │ +682 │ Res. + │ ^^^ Res. +Expression has type: [number() | dynamic()] +Context expected type: pid() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:685:1 + │ +685 │ not_list1_neg("atom" ++ A) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:689:1 + │ +689 │ ╭ not_list2_neg +690 │ │ (A, [_ | A]) -> A. + │ ╰─────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:694:1 + │ +694 │ ╭ not_list3_neg +695 │ │ (A, "atom" ++ A) -> A. + │ ╰─────────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/misc.erl:728:1 + │ +728 │ ╭ test91_neg(None) -> +729 │ │ Res = {None, err}, +730 │ │ Res. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:730:5 + │ +730 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: {none(), 'err'} +Context expected type: {'ok', 'ok'} + │ + +Because in the expression's type: + { none(), + Here the type is: 'err' + 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 + │ +741 │ X. + │ ^ + │ │ + │ X. +Expression has type: v2_op() +Context expected type: v1_op() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: v1_op() + However the following candidate: 'v2_op' + Differs from the expected type: 'v1_op1' | 'v1_op2' | stuff() | v0_op() + +------------------------------ 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 + │ +760 │ -opaque o_cycle() :: opaque:o_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type o_cycle/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/misc.erl:762:1 + │ +762 │ ╭ -spec use_o_cycle1(misc:o_cycle()) -> +763 │ │ opaque:o_cycle(). + │ ╰────────────────────^ use_o_cycle1/1 references types with invalid definitions: o_cycle/0, opaque:o_cycle/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/misc.erl:766:1 + │ +766 │ ╭ -spec use_o_cycle2(opaque:o_cycle()) -> +767 │ │ misc:o_cycle(). + │ ╰──────────────────^ use_o_cycle2/1 references types with invalid definitions: o_cycle/0, opaque:o_cycle/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/misc.erl:783:1 + │ +783 │ ╭ -type x_cycle() :: +784 │ │ recursive_aliases:x_cycle(). + │ ╰───────────────────────────────^ recursive type x_cycle/0 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/misc.erl:790:21 + │ +790 │ opaque:opair(a, x:y())) -> + │ ^^^^^ Unknown id: x:y/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:797:13 + │ +797 │ names(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:set() +Context expected type: sets:set() + │ + +Because in the expression's type: + Here the type is: [] + 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 + │ +850 │ {ok, lists, 1.0}. + │ ^^^^^^^^^^^^^^^^ + │ │ + │ {'ok', 'lists', float_lit}. +Expression has type: {'ok', 'lists', number()} +Context expected type: {'ok', atom(), pid()} + │ + +Because in the expression's type: + { 'ok', 'lists', + Here the type is: number() + 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 + │ +860 │ Mod. + │ ^^^ Mod. +Expression has type: atom() +Context expected type: 'wrong_ret' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:875:5 + │ +875 │ Arity. + │ ^^^^^ Arity. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:887:5 + │ +887 │ Node. + │ ^^^^ Node. +Expression has type: atom() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:929:16 + │ +929 │ iovec_neg() -> {<<>>}. + │ ^^^^^^ + │ │ + │ {<<..>>}. +Expression has type: {binary()} +Context expected type: erlang:iovec() + │ + +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 + │ +949 │ n_a_neg(N) -> N. + │ ^ N. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/misc.erl:965:5 + │ +965 │ MyPriorities. + │ ^^^^^^^^^^^^ + │ │ + │ MyPriorities. +Expression has type: ['MM' | 'MS' | 'EE' | 'MA' | 'GE'] +Context expected type: [erlang:priority_level()] + │ + +Because in the expression's type: + [ + Here the type is: 'MM' | 'MS' | 'EE' | 'MA' | 'GE' + Context expects type: 'low' | 'normal' | 'high' | 'max' + 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 + │ +971 │ X. + │ ^ + │ │ + │ X. +Expression has type: erlang:timestamp() +Context expected type: atom() + │ + +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 + │ +987 │ Res = (Flag orelse Pid), + │ ^^^^ Flag. +Expression has type: atom() +Context expected type: boolean() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/misc.erl:1005:20 + │ +1005 │ orelse Validator(), + │ ^^^^^^^^^ Validator. +Expected fun type with arity 0 +Got: 'undefined' + +error: non_exported_id (See https://fb.me/eqwalizer_errors#non_exported_id) + ┌─ check/src/misc.erl:1008:23 + │ +1008 │ -spec non_exported_id(any_fun_type:f1()) -> any_fun_type:f1(). + │ ^^^^^^^^^^^^^^^^^ Type exists but is not exported: any_fun_type:f1/0 + +error: non_exported_id (See https://fb.me/eqwalizer_errors#non_exported_id) + ┌─ check/src/misc.erl:1011:30 + │ +1011 │ -type non_exported_id_t() :: any_fun_type:f1(). + │ ^^^^^^^^^^^^^^^^^ Type exists but is not exported: any_fun_type:f1/0 + +93 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-28.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc_lib-OTP-28.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-27.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-27.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested1-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/misc_nested2-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_behaviour-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_gradual_behaviour-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/my_header-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-26.pretty similarity index 90% rename from crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-26.pretty index 585b5f8642..55ce8bf6f5 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/neg.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-26.pretty @@ -39,6 +39,13 @@ 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/neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-27.pretty new file mode 100644 index 0000000000..55ce8bf6f5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-27.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:13:11 + │ +13 │ foo(X) -> {X, X}. + │ ^^^^^^ {X, X}. +Expression has type: {A, A} +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:17:5 + │ +17 │ X. + │ ^ X. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:20:14 + │ +20 │ foo(X, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:24:13 + │ +24 │ {ok, _} = X. + │ ^^^ + │ │ + │ match_expr. +Expression has type: {'ok', term()} +Context expected type: {atom(), atom()} + │ + +Because in the expression's type: + { 'ok', + Here the type is: term() + 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 + │ +27 │ concat1() -> [1] ++ 1. + │ ^ 1. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:30:25 + │ +30 │ concat2(X, X) -> [1] ++ X. + │ ^ X. +Expression has type: X +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:33:25 + │ +33 │ concat3(X, X) -> [1] ++ X. + │ ^ X. +Expression has type: X +Context expected type: [term()] + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-28.pretty new file mode 100644 index 0000000000..55ce8bf6f5 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/neg-OTP-28.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:13:11 + │ +13 │ foo(X) -> {X, X}. + │ ^^^^^^ {X, X}. +Expression has type: {A, A} +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:17:5 + │ +17 │ X. + │ ^ X. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:20:14 + │ +20 │ foo(X, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:24:13 + │ +24 │ {ok, _} = X. + │ ^^^ + │ │ + │ match_expr. +Expression has type: {'ok', term()} +Context expected type: {atom(), atom()} + │ + +Because in the expression's type: + { 'ok', + Here the type is: term() + 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 + │ +27 │ concat1() -> [1] ++ 1. + │ ^ 1. +Expression has type: number() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:30:25 + │ +30 │ concat2(X, X) -> [1] ++ X. + │ ^ X. +Expression has type: X +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/neg.erl:33:25 + │ +33 │ concat3(X, X) -> [1] ++ X. + │ ^ X. +Expression has type: X +Context expected type: [term()] + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/nowarn.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-27.pretty new file mode 100644 index 0000000000..141f9e28db --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-27.pretty @@ -0,0 +1,21 @@ +error: redundant_nowarn_function (See https://fb.me/eqwalizer_errors#redundant_nowarn_function) + ┌─ check/src/nowarn.erl:10:1 + │ +10 │ -eqwalizer({nowarn_function, nowarn_redundant/0}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ redundant nowarn_function + +error: redundant_nowarn_function (See https://fb.me/eqwalizer_errors#redundant_nowarn_function) + ┌─ check/src/nowarn.erl:11:1 + │ +11 │ -eqwalizer({nowarn_function, nowarn_nonexist/0}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ redundant nowarn_function + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/nowarn.erl:21:3 + │ +21 │ ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-28.pretty new file mode 100644 index 0000000000..141f9e28db --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/nowarn-OTP-28.pretty @@ -0,0 +1,21 @@ +error: redundant_nowarn_function (See https://fb.me/eqwalizer_errors#redundant_nowarn_function) + ┌─ check/src/nowarn.erl:10:1 + │ +10 │ -eqwalizer({nowarn_function, nowarn_redundant/0}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ redundant nowarn_function + +error: redundant_nowarn_function (See https://fb.me/eqwalizer_errors#redundant_nowarn_function) + ┌─ check/src/nowarn.erl:11:1 + │ +11 │ -eqwalizer({nowarn_function, nowarn_nonexist/0}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ redundant nowarn_function + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/nowarn.erl:21:3 + │ +21 │ ok. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-27.pretty new file mode 100644 index 0000000000..cec080fa3e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-27.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/number_comparisons.erl:11:30 + │ +11 │ test_1_neg(X) when X >= 1 -> X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/number_comparisons.erl:253:3 + │ +253 │ X. + │ ^ X. +Expression has type: number() +Context expected type: none() + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-28.pretty new file mode 100644 index 0000000000..cec080fa3e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/number_comparisons-OTP-28.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/number_comparisons.erl:11:30 + │ +11 │ test_1_neg(X) when X >= 1 -> X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/number_comparisons.erl:253:3 + │ +253 │ X. + │ ^ X. +Expression has type: number() +Context expected type: none() + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-26.pretty similarity index 95% rename from crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-26.pretty index 8c78c1ffbe..8760657fa4 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/numbers.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-26.pretty @@ -150,6 +150,12 @@ 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 │ @@ -294,6 +300,13 @@ 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/numbers-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-27.pretty new file mode 100644 index 0000000000..8760657fa4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-27.pretty @@ -0,0 +1,318 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:121:3 + │ +121 │ A / 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:125:7 + │ +125 │ 2 / A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:129:7 + │ +129 │ 2 * A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:133:3 + │ +133 │ A * 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:137:7 + │ +137 │ X = A * 2, + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:142:9 + │ +142 │ 2 div A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:146:14 + │ +146 │ X = (2 div A), + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:151:3 + │ +151 │ A div 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:155:7 + │ +155 │ X = A div 2, + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:160:3 + │ +160 │ 1 div 2. + │ ^^^^^^^ _ div _. +Expression has type: number() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:182:3 + │ +182 │ bnot 0. + │ ^^^^^^ bnot _. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:350:5 + │ +350 │ (+X). + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:354:9 + │ +354 │ _ = (+X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:359:9 + │ +359 │ _ = (-X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:364:9 + │ +364 │ _ = (-X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:428:8 + │ +428 │ bnot X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:433:12 + │ +433 │ _ = bnot X, + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:491:3 + │ +491 │ U * N. + │ ^ + │ │ + │ U. +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' is not compatible with number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:538:1 + │ +538 │ ╭ test_85(X) -> +539 │ │ X div X. + │ ╰─────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:544:1 + │ +544 │ ╭ test_86(X) -> +545 │ │ X * X. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:3 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:10 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:16 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:23 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:3 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:8 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:14 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:19 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:576:1 + │ +576 │ ╭ test_91_neg(X) -> +577 │ │ -X. + │ ╰────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:577:3 + │ +577 │ -X. + │ ^^ - _. +Expression has type: number() +Context expected type: none() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:582:1 + │ +582 │ ╭ test_92_neg(X) -> +583 │ │ bnot X. + │ ╰────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:583:3 + │ +583 │ bnot X. + │ ^^^^^^ bnot _. +Expression has type: number() +Context expected type: none() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:588:1 + │ +588 │ ╭ test_93_neg(X) -> +589 │ │ 5 * X. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:589:3 + │ +589 │ 5 * X. + │ ^^^^^ _ * _. +Expression has type: number() +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:644:3 + │ +644 │ ╭ ╭ { +645 │ │ │ 1, +646 │ │ │ 0, +647 │ │ │ -1, +648 │ │ │ Int, +649 │ │ │ error +650 │ │ │ }. + │ ╰─│───^ {1, 0, - _, Int, 'error'}. +Expression has type: {number(), number(), number(), number(), 'error'} +Context expected type: {number(), number(), number(), number(), 'ok'} + │ ╰───' + +Because in the expression's type: + { number(), number(), number(), number(), + Here the type is: 'error' + 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 + │ +658 │ 1.0. + │ ^^^ float_lit. +Expression has type: number() +Context expected type: pid() + +36 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-28.pretty new file mode 100644 index 0000000000..8760657fa4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/numbers-OTP-28.pretty @@ -0,0 +1,318 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:121:3 + │ +121 │ A / 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:125:7 + │ +125 │ 2 / A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:129:7 + │ +129 │ 2 * A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:133:3 + │ +133 │ A * 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:137:7 + │ +137 │ X = A * 2, + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:142:9 + │ +142 │ 2 div A. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:146:14 + │ +146 │ X = (2 div A), + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:151:3 + │ +151 │ A div 2. + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:155:7 + │ +155 │ X = A div 2, + │ ^ A. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:160:3 + │ +160 │ 1 div 2. + │ ^^^^^^^ _ div _. +Expression has type: number() +Context expected type: 'a' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:182:3 + │ +182 │ bnot 0. + │ ^^^^^^ bnot _. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:350:5 + │ +350 │ (+X). + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:354:9 + │ +354 │ _ = (+X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:359:9 + │ +359 │ _ = (-X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:364:9 + │ +364 │ _ = (-X), + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:428:8 + │ +428 │ bnot X. + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:433:12 + │ +433 │ _ = bnot X, + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:491:3 + │ +491 │ U * N. + │ ^ + │ │ + │ U. +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' is not compatible with number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:538:1 + │ +538 │ ╭ test_85(X) -> +539 │ │ X div X. + │ ╰─────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:544:1 + │ +544 │ ╭ test_86(X) -> +545 │ │ X * X. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:3 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:10 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:16 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:552:23 + │ +552 │ U1 div U2 / (U2 bsl U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:3 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:8 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:14 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U2. +Expression has type: 'c' | 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:559:19 + │ +559 │ U1 * U2 / (U2 + U1). + │ ^^ U1. +Expression has type: 'a' | 'b' +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:576:1 + │ +576 │ ╭ test_91_neg(X) -> +577 │ │ -X. + │ ╰────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:577:3 + │ +577 │ -X. + │ ^^ - _. +Expression has type: number() +Context expected type: none() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:582:1 + │ +582 │ ╭ test_92_neg(X) -> +583 │ │ bnot X. + │ ╰────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:583:3 + │ +583 │ bnot X. + │ ^^^^^^ bnot _. +Expression has type: number() +Context expected type: none() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/numbers.erl:588:1 + │ +588 │ ╭ test_93_neg(X) -> +589 │ │ 5 * X. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:589:3 + │ +589 │ 5 * X. + │ ^^^^^ _ * _. +Expression has type: number() +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/numbers.erl:644:3 + │ +644 │ ╭ ╭ { +645 │ │ │ 1, +646 │ │ │ 0, +647 │ │ │ -1, +648 │ │ │ Int, +649 │ │ │ error +650 │ │ │ }. + │ ╰─│───^ {1, 0, - _, Int, 'error'}. +Expression has type: {number(), number(), number(), number(), 'error'} +Context expected type: {number(), number(), number(), number(), 'ok'} + │ ╰───' + +Because in the expression's type: + { number(), number(), number(), number(), + Here the type is: 'error' + 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 + │ +658 │ 1.0. + │ ^^^ float_lit. +Expression has type: number() +Context expected type: pid() + +36 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-26.pretty similarity index 80% rename from crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-26.pretty index 5c207ff164..eea53f78f7 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/opaque.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-26.pretty @@ -10,11 +10,19 @@ Context expected type: sets:set({'ok', 'ok'}) │ Because in the expression's type: - The type is a union type with some valid candidates: #set{} - However, the following candidate doesn't match: - Here the type is: #{{ok, error} => ...} - Context expects type: #{...} - The expected map has no corresponding key for: {ok, error}. + Here the type is a union type with some valid candidates: #set{} + However the following candidate: #{{ok, error} => []} + Differs from the expected type: #set{} | #{{ok, ok} => []} + +------------------------------ 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 @@ -37,6 +45,12 @@ 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 │ @@ -77,6 +91,14 @@ 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 │ @@ -93,6 +115,14 @@ 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/opaque-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-27.pretty new file mode 100644 index 0000000000..eea53f78f7 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-27.pretty @@ -0,0 +1,179 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:23:3 + │ +23 │ X. + │ ^ + │ │ + │ X. +Expression has type: sets:set({'ok', 'error'}) +Context expected type: sets:set({'ok', 'ok'}) + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #set{} + However the following candidate: #{{ok, error} => []} + Differs from the expected type: #set{} | #{{ok, ok} => []} + +------------------------------ 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 + │ +29 │ test2_neg(X=#{a := b}) -> X. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:57:11 + │ +57 │ X2 + │ ^^ + │ │ + │ X2. +Expression has type: misc:o() +Context expected type: none() + │ + +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 + │ +86 │ ╭ test10({rec_w_opaque_field, +87 │ │ {_, _}} +88 │ │ ) -> ok. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:95:6 + │ +95 │ ) -> X. + │ ^ X. +Expression has type: 'ok' +Context expected type: {'ok'} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:107:18 + │ +107 │ test14_neg(X) -> X =:= {ok}. + │ ^^^^^^^^^^ _ =:= _. +Expression has type: boolean() +Context expected type: 'true' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:125:18 + │ +125 │ test18_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:o() | 'a' +Context expected type: misc:o() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: misc:o() + 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 + │ +135 │ test20_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:o() | 'a' +Context expected type: misc:o() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: misc:o() + 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 + │ +150 │ -opaque o_cycle() :: misc:o_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type o_cycle/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:152:1 + │ +152 │ ╭ -spec use_o_cycle1(misc:o_cycle()) -> +153 │ │ opaque:o_cycle(). + │ ╰──────────────────^ use_o_cycle1/1 references types with invalid definitions: misc:o_cycle/0, o_cycle/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:156:1 + │ +156 │ ╭ -spec use_o_cycle2(opaque:o_cycle()) -> +157 │ │ misc:o_cycle(). + │ ╰────────────────^ use_o_cycle2/1 references types with invalid definitions: misc:o_cycle/0, o_cycle/0 + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:236:1 + │ +236 │ ╭ test29_neg(X) when X =:= {1.0} -> +237 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:240:1 + │ +240 │ ╭ test30_neg(X) when X =:= "" -> +241 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:256:1 + │ +256 │ ╭ test34_neg(X) when X =:= <<>> -> +257 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/opaque.erl:288:1 + │ +288 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:290:1 + │ +290 │ -opaque o_trans_invalid() :: invalid(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ o_trans_invalid/0 references type with invalid definition: invalid/0 + +16 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-28.pretty new file mode 100644 index 0000000000..eea53f78f7 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/opaque-OTP-28.pretty @@ -0,0 +1,179 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:23:3 + │ +23 │ X. + │ ^ + │ │ + │ X. +Expression has type: sets:set({'ok', 'error'}) +Context expected type: sets:set({'ok', 'ok'}) + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #set{} + However the following candidate: #{{ok, error} => []} + Differs from the expected type: #set{} | #{{ok, ok} => []} + +------------------------------ 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 + │ +29 │ test2_neg(X=#{a := b}) -> X. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:57:11 + │ +57 │ X2 + │ ^^ + │ │ + │ X2. +Expression has type: misc:o() +Context expected type: none() + │ + +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 + │ +86 │ ╭ test10({rec_w_opaque_field, +87 │ │ {_, _}} +88 │ │ ) -> ok. + │ ╰───────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:95:6 + │ +95 │ ) -> X. + │ ^ X. +Expression has type: 'ok' +Context expected type: {'ok'} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:107:18 + │ +107 │ test14_neg(X) -> X =:= {ok}. + │ ^^^^^^^^^^ _ =:= _. +Expression has type: boolean() +Context expected type: 'true' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/opaque.erl:125:18 + │ +125 │ test18_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:o() | 'a' +Context expected type: misc:o() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: misc:o() + 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 + │ +135 │ test20_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: misc:o() | 'a' +Context expected type: misc:o() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: misc:o() + 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 + │ +150 │ -opaque o_cycle() :: misc:o_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type o_cycle/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:152:1 + │ +152 │ ╭ -spec use_o_cycle1(misc:o_cycle()) -> +153 │ │ opaque:o_cycle(). + │ ╰──────────────────^ use_o_cycle1/1 references types with invalid definitions: misc:o_cycle/0, o_cycle/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:156:1 + │ +156 │ ╭ -spec use_o_cycle2(opaque:o_cycle()) -> +157 │ │ misc:o_cycle(). + │ ╰────────────────^ use_o_cycle2/1 references types with invalid definitions: misc:o_cycle/0, o_cycle/0 + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:236:1 + │ +236 │ ╭ test29_neg(X) when X =:= {1.0} -> +237 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:240:1 + │ +240 │ ╭ test30_neg(X) when X =:= "" -> +241 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/opaque.erl:256:1 + │ +256 │ ╭ test34_neg(X) when X =:= <<>> -> +257 │ │ ok. + │ ╰────^ Clause is not covered by spec + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/opaque.erl:288:1 + │ +288 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/opaque.erl:290:1 + │ +290 │ -opaque o_trans_invalid() :: invalid(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ o_trans_invalid/0 references type with invalid definition: invalid/0 + +16 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-26.pretty similarity index 55% rename from crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-26.pretty index da1b4e48cb..20764acf15 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/other.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-26.pretty @@ -13,10 +13,10 @@ Because in the expression's type: Here the type is: term() Context expects type: #{...} -error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) - ┌─ check/src/other.erl:59:27 - │ -59 │ eqwalizer:reveal_type(X), - │ ^ number() +------------------------------ Detailed message ------------------------------ -2 ERRORS + 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()} + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-27.pretty new file mode 100644 index 0000000000..20764acf15 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-27.pretty @@ -0,0 +1,22 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/other.erl:46:27 + │ +46 │ any_as_metadata_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: term() +Context expected type: logger:metadata() + │ + +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()} + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-28.pretty new file mode 100644 index 0000000000..20764acf15 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/other-OTP-28.pretty @@ -0,0 +1,22 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/other.erl:46:27 + │ +46 │ any_as_metadata_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: term() +Context expected type: logger:metadata() + │ + +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()} + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-26.pretty similarity index 68% rename from crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-26.pretty index 5113f80c15..026d55c435 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/otp28.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-26.pretty @@ -14,6 +14,14 @@ 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 │ @@ -31,6 +39,12 @@ 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 │ @@ -48,6 +62,12 @@ 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 │ @@ -75,6 +95,15 @@ 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 │ @@ -92,6 +121,13 @@ 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 │ @@ -111,4 +147,13 @@ 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/otp28-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-27.pretty new file mode 100644 index 0000000000..026d55c435 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-27.pretty @@ -0,0 +1,159 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp28.erl:16:21 + │ +16 │ mk_nominal_neg() -> warning. + │ ^^^^^^^ + │ │ + │ 'warning'. +Expression has type: 'warning' +Context expected type: foo() + │ + +Because in the expression's type: + Here the type is: 'warning' + 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 + │ +28 │ [N || {_, N} <:- L]. + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +40 │ [Bin || _ := {error, Bin} <:- L]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [binary()] +Context expected type: [pid()] + │ + +Because in the expression's type: + [ + Here the type is: binary() + 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 + │ +52 │ #{K => Bin || {K, {ok, Bin}} <:- L}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{ || }. +Expression has type: #{atom() => binary()} +Context expected type: [#{atom() => pid()}] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp28.erl:60:5 + │ +60 │ [{A, B} || A <- As && B <- Bs]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +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()}] + 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 + │ +68 │ #{A => B || A <- As && B <- Bs}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{ || }. +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/otp28.erl:76:5 + │ +76 │ [{A, B, V, K} || A <- As && B <- Bs && K := V <- M]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [{atom(), binary(), binary(), atom()}] +Context expected type: [{atom(), binary(), atom(), binary()}] + │ + +Because in the expression's type: + [ + { atom(), binary(), + Here the type is: binary() + Context expects type: atom() + , 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/otp28-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-28.pretty new file mode 100644 index 0000000000..026d55c435 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp28-OTP-28.pretty @@ -0,0 +1,159 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp28.erl:16:21 + │ +16 │ mk_nominal_neg() -> warning. + │ ^^^^^^^ + │ │ + │ 'warning'. +Expression has type: 'warning' +Context expected type: foo() + │ + +Because in the expression's type: + Here the type is: 'warning' + 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 + │ +28 │ [N || {_, N} <:- L]. + │ ^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [number()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: number() + 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 + │ +40 │ [Bin || _ := {error, Bin} <:- L]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [binary()] +Context expected type: [pid()] + │ + +Because in the expression's type: + [ + Here the type is: binary() + 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 + │ +52 │ #{K => Bin || {K, {ok, Bin}} <:- L}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{ || }. +Expression has type: #{atom() => binary()} +Context expected type: [#{atom() => pid()}] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp28.erl:60:5 + │ +60 │ [{A, B} || A <- As && B <- Bs]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +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()}] + 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 + │ +68 │ #{A => B || A <- As && B <- Bs}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{ || }. +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/otp28.erl:76:5 + │ +76 │ [{A, B, V, K} || A <- As && B <- Bs && K := V <- M]. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ [ || ]. +Expression has type: [{atom(), binary(), binary(), atom()}] +Context expected type: [{atom(), binary(), atom(), binary()}] + │ + +Because in the expression's type: + [ + { atom(), binary(), + Here the type is: binary() + Context expects type: atom() + , 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-OTP-26.pretty similarity index 65% rename from crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-26.pretty index 59bd5e6099..b41039f95b 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-26.pretty @@ -17,6 +17,17 @@ 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 │ @@ -34,6 +45,12 @@ 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 │ @@ -50,6 +67,14 @@ 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 │ @@ -74,6 +99,14 @@ 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/otp_opaques-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-27.pretty new file mode 100644 index 0000000000..b41039f95b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-27.pretty @@ -0,0 +1,126 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp_opaques.erl:18:5 + │ +18 │ gb_sets:from_list(Atoms). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ gb_sets:from_list(Atoms). +Expression has type: gb_sets:set(atom()) +Context expected type: gb_sets:set(number()) + │ + +Because in the expression's type: + { number(), + { + Here the type is: atom() + Context expects type: number() + , 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 + │ +42 │ List. + │ ^^^^ + │ │ + │ List. +Expression has type: [[A]] +Context expected type: [A] + │ + +Because in the expression's type: + [ + Here the type is: [A] + 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 + │ +99 │ test3_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: maps:iterator('k', 'v') +Context expected type: sets:set('a') + │ + +Because in the expression's type: + Here the type is: tuple()/3 | 'none' | [...] | [...] + 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 + │ +102 │ ts(X, X) -> X. + │ ^ X. +Expression has type: #set{} | #{a => []} +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp_opaques.erl:107:11 + │ +107 │ ts(X, X), + │ ^ + │ │ + │ X. +Expression has type: maps:iterator('k', 'v') +Context expected type: sets:set('a') + │ + +Because in the expression's type: + Here the type is: tuple()/3 | 'none' | [...] | [...] + 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 + │ +124 │ X(). + │ ^ X. +Expected fun type with arity 0 +Got: maps:iterator('k', 'v') | sets:set('a') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/otp_opaques.erl:130:5 + │ +130 │ X(). + │ ^ X. +Expected fun type with arity 0 +Got: sets:set('a') + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-28.pretty new file mode 100644 index 0000000000..b41039f95b --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/otp_opaques-OTP-28.pretty @@ -0,0 +1,126 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp_opaques.erl:18:5 + │ +18 │ gb_sets:from_list(Atoms). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ gb_sets:from_list(Atoms). +Expression has type: gb_sets:set(atom()) +Context expected type: gb_sets:set(number()) + │ + +Because in the expression's type: + { number(), + { + Here the type is: atom() + Context expects type: number() + , 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 + │ +42 │ List. + │ ^^^^ + │ │ + │ List. +Expression has type: [[A]] +Context expected type: [A] + │ + +Because in the expression's type: + [ + Here the type is: [A] + 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 + │ +99 │ test3_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: maps:iterator('k', 'v') +Context expected type: sets:set('a') + │ + +Because in the expression's type: + Here the type is: tuple()/3 | 'none' | [...] | [...] + 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 + │ +102 │ ts(X, X) -> X. + │ ^ X. +Expression has type: #set{} | #{a => []} +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/otp_opaques.erl:107:11 + │ +107 │ ts(X, X), + │ ^ + │ │ + │ X. +Expression has type: maps:iterator('k', 'v') +Context expected type: sets:set('a') + │ + +Because in the expression's type: + Here the type is: tuple()/3 | 'none' | [...] | [...] + 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 + │ +124 │ X(). + │ ^ X. +Expected fun type with arity 0 +Got: maps:iterator('k', 'v') | sets:set('a') + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ check/src/otp_opaques.erl:130:5 + │ +130 │ X(). + │ ^ X. +Expected fun type with arity 0 +Got: sets:set('a') + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-26.pretty similarity index 84% rename from crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-26.pretty index aa87b295ad..117ed8b349 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-26.pretty @@ -30,13 +30,26 @@ 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 @@ -94,6 +107,12 @@ 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 │ @@ -111,9 +130,16 @@ 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-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-27.pretty new file mode 100644 index 0000000000..117ed8b349 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-27.pretty @@ -0,0 +1,160 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:29:5 + │ +29 │ B. + │ ^ B. +Expression has type: binary() +Context expected type: atom() + +error: ambiguous_lambda (See https://fb.me/eqwalizer_errors#ambiguous_lambda) + ┌─ check/src/overloaded.erl:102:15 + │ +102 │ Res = bar(fun(a) -> a end), + │ ^^^^^^^^^^^^^^^ fun. +Functional expression can match multiple types in fun(('a') -> 'b') | fun(('a') -> 'c') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:117:15 + │ +117 │ Res = bar(F), + │ ^ + │ │ + │ F. +Expression has type: fun(('a') -> 'z') +Context expected type: fun(('a') -> 'b') | fun(('a') -> 'c') + │ + +Because in the expression's type: + fun(('a') -> + Here the type is: 'z' + 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}. +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 + │ +198 │ overlap_neg_1(X) when is_atom(X) -> 1; + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:203:37 + │ +203 │ overlap_neg_2(X) when is_atom(X) -> 1; + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:204:22 + │ +204 │ overlap_neg_2(_X) -> 1. + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:246:23 + │ +246 │ take_ok_or_any(ok) -> ok; + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: 'error' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:247:22 + │ +247 │ take_ok_or_any(_) -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:259:21 + │ +259 │ use_over(X) -> over(X). + │ ^ + │ │ + │ X. +Expression has type: 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: {} + 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 + │ +261 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/overloaded.erl:263:1 + │ +263 │ ╭ -spec use_invalid_neg(a) -> a; +264 │ │ (err) -> invalid(). + │ ╰─────────────────────────────────^ use_invalid_neg/1 references type with invalid definition: invalid/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:269:14 + │ +269 │ _ = swap(""), + │ ^^ + │ │ + │ 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 + │ +290 │ swap(ok). + │ ^^^^^^^^ swap('ok'). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:308:19 + │ +308 │ reachable_3(_) -> b. + │ ^ 'b'. +Expression has type: 'b' +Context expected type: 'a' + +15 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-28.pretty new file mode 100644 index 0000000000..117ed8b349 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded-OTP-28.pretty @@ -0,0 +1,160 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:29:5 + │ +29 │ B. + │ ^ B. +Expression has type: binary() +Context expected type: atom() + +error: ambiguous_lambda (See https://fb.me/eqwalizer_errors#ambiguous_lambda) + ┌─ check/src/overloaded.erl:102:15 + │ +102 │ Res = bar(fun(a) -> a end), + │ ^^^^^^^^^^^^^^^ fun. +Functional expression can match multiple types in fun(('a') -> 'b') | fun(('a') -> 'c') + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:117:15 + │ +117 │ Res = bar(F), + │ ^ + │ │ + │ F. +Expression has type: fun(('a') -> 'z') +Context expected type: fun(('a') -> 'b') | fun(('a') -> 'c') + │ + +Because in the expression's type: + fun(('a') -> + Here the type is: 'z' + 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}. +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 + │ +198 │ overlap_neg_1(X) when is_atom(X) -> 1; + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:203:37 + │ +203 │ overlap_neg_2(X) when is_atom(X) -> 1; + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:204:22 + │ +204 │ overlap_neg_2(_X) -> 1. + │ ^ 1. +Expression has type: number() +Context expected type: {T} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:246:23 + │ +246 │ take_ok_or_any(ok) -> ok; + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: 'error' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:247:22 + │ +247 │ take_ok_or_any(_) -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:259:21 + │ +259 │ use_over(X) -> over(X). + │ ^ + │ │ + │ X. +Expression has type: 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: {} + 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 + │ +261 │ -type invalid() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/overloaded.erl:263:1 + │ +263 │ ╭ -spec use_invalid_neg(a) -> a; +264 │ │ (err) -> invalid(). + │ ╰─────────────────────────────────^ use_invalid_neg/1 references type with invalid definition: invalid/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:269:14 + │ +269 │ _ = swap(""), + │ ^^ + │ │ + │ 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 + │ +290 │ swap(ok). + │ ^^^^^^^^ swap('ok'). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded.erl:308:19 + │ +308 │ reachable_3(_) -> b. + │ ^ 'b'. +Expression has type: 'b' +Context expected type: 'a' + +15 ERRORS 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-OTP-26.pretty similarity index 74% rename from crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union-OTP-26.pretty index 6687ec3321..3483635724 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-OTP-26.pretty @@ -14,6 +14,12 @@ 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 │ @@ -30,4 +36,12 @@ 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/overloaded_specs_union-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union-OTP-27.pretty new file mode 100644 index 0000000000..3483635724 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union-OTP-27.pretty @@ -0,0 +1,47 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded_specs_union.erl:16:29 + │ +16 │ use_over_neg_1(Arg) -> over(Arg). + │ ^^^ + │ │ + │ Arg. +Expression has type: atom() | pid() +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: 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 + │ +19 │ use_over_neg_2(Arg) -> over(Arg). + │ ^^^ + │ │ + │ Arg. +Expression has type: atom() | binary() | pid() +Context expected type: atom() | binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: atom() | binary() + 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/overloaded_specs_union-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union-OTP-28.pretty new file mode 100644 index 0000000000..3483635724 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/overloaded_specs_union-OTP-28.pretty @@ -0,0 +1,47 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/overloaded_specs_union.erl:16:29 + │ +16 │ use_over_neg_1(Arg) -> over(Arg). + │ ^^^ + │ │ + │ Arg. +Expression has type: atom() | pid() +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: 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 + │ +19 │ use_over_neg_2(Arg) -> over(Arg). + │ ^^^ + │ │ + │ Arg. +Expression has type: atom() | binary() | pid() +Context expected type: atom() | binary() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: atom() | binary() + 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/parametricity.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/parametricity.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-27.pretty new file mode 100644 index 0000000000..371fb3af3c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-27.pretty @@ -0,0 +1,57 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:178:19 + │ +178 │ refine01(X, X) -> X. + │ ^ X. +Expression has type: _B +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:186:22 + │ +186 │ [Elem || Elem <- X]. + │ ^ X. +Expression has type: B +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:218:3 + │ +218 │ select(T, <<>>, 0). + │ ^^^^^^^^^^^^^^^^^^ select(T, <<..>>, 0). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:218:3 + │ +218 │ select(T, <<>>, 0). + │ ^^^^^^^^^^^^^^^^^^ select(T, <<..>>, 0). +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:224:3 + │ +224 │ select(T, 0, 0). + │ ^^^^^^^^^^^^^^^ select(T, 0, 0). +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:229:31 + │ +229 │ unwrap(undefined, Default) -> Default; + │ ^^^^^^^ Default. +Expression has type: term() +Context expected type: Value + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:241:22 + │ +241 │ (A) -> F(A) + │ ^ A. +Expression has type: term() +Context expected type: A + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-28.pretty new file mode 100644 index 0000000000..371fb3af3c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/parametricity-OTP-28.pretty @@ -0,0 +1,57 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:178:19 + │ +178 │ refine01(X, X) -> X. + │ ^ X. +Expression has type: _B +Context expected type: A + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:186:22 + │ +186 │ [Elem || Elem <- X]. + │ ^ X. +Expression has type: B +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:218:3 + │ +218 │ select(T, <<>>, 0). + │ ^^^^^^^^^^^^^^^^^^ select(T, <<..>>, 0). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:218:3 + │ +218 │ select(T, <<>>, 0). + │ ^^^^^^^^^^^^^^^^^^ select(T, <<..>>, 0). +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:224:3 + │ +224 │ select(T, 0, 0). + │ ^^^^^^^^^^^^^^^ select(T, 0, 0). +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:229:31 + │ +229 │ unwrap(undefined, Default) -> Default; + │ ^^^^^^^ Default. +Expression has type: term() +Context expected type: Value + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/parametricity.erl:241:22 + │ +241 │ (A) -> F(A) + │ ^ A. +Expression has type: term() +Context expected type: A + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-26.pretty similarity index 72% rename from crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-26.pretty index 171c12dc46..d0d4f19c1a 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/pats.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-26.pretty @@ -14,4 +14,10 @@ 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/pats-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-27.pretty new file mode 100644 index 0000000000..d0d4f19c1a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-27.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/pats.erl:26:17 + │ +26 │ maps:get(a, M). + │ ^ + │ │ + │ M. +Expression has type: #{a => 'ok'} | 'error' +Context expected type: #{term() => term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #{a => 'ok'} + 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/pats-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-28.pretty new file mode 100644 index 0000000000..d0d4f19c1a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pats-OTP-28.pretty @@ -0,0 +1,23 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/pats.erl:26:17 + │ +26 │ maps:get(a, M). + │ ^ + │ │ + │ M. +Expression has type: #{a => 'ok'} | 'error' +Context expected type: #{term() => term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #{a => 'ok'} + 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/pinned-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pinned-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pos.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/pos.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-27.pretty new file mode 100644 index 0000000000..4b7dcf8b26 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-27.pretty @@ -0,0 +1,8 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/pos.erl:15:1 + │ +15 │ ╭ atom_pat(1) -> +16 │ │ ok. + │ ╰──────^ Clause is not covered by spec + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-28.pretty new file mode 100644 index 0000000000..4b7dcf8b26 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/pos-OTP-28.pretty @@ -0,0 +1,8 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/pos.erl:15:1 + │ +15 │ ╭ atom_pat(1) -> +16 │ │ ok. + │ ╰──────^ Clause is not covered by spec + +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-OTP-26.pretty similarity index 79% rename from crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-26.pretty index f59eda6cd2..77bb4cc37d 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/records.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-26.pretty @@ -18,9 +18,16 @@ 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 @@ -101,6 +108,13 @@ 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 │ @@ -167,6 +181,14 @@ 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 │ @@ -184,6 +206,12 @@ 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 │ @@ -195,10 +223,9 @@ Expression has type: 'a' Context expected type: number() | boolean() │ -Because in the expression's type: - Here the type is: 'a' - Context expects type: number() | 'false' | 'true' - No candidate matches in the expected union. + '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 @@ -211,10 +238,9 @@ Expression has type: 'a' Context expected type: number() | boolean() │ -Because in the expression's type: - Here the type is: 'a' - Context expects type: number() | 'false' | 'true' - No candidate matches in the expected union. + '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 @@ -233,6 +259,14 @@ 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 │ @@ -244,10 +278,9 @@ Expression has type: 'a' Context expected type: number() | boolean() │ -Because in the expression's type: - Here the type is: 'a' - Context expects type: number() | 'false' | 'true' - No candidate matches in the expected union. + '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 @@ -266,6 +299,14 @@ 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 │ @@ -281,6 +322,12 @@ 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 │ @@ -311,10 +358,19 @@ Context expected type: #int_bool_box{} Because in the expression's type: { 'int_bool_box', Here the type is: 'a' - Context expects type: number() | 'false' | 'true' + Context expects type: number() | boolean() 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 │ @@ -332,6 +388,15 @@ 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 │ @@ -345,9 +410,15 @@ Context expected type: 'true' Because in the expression's type: Here the type is a union type with some valid candidates: 'true' - However the following candidate: 'false' + However the following candidate: boolean() Differs from the expected type: 'true' +------------------------------ Detailed message ------------------------------ + + boolean() | 'true' is not compatible with 'true' + because + boolean() is not compatible with 'true' + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:355:13 │ @@ -371,6 +442,12 @@ 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 │ @@ -388,6 +465,13 @@ 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 │ @@ -405,6 +489,12 @@ 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 │ @@ -483,6 +573,12 @@ 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 │ @@ -501,25 +597,46 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ check/src/records.erl:536:5 │ 536 │ Id. - │ ^^ Id. -Expression has type: number() + │ ^^ + │ │ + │ Id. +Expression has type: number() | none() Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: number() + Context expects type: atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:545:5 │ 545 │ Rec#two_ref.a. - │ ^^^^^^^^^^^^^ ...#two_ref.a. -Expression has type: term() + │ ^^^^^^^^^^^^^ + │ │ + │ ...#two_ref.a. +Expression has type: term() | none() Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:551:5 │ 551 │ A. - │ ^ A. -Expression has type: term() + │ ^ + │ │ + │ A. +Expression has type: term() | none() Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ check/src/records.erl:557:5 diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-27.pretty new file mode 100644 index 0000000000..77bb4cc37d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-27.pretty @@ -0,0 +1,649 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:30:18 + │ +30 │ mk_rec1_neg() -> #rec2{}. + │ ^^^^^^^ #rec2{...}. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:40:5 + │ +40 │ R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:50:21 + │ +50 │ mk_rec_neg(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 + │ +59 │ Foo = #foo{name = bar}, + │ ^^^^^^^^^^^^^^^^ #foo{...}: id is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:73:5 + │ +73 │ Foo#foo_def{name = default, id = 0}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...#foo_def{...}. +Expression has type: #foo_def{} +Context expected type: #foo{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:73:5 + │ +73 │ Foo#foo_def{name = default, id = 0}. + │ ^^^ Foo. +Expression has type: #foo{} +Context expected type: #foo_def{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:86:5 + │ +86 │ #foo.name. + │ ^^^^^^^^^ #foo.name. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:91:5 + │ +91 │ Index. + │ ^^^^^ Index. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:104:5 + │ +104 │ Foo#foo.name. + │ ^^^^^^^^^^^^ ...#foo.name. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:109:5 + │ +109 │ Id. + │ ^^ Id. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:117:35 + │ +117 │ rec_index_pat_neg(#foo.id = I) -> I. + │ ^ I. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:127:5 + │ +127 │ {N, I}. + │ ^^^^^^ + │ │ + │ {N, I}. +Expression has type: {atom(), number()} +Context expected type: {number(), atom()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +144 │ when Foo == #foo{} -> ok. + │ ^^^^^^ #foo{...}: id is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:154:37 + │ +154 │ when Foo == #foo_def{id = X} -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:167:5 + │ +167 │ (X#rec_b.field)#rec_a.field. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...#rec_a.field. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:174:23 + │ +174 │ #iodata_box{iod = 's'}. + │ ^^^ 's'. +Expression has type: 's' +Context expected type: iodata() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:189:35 + │ +189 │ #mrec{id1 = 1, name1 = n, _ = ok}. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/records.erl:200:1 + │ +200 │ ╭ field_gen_pat_neg( +201 │ │ #mrec{id1 = 1, name1 = n, _ = A} +202 │ │ ) -> +203 │ │ % is both id2 (integer) +204 │ │ % and name2 (atom) +205 │ │ A. + │ ╰─────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:217:5 + │ +217 │ #any_box{inner = ok}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #any_box{...}. +Expression has type: #any_box{inner :: 'ok'} +Context expected type: int_box() + │ + +Because in the expression's type: + #any_box{inner :: + Here the type is: 'ok' + 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 + │ +222 │ #any_box{inner = ok}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #any_box{...}. +Expression has type: #any_box{inner :: 'ok'} +Context expected type: #any_box{inner :: number()} + │ + +Because in the expression's type: + #any_box{inner :: + Here the type is: 'ok' + 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 + │ +282 │ #int_bool_box{inner = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +287 │ #int_bool_box{_ = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +292 │ #int_bool_box{}. + │ ^^^^^^^^^^^^^^^ + │ │ + │ #int_bool_box{...}. +Expression has type: #int_bool_box{inner :: 'true'} +Context expected type: only_int_box() + │ + +Because in the expression's type: + #int_bool_box{inner :: + Here the type is: 'true' + 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 + │ +308 │ B#int_bool_box{inner = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +314 │ B#int_bool_box{inner = 4}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ...#int_bool_box{...}. +Expression has type: #int_bool_box{inner :: number()} +Context expected type: only_bool_box() + │ + +Because in the expression's type: + #int_bool_box{inner :: + Here the type is: number() + 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 + │ +319 │ R#any_box.inner. + │ ^ + │ │ + │ R. +Expression has type: only_int_box() +Context expected type: #any_box{} + │ + +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 + │ +319 │ R#any_box.inner. + │ ^^^^^^^^^^^^^^^ ...#any_box.inner. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:324:5 + │ +324 │ R#int_bool_box.inner. + │ ^^^^^^^^^^^^^^^^^^^^ ...#int_bool_box.inner. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:329:5 + │ +329 │ {int_bool_box, a}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ {'int_bool_box', 'a'}. +Expression has type: {'int_bool_box', 'a'} +Context expected type: #int_bool_box{} + │ + +Because in the expression's type: + { 'int_bool_box', + Here the type is: 'a' + Context expects type: number() | boolean() + 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 + │ +334 │ {int_bool_box, a}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ {'int_bool_box', 'a'}. +Expression has type: {'int_bool_box', 'a'} +Context expected type: only_int_box() + │ + +Because in the expression's type: + { 'int_bool_box', + Here the type is: 'a' + 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 + │ +352 │ R#int_bool_box.inner. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ...#int_bool_box.inner. +Expression has type: boolean() | 'true' +Context expected type: 'true' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'true' + However the following candidate: boolean() + Differs from the expected type: 'true' + +------------------------------ Detailed message ------------------------------ + + boolean() | 'true' is not compatible with 'true' + because + boolean() is not compatible with 'true' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:355:13 + │ +355 │ inner = true :: integer() + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:366:5 + │ +366 │ #bad_default{inner = 42}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #bad_default{...}. +Expression has type: #bad_default{} +Context expected type: only_int_box() + │ + +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 + │ +372 │ R#bad_default.inner. + │ ^ + │ │ + │ R. +Expression has type: {'int_bool_box', number()} +Context expected type: #bad_default{} + │ + +Because in the expression's type: + { + Here the type is: 'int_bool_box' + 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 + │ +388 │ test_subtype_refine_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: #refined_two_fields{} +Context expected type: #refined_two_fields{inner :: number()} + │ + +Because in the expression's type: + #refined_two_fields{inner :: + Here the type is: term() + 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 + │ +402 │ lists:nth(1, L) * 3. + │ ^^^^^^^^^^^^^^^ lists:nth(1, L). +Expression has type: atom() +Context expected type: number() + +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ check/src/records.erl:412:14 + │ +412 │ field :: _T + │ ^^ _T: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/records.erl:415:1 + │ +415 │ ╭ -spec test_recurd2_neg(#recurd2{}) -> +416 │ │ #recurd2{}. + │ ╰──────────────^ test_recurd2_neg/1 references type with invalid definition: #recurd2{} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/records.erl:420:1 + │ +420 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/records.erl:421:1 + │ +421 │ -record(invalid, {field :: loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:425:5 + │ +425 │ X#invalid.field. + │ ^ X. +Expression has type: term() +Context expected type: #invalid{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:429:9 + │ +429 │ _ = X#invalid.field, + │ ^ X. +Expression has type: term() +Context expected type: #invalid{} + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/records.erl:433:1 + │ +433 │ ╭ unbound_update_neg(X) -> +434 │ │ X#invalid{field = 2}. + │ ╰────────────────────────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/records.erl:445:27 + │ +445 │ eqwalizer:reveal_type(Field2), + │ ^^^^^^ dynamic() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:473:20 + │ +473 │ binary_to_atom(B); + │ ^ + │ │ + │ B. +Expression has type: 'my_record' | binary() +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: '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 + │ +517 │ narrow3({X, X}) -> X. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:523:24 + │ +523 │ narrow5_neg({X, X}) -> X. + │ ^ X. +Expression has type: #two_ref{b :: boolean(), a :: atom()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:536:5 + │ +536 │ Id. + │ ^^ + │ │ + │ Id. +Expression has type: number() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: number() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:545:5 + │ +545 │ Rec#two_ref.a. + │ ^^^^^^^^^^^^^ + │ │ + │ ...#two_ref.a. +Expression has type: term() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:551:5 + │ +551 │ A. + │ ^ + │ │ + │ A. +Expression has type: term() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:557:5 + │ +557 │ I#{a => b}. + │ ^^^^^^^^^^ ..#{..}. +Expression has type: #{a := 'b', dynamic() => dynamic()} +Context expected type: 'ok' + +51 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-28.pretty new file mode 100644 index 0000000000..77bb4cc37d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/records-OTP-28.pretty @@ -0,0 +1,649 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:30:18 + │ +30 │ mk_rec1_neg() -> #rec2{}. + │ ^^^^^^^ #rec2{...}. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:40:5 + │ +40 │ R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:50:21 + │ +50 │ mk_rec_neg(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 + │ +59 │ Foo = #foo{name = bar}, + │ ^^^^^^^^^^^^^^^^ #foo{...}: id is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:73:5 + │ +73 │ Foo#foo_def{name = default, id = 0}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...#foo_def{...}. +Expression has type: #foo_def{} +Context expected type: #foo{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:73:5 + │ +73 │ Foo#foo_def{name = default, id = 0}. + │ ^^^ Foo. +Expression has type: #foo{} +Context expected type: #foo_def{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:86:5 + │ +86 │ #foo.name. + │ ^^^^^^^^^ #foo.name. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:91:5 + │ +91 │ Index. + │ ^^^^^ Index. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:104:5 + │ +104 │ Foo#foo.name. + │ ^^^^^^^^^^^^ ...#foo.name. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:109:5 + │ +109 │ Id. + │ ^^ Id. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:117:35 + │ +117 │ rec_index_pat_neg(#foo.id = I) -> I. + │ ^ I. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:127:5 + │ +127 │ {N, I}. + │ ^^^^^^ + │ │ + │ {N, I}. +Expression has type: {atom(), number()} +Context expected type: {number(), atom()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +144 │ when Foo == #foo{} -> ok. + │ ^^^^^^ #foo{...}: id is 'undefined' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:154:37 + │ +154 │ when Foo == #foo_def{id = X} -> X. + │ ^ X. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:167:5 + │ +167 │ (X#rec_b.field)#rec_a.field. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...#rec_a.field. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:174:23 + │ +174 │ #iodata_box{iod = 's'}. + │ ^^^ 's'. +Expression has type: 's' +Context expected type: iodata() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:189:35 + │ +189 │ #mrec{id1 = 1, name1 = n, _ = ok}. + │ ^^ 'ok'. +Expression has type: 'ok' +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/records.erl:200:1 + │ +200 │ ╭ field_gen_pat_neg( +201 │ │ #mrec{id1 = 1, name1 = n, _ = A} +202 │ │ ) -> +203 │ │ % is both id2 (integer) +204 │ │ % and name2 (atom) +205 │ │ A. + │ ╰─────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:217:5 + │ +217 │ #any_box{inner = ok}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #any_box{...}. +Expression has type: #any_box{inner :: 'ok'} +Context expected type: int_box() + │ + +Because in the expression's type: + #any_box{inner :: + Here the type is: 'ok' + 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 + │ +222 │ #any_box{inner = ok}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #any_box{...}. +Expression has type: #any_box{inner :: 'ok'} +Context expected type: #any_box{inner :: number()} + │ + +Because in the expression's type: + #any_box{inner :: + Here the type is: 'ok' + 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 + │ +282 │ #int_bool_box{inner = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +287 │ #int_bool_box{_ = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +292 │ #int_bool_box{}. + │ ^^^^^^^^^^^^^^^ + │ │ + │ #int_bool_box{...}. +Expression has type: #int_bool_box{inner :: 'true'} +Context expected type: only_int_box() + │ + +Because in the expression's type: + #int_bool_box{inner :: + Here the type is: 'true' + 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 + │ +308 │ B#int_bool_box{inner = a}. + │ ^ + │ │ + │ 'a'. +Expression has type: 'a' +Context expected type: number() | boolean() + │ + + '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 + │ +314 │ B#int_bool_box{inner = 4}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ...#int_bool_box{...}. +Expression has type: #int_bool_box{inner :: number()} +Context expected type: only_bool_box() + │ + +Because in the expression's type: + #int_bool_box{inner :: + Here the type is: number() + 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 + │ +319 │ R#any_box.inner. + │ ^ + │ │ + │ R. +Expression has type: only_int_box() +Context expected type: #any_box{} + │ + +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 + │ +319 │ R#any_box.inner. + │ ^^^^^^^^^^^^^^^ ...#any_box.inner. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:324:5 + │ +324 │ R#int_bool_box.inner. + │ ^^^^^^^^^^^^^^^^^^^^ ...#int_bool_box.inner. +Expression has type: number() +Context expected type: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:329:5 + │ +329 │ {int_bool_box, a}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ {'int_bool_box', 'a'}. +Expression has type: {'int_bool_box', 'a'} +Context expected type: #int_bool_box{} + │ + +Because in the expression's type: + { 'int_bool_box', + Here the type is: 'a' + Context expects type: number() | boolean() + 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 + │ +334 │ {int_bool_box, a}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ {'int_bool_box', 'a'}. +Expression has type: {'int_bool_box', 'a'} +Context expected type: only_int_box() + │ + +Because in the expression's type: + { 'int_bool_box', + Here the type is: 'a' + 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 + │ +352 │ R#int_bool_box.inner. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ...#int_bool_box.inner. +Expression has type: boolean() | 'true' +Context expected type: 'true' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'true' + However the following candidate: boolean() + Differs from the expected type: 'true' + +------------------------------ Detailed message ------------------------------ + + boolean() | 'true' is not compatible with 'true' + because + boolean() is not compatible with 'true' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:355:13 + │ +355 │ inner = true :: integer() + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:366:5 + │ +366 │ #bad_default{inner = 42}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #bad_default{...}. +Expression has type: #bad_default{} +Context expected type: only_int_box() + │ + +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 + │ +372 │ R#bad_default.inner. + │ ^ + │ │ + │ R. +Expression has type: {'int_bool_box', number()} +Context expected type: #bad_default{} + │ + +Because in the expression's type: + { + Here the type is: 'int_bool_box' + 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 + │ +388 │ test_subtype_refine_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: #refined_two_fields{} +Context expected type: #refined_two_fields{inner :: number()} + │ + +Because in the expression's type: + #refined_two_fields{inner :: + Here the type is: term() + 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 + │ +402 │ lists:nth(1, L) * 3. + │ ^^^^^^^^^^^^^^^ lists:nth(1, L). +Expression has type: atom() +Context expected type: number() + +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ check/src/records.erl:412:14 + │ +412 │ field :: _T + │ ^^ _T: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/records.erl:415:1 + │ +415 │ ╭ -spec test_recurd2_neg(#recurd2{}) -> +416 │ │ #recurd2{}. + │ ╰──────────────^ test_recurd2_neg/1 references type with invalid definition: #recurd2{} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/records.erl:420:1 + │ +420 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/records.erl:421:1 + │ +421 │ -record(invalid, {field :: loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:425:5 + │ +425 │ X#invalid.field. + │ ^ X. +Expression has type: term() +Context expected type: #invalid{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:429:9 + │ +429 │ _ = X#invalid.field, + │ ^ X. +Expression has type: term() +Context expected type: #invalid{} + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/records.erl:433:1 + │ +433 │ ╭ unbound_update_neg(X) -> +434 │ │ X#invalid{field = 2}. + │ ╰────────────────────────^ Clause is not covered by spec + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/records.erl:445:27 + │ +445 │ eqwalizer:reveal_type(Field2), + │ ^^^^^^ dynamic() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:473:20 + │ +473 │ binary_to_atom(B); + │ ^ + │ │ + │ B. +Expression has type: 'my_record' | binary() +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: '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 + │ +517 │ narrow3({X, X}) -> X. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:523:24 + │ +523 │ narrow5_neg({X, X}) -> X. + │ ^ X. +Expression has type: #two_ref{b :: boolean(), a :: atom()} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:536:5 + │ +536 │ Id. + │ ^^ + │ │ + │ Id. +Expression has type: number() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: number() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:545:5 + │ +545 │ Rec#two_ref.a. + │ ^^^^^^^^^^^^^ + │ │ + │ ...#two_ref.a. +Expression has type: term() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:551:5 + │ +551 │ A. + │ ^ + │ │ + │ A. +Expression has type: term() | none() +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is: term() + Context expects type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/records.erl:557:5 + │ +557 │ I#{a => b}. + │ ^^^^^^^^^^ ..#{..}. +Expression has type: #{a := 'b', dynamic() => dynamic()} +Context expected type: 'ok' + +51 ERRORS 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-OTP-26.pretty similarity index 83% rename from crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-26.pretty index ffd83bc179..87d235e56c 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-26.pretty @@ -17,6 +17,16 @@ 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 │ @@ -36,6 +46,16 @@ 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 │ @@ -55,6 +75,16 @@ 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 │ @@ -74,6 +104,16 @@ 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 │ @@ -103,6 +143,17 @@ 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 │ @@ -115,11 +166,19 @@ Context expected type: mChainA() │ Because in the expression's type: - The type is a union type with some valid candidates: 'nil' - However, the following candidate doesn't match: - Here the type is: #{...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. + Here the type is a union type with some valid candidates: 'nil' + However the following candidate: #{b := mChainB()} + Differs from the expected type: 'nil' | #{a := mChainA()} + +------------------------------ 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 @@ -149,6 +208,16 @@ 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/recursive_aliases-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-27.pretty new file mode 100644 index 0000000000..87d235e56c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-27.pretty @@ -0,0 +1,441 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:59:20 + │ +59 │ subtype3_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainA() +Context expected type: chainB() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { + Here the type is: 'a' + 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 + │ +62 │ subtype4_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainB() +Context expected type: chainA() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { + Here the type is: 'b' + 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 + │ +65 │ subtype5_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainAB() +Context expected type: chainA() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' | {'a', chainAB()} + However, the following candidate doesn't match: + { + Here the type is: 'b' + 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 + │ +68 │ subtype6_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainAB() +Context expected type: chainB() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' | {'b', chainAB()} + However, the following candidate doesn't match: + { + Here the type is: 'a' + 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 + │ +70 │ -type non_prod() :: non_prod(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type non_prod/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:72:1 + │ +72 │ ╭ -spec non_prod_id(non_prod()) -> +73 │ │ non_prod() | number(). + │ ╰───────────────────────^ non_prod_id/1 references type with invalid definition: non_prod/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:81:22 + │ +81 │ subtype_p1_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: pchainA(atom()) +Context expected type: pchainAB(atom()) + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'nil' + 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 + │ +113 │ 1 + m_chainA_size(Chain). + │ ^^^^^ + │ │ + │ Chain. +Expression has type: mChainB() +Context expected type: mChainA() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'nil' + However the following candidate: #{b := mChainB()} + Differs from the expected type: 'nil' | #{a := mChainA()} + +------------------------------ 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 + │ +138 │ fun_fan(F) -> F(). + │ ^ F. +Expected fun type with arity 0 +Got: fan() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:187:21 + │ +187 │ subtype_6_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: pchainA('a') +Context expected type: pchainA('b' | 'c') + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { 'a', + Here the type is: 'a' + Context expects type: 'b' | 'c' + 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 + │ +202 │ -type mu_bad() :: mu_bad() | {a}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:204:1 + │ +204 │ ╭ -spec to_mu_bad_neg() -> +205 │ │ mu_bad(). + │ ╰────────^ to_mu_bad_neg/0 references type with invalid definition: mu_bad/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:208:1 + │ +208 │ ╭ -spec test_mu_bad_neg(mu_bad()) -> +209 │ │ pid(). + │ ╰─────^ test_mu_bad_neg/1 references type with invalid definition: mu_bad/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:212:1 + │ +212 │ -type mu(A) :: {mu, mu(mu(A))}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu/1 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:214:1 + │ +214 │ -spec test2_neg(mu(A)) -> mu(mu(A)). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ test2_neg/1 references type with invalid definition: mu/1 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:223:23 + │ +223 │ test4_neg({mu, M}) -> M. + │ ^ M. +Expression has type: atom() | mu2() +Context expected type: pid() + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:296:1 + │ +296 │ -type mu_bad2() :: id(mu_bad2()). + │ ^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad2/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:297:1 + │ +297 │ -type mu_bad2b() :: id(mu_bad2()). + │ ^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad2b/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:299:1 + │ +299 │ -type mu_bad3() :: id(mu_bad3()). + │ ^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad3/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:301:1 + │ +301 │ ╭ -spec to_mu_bad3_neg(mu_bad2()) -> +302 │ │ mu_bad3(). + │ ╰───────────^ to_mu_bad3_neg/1 references types with invalid definitions: mu_bad2/0, mu_bad3/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:305:1 + │ +305 │ ╭ -spec to_mu_bad2b_neg(mu_bad2()) -> +306 │ │ mu_bad2b(). + │ ╰──────────^ to_mu_bad2b_neg/1 references types with invalid definitions: mu_bad2/0, mu_bad2b/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:309:1 + │ +309 │ -type indirect2() :: mutual2(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type indirect2/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:310:1 + │ +310 │ -type indirect1() :: mutual1(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type indirect1/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:312:1 + │ +312 │ ╭ -type mutual1() :: indirect1() +313 │ │ | indirect2(). + │ ╰─────────────^ recursive type mutual1/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:314:1 + │ +314 │ ╭ -type mutual2() :: indirect1() +315 │ │ | indirect2(). + │ ╰─────────────^ recursive type mutual2/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:317:1 + │ +317 │ ╭ -spec test_mutual_neg(mutual1()) -> +318 │ │ mutual2(). + │ ╰───────────^ test_mutual_neg/1 references types with invalid definitions: mutual1/0, mutual2/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:342:1 + │ +342 │ -type mu_bad5() :: opaque_id(mu_bad5()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad5/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:343:1 + │ +343 │ -type mu_bad5b() :: opaque_id(mu_bad5()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad5b/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:345:1 + │ +345 │ ╭ -spec test_mu_bad5( +346 │ │ mu_bad5() +347 │ │ ) -> mu_bad5b(). + │ ╰─────────────^ test_mu_bad5/1 references types with invalid definitions: mu_bad5/0, mu_bad5b/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:392:1 + │ +392 │ -type recur() :: foo(recur()) | a. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type recur/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:395:1 + │ +395 │ -spec un_recur_neg(recur()) -> ok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ un_recur_neg/1 references type with invalid definition: recur/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:399:1 + │ +399 │ ╭ -spec term_to_recur_neg(term()) -> +400 │ │ recur(). + │ ╰───────^ term_to_recur_neg/1 references type with invalid definition: recur/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:409:1 + │ +409 │ ╭ -type foo7_neg(T) :: +410 │ │ {unit, foo7_neg(foo7_neg(T))}. + │ ╰───────────────────────────────^ recursive type foo7_neg/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:441:1 + │ +441 │ -type x_cycle() :: misc:x_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type x_cycle/0 is not productive + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/recursive_aliases.erl:460:1 + │ +460 │ trick_me(X, X) -> X. + │ ^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:477:1 + │ +477 │ ╭ -type t_tree2(Z) :: +478 │ │ {leaf, Z} | +479 │ │ {node, t_tree2(simple_id(Z))}. + │ ╰───────────────────────────────^ recursive type t_tree2/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:481:1 + │ +481 │ ╭ -type t_tree3(Z) :: +482 │ │ {leaf, Z} | +483 │ │ {node, t_tree3(t_tree3(Z))}. + │ ╰─────────────────────────────^ recursive type t_tree3/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:493:1 + │ +493 │ ╭ -type stream2(Z) :: +494 │ │ {chunk, stream2(simple_id(Z))}. + │ ╰────────────────────────────────^ recursive type stream2/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:505:1 + │ +505 │ ╭ -type d_stream(Z) :: +506 │ │ {chunk, d_stream([Z])}. + │ ╰────────────────────────^ recursive type d_stream/1 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/recursive_aliases.erl:508:23 + │ +508 │ -type invalid_ty() :: non_exist:invalid(). + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: non_exist:invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:510:1 + │ +510 │ ╭ -type invalid_rec() :: +511 │ │ invalid_ty() | invalid_transitive(). + │ ╰───────────────────────────────────^ invalid_rec/0 references type with invalid definition: invalid_ty/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:513:1 + │ +513 │ ╭ -type invalid_transitive() :: +514 │ │ {a, invalid_rec()}. + │ ╰────────────────────^ invalid_transitive/0 references type with invalid definition: invalid_rec/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:516:1 + │ +516 │ ╭ -spec use_invalid +517 │ │ (invalid_transitive()) -> a. + │ ╰─────────────────────────────^ use_invalid/1 references type with invalid definition: invalid_transitive/0 + +43 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-28.pretty new file mode 100644 index 0000000000..87d235e56c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/recursive_aliases-OTP-28.pretty @@ -0,0 +1,441 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:59:20 + │ +59 │ subtype3_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainA() +Context expected type: chainB() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { + Here the type is: 'a' + 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 + │ +62 │ subtype4_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainB() +Context expected type: chainA() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { + Here the type is: 'b' + 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 + │ +65 │ subtype5_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainAB() +Context expected type: chainA() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' | {'a', chainAB()} + However, the following candidate doesn't match: + { + Here the type is: 'b' + 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 + │ +68 │ subtype6_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: chainAB() +Context expected type: chainB() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' | {'b', chainAB()} + However, the following candidate doesn't match: + { + Here the type is: 'a' + 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 + │ +70 │ -type non_prod() :: non_prod(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type non_prod/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:72:1 + │ +72 │ ╭ -spec non_prod_id(non_prod()) -> +73 │ │ non_prod() | number(). + │ ╰───────────────────────^ non_prod_id/1 references type with invalid definition: non_prod/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:81:22 + │ +81 │ subtype_p1_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: pchainA(atom()) +Context expected type: pchainAB(atom()) + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'nil' + 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 + │ +113 │ 1 + m_chainA_size(Chain). + │ ^^^^^ + │ │ + │ Chain. +Expression has type: mChainB() +Context expected type: mChainA() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'nil' + However the following candidate: #{b := mChainB()} + Differs from the expected type: 'nil' | #{a := mChainA()} + +------------------------------ 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 + │ +138 │ fun_fan(F) -> F(). + │ ^ F. +Expected fun type with arity 0 +Got: fan() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:187:21 + │ +187 │ subtype_6_neg(C) -> C. + │ ^ + │ │ + │ C. +Expression has type: pchainA('a') +Context expected type: pchainA('b' | 'c') + │ + +Because in the expression's type: + The type is a union type with some valid candidates: 'nil' + However, the following candidate doesn't match: + { 'a', + Here the type is: 'a' + Context expects type: 'b' | 'c' + 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 + │ +202 │ -type mu_bad() :: mu_bad() | {a}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:204:1 + │ +204 │ ╭ -spec to_mu_bad_neg() -> +205 │ │ mu_bad(). + │ ╰────────^ to_mu_bad_neg/0 references type with invalid definition: mu_bad/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:208:1 + │ +208 │ ╭ -spec test_mu_bad_neg(mu_bad()) -> +209 │ │ pid(). + │ ╰─────^ test_mu_bad_neg/1 references type with invalid definition: mu_bad/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:212:1 + │ +212 │ -type mu(A) :: {mu, mu(mu(A))}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu/1 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:214:1 + │ +214 │ -spec test2_neg(mu(A)) -> mu(mu(A)). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ test2_neg/1 references type with invalid definition: mu/1 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/recursive_aliases.erl:223:23 + │ +223 │ test4_neg({mu, M}) -> M. + │ ^ M. +Expression has type: atom() | mu2() +Context expected type: pid() + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:296:1 + │ +296 │ -type mu_bad2() :: id(mu_bad2()). + │ ^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad2/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:297:1 + │ +297 │ -type mu_bad2b() :: id(mu_bad2()). + │ ^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad2b/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:299:1 + │ +299 │ -type mu_bad3() :: id(mu_bad3()). + │ ^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad3/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:301:1 + │ +301 │ ╭ -spec to_mu_bad3_neg(mu_bad2()) -> +302 │ │ mu_bad3(). + │ ╰───────────^ to_mu_bad3_neg/1 references types with invalid definitions: mu_bad2/0, mu_bad3/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:305:1 + │ +305 │ ╭ -spec to_mu_bad2b_neg(mu_bad2()) -> +306 │ │ mu_bad2b(). + │ ╰──────────^ to_mu_bad2b_neg/1 references types with invalid definitions: mu_bad2/0, mu_bad2b/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:309:1 + │ +309 │ -type indirect2() :: mutual2(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type indirect2/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:310:1 + │ +310 │ -type indirect1() :: mutual1(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type indirect1/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:312:1 + │ +312 │ ╭ -type mutual1() :: indirect1() +313 │ │ | indirect2(). + │ ╰─────────────^ recursive type mutual1/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:314:1 + │ +314 │ ╭ -type mutual2() :: indirect1() +315 │ │ | indirect2(). + │ ╰─────────────^ recursive type mutual2/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:317:1 + │ +317 │ ╭ -spec test_mutual_neg(mutual1()) -> +318 │ │ mutual2(). + │ ╰───────────^ test_mutual_neg/1 references types with invalid definitions: mutual1/0, mutual2/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:342:1 + │ +342 │ -type mu_bad5() :: opaque_id(mu_bad5()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad5/0 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:343:1 + │ +343 │ -type mu_bad5b() :: opaque_id(mu_bad5()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type mu_bad5b/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:345:1 + │ +345 │ ╭ -spec test_mu_bad5( +346 │ │ mu_bad5() +347 │ │ ) -> mu_bad5b(). + │ ╰─────────────^ test_mu_bad5/1 references types with invalid definitions: mu_bad5/0, mu_bad5b/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:392:1 + │ +392 │ -type recur() :: foo(recur()) | a. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type recur/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:395:1 + │ +395 │ -spec un_recur_neg(recur()) -> ok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ un_recur_neg/1 references type with invalid definition: recur/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:399:1 + │ +399 │ ╭ -spec term_to_recur_neg(term()) -> +400 │ │ recur(). + │ ╰───────^ term_to_recur_neg/1 references type with invalid definition: recur/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:409:1 + │ +409 │ ╭ -type foo7_neg(T) :: +410 │ │ {unit, foo7_neg(foo7_neg(T))}. + │ ╰───────────────────────────────^ recursive type foo7_neg/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:441:1 + │ +441 │ -type x_cycle() :: misc:x_cycle(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type x_cycle/0 is not productive + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/recursive_aliases.erl:460:1 + │ +460 │ trick_me(X, X) -> X. + │ ^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:477:1 + │ +477 │ ╭ -type t_tree2(Z) :: +478 │ │ {leaf, Z} | +479 │ │ {node, t_tree2(simple_id(Z))}. + │ ╰───────────────────────────────^ recursive type t_tree2/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:481:1 + │ +481 │ ╭ -type t_tree3(Z) :: +482 │ │ {leaf, Z} | +483 │ │ {node, t_tree3(t_tree3(Z))}. + │ ╰─────────────────────────────^ recursive type t_tree3/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:493:1 + │ +493 │ ╭ -type stream2(Z) :: +494 │ │ {chunk, stream2(simple_id(Z))}. + │ ╰────────────────────────────────^ recursive type stream2/1 is not productive + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/recursive_aliases.erl:505:1 + │ +505 │ ╭ -type d_stream(Z) :: +506 │ │ {chunk, d_stream([Z])}. + │ ╰────────────────────────^ recursive type d_stream/1 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/recursive_aliases.erl:508:23 + │ +508 │ -type invalid_ty() :: non_exist:invalid(). + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: non_exist:invalid/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:510:1 + │ +510 │ ╭ -type invalid_rec() :: +511 │ │ invalid_ty() | invalid_transitive(). + │ ╰───────────────────────────────────^ invalid_rec/0 references type with invalid definition: invalid_ty/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:513:1 + │ +513 │ ╭ -type invalid_transitive() :: +514 │ │ {a, invalid_rec()}. + │ ╰────────────────────^ invalid_transitive/0 references type with invalid definition: invalid_rec/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/recursive_aliases.erl:516:1 + │ +516 │ ╭ -spec use_invalid +517 │ │ (invalid_transitive()) -> a. + │ ╰─────────────────────────────^ use_invalid/1 references type with invalid definition: invalid_transitive/0 + +43 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-26.pretty similarity index 61% rename from crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-26.pretty index 711e1dc2e9..20a5b05732 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/refine.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-26.pretty @@ -23,6 +23,13 @@ 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 │ @@ -48,6 +55,12 @@ 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 │ @@ -65,6 +78,12 @@ 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 │ @@ -114,6 +133,13 @@ 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 │ @@ -131,6 +157,13 @@ 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 │ @@ -157,6 +190,14 @@ 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 │ @@ -183,6 +224,14 @@ 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 │ @@ -192,25 +241,84 @@ Expression has type: {'cons', 'nil' | my_list()} Context expected type: number() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:175:31 + ┌─ check/src/refine.erl:177:24 │ -175 │ record_as_tuple1_neg(R, R) -> R. +177 │ opaque1_neg({X, _}) -> X. + │ ^ X. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:183:24 + │ +183 │ opaque2_neg([X, _]) -> X. + │ ^ + │ │ + │ X. +Expression has type: erl_anno:annotation() +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 + Context expects type: pid() + No candidate of the expression's type matches the expected type. + +------------------------------ Detailed message ------------------------------ + + erl_anno:annotation() is not compatible with pid() + because + {'file', erl_anno:filename()} | {'generated', erl_anno:generated()} | {'record', erl_anno:record()} | {'text', string()} | {'location', erl_anno:location()} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:191:7 + │ +191 │ }) -> X. + │ ^ X. +Expression has type: 'set' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:198:23 + │ +198 │ opaque_4_neg(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: gb_sets:set(atom()) +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: {number(), gb_sets:gb_set_node(atom())} + Context expects type: pid() + +------------------------------ Detailed message ------------------------------ + + gb_sets:set(atom()) is not compatible with pid() + because + {number(), gb_sets:gb_set_node(atom())} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:202:31 + │ +202 │ record_as_tuple1_neg(R, R) -> R. │ ^ R. Expression has type: #my_rec{} Context expected type: none() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:179:31 + ┌─ check/src/refine.erl:206:31 │ -179 │ record_as_tuple2_neg(R, R) -> R. +206 │ record_as_tuple2_neg(R, R) -> R. │ ^ R. Expression has type: #my_rec{} Context expected type: none() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:195:28 + ┌─ check/src/refine.erl:222:28 │ -195 │ record_as_tuple6_neg(R) -> R. +222 │ record_as_tuple6_neg(R) -> R. │ ^ │ │ │ R. @@ -224,23 +332,30 @@ 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 + ┌─ check/src/refine.erl:226:10 │ -199 │ i :: unknown:unknown() +226 │ i :: unknown:unknown() │ ^^^^^^^^^^^^^^^^^ Unknown id: unknown:unknown/0 error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) - ┌─ check/src/refine.erl:201:1 + ┌─ check/src/refine.erl:228:1 │ -201 │ ╭ -spec record_as_tuple7_neg -202 │ │ (#bad_rec{}) -> {bad_rec, atom()}. +228 │ ╭ -spec record_as_tuple7_neg +229 │ │ (#bad_rec{}) -> {bad_rec, atom()}. │ ╰─────────────────────────────────^ record_as_tuple7_neg/1 references type with invalid definition: #bad_rec{} error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:213:28 + ┌─ check/src/refine.erl:240:28 │ -213 │ tuple_as_record2_neg(R) -> R. +240 │ tuple_as_record2_neg(R) -> R. │ ^ │ │ │ R. @@ -254,50 +369,57 @@ 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 + ┌─ check/src/refine.erl:245:1 │ -218 │ ╭ tup_guard_record1(X) -219 │ │ when is_record(X, my_rec) -> -220 │ │ X * 2. +245 │ ╭ tup_guard_record1(X) +246 │ │ when is_record(X, my_rec) -> +247 │ │ X * 2. │ ╰─────────^ Clause is not covered by spec error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:226:5 + ┌─ check/src/refine.erl:253:5 │ -226 │ X * 2. +253 │ X * 2. │ ^ X. Expression has type: #my_rec{} Context expected type: number() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:232:5 + ┌─ check/src/refine.erl:259:5 │ -232 │ X * 2. +259 │ X * 2. │ ^ X. Expression has type: #my_rec{} Context expected type: number() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:235:14 + ┌─ check/src/refine.erl:262:14 │ -235 │ ftt(X, _) -> X. +262 │ ftt(X, _) -> X. │ ^ X. Expression has type: fun((T) -> 'ok') Context expected type: T error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:238:20 + ┌─ check/src/refine.erl:265:20 │ -238 │ my_rec_to_ok(X) -> X. +265 │ my_rec_to_ok(X) -> X. │ ^ X. Expression has type: #my_rec{} Context expected type: 'ok' error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:250:26 + ┌─ check/src/refine.erl:277:26 │ -250 │ use2_neg(F, X) -> ftt(F, X). +277 │ use2_neg(F, X) -> ftt(F, X). │ ^ │ │ │ X. @@ -311,10 +433,17 @@ 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 + ┌─ check/src/refine.erl:277:26 │ -250 │ use2_neg(F, X) -> ftt(F, X). +277 │ use2_neg(F, X) -> ftt(F, X). │ ^ │ │ │ X. @@ -328,10 +457,19 @@ 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 + ┌─ check/src/refine.erl:289:26 │ -262 │ use4_neg(F, X) -> ftt(F, X). +289 │ use4_neg(F, X) -> ftt(F, X). │ ^ │ │ │ X. @@ -345,10 +483,17 @@ 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 + ┌─ check/src/refine.erl:289:26 │ -262 │ use4_neg(F, X) -> ftt(F, X). +289 │ use4_neg(F, X) -> ftt(F, X). │ ^ │ │ │ X. @@ -362,10 +507,19 @@ 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 + ┌─ check/src/refine.erl:305:21 │ -278 │ use6_neg(X) -> ttt2(X, x). +305 │ use6_neg(X) -> ttt2(X, x). │ ^ │ │ │ X. @@ -379,10 +533,17 @@ 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 + ┌─ check/src/refine.erl:309:16 │ -282 │ use7_neg(X) -> ttt1(X, 1). +309 │ use7_neg(X) -> ttt1(X, 1). │ ^^^^^^^^^^ │ │ │ ttt1(X, 1). @@ -395,10 +556,16 @@ 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 + ┌─ check/src/refine.erl:313:13 │ -286 │ deets(X) -> X. +313 │ deets(X) -> X. │ ^ │ │ │ X. @@ -411,10 +578,16 @@ 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 + ┌─ check/src/refine.erl:323:5 │ -296 │ B. +323 │ B. │ ^ │ │ │ B. @@ -427,18 +600,24 @@ 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 + ┌─ check/src/refine.erl:405:27 │ -378 │ eqwalizer:reveal_type(Name), +405 │ eqwalizer:reveal_type(Name), │ ^^^^ atom() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/refine.erl:379:5 + ┌─ check/src/refine.erl:406:5 │ -379 │ Name. +406 │ Name. │ ^^^^ Name. Expression has type: atom() Context expected type: 'ok' -37 ERRORS +41 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-27.pretty new file mode 100644 index 0000000000..20a5b05732 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-27.pretty @@ -0,0 +1,623 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:12:17 + │ +12 │ param1(X, X) -> X. + │ ^ X. +Expression has type: _B +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:16:17 + │ +16 │ param2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: {B, A} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: B + 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 + │ +22 │ maps(M, M) -> M. + │ ^ M. +Expression has type: #{a := atom()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:28:16 + │ +28 │ funs1(F, F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((B | A) -> A) +Context expected type: fun((A | B) -> none()) + │ + +Because in the expression's type: + fun((B | A) -> + Here the type is: A + 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 + │ +34 │ funs2(F, F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((atom() | A) -> A) +Context expected type: fun((A | atom()) -> none()) + │ + +Because in the expression's type: + fun((atom() | A) -> + Here the type is: A + 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 + │ +84 │ list_trick_neg([X], _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:92:28 + │ +92 │ tuple_trick_neg({X}, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:101:8 + │ +101 │ -> X. + │ ^ X. +Expression has type: _T +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:112:24 + │ +112 │ (#{key := Val}) -> Val. + │ ^^^ Val. +Expression has type: term() +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:117:26 + │ +117 │ (<>) -> {X, Y}. + │ ^^^^^^ + │ │ + │ {X, Y}. +Expression has type: {number(), binary()} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +126 │ (#my_rec{n = N, a = A}) -> {N, A}. + │ ^^^^^^ + │ │ + │ {N, A}. +Expression has type: {number(), atom()} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +139 │ refine_alist1_neg(X, X) -> X. + │ ^ X. +Expression has type: ['a' | 'b'] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:144:24 + │ +144 │ refine_alist2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: ['a' | 'b'] +Context expected type: ['a'] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +151 │ refine_tlist1_neg(X, X) -> X. + │ ^ X. +Expression has type: ['a' | 'b'] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:156:24 + │ +156 │ refine_tlist2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: ['a' | 'b'] +Context expected type: ['a'] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +166 │ Res. + │ ^^^ Res. +Expression has type: {'cons', 'nil' | my_list()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:177:24 + │ +177 │ opaque1_neg({X, _}) -> X. + │ ^ X. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:183:24 + │ +183 │ opaque2_neg([X, _]) -> X. + │ ^ + │ │ + │ X. +Expression has type: erl_anno:annotation() +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 + Context expects type: pid() + No candidate of the expression's type matches the expected type. + +------------------------------ Detailed message ------------------------------ + + erl_anno:annotation() is not compatible with pid() + because + {'file', erl_anno:filename()} | {'generated', erl_anno:generated()} | {'record', erl_anno:record()} | {'text', string()} | {'location', erl_anno:location()} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:191:7 + │ +191 │ }) -> X. + │ ^ X. +Expression has type: 'set' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:198:23 + │ +198 │ opaque_4_neg(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: gb_sets:set(atom()) +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: {number(), gb_sets:gb_set_node(atom())} + Context expects type: pid() + +------------------------------ Detailed message ------------------------------ + + gb_sets:set(atom()) is not compatible with pid() + because + {number(), gb_sets:gb_set_node(atom())} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:202:31 + │ +202 │ record_as_tuple1_neg(R, R) -> R. + │ ^ R. +Expression has type: #my_rec{} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:206:31 + │ +206 │ record_as_tuple2_neg(R, R) -> R. + │ ^ R. +Expression has type: #my_rec{} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:222:28 + │ +222 │ record_as_tuple6_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: #my_rec{} +Context expected type: {'not_my_rec', term(), term()} + │ + +Because in the expression's type: + { + Here the type is: 'my_rec' + 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:226:10 + │ +226 │ i :: unknown:unknown() + │ ^^^^^^^^^^^^^^^^^ Unknown id: unknown:unknown/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/refine.erl:228:1 + │ +228 │ ╭ -spec record_as_tuple7_neg +229 │ │ (#bad_rec{}) -> {bad_rec, atom()}. + │ ╰─────────────────────────────────^ record_as_tuple7_neg/1 references type with invalid definition: #bad_rec{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:240:28 + │ +240 │ tuple_as_record2_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: {'my_rec', atom(), number()} +Context expected type: #my_rec{} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:245:1 + │ +245 │ ╭ tup_guard_record1(X) +246 │ │ when is_record(X, my_rec) -> +247 │ │ X * 2. + │ ╰─────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:253:5 + │ +253 │ X * 2. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:259:5 + │ +259 │ X * 2. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:262:14 + │ +262 │ ftt(X, _) -> X. + │ ^ X. +Expression has type: fun((T) -> 'ok') +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:265:20 + │ +265 │ my_rec_to_ok(X) -> X. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:277:26 + │ +277 │ use2_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: {'my_rec', atom(), number()} +Context expected type: #my_rec{} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:277:26 + │ +277 │ use2_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: {'my_rec', atom(), number()} +Context expected type: dynamic(#my_rec{}) + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:289:26 + │ +289 │ use4_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: {'my_rec', atom(), number()} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:289:26 + │ +289 │ use4_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: dynamic({'my_rec', atom(), number()}) + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:305:21 + │ +305 │ use6_neg(X) -> ttt2(X, x). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: {'my_rec', atom(), atom()} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:309:16 + │ +309 │ use7_neg(X) -> ttt1(X, 1). + │ ^^^^^^^^^^ + │ │ + │ ttt1(X, 1). +Expression has type: atom() | 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: 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:313:13 + │ +313 │ deets(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} | 'a' +Context expected type: {'my_rec', term(), term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #my_rec{} + 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:323:5 + │ +323 │ B. + │ ^ + │ │ + │ B. +Expression has type: 'undefined' | binary() +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: '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:405:27 + │ +405 │ eqwalizer:reveal_type(Name), + │ ^^^^ atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:406:5 + │ +406 │ Name. + │ ^^^^ Name. +Expression has type: atom() +Context expected type: 'ok' + +41 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-28.pretty new file mode 100644 index 0000000000..ded381093a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/refine-OTP-28.pretty @@ -0,0 +1,623 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:12:17 + │ +12 │ param1(X, X) -> X. + │ ^ X. +Expression has type: _B +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:16:17 + │ +16 │ param2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: {B, A} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: B + 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 + │ +22 │ maps(M, M) -> M. + │ ^ M. +Expression has type: #{a := atom()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:28:16 + │ +28 │ funs1(F, F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((B | A) -> A) +Context expected type: fun((A | B) -> none()) + │ + +Because in the expression's type: + fun((B | A) -> + Here the type is: A + 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 + │ +34 │ funs2(F, F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((atom() | A) -> A) +Context expected type: fun((A | atom()) -> none()) + │ + +Because in the expression's type: + fun((atom() | A) -> + Here the type is: A + 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 + │ +84 │ list_trick_neg([X], _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:92:28 + │ +92 │ tuple_trick_neg({X}, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:101:8 + │ +101 │ -> X. + │ ^ X. +Expression has type: _T +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:112:24 + │ +112 │ (#{key := Val}) -> Val. + │ ^^^ Val. +Expression has type: term() +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:117:26 + │ +117 │ (<>) -> {X, Y}. + │ ^^^^^^ + │ │ + │ {X, Y}. +Expression has type: {number(), binary()} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +126 │ (#my_rec{n = N, a = A}) -> {N, A}. + │ ^^^^^^ + │ │ + │ {N, A}. +Expression has type: {number(), atom()} +Context expected type: {none(), none()} + │ + +Because in the expression's type: + { + Here the type is: number() + 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 + │ +139 │ refine_alist1_neg(X, X) -> X. + │ ^ X. +Expression has type: ['a' | 'b'] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:144:24 + │ +144 │ refine_alist2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: ['a' | 'b'] +Context expected type: ['a'] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +151 │ refine_tlist1_neg(X, X) -> X. + │ ^ X. +Expression has type: ['a' | 'b'] +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:156:24 + │ +156 │ refine_tlist2(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: ['a' | 'b'] +Context expected type: ['a'] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +166 │ Res. + │ ^^^ Res. +Expression has type: {'cons', 'nil' | my_list()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:177:24 + │ +177 │ opaque1_neg({X, _}) -> X. + │ ^ X. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:183:24 + │ +183 │ opaque2_neg([X, _]) -> X. + │ ^ + │ │ + │ X. +Expression has type: erl_anno:annotation() +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 | tuple()/2 | ... + Context expects type: pid() + No candidate of the expression's type matches the expected type. + +------------------------------ Detailed message ------------------------------ + + erl_anno:annotation() is not compatible with pid() + because + {'file', erl_anno:filename()} | {'end_location', erl_anno:location()} | {'generated', erl_anno:generated()} | {'record', erl_anno:record()} | {'text', string()} | {'location', erl_anno:location()} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:191:7 + │ +191 │ }) -> X. + │ ^ X. +Expression has type: 'set' +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:198:23 + │ +198 │ opaque_4_neg(X, X) -> X. + │ ^ + │ │ + │ X. +Expression has type: gb_sets:set(atom()) +Context expected type: pid() + │ + +Because in the expression's type: + Here the type is: {number(), gb_sets:gb_set_node(atom())} + Context expects type: pid() + +------------------------------ Detailed message ------------------------------ + + gb_sets:set(atom()) is not compatible with pid() + because + {number(), gb_sets:gb_set_node(atom())} is not compatible with pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:202:31 + │ +202 │ record_as_tuple1_neg(R, R) -> R. + │ ^ R. +Expression has type: #my_rec{} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:206:31 + │ +206 │ record_as_tuple2_neg(R, R) -> R. + │ ^ R. +Expression has type: #my_rec{} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:222:28 + │ +222 │ record_as_tuple6_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: #my_rec{} +Context expected type: {'not_my_rec', term(), term()} + │ + +Because in the expression's type: + { + Here the type is: 'my_rec' + 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:226:10 + │ +226 │ i :: unknown:unknown() + │ ^^^^^^^^^^^^^^^^^ Unknown id: unknown:unknown/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/refine.erl:228:1 + │ +228 │ ╭ -spec record_as_tuple7_neg +229 │ │ (#bad_rec{}) -> {bad_rec, atom()}. + │ ╰─────────────────────────────────^ record_as_tuple7_neg/1 references type with invalid definition: #bad_rec{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:240:28 + │ +240 │ tuple_as_record2_neg(R) -> R. + │ ^ + │ │ + │ R. +Expression has type: {'my_rec', atom(), number()} +Context expected type: #my_rec{} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:245:1 + │ +245 │ ╭ tup_guard_record1(X) +246 │ │ when is_record(X, my_rec) -> +247 │ │ X * 2. + │ ╰─────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:253:5 + │ +253 │ X * 2. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:259:5 + │ +259 │ X * 2. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:262:14 + │ +262 │ ftt(X, _) -> X. + │ ^ X. +Expression has type: fun((T) -> 'ok') +Context expected type: T + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:265:20 + │ +265 │ my_rec_to_ok(X) -> X. + │ ^ X. +Expression has type: #my_rec{} +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:277:26 + │ +277 │ use2_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: {'my_rec', atom(), number()} +Context expected type: #my_rec{} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:277:26 + │ +277 │ use2_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: {'my_rec', atom(), number()} +Context expected type: dynamic(#my_rec{}) + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: atom() + 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:289:26 + │ +289 │ use4_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: {'my_rec', atom(), number()} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:289:26 + │ +289 │ use4_neg(F, X) -> ftt(F, X). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: dynamic({'my_rec', atom(), number()}) + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:305:21 + │ +305 │ use6_neg(X) -> ttt2(X, x). + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} +Context expected type: {'my_rec', atom(), atom()} + │ + +Because in the expression's type: + { 'my_rec', + Here the type is: number() + 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:309:16 + │ +309 │ use7_neg(X) -> ttt1(X, 1). + │ ^^^^^^^^^^ + │ │ + │ ttt1(X, 1). +Expression has type: atom() | 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: 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:313:13 + │ +313 │ deets(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: #my_rec{} | 'a' +Context expected type: {'my_rec', term(), term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #my_rec{} + 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:323:5 + │ +323 │ B. + │ ^ + │ │ + │ B. +Expression has type: 'undefined' | binary() +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: '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:405:27 + │ +405 │ eqwalizer:reveal_type(Name), + │ ^^^^ atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/refine.erl:406:5 + │ +406 │ Name. + │ ^^^^ Name. +Expression has type: atom() +Context expected type: 'ok' + +41 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/scoping-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/skip-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-27.pretty new file mode 100644 index 0000000000..534ecac9b6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-27.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/static_maybe.erl:26:9 + │ +26 │ A + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/static_maybe.erl:50:14 + │ +50 │ _ -> 3 + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-28.pretty new file mode 100644 index 0000000000..534ecac9b6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/static_maybe-OTP-28.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/static_maybe.erl:26:9 + │ +26 │ A + │ ^ A. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/static_maybe.erl:50:14 + │ +50 │ _ -> 3 + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +2 ERRORS 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types-OTP-26.pretty index 8943deb90a..cc9f9d0634 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-OTP-26.pretty @@ -13,6 +13,12 @@ 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 │ @@ -40,6 +46,12 @@ 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 │ @@ -55,4 +67,10 @@ 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/strict_complex_types-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types-OTP-27.pretty new file mode 100644 index 0000000000..cc9f9d0634 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types-OTP-27.pretty @@ -0,0 +1,76 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_complex_types.erl:24:3 + │ +24 │ undefined. + │ ^^^^^^^^^ + │ │ + │ 'undefined'. +Expression has type: 'undefined' +Context expected type: complex_map() + │ + +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 + │ +29 │ eqwalizer:reveal_type(Map), + │ ^^^ complex_map() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/strict_complex_types.erl:35:25 + │ +35 │ eqwalizer:reveal_type(List), + │ ^^^^ [complex_map()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_complex_types.erl:48:16 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [T] + │ + +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 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [dynamic()] + │ + +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/strict_complex_types-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types-OTP-28.pretty new file mode 100644 index 0000000000..cc9f9d0634 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_complex_types-OTP-28.pretty @@ -0,0 +1,76 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_complex_types.erl:24:3 + │ +24 │ undefined. + │ ^^^^^^^^^ + │ │ + │ 'undefined'. +Expression has type: 'undefined' +Context expected type: complex_map() + │ + +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 + │ +29 │ eqwalizer:reveal_type(Map), + │ ^^^ complex_map() + +error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type) + ┌─ check/src/strict_complex_types.erl:35:25 + │ +35 │ eqwalizer:reveal_type(List), + │ ^^^^ [complex_map()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_complex_types.erl:48:16 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [T] + │ + +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 + │ +48 │ lists:nth(7, Map). + │ ^^^ + │ │ + │ Map. +Expression has type: complex_map() +Context expected type: [dynamic()] + │ + +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/strict_fun-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_fun-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-27.pretty new file mode 100644 index 0000000000..9bff7ab9cc --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-27.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_receive.erl:71:9 + │ +71 │ after T -> ok + │ ^ T. +Expression has type: atom() +Context expected type: timeout() + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-28.pretty new file mode 100644 index 0000000000..9bff7ab9cc --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/strict_receive-OTP-28.pretty @@ -0,0 +1,9 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/strict_receive.erl:71:9 + │ +71 │ after T -> ok + │ ^ T. +Expression has type: atom() +Context expected type: timeout() + +1 ERROR 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-OTP-26.pretty similarity index 72% rename from crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-26.pretty index 4413a8fee8..1ee53764d2 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-26.pretty @@ -31,6 +31,12 @@ 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 │ @@ -47,6 +53,14 @@ 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 │ @@ -73,6 +87,17 @@ 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 │ @@ -91,6 +116,17 @@ 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 │ @@ -115,6 +151,10 @@ 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 │ @@ -131,6 +171,10 @@ 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 │ @@ -147,6 +191,10 @@ 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 │ @@ -164,6 +212,13 @@ 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 │ @@ -181,6 +236,13 @@ 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 │ @@ -197,6 +259,13 @@ 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 │ @@ -222,6 +291,13 @@ 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 │ @@ -239,6 +315,13 @@ 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 │ @@ -256,6 +339,13 @@ 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 │ @@ -292,4 +382,10 @@ 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/subtype_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-27.pretty new file mode 100644 index 0000000000..1ee53764d2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-27.pretty @@ -0,0 +1,391 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:17:11 + │ +17 │ f01(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: {A, A} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:20:11 + │ +20 │ f02(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:24:11 + │ +24 │ f03(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((atom()) -> term()) +Context expected type: fun((term()) -> atom()) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: term() + 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 + │ +27 │ f04(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: 'a' | 'b' | 'c' +Context expected type: 'a' | 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' | 'b' + 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 + │ +30 │ f05(X) -> X. + │ ^ X. +Expression has type: 'a' | 'b' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:34:11 + │ +34 │ f06(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: {'a' | 'b', 'a' | 'b'} +Context expected type: {'a', 'b'} | {'b', 'a'} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +38 │ f07(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: pair_ab() +Context expected type: pair_diff_elems() + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +41 │ f08(X) -> X. + │ ^ X. +Expression has type: {none(), none()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:48:13 + │ +48 │ map02(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a => atom()} +Context expected type: #{a := atom()} + │ + +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/subtype_neg.erl:52:13 + │ +52 │ map03(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a := atom(), b := atom()} +Context expected type: #{a => atom()} + │ + +Because in the expression's type: + Here the type is: #{b := ...} + 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 + │ +60 │ map05(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => 'a' | 'b' | 'c'} +Context expected type: #{a := 'a' | 'b', b => 'a' | 'b'} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +64 │ map06(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{term() => number()} +Context expected type: #{atom() => number()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: term() + 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 + │ +68 │ map07(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{atom() => term()} +Context expected type: #{atom() => number()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: term() + 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 + │ +72 │ map08(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{atom() => term()} +Context expected type: #{} + │ + +Because in the expression's type: + Here the type is: #{atom() => term()} + 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 + │ +79 │ any_fun(F) -> F. + │ ^ F. +Expression has type: term() +Context expected type: fun() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:83:5 + │ +83 │ {{}, error}. + │ ^^^^^^^^^^^ + │ │ + │ {{}, 'error'}. +Expression has type: {{}, 'error'} +Context expected type: {tuple(), 'ok'} + │ + +Because in the expression's type: + { {}, + Here the type is: 'error' + 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 + │ +87 │ {[], error}. + │ ^^^^^^^^^^^ + │ │ + │ {[], 'error'}. +Expression has type: {[], 'error'} +Context expected type: {[pid()], 'ok'} + │ + +Because in the expression's type: + { [], + Here the type is: 'error' + 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 + │ +91 │ {[], error}. + │ ^^^^^^^^^^^ + │ │ + │ {[], 'error'}. +Expression has type: {[], 'error'} +Context expected type: {iolist(), 'ok'} + │ + +Because in the expression's type: + { [], + Here the type is: 'error' + 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 + │ +94 │ nil_1(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: ['a'] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: 'a' + Context expects type: none() + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:97:13 + │ +97 │ nil_2(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: ['a'] | [none()] +Context expected type: [] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [none()] + However, the following candidate doesn't match: + [ + Here the type is: 'a' + 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/subtype_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-28.pretty new file mode 100644 index 0000000000..1ee53764d2 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_neg-OTP-28.pretty @@ -0,0 +1,391 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:17:11 + │ +17 │ f01(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: {A, A} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:20:11 + │ +20 │ f02(X) -> X. + │ ^ X. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:24:11 + │ +24 │ f03(F) -> F. + │ ^ + │ │ + │ F. +Expression has type: fun((atom()) -> term()) +Context expected type: fun((term()) -> atom()) + │ + +Because in the expression's type: + fun((atom()) -> + Here the type is: term() + 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 + │ +27 │ f04(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: 'a' | 'b' | 'c' +Context expected type: 'a' | 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'a' | 'b' + 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 + │ +30 │ f05(X) -> X. + │ ^ X. +Expression has type: 'a' | 'b' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:34:11 + │ +34 │ f06(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: {'a' | 'b', 'a' | 'b'} +Context expected type: {'a', 'b'} | {'b', 'a'} + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +38 │ f07(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: pair_ab() +Context expected type: pair_diff_elems() + │ + +Because in the expression's type: + { + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +41 │ f08(X) -> X. + │ ^ X. +Expression has type: {none(), none()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:48:13 + │ +48 │ map02(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a => atom()} +Context expected type: #{a := atom()} + │ + +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/subtype_neg.erl:52:13 + │ +52 │ map03(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a := atom(), b := atom()} +Context expected type: #{a => atom()} + │ + +Because in the expression's type: + Here the type is: #{b := ...} + 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 + │ +60 │ map05(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => 'a' | 'b' | 'c'} +Context expected type: #{a := 'a' | 'b', b => 'a' | 'b'} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +64 │ map06(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{term() => number()} +Context expected type: #{atom() => number()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: term() + 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 + │ +68 │ map07(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{atom() => term()} +Context expected type: #{atom() => number()} + │ + +Because in the expression's type: + #{ atom() => + Here the type is: term() + 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 + │ +72 │ map08(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{atom() => term()} +Context expected type: #{} + │ + +Because in the expression's type: + Here the type is: #{atom() => term()} + 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 + │ +79 │ any_fun(F) -> F. + │ ^ F. +Expression has type: term() +Context expected type: fun() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:83:5 + │ +83 │ {{}, error}. + │ ^^^^^^^^^^^ + │ │ + │ {{}, 'error'}. +Expression has type: {{}, 'error'} +Context expected type: {tuple(), 'ok'} + │ + +Because in the expression's type: + { {}, + Here the type is: 'error' + 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 + │ +87 │ {[], error}. + │ ^^^^^^^^^^^ + │ │ + │ {[], 'error'}. +Expression has type: {[], 'error'} +Context expected type: {[pid()], 'ok'} + │ + +Because in the expression's type: + { [], + Here the type is: 'error' + 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 + │ +91 │ {[], error}. + │ ^^^^^^^^^^^ + │ │ + │ {[], 'error'}. +Expression has type: {[], 'error'} +Context expected type: {iolist(), 'ok'} + │ + +Because in the expression's type: + { [], + Here the type is: 'error' + 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 + │ +94 │ nil_1(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: ['a'] +Context expected type: [] + │ + +Because in the expression's type: + [ + Here the type is: 'a' + Context expects type: none() + ] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/subtype_neg.erl:97:13 + │ +97 │ nil_2(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: ['a'] | [none()] +Context expected type: [] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [none()] + However, the following candidate doesn't match: + [ + Here the type is: 'a' + 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/subtype_pos.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-27.pretty new file mode 100644 index 0000000000..20634a306c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-27.pretty @@ -0,0 +1,13 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/subtype_pos.erl:30:1 + │ +30 │ f05(X) -> X. + │ ^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/subtype_pos.erl:41:1 + │ +41 │ f08(X) -> X. + │ ^^^^^^^^^^^ Clause is not covered by spec + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-28.pretty new file mode 100644 index 0000000000..20634a306c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/subtype_pos-OTP-28.pretty @@ -0,0 +1,13 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/subtype_pos.erl:30:1 + │ +30 │ f05(X) -> X. + │ ^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/subtype_pos.erl:41:1 + │ +41 │ f08(X) -> X. + │ ^^^^^^^^^^^ Clause is not covered by spec + +2 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-OTP-26.pretty similarity index 66% rename from crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-26.pretty index 5c2b07ae96..d0e08f9ac4 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-26.pretty @@ -15,6 +15,13 @@ 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 │ @@ -33,6 +40,15 @@ 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 │ @@ -50,6 +66,15 @@ 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 │ @@ -83,6 +108,15 @@ 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 │ @@ -100,6 +134,17 @@ 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 │ @@ -117,6 +162,17 @@ 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 │ @@ -133,6 +189,10 @@ 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 │ @@ -156,6 +216,17 @@ 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 │ @@ -173,6 +244,17 @@ 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 │ @@ -190,6 +272,17 @@ 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 │ @@ -207,6 +300,17 @@ 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 │ @@ -224,6 +328,17 @@ 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 │ @@ -241,6 +356,17 @@ 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 │ @@ -259,6 +385,17 @@ 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 │ @@ -275,6 +412,10 @@ 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 │ @@ -292,6 +433,17 @@ 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 │ @@ -309,6 +461,17 @@ 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 │ @@ -326,6 +489,13 @@ 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 │ @@ -345,6 +515,17 @@ 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 │ @@ -375,6 +556,15 @@ 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 │ @@ -421,6 +611,10 @@ 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 │ @@ -439,6 +633,17 @@ 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 │ @@ -455,6 +660,15 @@ 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 │ @@ -473,6 +687,14 @@ 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 │ @@ -489,6 +711,15 @@ 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 │ @@ -505,6 +736,10 @@ 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 │ @@ -522,6 +757,15 @@ 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 │ @@ -538,6 +782,10 @@ 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 │ @@ -554,6 +802,10 @@ 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 │ @@ -570,6 +822,10 @@ 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 │ @@ -586,6 +842,10 @@ 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 │ @@ -602,6 +862,13 @@ 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 │ @@ -618,6 +885,10 @@ 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 │ @@ -634,4 +905,11 @@ 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/t_maps-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-27.pretty new file mode 100644 index 0000000000..d0e08f9ac4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-27.pretty @@ -0,0 +1,915 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:36:5 + │ +36 │ #{0 => 1, 1 => 2}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{number() => number()} +Context expected type: #{number() => atom()} + │ + +Because in the expression's type: + #{ number() => + Here the type is: number() + 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 + │ +41 │ #{0 => zero, 1 => 2}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{number() => 'zero' | number()} +Context expected type: #{number() => atom()} + │ + +Because in the expression's type: + #{ number() => + Here the type is a union type with some valid candidates: 'zero' + However the following candidate: number() + 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 + │ +56 │ #{zero => 0, one => one}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{one := 'one', zero := number()} +Context expected type: #{one => number(), zero := number()} + │ + +Because in the expression's type: + #{ one => + Here the type is: 'one' + 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 + │ +80 │ M#{}. + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:120:5 + │ +120 │ S#{bar := true}. + │ ^ S. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:130:25 + │ +130 │ meet_dict2_neg(D, D) -> D. + │ ^ + │ │ + │ D. +Expression has type: #{b() => term()} +Context expected type: #{n() => term()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: boolean() + 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 + │ +140 │ meet_shape2_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a := b()} +Context expected type: #{a := n()} + │ + +Because in the expression's type: + #{ a => + Here the type is: boolean() + 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 + │ +145 │ meet_shape3_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a := b()} +Context expected type: #{a => n()} + │ + +Because in the expression's type: + #{ a => + Here the type is: boolean() + 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 + │ +155 │ meet_shape5_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a => b()} +Context expected type: #{a := b()} + │ + +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: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:165:1 + │ +165 │ meet_shape7(S, S) -> S. + │ ^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:202:5 + │ +202 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{a() => a()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: number() + 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 + │ +208 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +216 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +224 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a => a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +242 │ FB#{foo := F1, bar := B1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{bar := B1, foo := F1} +Context expected type: foo_bar(B1, F1) + │ + +Because in the expression's type: + #{ bar => + Here the type is: B1 + 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 + │ +254 │ FB#{foo => F1, bar => B1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{bar := B1, foo := F1} +Context expected type: foo_bar_opt(B1, F1) + │ + +Because in the expression's type: + #{ bar => + Here the type is: B1 + 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 + │ +266 │ Dict#{V2 => K2}. + │ ^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{K1 | V2 => V1 | K2} +Context expected type: kv(K1 | K2, V1 | V2) + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is a union type with some valid candidates: K1 + However the following candidate: V2 + 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 + │ +283 │ needs_shape_a(X#{b => hello}). + │ ^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := number(), b := 'hello'} +Context expected type: #{a := term()} + │ + +Because in the expression's type: + Here the type is: #{b := ...} + 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 + │ +307 │ #{K := V} -> {K, V}; + │ ^^^^^^ + │ │ + │ {K, V}. +Expression has type: {a(), n()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +321 │ {A, N}. + │ ^^^^^^ + │ │ + │ {A, N}. +Expression has type: {a(), n()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +334 │ to_map3_neg(#{} = M) -> M; + │ ^ + │ │ + │ M. +Expression has type: #{V => K} +Context expected type: #{K => V} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: V + 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 + │ +351 │ to_map5_neg(#{} = M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a() => n()} | #{n() => a()} | #{id => 'id' | 'no_id'} +Context expected type: #{a() | n() => a()} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: #{n() => a()} | #{id => 'id' | 'no_id'} + However, the following candidate doesn't match: + #{ a() => + Here the type is: number() + 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 + │ +356 │ no_map(#{} = M) -> M. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:361:1 + │ +361 │ no_prop(#{foo := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:377:22 + │ +377 │ #{K := V} -> {K, V}; + │ ^^^^^^ + │ │ + │ {K, V}. +Expression has type: {'a' | 'b', a()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: 'a' | 'b' + Context expects type: number() + 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 + │ +429 │ val3(#{a := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:434:1 + │ +434 │ val4(#{a := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:451:1 + │ +451 │ guard2(M) when is_map(M) -> M. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:493:1 + │ +493 │ update_none1(N) -> N#{a := 1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:496:1 + │ +496 │ update_none2(N) -> N#{a => 1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:508:17 + │ +508 │ sub_3_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a := atom()} +Context expected type: #{a => atom(), n := number()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +536 │ maps:update_with(a, F, a, Map). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ maps:update_with('a', F, 'a', Map). +Expression has type: #{a => 'b' | 'c' | 'a'} +Context expected type: #{a => 'b' | 'c'} + │ + +Because in the expression's type: + #{ a => + Here the type is a union type with some valid candidates: 'b' | 'c' + However the following candidate: 'a' + 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 + │ +571 │ X. + │ ^ + │ │ + │ X. +Expression has type: rec_shape() +Context expected type: rec_shape_v2() + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +629 │ X. + │ ^ + │ │ + │ X. +Expression has type: gen_shape('a' | 'b') +Context expected type: 'a' | #{item := 'a' | #{item := 'a' | gen_shape('a')}} + │ + +Because in the expression's type: + #{ item => + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +641 │ X. + │ ^ + │ │ + │ X. +Expression has type: gen_shape_v2('a') +Context expected type: gen_shape('a') + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +648 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'va', b => 'vb', c => 'vc', d => 'vd'} +Context expected type: #{a => 'va', b => 'vb', c => 'vc'} + │ + +Because in the expression's type: + Here the type is: #{d => ...} + 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 + │ +661 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'va', b => 'vb', c => 'vc', d => 'vd', e => 've'} +Context expected type: #{a => 'ka', b => 'kb', c => 'kc'} + │ + +Because in the expression's type: + #{ a => + Here the type is: 'va' + 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 + │ +675 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{} +Context expected type: #{a => 'va', b => 'vb', c := 'vc'} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +682 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'ka', b => 'kb', c => 'kc'} +Context expected type: #{a => 'ka', b := 'kb', c => 'kc'} + │ + +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/t_maps.erl:689:5 + │ +689 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'ka', b => 'kb', c => 'kc'} +Context expected type: #{a => 'ka', b := 'kb', c => 'kc'} + │ + +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/t_maps.erl:710:5 + │ +710 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()} +Context expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()} + │ + +Because in the expression's type: + Here the type is: #{k_req2 => ..., k_req1 => ..., ...} + 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 + │ +732 │ dict_to_shape_neg_1(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => 'true', b => boolean()} + │ + +Because in the expression's type: + Here the type is: #{'a' | 'b' => boolean()} + 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 + │ +737 │ dict_to_shape_neg_2(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => boolean(), b := term()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +742 │ dict_to_shape_neg_3(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => boolean()} + │ + +Because in the expression's type: + Here the type is: #{'a' | 'b' => boolean()} + 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/t_maps-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-28.pretty new file mode 100644 index 0000000000..d0e08f9ac4 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/t_maps-OTP-28.pretty @@ -0,0 +1,915 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:36:5 + │ +36 │ #{0 => 1, 1 => 2}. + │ ^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{number() => number()} +Context expected type: #{number() => atom()} + │ + +Because in the expression's type: + #{ number() => + Here the type is: number() + 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 + │ +41 │ #{0 => zero, 1 => 2}. + │ ^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{number() => 'zero' | number()} +Context expected type: #{number() => atom()} + │ + +Because in the expression's type: + #{ number() => + Here the type is a union type with some valid candidates: 'zero' + However the following candidate: number() + 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 + │ +56 │ #{zero => 0, one => one}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ #{..}. +Expression has type: #{one := 'one', zero := number()} +Context expected type: #{one => number(), zero := number()} + │ + +Because in the expression's type: + #{ one => + Here the type is: 'one' + 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 + │ +80 │ M#{}. + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:120:5 + │ +120 │ S#{bar := true}. + │ ^ S. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:130:25 + │ +130 │ meet_dict2_neg(D, D) -> D. + │ ^ + │ │ + │ D. +Expression has type: #{b() => term()} +Context expected type: #{n() => term()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: boolean() + 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 + │ +140 │ meet_shape2_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a := b()} +Context expected type: #{a := n()} + │ + +Because in the expression's type: + #{ a => + Here the type is: boolean() + 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 + │ +145 │ meet_shape3_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a := b()} +Context expected type: #{a => n()} + │ + +Because in the expression's type: + #{ a => + Here the type is: boolean() + 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 + │ +155 │ meet_shape5_neg(S, S) -> S. + │ ^ + │ │ + │ S. +Expression has type: #{a => b()} +Context expected type: #{a := b()} + │ + +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: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:165:1 + │ +165 │ meet_shape7(S, S) -> S. + │ ^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:202:5 + │ +202 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{a() => a()} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: number() + 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 + │ +208 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +216 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +224 │ S#{K => V}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a => a(), n() => a()} +Context expected type: #{n() => a()} + │ + +Because in the expression's type: + #{ incompatible map key a: + Here the type is: 'a' + 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 + │ +242 │ FB#{foo := F1, bar := B1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{bar := B1, foo := F1} +Context expected type: foo_bar(B1, F1) + │ + +Because in the expression's type: + #{ bar => + Here the type is: B1 + 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 + │ +254 │ FB#{foo => F1, bar => B1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{bar := B1, foo := F1} +Context expected type: foo_bar_opt(B1, F1) + │ + +Because in the expression's type: + #{ bar => + Here the type is: B1 + 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 + │ +266 │ Dict#{V2 => K2}. + │ ^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{K1 | V2 => V1 | K2} +Context expected type: kv(K1 | K2, V1 | V2) + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is a union type with some valid candidates: K1 + However the following candidate: V2 + 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 + │ +283 │ needs_shape_a(X#{b => hello}). + │ ^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := number(), b := 'hello'} +Context expected type: #{a := term()} + │ + +Because in the expression's type: + Here the type is: #{b := ...} + 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 + │ +307 │ #{K := V} -> {K, V}; + │ ^^^^^^ + │ │ + │ {K, V}. +Expression has type: {a(), n()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +321 │ {A, N}. + │ ^^^^^^ + │ │ + │ {A, N}. +Expression has type: {a(), n()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: atom() + 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 + │ +334 │ to_map3_neg(#{} = M) -> M; + │ ^ + │ │ + │ M. +Expression has type: #{V => K} +Context expected type: #{K => V} + │ + +Because in the expression's type: + #{ map domains are incompatible: + Here the type is: V + 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 + │ +351 │ to_map5_neg(#{} = M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a() => n()} | #{n() => a()} | #{id => 'id' | 'no_id'} +Context expected type: #{a() | n() => a()} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: #{n() => a()} | #{id => 'id' | 'no_id'} + However, the following candidate doesn't match: + #{ a() => + Here the type is: number() + 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 + │ +356 │ no_map(#{} = M) -> M. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:361:1 + │ +361 │ no_prop(#{foo := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:377:22 + │ +377 │ #{K := V} -> {K, V}; + │ ^^^^^^ + │ │ + │ {K, V}. +Expression has type: {'a' | 'b', a()} +Context expected type: {n(), a()} + │ + +Because in the expression's type: + { + Here the type is: 'a' | 'b' + Context expects type: number() + 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 + │ +429 │ val3(#{a := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:434:1 + │ +434 │ val4(#{a := V}) -> V. + │ ^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:451:1 + │ +451 │ guard2(M) when is_map(M) -> M. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:493:1 + │ +493 │ update_none1(N) -> N#{a := 1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/t_maps.erl:496:1 + │ +496 │ update_none2(N) -> N#{a => 1}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/t_maps.erl:508:17 + │ +508 │ sub_3_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{a := atom()} +Context expected type: #{a => atom(), n := number()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +536 │ maps:update_with(a, F, a, Map). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ maps:update_with('a', F, 'a', Map). +Expression has type: #{a => 'b' | 'c' | 'a'} +Context expected type: #{a => 'b' | 'c'} + │ + +Because in the expression's type: + #{ a => + Here the type is a union type with some valid candidates: 'b' | 'c' + However the following candidate: 'a' + 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 + │ +571 │ X. + │ ^ + │ │ + │ X. +Expression has type: rec_shape() +Context expected type: rec_shape_v2() + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +629 │ X. + │ ^ + │ │ + │ X. +Expression has type: gen_shape('a' | 'b') +Context expected type: 'a' | #{item := 'a' | #{item := 'a' | gen_shape('a')}} + │ + +Because in the expression's type: + #{ item => + Here the type is a union type with some valid candidates: 'a' + However the following candidate: 'b' + 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 + │ +641 │ X. + │ ^ + │ │ + │ X. +Expression has type: gen_shape_v2('a') +Context expected type: gen_shape('a') + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +648 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'va', b => 'vb', c => 'vc', d => 'vd'} +Context expected type: #{a => 'va', b => 'vb', c => 'vc'} + │ + +Because in the expression's type: + Here the type is: #{d => ...} + 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 + │ +661 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'va', b => 'vb', c => 'vc', d => 'vd', e => 've'} +Context expected type: #{a => 'ka', b => 'kb', c => 'kc'} + │ + +Because in the expression's type: + #{ a => + Here the type is: 'va' + 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 + │ +675 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{} +Context expected type: #{a => 'va', b => 'vb', c := 'vc'} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +682 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'ka', b => 'kb', c => 'kc'} +Context expected type: #{a => 'ka', b := 'kb', c => 'kc'} + │ + +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/t_maps.erl:689:5 + │ +689 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{a => 'ka', b => 'kb', c => 'kc'} +Context expected type: #{a => 'ka', b := 'kb', c => 'kc'} + │ + +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/t_maps.erl:710:5 + │ +710 │ X. + │ ^ + │ │ + │ X. +Expression has type: #{k_extra => term(), k_ok => term(), k_req1 => term(), k_req2 => term(), k_wrong1 => pid(), k_wrong2 => pid()} +Context expected type: #{k_ok => term(), k_req1 := atom(), k_req2 := atom(), k_req3 := atom(), k_wrong1 => atom(), k_wrong2 => atom()} + │ + +Because in the expression's type: + Here the type is: #{k_req2 => ..., k_req1 => ..., ...} + 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 + │ +732 │ dict_to_shape_neg_1(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => 'true', b => boolean()} + │ + +Because in the expression's type: + Here the type is: #{'a' | 'b' => boolean()} + 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 + │ +737 │ dict_to_shape_neg_2(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => boolean(), b := term()} + │ + +Because in the expression's type: + Here the type is: #{...} + 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 + │ +742 │ dict_to_shape_neg_3(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: #{'a' | 'b' => boolean()} +Context expected type: #{a => boolean()} + │ + +Because in the expression's type: + Here the type is: #{'a' | 'b' => boolean()} + 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/tagged_tuples-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tagged_tuples-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/test-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-26.pretty similarity index 92% rename from crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-26.pretty index 5f9c8914f4..471b5c8ebe 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/tries.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-26.pretty @@ -22,6 +22,12 @@ 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/tries-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-27.pretty new file mode 100644 index 0000000000..471b5c8ebe --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-27.pretty @@ -0,0 +1,79 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:30:19 + │ +30 │ {_, Y} -> Y + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:42:5 + │ +42 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [] | 'error' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'error' + 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 + │ +75 │ catch _ -> error + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:80:9 + │ +80 │ try ok() + │ ^^^^ ok(). +Expression has type: 'ok' +Context expected type: 'error' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:88:19 + │ +88 │ only_atom(1) + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:130:17 + │ +130 │ true -> Y + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:136:19 + │ +136 │ {X, X} -> X + │ ^ X. +Expression has type: term() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:148:5 + │ +148 │ Res. + │ ^^^ Res. +Expression has type: term() +Context expected type: 'ok' + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-28.pretty new file mode 100644 index 0000000000..471b5c8ebe --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tries-OTP-28.pretty @@ -0,0 +1,79 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:30:19 + │ +30 │ {_, Y} -> Y + │ ^ Y. +Expression has type: term() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:42:5 + │ +42 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [] | 'error' +Context expected type: atom() + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'error' + 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 + │ +75 │ catch _ -> error + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:80:9 + │ +80 │ try ok() + │ ^^^^ ok(). +Expression has type: 'ok' +Context expected type: 'error' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:88:19 + │ +88 │ only_atom(1) + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:130:17 + │ +130 │ true -> Y + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:136:19 + │ +136 │ {X, X} -> X + │ ^ X. +Expression has type: term() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tries.erl:148:5 + │ +148 │ Res. + │ ^^^ Res. +Expression has type: term() +Context expected type: 'ok' + +8 ERRORS 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-OTP-26.pretty similarity index 56% rename from crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-26.pretty index 5b2cca1631..e02af30869 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-26.pretty @@ -16,6 +16,17 @@ 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 │ @@ -36,4 +47,14 @@ 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/tuple_union-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-27.pretty new file mode 100644 index 0000000000..e02af30869 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-27.pretty @@ -0,0 +1,60 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tuple_union.erl:27:19 + │ +27 │ test_03_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: t4() +Context expected type: t5() + │ + +Because in the expression's type: + { 'msg', + Here the type is a union type with some valid candidates: 'ok' + However the following candidate: 'err' + 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 + │ +66 │ tree3_as_tree1_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: tree3() +Context expected type: tree1() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {'leaf', atom()} + However, the following candidate doesn't match: + { + Here the type is: 'b1' | 'b2' | 'b3' + Context expects type: 'leaf' + 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/tuple_union-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-28.pretty new file mode 100644 index 0000000000..e02af30869 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/tuple_union-OTP-28.pretty @@ -0,0 +1,60 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/tuple_union.erl:27:19 + │ +27 │ test_03_neg(X) -> X. + │ ^ + │ │ + │ X. +Expression has type: t4() +Context expected type: t5() + │ + +Because in the expression's type: + { 'msg', + Here the type is a union type with some valid candidates: 'ok' + However the following candidate: 'err' + 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 + │ +66 │ tree3_as_tree1_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: tree3() +Context expected type: tree1() + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {'leaf', atom()} + However, the following candidate doesn't match: + { + Here the type is: 'b1' | 'b2' | 'b3' + Context expects type: 'leaf' + 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_aliases.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-27.pretty new file mode 100644 index 0000000000..0518a9a064 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-27.pretty @@ -0,0 +1,157 @@ +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:9:1 + │ +9 │ -type w_unbound_var() :: {_A, _B}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:10:1 + │ +10 │ ╭ -type trans_unbound_var() +11 │ │ :: w_unbound_var(). + │ ╰────────────────────^ trans_unbound_var/0 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:13:1 + │ +13 │ ╭ -spec uses_ty_w_unbound_var(_A, _B) -> +14 │ │ w_unbound_var(). + │ ╰─────────────────^ uses_ty_w_unbound_var/2 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:17:1 + │ +17 │ ╭ -spec uses_trans_unbound_var(_A, _B) -> +18 │ │ trans_unbound_var(). + │ ╰─────────────────────^ uses_trans_unbound_var/2 references type with invalid definition: trans_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:21:1 + │ +21 │ ╭ -type w_unbound_var2() +22 │ │ :: _A. + │ ╰─────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:24:1 + │ +24 │ ╭ -spec uses_ty_w_unbound_var2() -> +25 │ │ w_unbound_var2(). + │ ╰──────────────────^ uses_ty_w_unbound_var2/0 references type with invalid definition: w_unbound_var2/0 + +error: repated_type_var_in_type_decl (See https://fb.me/eqwalizer_errors#repated_type_var_in_type_decl) + ┌─ check/src/type_aliases.erl:28:1 + │ +28 │ -type repeated(T, T) :: T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ T. Type vars in type declarations must be distinct + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:30:1 + │ +30 │ ╭ -spec test_repeated1_neg(a, b) -> +31 │ │ repeated(a, b). + │ ╰────────────^ test_repeated1_neg/2 references type with invalid definition: repeated/2 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:34:1 + │ +34 │ ╭ -spec test_repeated2_neg(a, b) -> +35 │ │ repeated(a, b). + │ ╰────────────^ test_repeated2_neg/2 references type with invalid definition: repeated/2 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:39:1 + │ +39 │ ╭ -spec test_use_bad_type +40 │ │ (undefined | w_unbound_var()) -> +41 │ │ w_unbound_var() | undefined. + │ ╰───────────────────────────────^ test_use_bad_type/1 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:48:1 + │ +48 │ ╭ -spec test_use_bad_type_gen( +49 │ │ w_unbound_var(), +50 │ │ undefined | w_unbound_var()) -> +51 │ │ term(). + │ ╰────────────^ test_use_bad_type_gen/2 references type with invalid definition: w_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:55:1 + │ +55 │ ╭ -type tup_w_unbound(T) +56 │ │ :: {T, _A}. + │ ╰──────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:58:1 + │ +58 │ ╭ -spec unpack_tup_w_unbound( +59 │ │ tup_w_unbound(T) +60 │ │ ) -> T. + │ ╰──────^ unpack_tup_w_unbound/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:64:1 + │ +64 │ ╭ -spec test_use_unpack_tup_w_unbound( +65 │ │ tup_w_unbound(a)) -> a. + │ ╰──────────────────────────^ test_use_unpack_tup_w_unbound/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:69:1 + │ +69 │ ╭ -spec test_use_tup_w_unbound_cov( +70 │ │ tup_w_unbound(a)) -> +71 │ │ tup_w_unbound(a | b) . + │ ╰─────────────────^ test_use_tup_w_unbound_cov/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:75:1 + │ +75 │ ╭ -spec test_use_tup_w_unbound_contra_neg( +76 │ │ tup_w_unbound(a | b)) -> +77 │ │ tup_w_unbound(a) . + │ ╰─────────────────^ test_use_tup_w_unbound_contra_neg/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:81:1 + │ +81 │ ╭ -spec test_use_tup_w_unbound_contra_neg2( +82 │ │ tup_w_unbound(a | b)) -> +83 │ │ tup_w_unbound(a) | w_unbound_var(). + │ ╰────────────────────────────────────^ test_use_tup_w_unbound_contra_neg2/1 references types with invalid definitions: tup_w_unbound/1, w_unbound_var/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/type_aliases.erl:87:1 + │ +87 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:89:1 + │ +89 │ -record(rec1, {l :: loop() | undefined}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rec1 references type with invalid definition: loop/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:90:1 + │ +90 │ -record(rec2, {l :: undefined | loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rec2 references type with invalid definition: loop/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_aliases.erl:104:21 + │ +104 │ opaque:opair(a, x:y()). + │ ^^^^^ Unknown id: x:y/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:106:1 + │ +106 │ ╭ -spec use_bad_inside_neg( +107 │ │ bad_inside() +108 │ │ ) -> bad_inside(). + │ ╰───────────────^ use_bad_inside_neg/1 references type with invalid definition: bad_inside/0 + +22 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-28.pretty new file mode 100644 index 0000000000..0518a9a064 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_aliases-OTP-28.pretty @@ -0,0 +1,157 @@ +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:9:1 + │ +9 │ -type w_unbound_var() :: {_A, _B}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:10:1 + │ +10 │ ╭ -type trans_unbound_var() +11 │ │ :: w_unbound_var(). + │ ╰────────────────────^ trans_unbound_var/0 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:13:1 + │ +13 │ ╭ -spec uses_ty_w_unbound_var(_A, _B) -> +14 │ │ w_unbound_var(). + │ ╰─────────────────^ uses_ty_w_unbound_var/2 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:17:1 + │ +17 │ ╭ -spec uses_trans_unbound_var(_A, _B) -> +18 │ │ trans_unbound_var(). + │ ╰─────────────────────^ uses_trans_unbound_var/2 references type with invalid definition: trans_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:21:1 + │ +21 │ ╭ -type w_unbound_var2() +22 │ │ :: _A. + │ ╰─────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:24:1 + │ +24 │ ╭ -spec uses_ty_w_unbound_var2() -> +25 │ │ w_unbound_var2(). + │ ╰──────────────────^ uses_ty_w_unbound_var2/0 references type with invalid definition: w_unbound_var2/0 + +error: repated_type_var_in_type_decl (See https://fb.me/eqwalizer_errors#repated_type_var_in_type_decl) + ┌─ check/src/type_aliases.erl:28:1 + │ +28 │ -type repeated(T, T) :: T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ T. Type vars in type declarations must be distinct + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:30:1 + │ +30 │ ╭ -spec test_repeated1_neg(a, b) -> +31 │ │ repeated(a, b). + │ ╰────────────^ test_repeated1_neg/2 references type with invalid definition: repeated/2 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:34:1 + │ +34 │ ╭ -spec test_repeated2_neg(a, b) -> +35 │ │ repeated(a, b). + │ ╰────────────^ test_repeated2_neg/2 references type with invalid definition: repeated/2 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:39:1 + │ +39 │ ╭ -spec test_use_bad_type +40 │ │ (undefined | w_unbound_var()) -> +41 │ │ w_unbound_var() | undefined. + │ ╰───────────────────────────────^ test_use_bad_type/1 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:48:1 + │ +48 │ ╭ -spec test_use_bad_type_gen( +49 │ │ w_unbound_var(), +50 │ │ undefined | w_unbound_var()) -> +51 │ │ term(). + │ ╰────────────^ test_use_bad_type_gen/2 references type with invalid definition: w_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ check/src/type_aliases.erl:55:1 + │ +55 │ ╭ -type tup_w_unbound(T) +56 │ │ :: {T, _A}. + │ ╰──────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:58:1 + │ +58 │ ╭ -spec unpack_tup_w_unbound( +59 │ │ tup_w_unbound(T) +60 │ │ ) -> T. + │ ╰──────^ unpack_tup_w_unbound/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:64:1 + │ +64 │ ╭ -spec test_use_unpack_tup_w_unbound( +65 │ │ tup_w_unbound(a)) -> a. + │ ╰──────────────────────────^ test_use_unpack_tup_w_unbound/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:69:1 + │ +69 │ ╭ -spec test_use_tup_w_unbound_cov( +70 │ │ tup_w_unbound(a)) -> +71 │ │ tup_w_unbound(a | b) . + │ ╰─────────────────^ test_use_tup_w_unbound_cov/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:75:1 + │ +75 │ ╭ -spec test_use_tup_w_unbound_contra_neg( +76 │ │ tup_w_unbound(a | b)) -> +77 │ │ tup_w_unbound(a) . + │ ╰─────────────────^ test_use_tup_w_unbound_contra_neg/1 references type with invalid definition: tup_w_unbound/1 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:81:1 + │ +81 │ ╭ -spec test_use_tup_w_unbound_contra_neg2( +82 │ │ tup_w_unbound(a | b)) -> +83 │ │ tup_w_unbound(a) | w_unbound_var(). + │ ╰────────────────────────────────────^ test_use_tup_w_unbound_contra_neg2/1 references types with invalid definitions: tup_w_unbound/1, w_unbound_var/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ check/src/type_aliases.erl:87:1 + │ +87 │ -type loop() :: loop(). + │ ^^^^^^^^^^^^^^^^^^^^ recursive type loop/0 is not productive + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:89:1 + │ +89 │ -record(rec1, {l :: loop() | undefined}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rec1 references type with invalid definition: loop/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:90:1 + │ +90 │ -record(rec2, {l :: undefined | loop()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rec2 references type with invalid definition: loop/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_aliases.erl:104:21 + │ +104 │ opaque:opair(a, x:y()). + │ ^^^^^ Unknown id: x:y/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ check/src/type_aliases.erl:106:1 + │ +106 │ ╭ -spec use_bad_inside_neg( +107 │ │ bad_inside() +108 │ │ ) -> bad_inside(). + │ ╰───────────────^ use_bad_inside_neg/1 references type with invalid definition: bad_inside/0 + +22 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-OTP-26.pretty similarity index 61% rename from crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-26.pretty index 069950f003..c5fb63ed41 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-26.pretty @@ -22,6 +22,12 @@ 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 │ @@ -57,6 +63,17 @@ 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 │ @@ -74,6 +91,13 @@ 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 │ @@ -126,6 +150,17 @@ 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 │ @@ -144,6 +179,17 @@ 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 │ @@ -162,6 +208,17 @@ 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/type_asserts-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-27.pretty new file mode 100644 index 0000000000..c5fb63ed41 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-27.pretty @@ -0,0 +1,256 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:39:3 + │ +39 │ Arg. + │ ^^^ Arg. +Expression has type: string() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:48:3 + │ +48 │ Arg. + │ ^^^ + │ │ + │ Arg. +Expression has type: string() | binary() +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 ------------------------------ + + 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 │ andalso list_to_atom(A). + │ ^ A. +Expression has type: binary() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:70:26 + │ +70 │ andalso list_to_atom(A). + │ ^ A. +Expression has type: binary() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:84:3 + │ +84 │ ╭ ╭ is_number(N) +85 │ │ │ andalso is_atom(A) +86 │ │ │ andalso {N, A}. + │ ╰─│──────────────────^ _ andalso _. +Expression has type: 'false' | {number(), atom()} +Context expected type: 'false' | {atom(), number()} + │ ╰──────────────────' + +Because in the expression's type: + The type is a union type with some valid candidates: 'false' + However, the following candidate doesn't match: + { + Here the type is: number() + 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 + │ +92 │ {X, A}. + │ ^^^^^^ + │ │ + │ {X, A}. +Expression has type: {'false' | number(), term()} +Context expected type: {'false' | number(), number()} + │ + +Because in the expression's type: + { 'false' | number(), + Here the type is: term() + 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 + │ +105 │ checked_cast1_neg(A) -> ?checked_cast(A, ok). + │ ^ A. +Expression has type: atom() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:108:25 + │ +108 │ checked_cast2_neg(A) -> ?checked_cast(A, atom()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ .. :: ... +Expression has type: atom() +Context expected type: 'ok' + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_asserts.erl:110:20 + │ +110 │ -type invalid() :: foo:non_exist(). + │ ^^^^^^^^^^^^^^^ Unknown id: foo:non_exist/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_asserts.erl:113:42 + │ +113 │ checked_cast3_neg(A) -> ?checked_cast(A, foo:non_exist()). + │ ^^^^^^^^^^^^^^^ Unknown id: foo:non_exist/0 + +error: invalid_ref_in_type_cast (See https://fb.me/eqwalizer_errors#invalid_ref_in_type_cast) + ┌─ check/src/type_asserts.erl:116:42 + │ +116 │ checked_cast4_neg(A) -> ?checked_cast(A, {ok, invalid()}). + │ ^^^^^^^^^^^^^^^ type cast references type with invalid definition: invalid/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:126:9 + │ +126 │ Acc#{K => V#{ka => va}}; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +129 │ Acc#{K => V#{kb => vb}}; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +132 │ Acc#{K => V#{kc => vc}} + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +156 │ unsafe_cast1_neg(A) -> ?unchecked_cast(A, err). + │ ^^^^^^^^^^^^^^^^^^^^^^^ .. :: ... +Expression has type: 'err' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:159:40 + │ +159 │ unsafe_cast2_neg(D) -> ?unchecked_cast(a + D, ok). + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: variables_in_type_cast (See https://fb.me/eqwalizer_errors#variables_in_type_cast) + ┌─ check/src/type_asserts.erl:163:21 + │ +163 │ ?checked_cast(ok, A). + │ ^ type variables are not allowed in type casts, found: A + +error: variables_in_type_cast (See https://fb.me/eqwalizer_errors#variables_in_type_cast) + ┌─ check/src/type_asserts.erl:167:23 + │ +167 │ ?unchecked_cast(ok, A | B). + │ ^^^^^ type variables are not allowed in type casts, found: A, B + +error: refined_record_in_type_cast (See https://fb.me/eqwalizer_errors#refined_record_in_type_cast) + ┌─ check/src/type_asserts.erl:173:22 + │ +173 │ ?unchecked_cast(1, #rec{bad_field :: atom()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ refined records are not allowed in type casts, found: rec + +19 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-28.pretty new file mode 100644 index 0000000000..c5fb63ed41 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_asserts-OTP-28.pretty @@ -0,0 +1,256 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:39:3 + │ +39 │ Arg. + │ ^^^ Arg. +Expression has type: string() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:48:3 + │ +48 │ Arg. + │ ^^^ + │ │ + │ Arg. +Expression has type: string() | binary() +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 ------------------------------ + + 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 │ andalso list_to_atom(A). + │ ^ A. +Expression has type: binary() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:70:26 + │ +70 │ andalso list_to_atom(A). + │ ^ A. +Expression has type: binary() +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:84:3 + │ +84 │ ╭ ╭ is_number(N) +85 │ │ │ andalso is_atom(A) +86 │ │ │ andalso {N, A}. + │ ╰─│──────────────────^ _ andalso _. +Expression has type: 'false' | {number(), atom()} +Context expected type: 'false' | {atom(), number()} + │ ╰──────────────────' + +Because in the expression's type: + The type is a union type with some valid candidates: 'false' + However, the following candidate doesn't match: + { + Here the type is: number() + 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 + │ +92 │ {X, A}. + │ ^^^^^^ + │ │ + │ {X, A}. +Expression has type: {'false' | number(), term()} +Context expected type: {'false' | number(), number()} + │ + +Because in the expression's type: + { 'false' | number(), + Here the type is: term() + 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 + │ +105 │ checked_cast1_neg(A) -> ?checked_cast(A, ok). + │ ^ A. +Expression has type: atom() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:108:25 + │ +108 │ checked_cast2_neg(A) -> ?checked_cast(A, atom()). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ .. :: ... +Expression has type: atom() +Context expected type: 'ok' + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_asserts.erl:110:20 + │ +110 │ -type invalid() :: foo:non_exist(). + │ ^^^^^^^^^^^^^^^ Unknown id: foo:non_exist/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ check/src/type_asserts.erl:113:42 + │ +113 │ checked_cast3_neg(A) -> ?checked_cast(A, foo:non_exist()). + │ ^^^^^^^^^^^^^^^ Unknown id: foo:non_exist/0 + +error: invalid_ref_in_type_cast (See https://fb.me/eqwalizer_errors#invalid_ref_in_type_cast) + ┌─ check/src/type_asserts.erl:116:42 + │ +116 │ checked_cast4_neg(A) -> ?checked_cast(A, {ok, invalid()}). + │ ^^^^^^^^^^^^^^^ type cast references type with invalid definition: invalid/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:126:9 + │ +126 │ Acc#{K => V#{ka => va}}; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +129 │ Acc#{K => V#{kb => vb}}; + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +132 │ Acc#{K => V#{kc => vc}} + │ ^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{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'}} +Context expected type: #{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 in the expression's type: + #{ dynamic() => + Here the type is: #{kb := ...} + Context expects 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 + │ +156 │ unsafe_cast1_neg(A) -> ?unchecked_cast(A, err). + │ ^^^^^^^^^^^^^^^^^^^^^^^ .. :: ... +Expression has type: 'err' +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_asserts.erl:159:40 + │ +159 │ unsafe_cast2_neg(D) -> ?unchecked_cast(a + D, ok). + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: variables_in_type_cast (See https://fb.me/eqwalizer_errors#variables_in_type_cast) + ┌─ check/src/type_asserts.erl:163:21 + │ +163 │ ?checked_cast(ok, A). + │ ^ type variables are not allowed in type casts, found: A + +error: variables_in_type_cast (See https://fb.me/eqwalizer_errors#variables_in_type_cast) + ┌─ check/src/type_asserts.erl:167:23 + │ +167 │ ?unchecked_cast(ok, A | B). + │ ^^^^^ type variables are not allowed in type casts, found: A, B + +error: refined_record_in_type_cast (See https://fb.me/eqwalizer_errors#refined_record_in_type_cast) + ┌─ check/src/type_asserts.erl:173:22 + │ +173 │ ?unchecked_cast(1, #rec{bad_field :: atom()}). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ refined records are not allowed in type casts, found: rec + +19 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-27.pretty new file mode 100644 index 0000000000..2725e97755 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-27.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:102:32 + │ +102 │ when is_record(R, rec2) -> R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:107:35 + │ +107 │ when is_record(R, rec2, 1) -> R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:149:39 + │ +149 │ when is_function(F, 2) -> #f1{f = F}. + │ ^ F. +Expression has type: fun((term(), term()) -> term()) +Context expected type: fun((term()) -> term()) + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-28.pretty new file mode 100644 index 0000000000..2725e97755 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/type_predicates-OTP-28.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:102:32 + │ +102 │ when is_record(R, rec2) -> R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:107:35 + │ +107 │ when is_record(R, rec2, 1) -> R. + │ ^ R. +Expression has type: #rec2{} +Context expected type: #rec1{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/type_predicates.erl:149:39 + │ +149 │ when is_function(F, 2) -> #f1{f = F}. + │ ^ F. +Expression has type: fun((term(), term()) -> term()) +Context expected type: fun((term()) -> term()) + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/united_fun-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/unspecced.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-27.pretty new file mode 100644 index 0000000000..b3a6323374 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-27.pretty @@ -0,0 +1,63 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/unspecced.erl:11:1 + │ +11 │ expect_none(None) -> None. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:14:15 + │ +14 │ expect_none({Id}). + │ ^^^^ {Id}. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:20:15 + │ +20 │ expect_none(Res). + │ ^^^ Res. +Expression has type: #{dynamic() => {dynamic()}} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:24:15 + │ +24 │ expect_none({Id}). + │ ^^^^ {Id}. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:28:15 + │ +28 │ expect_none({Res}). + │ ^^^^^ {Res}. +Expression has type: {#{id := number(), dynamic() => dynamic()}} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:32:15 + │ +32 │ expect_none(Res). + │ ^^^ Res. +Expression has type: {dynamic(), dynamic([term()])} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:36:15 + │ +36 │ expect_none(Res). + │ ^^^ Res. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:41:18 + │ +41 │ atom_to_binary(Rec#rec.id). + │ ^^^^^^^^^^ ...#rec.id. +Expression has type: number() +Context expected type: atom() + +8 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-28.pretty new file mode 100644 index 0000000000..b3a6323374 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/unspecced-OTP-28.pretty @@ -0,0 +1,63 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ check/src/unspecced.erl:11:1 + │ +11 │ expect_none(None) -> None. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:14:15 + │ +14 │ expect_none({Id}). + │ ^^^^ {Id}. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:20:15 + │ +20 │ expect_none(Res). + │ ^^^ Res. +Expression has type: #{dynamic() => {dynamic()}} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:24:15 + │ +24 │ expect_none({Id}). + │ ^^^^ {Id}. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:28:15 + │ +28 │ expect_none({Res}). + │ ^^^^^ {Res}. +Expression has type: {#{id := number(), dynamic() => dynamic()}} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:32:15 + │ +32 │ expect_none(Res). + │ ^^^ Res. +Expression has type: {dynamic(), dynamic([term()])} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:36:15 + │ +36 │ expect_none(Res). + │ ^^^ Res. +Expression has type: {dynamic()} +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/unspecced.erl:41:18 + │ +41 │ atom_to_binary(Rec#rec.id). + │ ^^^^^^^^^^ ...#rec.id. +Expression has type: number() +Context expected type: atom() + +8 ERRORS 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual-OTP-26.pretty index b0892292bd..d03afc8cf8 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-OTP-26.pretty @@ -15,4 +15,11 @@ 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_gradual-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual-OTP-27.pretty new file mode 100644 index 0000000000..d03afc8cf8 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual-OTP-27.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/use_dynamic_gradual.erl:19:3 + │ +19 │ {erlang:atom_to_binary(Dyn)}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ {erlang:atom_to_binary(Dyn)}. +Expression has type: {binary()} +Context expected type: {atom()} + │ + +Because in the expression's type: + { + Here the type is: binary() + 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_gradual-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual-OTP-28.pretty new file mode 100644 index 0000000000..d03afc8cf8 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_gradual-OTP-28.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/use_dynamic_gradual.erl:19:3 + │ +19 │ {erlang:atom_to_binary(Dyn)}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ {erlang:atom_to_binary(Dyn)}. +Expression has type: {binary()} +Context expected type: {atom()} + │ + +Because in the expression's type: + { + Here the type is: binary() + 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict-OTP-26.pretty index 034dbd779d..12f1d81948 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-OTP-26.pretty @@ -15,4 +15,11 @@ 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-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict-OTP-27.pretty new file mode 100644 index 0000000000..12f1d81948 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict-OTP-27.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/use_dynamic_strict.erl:17:3 + │ +17 │ {erlang:atom_to_binary(Dyn)}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ {erlang:atom_to_binary(Dyn)}. +Expression has type: {binary()} +Context expected type: {atom()} + │ + +Because in the expression's type: + { + Here the type is: binary() + 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-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict-OTP-28.pretty new file mode 100644 index 0000000000..12f1d81948 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/use_dynamic_strict-OTP-28.pretty @@ -0,0 +1,25 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ check/src/use_dynamic_strict.erl:17:3 + │ +17 │ {erlang:atom_to_binary(Dyn)}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ {erlang:atom_to_binary(Dyn)}. +Expression has type: {binary()} +Context expected type: {atom()} + │ + +Because in the expression's type: + { + Here the type is: binary() + 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/vars1-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars1-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/vars2-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/attributes-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-27.pretty new file mode 100644 index 0000000000..558b077666 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-27.pretty @@ -0,0 +1,7 @@ +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/debug_header.erl:14:1 + │ +14 │ -type t() :: has_unbound(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ t/0 references type with invalid definition: has_unbound/0 + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-28.pretty new file mode 100644 index 0000000000..558b077666 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/debug_header-OTP-28.pretty @@ -0,0 +1,7 @@ +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/debug_header.erl:14:1 + │ +14 │ -type t() :: has_unbound(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^ t/0 references type with invalid definition: has_unbound/0 + +1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expand.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/expand.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-27.pretty new file mode 100644 index 0000000000..7082351ffd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-27.pretty @@ -0,0 +1,98 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:27:8 + │ +27 │ -> types1:bin_tree_typo(). + │ ^^^^^^^^^^^^^^^^^^^^^^ Unknown id: types1:bin_tree_typo/0 + +error: recursive_constraint (See https://fb.me/eqwalizer_errors#recursive_constraint) + ┌─ debug/src/expand.erl:35:27 + │ +35 │ Rec when Rec :: {rec, Rec}. + │ ^^^ Recursive constraint: Rec + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/expand.erl:42:1 + │ +42 │ -type w_unbound_var() :: {_A, _B}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:43:1 + │ +43 │ ╭ -type trans_unbound_var() +44 │ │ :: w_unbound_var(). + │ ╰────────────────────^ trans_unbound_var/0 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:46:1 + │ +46 │ ╭ -spec uses_ty_w_unbound_var(_A, _B) -> +47 │ │ w_unbound_var(). + │ ╰─────────────────^ uses_ty_w_unbound_var/2 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:50:1 + │ +50 │ ╭ -spec uses_trans_unbound_var(_A, _B) -> +51 │ │ trans_unbound_var(). + │ ╰─────────────────────^ uses_trans_unbound_var/2 references type with invalid definition: trans_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/expand.erl:54:1 + │ +54 │ ╭ -type w_unbound_var2() +55 │ │ :: _A. + │ ╰─────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:57:1 + │ +57 │ ╭ -spec uses_ty_w_unbound_var2() -> +58 │ │ w_unbound_var2(). + │ ╰──────────────────^ uses_ty_w_unbound_var2/0 references type with invalid definition: w_unbound_var2/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:62:12 + │ +62 │ ref :: undefined_mod:ref() + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: undefined_mod:ref/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:64:1 + │ +64 │ -type bad_ref() :: #bad_ref{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad_ref/0 references type with invalid definition: #bad_ref{} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ debug/src/expand.erl:66:1 + │ +66 │ -opaque bad_opaque() :: bad_opaque(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type bad_opaque/0 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:68:25 + │ +68 │ -record(bad_rec, {id :: unknown:id()}). + │ ^^^^^^^^^^^^ Unknown id: unknown:id/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:69:1 + │ +69 │ -spec mk_bad_rec() -> #bad_rec{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mk_bad_rec/0 references type with invalid definition: #bad_rec{} + +error: unbound_record (See https://fb.me/eqwalizer_errors#unbound_record) + ┌─ debug/src/expand.erl:70:17 + │ +70 │ mk_bad_rec() -> #bad_rec{}. + │ ^^^^^^^^^^ Unbound rec: bad_rec + +error: ty_var_with_multiple_constraints (See https://fb.me/eqwalizer_errors#ty_var_with_multiple_constraints) + ┌─ debug/src/expand.erl:72:23 + │ +72 │ -spec double_constrain(Thing) -> Thing + │ ╭───────────────────────^ +73 │ │ when Thing :: pid(), Thing :: atom(). + │ ╰────────────────────────────────────^ Type variable 'Thing' is constrained multiple times, please remove the extra constraints + +15 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-28.pretty new file mode 100644 index 0000000000..7082351ffd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expand-OTP-28.pretty @@ -0,0 +1,98 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:27:8 + │ +27 │ -> types1:bin_tree_typo(). + │ ^^^^^^^^^^^^^^^^^^^^^^ Unknown id: types1:bin_tree_typo/0 + +error: recursive_constraint (See https://fb.me/eqwalizer_errors#recursive_constraint) + ┌─ debug/src/expand.erl:35:27 + │ +35 │ Rec when Rec :: {rec, Rec}. + │ ^^^ Recursive constraint: Rec + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/expand.erl:42:1 + │ +42 │ -type w_unbound_var() :: {_A, _B}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:43:1 + │ +43 │ ╭ -type trans_unbound_var() +44 │ │ :: w_unbound_var(). + │ ╰────────────────────^ trans_unbound_var/0 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:46:1 + │ +46 │ ╭ -spec uses_ty_w_unbound_var(_A, _B) -> +47 │ │ w_unbound_var(). + │ ╰─────────────────^ uses_ty_w_unbound_var/2 references type with invalid definition: w_unbound_var/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:50:1 + │ +50 │ ╭ -spec uses_trans_unbound_var(_A, _B) -> +51 │ │ trans_unbound_var(). + │ ╰─────────────────────^ uses_trans_unbound_var/2 references type with invalid definition: trans_unbound_var/0 + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/expand.erl:54:1 + │ +54 │ ╭ -type w_unbound_var2() +55 │ │ :: _A. + │ ╰─────────^ _A: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:57:1 + │ +57 │ ╭ -spec uses_ty_w_unbound_var2() -> +58 │ │ w_unbound_var2(). + │ ╰──────────────────^ uses_ty_w_unbound_var2/0 references type with invalid definition: w_unbound_var2/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:62:12 + │ +62 │ ref :: undefined_mod:ref() + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: undefined_mod:ref/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:64:1 + │ +64 │ -type bad_ref() :: #bad_ref{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad_ref/0 references type with invalid definition: #bad_ref{} + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ debug/src/expand.erl:66:1 + │ +66 │ -opaque bad_opaque() :: bad_opaque(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type bad_opaque/0 is not productive + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/expand.erl:68:25 + │ +68 │ -record(bad_rec, {id :: unknown:id()}). + │ ^^^^^^^^^^^^ Unknown id: unknown:id/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/expand.erl:69:1 + │ +69 │ -spec mk_bad_rec() -> #bad_rec{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mk_bad_rec/0 references type with invalid definition: #bad_rec{} + +error: unbound_record (See https://fb.me/eqwalizer_errors#unbound_record) + ┌─ debug/src/expand.erl:70:17 + │ +70 │ mk_bad_rec() -> #bad_rec{}. + │ ^^^^^^^^^^ Unbound rec: bad_rec + +error: ty_var_with_multiple_constraints (See https://fb.me/eqwalizer_errors#ty_var_with_multiple_constraints) + ┌─ debug/src/expand.erl:72:23 + │ +72 │ -spec double_constrain(Thing) -> Thing + │ ╭───────────────────────^ +73 │ │ when Thing :: pid(), Thing :: atom(). + │ ╰────────────────────────────────────^ Type variable 'Thing' is constrained multiple times, please remove the extra constraints + +15 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/expr1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-27.pretty new file mode 100644 index 0000000000..85e1a2782c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-27.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ debug/src/expr1.erl:83:8 + │ +83 │ << X || X <- [1, 2, 3] >>. + │ ^ X. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ debug/src/expr1.erl:86:8 + │ +86 │ << X || <> <= <<1, 2, 3>> >>. + │ ^ X. +Expression has type: number() +Context expected type: binary() + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-28.pretty new file mode 100644 index 0000000000..85e1a2782c --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr1-OTP-28.pretty @@ -0,0 +1,17 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ debug/src/expr1.erl:83:8 + │ +83 │ << X || X <- [1, 2, 3] >>. + │ ^ X. +Expression has type: number() +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ debug/src/expr1.erl:86:8 + │ +86 │ << X || <> <= <<1, 2, 3>> >>. + │ ^ X. +Expression has type: number() +Context expected type: binary() + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/expr2-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-27.pretty new file mode 100644 index 0000000000..6ce3d4eb83 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-27.pretty @@ -0,0 +1,46 @@ +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ debug/src/records_wip.erl:60:14 + │ +60 │ field :: _TyVar + │ ^^^^^^ _TyVar: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:62:1 + │ +62 │ -type ty_var_in_field() :: #ty_var_in_field{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field/0 references type with invalid definition: #ty_var_in_field{} + +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ debug/src/records_wip.erl:66:9 + │ +66 │ _TyVar + │ ^^^^^^ _TyVar: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:68:1 + │ +68 │ -type ty_var_in_field2() :: #ty_var_in_field2{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field2/0 references type with invalid definition: #ty_var_in_field2{} + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/records_wip.erl:70:1 + │ +70 │ -type var_from_nowhere() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:72:1 + │ +72 │ ╭ -record(ty_var_in_field3, { +73 │ │ field :: +74 │ │ var_from_nowhere() +75 │ │ }). + │ ╰──^ ty_var_in_field3 references type with invalid definition: var_from_nowhere/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:76:1 + │ +76 │ -type ty_var_in_field3() :: #ty_var_in_field3{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field3/0 references type with invalid definition: #ty_var_in_field3{} + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-28.pretty new file mode 100644 index 0000000000..6ce3d4eb83 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/records_wip-OTP-28.pretty @@ -0,0 +1,46 @@ +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ debug/src/records_wip.erl:60:14 + │ +60 │ field :: _TyVar + │ ^^^^^^ _TyVar: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:62:1 + │ +62 │ -type ty_var_in_field() :: #ty_var_in_field{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field/0 references type with invalid definition: #ty_var_in_field{} + +error: type_var_in_record_field (See https://fb.me/eqwalizer_errors#type_var_in_record_field) + ┌─ debug/src/records_wip.erl:66:9 + │ +66 │ _TyVar + │ ^^^^^^ _TyVar: Type variables are meaningless in record fields. Did you mean to use an alias? + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:68:1 + │ +68 │ -type ty_var_in_field2() :: #ty_var_in_field2{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field2/0 references type with invalid definition: #ty_var_in_field2{} + +error: unbound_type_var (See https://fb.me/eqwalizer_errors#unbound_type_var) + ┌─ debug/src/records_wip.erl:70:1 + │ +70 │ -type var_from_nowhere() :: _T. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _T: Type variable is unbound. + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:72:1 + │ +72 │ ╭ -record(ty_var_in_field3, { +73 │ │ field :: +74 │ │ var_from_nowhere() +75 │ │ }). + │ ╰──^ ty_var_in_field3 references type with invalid definition: var_from_nowhere/0 + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ debug/src/records_wip.erl:76:1 + │ +76 │ -type ty_var_in_field3() :: #ty_var_in_field3{}. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ty_var_in_field3/0 references type with invalid definition: #ty_var_in_field3{} + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types1.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/types1.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-27.pretty new file mode 100644 index 0000000000..d20e8068bd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-27.pretty @@ -0,0 +1,15 @@ +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ debug/src/types1.erl:311:5 + │ +311 │ integer() => integer() + │ ^^^^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Only one default association per map is allowed, all other keys should be composed of statically defined atoms or tuples. + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ debug/src/types1.erl:315:5 + │ +315 │ atom() := atom(), + │ ^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-28.pretty new file mode 100644 index 0000000000..d20e8068bd --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/types1-OTP-28.pretty @@ -0,0 +1,15 @@ +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ debug/src/types1.erl:311:5 + │ +311 │ integer() => integer() + │ ^^^^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Only one default association per map is allowed, all other keys should be composed of statically defined atoms or tuples. + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ debug/src/types1.erl:315:5 + │ +315 │ atom() := atom(), + │ ^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types2.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/debug/types2.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-27.pretty new file mode 100644 index 0000000000..1bab90f3a6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-27.pretty @@ -0,0 +1,19 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/types2.erl:18:5 + │ +18 │ forms1:my_number(). + │ ^^^^^^^^^^^^^^^^^^ Unknown id: forms1:my_number/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/types2.erl:21:5 + │ +21 │ unknown:my_number(). + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: unknown:my_number/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ debug/src/types2.erl:30:1 + │ +30 │ -type recur_invalid(X) :: o( + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type recur_invalid/1 is not productive + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-28.pretty new file mode 100644 index 0000000000..1bab90f3a6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/types2-OTP-28.pretty @@ -0,0 +1,19 @@ +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/types2.erl:18:5 + │ +18 │ forms1:my_number(). + │ ^^^^^^^^^^^^^^^^^^ Unknown id: forms1:my_number/0 + +error: unknown_id (See https://fb.me/eqwalizer_errors#unknown_id) + ┌─ debug/src/types2.erl:21:5 + │ +21 │ unknown:my_number(). + │ ^^^^^^^^^^^^^^^^^^^ Unknown id: unknown:my_number/0 + +error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive) + ┌─ debug/src/types2.erl:30:1 + │ +30 │ -type recur_invalid(X) :: o( + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive type recur_invalid/1 is not productive + +3 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/debug/wip_maps-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/basics-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/list-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/map_ffi-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/maybe-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/result-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/elm_core/tuple-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-27.pretty new file mode 100644 index 0000000000..5a3c9743cb --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-27.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:14:3 + │ +14 │ Tag1. + │ ^^^^ Tag1. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:20:3 + │ +20 │ Tag2. + │ ^^^^ Tag2. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:26:3 + │ +26 │ Tag1. + │ ^^^^ Tag1. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:32:3 + │ +32 │ Tag2. + │ ^^^^ Tag2. +Expression has type: 'ok' +Context expected type: none() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-28.pretty new file mode 100644 index 0000000000..5a3c9743cb --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/deep_tuples-OTP-28.pretty @@ -0,0 +1,33 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:14:3 + │ +14 │ Tag1. + │ ^^^^ Tag1. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:20:3 + │ +20 │ Tag2. + │ ^^^^ Tag2. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:26:3 + │ +26 │ Tag1. + │ ^^^^ Tag1. +Expression has type: 'ok' +Context expected type: none() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/deep_tuples.erl:32:3 + │ +32 │ Tag2. + │ ^^^^ Tag2. +Expression has type: 'ok' +Context expected type: none() + +4 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-26.pretty similarity index 72% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-26.pretty index 3c6de10f94..05fbf33987 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-26.pretty @@ -5,7 +5,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ B. -Expression has type: number() | binary() +Expression has type: none() | number() | binary() Context expected type: binary() │ @@ -14,6 +14,12 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() +------------------------------ Detailed message ------------------------------ + + none() | 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 │ @@ -21,7 +27,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ B. -Expression has type: number() | binary() +Expression has type: none() | number() | binary() Context expected type: binary() │ @@ -30,6 +36,12 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() +------------------------------ Detailed message ------------------------------ + + none() | 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 │ @@ -37,7 +49,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ A. -Expression has type: number() | binary() +Expression has type: none() | number() | binary() Context expected type: binary() │ @@ -46,6 +58,12 @@ Because in the expression's type: However the following candidate: number() Differs from the expected type: binary() +------------------------------ Detailed message ------------------------------ + + none() | 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 │ @@ -68,6 +86,12 @@ 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 │ @@ -84,6 +108,12 @@ 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 │ @@ -122,6 +152,12 @@ 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 │ @@ -146,6 +182,12 @@ 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 │ @@ -162,13 +204,26 @@ 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 │ 496 │ A. - │ ^ A. -Expression has type: atom() + │ ^ + │ │ + │ A. +Expression has type: atom() | none() Context expected type: binary() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects type: binary() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:521:22 @@ -187,6 +242,13 @@ Because in the expression's type: Context expects type: atom() } +------------------------------ Detailed message ------------------------------ + + at tuple index 2: + {'union_field', none() | binary()} is not compatible with {'union_field', atom()} + because + none() | binary() is not compatible with atom() + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:559:50 │ @@ -203,6 +265,12 @@ 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 │ @@ -210,7 +278,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ B. -Expression has type: #b{} | #c{} +Expression has type: none() | #b{} | #c{} Context expected type: #b{} │ @@ -219,6 +287,12 @@ Because in the expression's type: However the following candidate: #c{} Differs from the expected type: #b{} +------------------------------ Detailed message ------------------------------ + + none() | #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 │ @@ -235,6 +309,12 @@ 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 │ @@ -251,6 +331,12 @@ 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 │ @@ -267,6 +353,34 @@ 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:681:18 + │ +681 │ {_N, _N1} -> X#one_field.f1 + │ ^ + │ │ + │ X. +Expression has type: #one_field{} | #two_fields1{} +Context expected type: #one_field{} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #one_field{} + However the following candidate: #two_fields1{} + Differs from the expected type: #one_field{} + +------------------------------ Detailed message ------------------------------ + + #one_field{} | #two_fields1{} is not compatible with #one_field{} + because + #two_fields1{} is not compatible with #one_field{} + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:839:27 │ @@ -283,6 +397,12 @@ 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 │ @@ -321,6 +441,12 @@ 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 │ @@ -340,6 +466,14 @@ 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 │ @@ -359,6 +493,14 @@ 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 │ @@ -375,6 +517,12 @@ 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 │ @@ -391,6 +539,12 @@ 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 │ @@ -407,6 +561,12 @@ 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 │ @@ -423,6 +583,12 @@ 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 │ @@ -430,7 +596,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^^^^ │ │ │ Args. -Expression has type: {term(), my_list()} | number() +Expression has type: {term(), my_list()} | none() | number() Context expected type: number() │ @@ -439,6 +605,12 @@ Because in the expression's type: However the following candidate: {term(), my_list()} Differs from the expected type: number() +------------------------------ Detailed message ------------------------------ + + {term(), my_list()} | none() | 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 │ @@ -463,21 +635,42 @@ 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 │ 1269 │ V. - │ ^ V. -Expression has type: 'a' + │ ^ + │ │ + │ V. +Expression has type: 'a' | none() Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is: 'a' + Context expects type: 'b' 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. -Expression has type: 'c' + │ ^ + │ │ + │ A. +Expression has type: none() | 'c' Context expected type: 'a' | 'b' + │ + +Because in the expression's type: + Here the type is: 'c' + Context expects type: 'a' | 'b' + No candidate matches in the expected union. error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ eqwater/src/eqwater.erl:1291:43 @@ -487,12 +680,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: number() Context expected type: 'ok' -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ eqwater/src/eqwater.erl:1309:45 - │ -1309 │ negate_fun_neg(F) when is_function(F, 2) -> F; - │ ^ F. -Expression has type: fun((term(), term()) -> term()) -Context expected type: 'ok' - 38 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-27.pretty new file mode 100644 index 0000000000..05fbf33987 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-27.pretty @@ -0,0 +1,683 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:162:7 + │ +162 │ B + │ ^ + │ │ + │ B. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +172 │ occ05_3_neg_cl(B) -> B. + │ ^ + │ │ + │ B. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +183 │ A + │ ^ + │ │ + │ A. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +228 │ occ08(A) when is_number(A) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:340:13 + │ +340 │ occ11(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: #ab_rec{} | 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: #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 + │ +356 │ occ13(A, _) -> A. + │ ^ + │ │ + │ A. +Expression has type: #ab_rec{} | 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: #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 + │ +404 │ get_int4_neg({_, I}) -> I. + │ ^ I. +Expression has type: atom() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:408:1 + │ +408 │ ╭ get_int5({A, A}) +409 │ │ when is_atom(A) -> A. + │ ╰──────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:413:1 + │ +413 │ ╭ get_int6({A1, A2}) +414 │ │ when is_atom(A1) and is_atom(A2) -> 1; + │ ╰───────────────────────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:415:21 + │ +415 │ get_int6({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +419 │ ╭ get_int7_neg({A, A}) +420 │ │ % A is none() +421 │ │ when is_atom(A) -> 0; + │ ╰──────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:422:25 + │ +422 │ get_int7_neg({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +429 │ get_int8_neg({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +496 │ A. + │ ^ + │ │ + │ A. +Expression has type: atom() | none() +Context expected type: binary() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:521:22 + │ +521 │ record_occ07_tuple(R). + │ ^ + │ │ + │ R. +Expression has type: #union_field{} +Context expected type: {'union_field', atom()} + │ + +Because in the expression's type: + { 'union_field', + Here the type is: binary() + Context expects type: atom() + } + +------------------------------ Detailed message ------------------------------ + + at tuple index 2: + {'union_field', none() | binary()} is not compatible with {'union_field', atom()} + because + none() | binary() is not compatible with atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:559:50 + │ +559 │ record_occ13_neg(#union_field4{x = A, y = _}) -> A. + │ ^ + │ │ + │ A. +Expression has type: number() | 'ok' +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: '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 + │ +590 │ -> B#b.id; + │ ^ + │ │ + │ B. +Expression has type: none() | #b{} | #c{} +Context expected type: #b{} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #b{} + However the following candidate: #c{} + Differs from the expected type: #b{} + +------------------------------ Detailed message ------------------------------ + + none() | #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 + │ +631 │ occ23_neg({_, B}, F) -> F(B). + │ ^ + │ │ + │ B. +Expression has type: A | B +Context expected type: B + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: B + 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 + │ +649 │ occ26_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: fun() | {term()} +Context expected type: {term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: {term()} + 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 + │ +660 │ occ28_neg(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: tuple() | 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: 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:681:18 + │ +681 │ {_N, _N1} -> X#one_field.f1 + │ ^ + │ │ + │ X. +Expression has type: #one_field{} | #two_fields1{} +Context expected type: #one_field{} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #one_field{} + However the following candidate: #two_fields1{} + Differs from the expected type: #one_field{} + +------------------------------ Detailed message ------------------------------ + + #one_field{} | #two_fields1{} is not compatible with #one_field{} + because + #two_fields1{} is not compatible with #one_field{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:839:27 + │ +839 │ multi_param2_neg(_, Y) -> Y. + │ ^ + │ │ + │ Y. +Expression has type: number() | atom() +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: 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 + │ +997 │ occ42_neg({F, S} = {_, _}) -> F. + │ ^ F. +Expression has type: string() | 'undefined' +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1004:31 + │ +1004 │ occ43_neg({F, S} = {_, _}) -> F. + │ ^ F. +Expression has type: string() | 'undefined' +Context expected type: string() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:1090:1 + │ +1090 │ occ53_neg(L, undefined) when is_integer(L) -> 0; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1091:20 + │ +1091 │ occ53_neg(_, I) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | 'undefined' +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: '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 + │ +1122 │ A -> [A] + │ ^^^ + │ │ + │ [A]. +Expression has type: [[atom()]] | [atom()] +Context expected type: [atom()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [atom()] + However, the following candidate doesn't match: + [ + Here the type is: [atom()] + 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 + │ +1131 │ A2 -> [A2] + │ ^^^^ + │ │ + │ [A2]. +Expression has type: [[atom()]] | [atom()] +Context expected type: [atom()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [atom()] + However, the following candidate doesn't match: + [ + Here the type is: [atom()] + 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 + │ +1155 │ X + │ ^ + │ │ + │ X. +Expression has type: binary() | string() +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 ------------------------------ + + 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 + │ +1181 │ Y + │ ^ + │ │ + │ Y. +Expression has type: binary() | atom() +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: 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 + │ +1189 │ occ_guard_binary_2_neg(V) when V =:= <<"ok">>; V =:= ok -> V; + │ ^ + │ │ + │ V. +Expression has type: 'ok' | binary() +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: '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 + │ +1194 │ occ_guard_binary_3_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: binary() | 'ok' +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: 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 + │ +1225 │ _ -> Args + │ ^^^^ + │ │ + │ Args. +Expression has type: {term(), my_list()} | none() | 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: {term(), my_list()} + Differs from the expected type: number() + +------------------------------ Detailed message ------------------------------ + + {term(), my_list()} | none() | 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 + │ +1231 │ _ when is_tuple(T) -> T + │ ^ T. +Expression has type: tuple() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1257:29 + │ +1257 │ non_linear_neg_1({B, B}) -> B. + │ ^ + │ │ + │ B. +Expression has type: 'a' | 'b' +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' + 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 + │ +1269 │ V. + │ ^ + │ │ + │ V. +Expression has type: 'a' | none() +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is: 'a' + Context expects type: 'b' + +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. +Expression has type: none() | 'c' +Context expected type: 'a' | 'b' + │ + +Because in the expression's type: + Here the type is: 'c' + Context expects type: 'a' | 'b' + No candidate matches in the expected union. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1291:43 + │ +1291 │ negate_number_neg(N) when not (N == 1) -> N; + │ ^ N. +Expression has type: number() +Context expected type: 'ok' + +38 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-28.pretty new file mode 100644 index 0000000000..05fbf33987 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater-OTP-28.pretty @@ -0,0 +1,683 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:162:7 + │ +162 │ B + │ ^ + │ │ + │ B. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +172 │ occ05_3_neg_cl(B) -> B. + │ ^ + │ │ + │ B. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +183 │ A + │ ^ + │ │ + │ A. +Expression has type: none() | number() | binary() +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: number() + Differs from the expected type: binary() + +------------------------------ Detailed message ------------------------------ + + none() | 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 + │ +228 │ occ08(A) when is_number(A) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:340:13 + │ +340 │ occ11(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: #ab_rec{} | 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: #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 + │ +356 │ occ13(A, _) -> A. + │ ^ + │ │ + │ A. +Expression has type: #ab_rec{} | 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: #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 + │ +404 │ get_int4_neg({_, I}) -> I. + │ ^ I. +Expression has type: atom() +Context expected type: number() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:408:1 + │ +408 │ ╭ get_int5({A, A}) +409 │ │ when is_atom(A) -> A. + │ ╰──────────────────────^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:413:1 + │ +413 │ ╭ get_int6({A1, A2}) +414 │ │ when is_atom(A1) and is_atom(A2) -> 1; + │ ╰───────────────────────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:415:21 + │ +415 │ get_int6({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +419 │ ╭ get_int7_neg({A, A}) +420 │ │ % A is none() +421 │ │ when is_atom(A) -> 0; + │ ╰──────────────────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:422:25 + │ +422 │ get_int7_neg({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +429 │ get_int8_neg({_, I}) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | atom() +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: 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 + │ +496 │ A. + │ ^ + │ │ + │ A. +Expression has type: atom() | none() +Context expected type: binary() + │ + +Because in the expression's type: + Here the type is: atom() + Context expects type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:521:22 + │ +521 │ record_occ07_tuple(R). + │ ^ + │ │ + │ R. +Expression has type: #union_field{} +Context expected type: {'union_field', atom()} + │ + +Because in the expression's type: + { 'union_field', + Here the type is: binary() + Context expects type: atom() + } + +------------------------------ Detailed message ------------------------------ + + at tuple index 2: + {'union_field', none() | binary()} is not compatible with {'union_field', atom()} + because + none() | binary() is not compatible with atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:559:50 + │ +559 │ record_occ13_neg(#union_field4{x = A, y = _}) -> A. + │ ^ + │ │ + │ A. +Expression has type: number() | 'ok' +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: '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 + │ +590 │ -> B#b.id; + │ ^ + │ │ + │ B. +Expression has type: none() | #b{} | #c{} +Context expected type: #b{} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #b{} + However the following candidate: #c{} + Differs from the expected type: #b{} + +------------------------------ Detailed message ------------------------------ + + none() | #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 + │ +631 │ occ23_neg({_, B}, F) -> F(B). + │ ^ + │ │ + │ B. +Expression has type: A | B +Context expected type: B + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: B + 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 + │ +649 │ occ26_neg(T) -> T. + │ ^ + │ │ + │ T. +Expression has type: fun() | {term()} +Context expected type: {term()} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: {term()} + 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 + │ +660 │ occ28_neg(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: tuple() | 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: 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:681:18 + │ +681 │ {_N, _N1} -> X#one_field.f1 + │ ^ + │ │ + │ X. +Expression has type: #one_field{} | #two_fields1{} +Context expected type: #one_field{} + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: #one_field{} + However the following candidate: #two_fields1{} + Differs from the expected type: #one_field{} + +------------------------------ Detailed message ------------------------------ + + #one_field{} | #two_fields1{} is not compatible with #one_field{} + because + #two_fields1{} is not compatible with #one_field{} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:839:27 + │ +839 │ multi_param2_neg(_, Y) -> Y. + │ ^ + │ │ + │ Y. +Expression has type: number() | atom() +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: 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 + │ +997 │ occ42_neg({F, S} = {_, _}) -> F. + │ ^ F. +Expression has type: string() | 'undefined' +Context expected type: string() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1004:31 + │ +1004 │ occ43_neg({F, S} = {_, _}) -> F. + │ ^ F. +Expression has type: string() | 'undefined' +Context expected type: string() + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ eqwater/src/eqwater.erl:1090:1 + │ +1090 │ occ53_neg(L, undefined) when is_integer(L) -> 0; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1091:20 + │ +1091 │ occ53_neg(_, I) -> I. + │ ^ + │ │ + │ I. +Expression has type: number() | 'undefined' +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: '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 + │ +1122 │ A -> [A] + │ ^^^ + │ │ + │ [A]. +Expression has type: [[atom()]] | [atom()] +Context expected type: [atom()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [atom()] + However, the following candidate doesn't match: + [ + Here the type is: [atom()] + 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 + │ +1131 │ A2 -> [A2] + │ ^^^^ + │ │ + │ [A2]. +Expression has type: [[atom()]] | [atom()] +Context expected type: [atom()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [atom()] + However, the following candidate doesn't match: + [ + Here the type is: [atom()] + 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 + │ +1155 │ X + │ ^ + │ │ + │ X. +Expression has type: binary() | string() +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 ------------------------------ + + 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 + │ +1181 │ Y + │ ^ + │ │ + │ Y. +Expression has type: binary() | atom() +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: 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 + │ +1189 │ occ_guard_binary_2_neg(V) when V =:= <<"ok">>; V =:= ok -> V; + │ ^ + │ │ + │ V. +Expression has type: 'ok' | binary() +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: '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 + │ +1194 │ occ_guard_binary_3_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: binary() | 'ok' +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: 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 + │ +1225 │ _ -> Args + │ ^^^^ + │ │ + │ Args. +Expression has type: {term(), my_list()} | none() | 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: {term(), my_list()} + Differs from the expected type: number() + +------------------------------ Detailed message ------------------------------ + + {term(), my_list()} | none() | 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 + │ +1231 │ _ when is_tuple(T) -> T + │ ^ T. +Expression has type: tuple() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1257:29 + │ +1257 │ non_linear_neg_1({B, B}) -> B. + │ ^ + │ │ + │ B. +Expression has type: 'a' | 'b' +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'b' + 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 + │ +1269 │ V. + │ ^ + │ │ + │ V. +Expression has type: 'a' | none() +Context expected type: 'b' + │ + +Because in the expression's type: + Here the type is: 'a' + Context expects type: 'b' + +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. +Expression has type: none() | 'c' +Context expected type: 'a' | 'b' + │ + +Because in the expression's type: + Here the type is: 'c' + Context expects type: 'a' | 'b' + No candidate matches in the expected union. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater.erl:1291:43 + │ +1291 │ negate_number_neg(N) when not (N == 1) -> N; + │ ^ N. +Expression has type: number() +Context expected type: 'ok' + +38 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_aliases-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_generics-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS 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-OTP-26.pretty similarity index 76% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-26.pretty index bfb8103b16..b505f94dee 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-26.pretty @@ -16,6 +16,14 @@ 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 │ @@ -40,7 +48,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ A. -Expression has type: atom() | [] +Expression has type: none() | atom() | [] Context expected type: atom() │ @@ -49,6 +57,12 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() +------------------------------ Detailed message ------------------------------ + + none() | 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 │ @@ -56,7 +70,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ A. -Expression has type: atom() | [] +Expression has type: none() | atom() | [] Context expected type: atom() │ @@ -65,6 +79,12 @@ Because in the expression's type: However the following candidate: [] Differs from the expected type: atom() +------------------------------ Detailed message ------------------------------ + + none() | 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 │ @@ -101,6 +121,14 @@ 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 │ @@ -127,4 +155,14 @@ 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_lists-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-27.pretty new file mode 100644 index 0000000000..b505f94dee --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-27.pretty @@ -0,0 +1,168 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_lists.erl:31:51 + │ +31 │ occ_list_04_neg(L = [H | _]) when is_binary(H) -> L. + │ ^ + │ │ + │ L. +Expression has type: [binary() | atom()] +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: atom() + 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 + │ +48 │ occ_list_07_neg(L) -> L. + │ ^ + │ │ + │ L. +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) + ┌─ eqwater/src/eqwater_lists.erl:54:23 + │ +54 │ occ_list_08_neg(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: none() | 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 ------------------------------ + + none() | 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 + │ +61 │ occ_list_09(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: none() | 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 ------------------------------ + + none() | 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 + │ +69 │ occ_list_10(L) -> L. + │ ^ + │ │ + │ L. +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) + ┌─ eqwater/src/eqwater_lists.erl:76:23 + │ +76 │ occ_list_11_neg(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: [atom()] | [binary()] +Context expected type: [binary()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [binary()] + However, the following candidate doesn't match: + [ + Here the type is: atom() + 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 + │ +97 │ occ_list_14_neg(L) -> L. + │ ^ L. +Expression has type: [] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_lists.erl:130:23 + │ +130 │ occ_list_20_neg(V) -> [ok | [V]]. + │ ^^^^^^^^^^ + │ │ + │ ['ok', V]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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_lists-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-28.pretty new file mode 100644 index 0000000000..b505f94dee --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_lists-OTP-28.pretty @@ -0,0 +1,168 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_lists.erl:31:51 + │ +31 │ occ_list_04_neg(L = [H | _]) when is_binary(H) -> L. + │ ^ + │ │ + │ L. +Expression has type: [binary() | atom()] +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: atom() + 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 + │ +48 │ occ_list_07_neg(L) -> L. + │ ^ + │ │ + │ L. +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) + ┌─ eqwater/src/eqwater_lists.erl:54:23 + │ +54 │ occ_list_08_neg(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: none() | 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 ------------------------------ + + none() | 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 + │ +61 │ occ_list_09(A) -> A. + │ ^ + │ │ + │ A. +Expression has type: none() | 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 ------------------------------ + + none() | 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 + │ +69 │ occ_list_10(L) -> L. + │ ^ + │ │ + │ L. +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) + ┌─ eqwater/src/eqwater_lists.erl:76:23 + │ +76 │ occ_list_11_neg(L) -> L. + │ ^ + │ │ + │ L. +Expression has type: [atom()] | [binary()] +Context expected type: [binary()] + │ + +Because in the expression's type: + The type is a union type with some valid candidates: [binary()] + However, the following candidate doesn't match: + [ + Here the type is: atom() + 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 + │ +97 │ occ_list_14_neg(L) -> L. + │ ^ L. +Expression has type: [] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_lists.erl:130:23 + │ +130 │ occ_list_20_neg(V) -> [ok | [V]]. + │ ^^^^^^^^^^ + │ │ + │ ['ok', V]. +Expression has type: [atom() | binary()] +Context expected type: [atom()] | [binary()] + │ + +Because in the expression's type: + [ + Here the type is a union type with some valid candidates: atom() + However the following candidate: binary() + 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-OTP-26.pretty similarity index 81% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-26.pretty index b5f637ead3..0b5e330414 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-26.pretty @@ -14,6 +14,10 @@ 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 │ @@ -30,6 +34,10 @@ 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 │ @@ -46,6 +54,12 @@ 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 │ @@ -53,7 +67,7 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types │ ^ │ │ │ M. -Expression has type: #{c := number()} +Expression has type: none() | #{c := number()} Context expected type: #{b := number()} │ @@ -70,20 +84,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: #{a := dynamic(), dynamic() => dynamic()} Context expected type: 'err' -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 +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-27.pretty new file mode 100644 index 0000000000..0b5e330414 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-27.pretty @@ -0,0 +1,87 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:26:53 + │ +26 │ map_occ_04_neg(M = #{a := I}) when is_integer(I) -> M; + │ ^ + │ │ + │ M. +Expression has type: #{a := number(), b => number()} +Context expected type: #{a := number(), b := number()} + │ + +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) + ┌─ eqwater/src/eqwater_maps.erl:27:22 + │ +27 │ map_occ_04_neg(M) -> M#{a => 0}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := number(), b => number()} +Context expected type: #{a := number(), b := number()} + │ + +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) + ┌─ eqwater/src/eqwater_maps.erl:36:22 + │ +36 │ map_occ_06_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: #{} | 'ok' +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: #{} + 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 + │ +42 │ map_occ_07_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: none() | #{c := number()} +Context expected type: #{b := number()} + │ + +Because in the expression's type: + Here the type is: #{...} + Context expects type: #{b := ..., ...} + The type of the expression is missing the following required keys: b. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:55:34 + │ +55 │ map_occ_08_neg(#{a := _} = M) -> M; + │ ^ M. +Expression has type: #{a := dynamic(), dynamic() => dynamic()} +Context expected type: 'err' + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-28.pretty new file mode 100644 index 0000000000..0b5e330414 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps-OTP-28.pretty @@ -0,0 +1,87 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:26:53 + │ +26 │ map_occ_04_neg(M = #{a := I}) when is_integer(I) -> M; + │ ^ + │ │ + │ M. +Expression has type: #{a := number(), b => number()} +Context expected type: #{a := number(), b := number()} + │ + +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) + ┌─ eqwater/src/eqwater_maps.erl:27:22 + │ +27 │ map_occ_04_neg(M) -> M#{a => 0}. + │ ^^^^^^^^^^ + │ │ + │ ..#{..}. +Expression has type: #{a := number(), b => number()} +Context expected type: #{a := number(), b := number()} + │ + +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) + ┌─ eqwater/src/eqwater_maps.erl:36:22 + │ +36 │ map_occ_06_neg(V) -> V. + │ ^ + │ │ + │ V. +Expression has type: #{} | 'ok' +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: #{} + 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 + │ +42 │ map_occ_07_neg(M) -> M. + │ ^ + │ │ + │ M. +Expression has type: none() | #{c := number()} +Context expected type: #{b := number()} + │ + +Because in the expression's type: + Here the type is: #{...} + Context expects type: #{b := ..., ...} + The type of the expression is missing the following required keys: b. + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:55:34 + │ +55 │ map_occ_08_neg(#{a := _} = M) -> M; + │ ^ M. +Expression has type: #{a := dynamic(), dynamic() => dynamic()} +Context expected type: 'err' + +5 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-OTP-26.pretty similarity index 70% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-26.pretty index 245813d1e8..5a01001d2e 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-26.pretty @@ -14,6 +14,14 @@ 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 │ @@ -30,6 +38,14 @@ 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 │ @@ -46,6 +62,14 @@ 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 │ @@ -72,6 +96,15 @@ 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 │ @@ -91,4 +124,13 @@ 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_records-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-27.pretty new file mode 100644 index 0000000000..5a01001d2e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-27.pretty @@ -0,0 +1,136 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_records.erl:35:16 + │ +35 │ id3_neg(Id) -> Id. + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +40 │ id4_neg(Id) -> Id. + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +47 │ Id -> Id + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +104 │ ╭ normalize_neg1({R = #rec2{}, R}) -> +105 │ │ {R, R}; + │ ╰────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_records.erl:106:25 + │ +106 │ normalize_neg1(Pair) -> Pair. + │ ^^^^ + │ │ + │ Pair. +Expression has type: {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} +Context expected type: {#rec1{}, #rec2{}} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {#rec1{}, #rec2{}} + However, the following candidate doesn't match: + { + Here the type is: #rec2{} + 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 + │ +115 │ normalize_neg2(Pair) -> Pair. + │ ^^^^ + │ │ + │ Pair. +Expression has type: {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} +Context expected type: {#rec1{}, #rec2{}} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {#rec1{}, #rec2{}} + However, the following candidate doesn't match: + { + Here the type is: #rec2{} + 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_records-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-28.pretty new file mode 100644 index 0000000000..5a01001d2e --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_records-OTP-28.pretty @@ -0,0 +1,136 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_records.erl:35:16 + │ +35 │ id3_neg(Id) -> Id. + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +40 │ id4_neg(Id) -> Id. + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +47 │ Id -> Id + │ ^^ + │ │ + │ Id. +Expression has type: user() +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: #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 + │ +104 │ ╭ normalize_neg1({R = #rec2{}, R}) -> +105 │ │ {R, R}; + │ ╰────────^ Clause is not covered by spec + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_records.erl:106:25 + │ +106 │ normalize_neg1(Pair) -> Pair. + │ ^^^^ + │ │ + │ Pair. +Expression has type: {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} +Context expected type: {#rec1{}, #rec2{}} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {#rec1{}, #rec2{}} + However, the following candidate doesn't match: + { + Here the type is: #rec2{} + 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 + │ +115 │ normalize_neg2(Pair) -> Pair. + │ ^^^^ + │ │ + │ Pair. +Expression has type: {#rec1{}, #rec2{}} | {#rec2{}, #rec1{}} +Context expected type: {#rec1{}, #rec2{}} + │ + +Because in the expression's type: + The type is a union type with some valid candidates: {#rec1{}, #rec2{}} + However, the following candidate doesn't match: + { + Here the type is: #rec2{} + 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-OTP-26.pretty similarity index 79% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-26.pretty index 332e999cda..4ee35cc78d 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-26.pretty @@ -14,6 +14,14 @@ 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 │ @@ -30,6 +38,14 @@ 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/eqwater_sound-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-27.pretty new file mode 100644 index 0000000000..4ee35cc78d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-27.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:44:26 + │ +44 │ t02(A) -> atom_to_binary(A). + │ ^ + │ │ + │ A. +Expression has type: ab() +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: 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 + │ +50 │ t03(_, B) -> B. + │ ^ + │ │ + │ B. +Expression has type: ab() +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: 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 + │ +53 │ tuple_trick_neg({X}, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:57:6 + │ +57 │ -> X. + │ ^ X. +Expression has type: _T +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:60:24 + │ +60 │ union_trick(_, {B}) -> B. + │ ^ B. +Expression has type: term() +Context expected type: B + +5 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-28.pretty new file mode 100644 index 0000000000..4ee35cc78d --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_sound-OTP-28.pretty @@ -0,0 +1,73 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:44:26 + │ +44 │ t02(A) -> atom_to_binary(A). + │ ^ + │ │ + │ A. +Expression has type: ab() +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: 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 + │ +50 │ t03(_, B) -> B. + │ ^ + │ │ + │ B. +Expression has type: ab() +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: 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 + │ +53 │ tuple_trick_neg({X}, _) -> X. + │ ^ X. +Expression has type: term() +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:57:6 + │ +57 │ -> X. + │ ^ X. +Expression has type: _T +Context expected type: U + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_sound.erl:60:24 + │ +60 │ union_trick(_, {B}) -> B. + │ ^ B. +Expression has type: term() +Context expected type: B + +5 ERRORS 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-OTP-26.pretty similarity index 83% rename from crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-26.pretty index c0c48c5e3c..8b70994f10 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-26.pretty @@ -14,6 +14,12 @@ 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/eqwater/unlimited_refinement-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-27.pretty new file mode 100644 index 0000000000..8b70994f10 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-27.pretty @@ -0,0 +1,31 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/unlimited_refinement.erl:60:14 + │ +60 │ is_special(X). + │ ^ + │ │ + │ X. +Expression has type: binary() | 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: 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 + │ +94 │ V -> V + │ ^ V. +Expression has type: {'ok7' | 'ok2' | 'ok6' | 'ok1' | 'ok5' | 'ok8' | 'ok4' | 'ok9' | 'ok3', map3() | map5() | map2() | map9() | map1() | map7() | map6() | map8() | map4()} +Context expected type: 'ok' + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-28.pretty new file mode 100644 index 0000000000..8b70994f10 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/unlimited_refinement-OTP-28.pretty @@ -0,0 +1,31 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/unlimited_refinement.erl:60:14 + │ +60 │ is_special(X). + │ ^ + │ │ + │ X. +Expression has type: binary() | 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: 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 + │ +94 │ V -> V + │ ^ V. +Expression has type: {'ok7' | 'ok2' | 'ok6' | 'ok1' | 'ok5' | 'ok8' | 'ok4' | 'ok9' | 'ok3', map3() | map5() | map2() | map9() | map1() | map7() | map6() | map8() | map4()} +Context expected type: 'ok' + +2 ERRORS 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-OTP-26.pretty similarity index 97% rename from crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-26.pretty index 554970c163..7e7da4fad6 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-OTP-26.pretty @@ -18,9 +18,16 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ fault_tolerance/src/fault_tolerance.erl:32:3 │ 32 │ A. - │ ^ A. -Expression has type: 'false' + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ fault_tolerance/src/fault_tolerance.erl:37:3 @@ -34,9 +41,16 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types ┌─ fault_tolerance/src/fault_tolerance.erl:39:3 │ 39 │ A. - │ ^ A. -Expression has type: 'false' + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ fault_tolerance/src/fault_tolerance.erl:44:3 @@ -416,6 +430,12 @@ 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 │ @@ -666,6 +686,12 @@ 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/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-27.pretty new file mode 100644 index 0000000000..7e7da4fad6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-27.pretty @@ -0,0 +1,943 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:23:3 + │ +23 │ true; + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:25:3 + │ +25 │ false. + │ ^^^^^ 'false'. +Expression has type: 'false' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:32:3 + │ +32 │ A. + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:37:3 + │ +37 │ true; + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:39:3 + │ +39 │ A. + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:44:3 + │ +44 │ 1; + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:46:3 + │ +46 │ 2; + │ ^ 2. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:48:3 + │ +48 │ 3; + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:50:3 + │ +50 │ 4. + │ ^ 4. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:55:3 + │ +55 │ 1; + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:57:3 + │ +57 │ 2; + │ ^ 2. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:59:3 + │ +59 │ 3; + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:61:3 + │ +61 │ 4; + │ ^ 4. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:63:3 + │ +63 │ 5; + │ ^ 5. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:65:3 + │ +65 │ 6; + │ ^ 6. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:67:3 + │ +67 │ 7; + │ ^ 7. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:69:3 + │ +69 │ 8; + │ ^ 8. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:71:3 + │ +71 │ 9; + │ ^ 9. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:73:3 + │ +73 │ 10. + │ ^^ 10. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:78:3 + │ +78 │ ╭ erlang:atom_to_binary( +79 │ │ b_to_n( +80 │ │ N +81 │ │ ) +82 │ │ ). + │ ╰───^ erlang:atom_to_binary(b_to_n(N)). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:79:5 + │ +79 │ ╭ b_to_n( +80 │ │ N +81 │ │ ) + │ ╰─────^ b_to_n(N). +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:80:7 + │ +80 │ N + │ ^ N. +Expression has type: number() +Context expected type: boolean() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:87:7 + │ +87 │ X = F(), + │ ^ F. +Expected fun type with arity 0 +Got: term() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:88:7 + │ +88 │ Y = F(), + │ ^ F. +Expected fun type with arity 0 +Got: term() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:89:3 + │ +89 │ {X, Y}. + │ ^^^^^^ {X, Y}. +Expression has type: {dynamic(), dynamic()} +Context expected type: number() + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ fault_tolerance/src/fault_tolerance.erl:94:17 + │ +94 │ X = lists:map(fun(_X, _Y) -> 1 end, Ns), + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 2 used as fun with 1 arguments + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:96:5 + │ +96 │ X + + │ ^ X. +Expression has type: [number()] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:97:7 + │ +97 │ A, + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:108:5 + │ +108 │ M1, + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:108:5 + │ +108 │ M1, + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:109:5 + │ +109 │ M2 + │ ^^ M2. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:109:5 + │ +109 │ M2 + │ ^^ M2. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:111:3 + │ +111 │ Res. + │ ^^^ Res. +Expression has type: [dynamic()] +Context expected type: #{dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:116:8 + │ +116 │ M1 = M#{ + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:118:7 + │ +118 │ V + + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:119:9 + │ +119 │ V}, + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:120:3 + │ +120 │ M1. + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:126:8 + │ +126 │ M1 = M#{ + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:128:5 + │ +128 │ V + + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:129:7 + │ +129 │ V}, + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:130:3 + │ +130 │ M1. + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:138:7 + │ +138 │ L1, + │ ^^ L1. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:139:7 + │ +139 │ L2 + │ ^^ L2. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:141:3 + │ +141 │ Res. + │ ^^^ Res. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:150:7 + │ +150 │ X + + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:151:9 + │ +151 │ Y, + │ ^ Y. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:152:5 + │ +152 │ Z + │ ^ Z. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:159:8 + │ +159 │ [H | + │ ╭────────^ +160 │ │ T], + │ ╰────────^ T. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:161:8 + │ +161 │ [T | + │ ╭────────^ +162 │ │ H] + │ ╰────────^ H. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:164:3 + │ +164 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [term()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: term() + 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 + │ +169 │ case F() of + │ ^ F. +Expected fun type with arity 0 +Got: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:170:13 + │ +170 │ true -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:171:14 + │ +171 │ false -> A - 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:178:10 + │ +178 │ F -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:179:13 + │ +179 │ true -> A - 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:186:5 + │ +186 │ F + 1, + │ ^ F. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:187:6 + │ +187 │ -M + │ ^ M. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:192:3 + │ +192 │ ╭ [X || +193 │ │ X <- #{ +194 │ │ a => b, +195 │ │ c => d + 1 + · │ +198 │ │ {X} +199 │ │ )]. + │ ╰──────^ [ || ]. +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:193:10 + │ +193 │ X <- #{ + │ ╭──────────^ +194 │ │ a => b, +195 │ │ c => d + 1 +196 │ │ }, + │ ╰─────^ #{..}. +Expression has type: #{a := 'b', c := number()} +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:195:12 + │ +195 │ c => d + 1 + │ ^ 'd'. +Expression has type: 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:198:7 + │ +198 │ {X} + │ ^^^ {X}. +Expression has type: {dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:204:20 + │ +204 │ atom_to_binary(Y) || + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:205:14 + │ +205 │ <> <= L + │ ^ L. +Expression has type: [term()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:207:3 + │ +207 │ Res. + │ ^^^ Res. +Expression has type: [binary()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:213:7 + │ +213 │ B + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:215:3 + │ +215 │ Res. + │ ^^^ Res. +Expression has type: binary() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:221:13 + │ +221 │ #{id := a + 1}, + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:223:13 + │ +223 │ #{id := b + 2}, + │ ^ 'b'. +Expression has type: 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:3 + │ +224 │ M1 + M2. + │ ^^ M1. +Expression has type: #{id := number(), dynamic() => dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:3 + │ +224 │ M1 + M2. + │ ^^^^^^^ _ + _. +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:8 + │ +224 │ M1 + M2. + │ ^^ M2. +Expression has type: #{id := number(), dynamic() => dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:229:5 + │ +229 │ A: + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:230:5 + │ +230 │ S + │ ^ S. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:232:3 + │ +232 │ B + 1. + │ ^ B. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:232:3 + │ +232 │ B + 1. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: atom() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:237:11 + │ +237 │ catch F(), + │ ^ F. +Expected fun type with arity 0 +Got: atom() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:243:5 + │ +243 │ A() + │ ^ A. +Expected fun type with arity 0 +Got: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:247:9 + │ +247 │ 1 + a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:249:3 + │ +249 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: dynamic() | [] +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: [] + 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 + │ +254 │ A() + │ ^ A. +Expected fun type with arity 0 +Got: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:257:13 + │ +257 │ A + 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:261:15 + │ +261 │ 1 + a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:263:3 + │ +263 │ Res. + │ ^^^ Res. +Expression has type: number() | [] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:273:10 + │ +273 │ id = Pid, + │ ^^^ Pid. +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:274:11 + │ +274 │ pid = Id + │ ^^ Id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:276:3 + │ +276 │ Res. + │ ^^^ Res. +Expression has type: #rec{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:281:11 + │ +281 │ pid = Id + │ ^^ Id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:283:3 + │ +283 │ Res#rec.id. + │ ^^^^^^^^^^ ...#rec.id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:288:9 + │ +288 │ _ = <<>> + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:288:9 + │ +288 │ _ = <<>> + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:290:3 + │ +290 │ Res. + │ ^^^ Res. +Expression has type: #rec{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:297:7 + │ +297 │ ╭ atom_to_binary( +298 │ │ [N] +299 │ │ ++ a +300 │ │ ); + │ ╰───────^ erlang:atom_to_binary(_ ++ _). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:298:9 + │ +298 │ ╭ [N] +299 │ │ ++ a + │ ╰──────────────^ _ ++ _. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:299:14 + │ +299 │ ++ a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:302:7 + │ +302 │ ╭ atom_to_binary( +303 │ │ <> +304 │ │ ++ b +305 │ │ ) + │ ╰───────^ erlang:atom_to_binary(_ ++ _). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:303:9 + │ +303 │ <> + │ ^^^^^ <<..>>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:303:9 + │ +303 │ ╭ <> +304 │ │ ++ b + │ ╰──────────────^ _ ++ _. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:304:14 + │ +304 │ ++ b + │ ^ 'b'. +Expression has type: 'b' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:313:7 + │ +313 │ ╭ atom_to_binary( +314 │ │ [N] +315 │ │ ); + │ ╰───────^ erlang:atom_to_binary([N]). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:314:9 + │ +314 │ [N] + │ ^^^ [N]. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:317:7 + │ +317 │ ╭ atom_to_binary( +318 │ │ <> +319 │ │ ) + │ ╰───────^ erlang:atom_to_binary(<<..>>). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:318:9 + │ +318 │ <> + │ ^^^^^ <<..>>. +Expression has type: binary() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:321:5 + │ +321 │ [] + │ ^^ []. +Expression has type: [] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:326:3 + │ +326 │ a + b. + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:326:7 + │ +326 │ a + b. + │ ^ 'b'. +Expression has type: 'b' +Context expected type: number() + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:331:1 + │ +331 │ -spec use_invalid(loop()) -> none(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use_invalid/1 references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:348:3 + │ +348 │ error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: redundant_fixme (See https://fb.me/eqwalizer_errors#redundant_fixme) + ┌─ fault_tolerance/src/fault_tolerance.erl:352:3 + │ +352 │ % eqwalizer:fixme + │ ^^^^^^^^^^^^^^^^^ redundant fixme + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:364:17 + │ +364 │ map_pair(L, fun make_pair/2). + │ ^^^^^^^^^^^^^^^ Arg 2 of 'make_pair/2'. +Expression has type: term() +Context expected type: number() + +109 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-28.pretty new file mode 100644 index 0000000000..7e7da4fad6 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/fault_tolerance/fault_tolerance-OTP-28.pretty @@ -0,0 +1,943 @@ +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:23:3 + │ +23 │ true; + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:25:3 + │ +25 │ false. + │ ^^^^^ 'false'. +Expression has type: 'false' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:32:3 + │ +32 │ A. + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:37:3 + │ +37 │ true; + │ ^^^^ 'true'. +Expression has type: 'true' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:39:3 + │ +39 │ A. + │ ^ + │ │ + │ A. +Expression has type: 'false' | none() +Context expected type: number() + │ + +Because in the expression's type: + Here the type is: 'false' + Context expects type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:44:3 + │ +44 │ 1; + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:46:3 + │ +46 │ 2; + │ ^ 2. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:48:3 + │ +48 │ 3; + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:50:3 + │ +50 │ 4. + │ ^ 4. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:55:3 + │ +55 │ 1; + │ ^ 1. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:57:3 + │ +57 │ 2; + │ ^ 2. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:59:3 + │ +59 │ 3; + │ ^ 3. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:61:3 + │ +61 │ 4; + │ ^ 4. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:63:3 + │ +63 │ 5; + │ ^ 5. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:65:3 + │ +65 │ 6; + │ ^ 6. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:67:3 + │ +67 │ 7; + │ ^ 7. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:69:3 + │ +69 │ 8; + │ ^ 8. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:71:3 + │ +71 │ 9; + │ ^ 9. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:73:3 + │ +73 │ 10. + │ ^^ 10. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:78:3 + │ +78 │ ╭ erlang:atom_to_binary( +79 │ │ b_to_n( +80 │ │ N +81 │ │ ) +82 │ │ ). + │ ╰───^ erlang:atom_to_binary(b_to_n(N)). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:79:5 + │ +79 │ ╭ b_to_n( +80 │ │ N +81 │ │ ) + │ ╰─────^ b_to_n(N). +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:80:7 + │ +80 │ N + │ ^ N. +Expression has type: number() +Context expected type: boolean() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:87:7 + │ +87 │ X = F(), + │ ^ F. +Expected fun type with arity 0 +Got: term() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:88:7 + │ +88 │ Y = F(), + │ ^ F. +Expected fun type with arity 0 +Got: term() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:89:3 + │ +89 │ {X, Y}. + │ ^^^^^^ {X, Y}. +Expression has type: {dynamic(), dynamic()} +Context expected type: number() + +error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) + ┌─ fault_tolerance/src/fault_tolerance.erl:94:17 + │ +94 │ X = lists:map(fun(_X, _Y) -> 1 end, Ns), + │ ^^^^^^^^^^^^^^^^^^^^ fun. +fun with arity 2 used as fun with 1 arguments + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:96:5 + │ +96 │ X + + │ ^ X. +Expression has type: [number()] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:97:7 + │ +97 │ A, + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:108:5 + │ +108 │ M1, + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:108:5 + │ +108 │ M1, + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:109:5 + │ +109 │ M2 + │ ^^ M2. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [T] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:109:5 + │ +109 │ M2 + │ ^^ M2. +Expression has type: #{dynamic() => dynamic()} +Context expected type: [dynamic()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:111:3 + │ +111 │ Res. + │ ^^^ Res. +Expression has type: [dynamic()] +Context expected type: #{dynamic() => dynamic()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:116:8 + │ +116 │ M1 = M#{ + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:118:7 + │ +118 │ V + + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:119:9 + │ +119 │ V}, + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:120:3 + │ +120 │ M1. + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:126:8 + │ +126 │ M1 = M#{ + │ ^ M. +Expression has type: term() +Context expected type: #{term() => term()} + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:128:5 + │ +128 │ V + + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:129:7 + │ +129 │ V}, + │ ^ V. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:130:3 + │ +130 │ M1. + │ ^^ M1. +Expression has type: #{dynamic() => dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:138:7 + │ +138 │ L1, + │ ^^ L1. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:139:7 + │ +139 │ L2 + │ ^^ L2. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:141:3 + │ +141 │ Res. + │ ^^^ Res. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:150:7 + │ +150 │ X + + │ ^ X. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:151:9 + │ +151 │ Y, + │ ^ Y. +Expression has type: term() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:152:5 + │ +152 │ Z + │ ^ Z. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:159:8 + │ +159 │ [H | + │ ╭────────^ +160 │ │ T], + │ ╰────────^ T. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:161:8 + │ +161 │ [T | + │ ╭────────^ +162 │ │ H] + │ ╰────────^ H. +Expression has type: term() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:164:3 + │ +164 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: [term()] +Context expected type: [atom()] + │ + +Because in the expression's type: + [ + Here the type is: term() + 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 + │ +169 │ case F() of + │ ^ F. +Expected fun type with arity 0 +Got: boolean() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:170:13 + │ +170 │ true -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:171:14 + │ +171 │ false -> A - 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:178:10 + │ +178 │ F -> A + 1; + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:179:13 + │ +179 │ true -> A - 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:186:5 + │ +186 │ F + 1, + │ ^ F. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:187:6 + │ +187 │ -M + │ ^ M. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:192:3 + │ +192 │ ╭ [X || +193 │ │ X <- #{ +194 │ │ a => b, +195 │ │ c => d + 1 + · │ +198 │ │ {X} +199 │ │ )]. + │ ╰──────^ [ || ]. +Expression has type: [dynamic()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:193:10 + │ +193 │ X <- #{ + │ ╭──────────^ +194 │ │ a => b, +195 │ │ c => d + 1 +196 │ │ }, + │ ╰─────^ #{..}. +Expression has type: #{a := 'b', c := number()} +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:195:12 + │ +195 │ c => d + 1 + │ ^ 'd'. +Expression has type: 'd' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:198:7 + │ +198 │ {X} + │ ^^^ {X}. +Expression has type: {dynamic()} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:204:20 + │ +204 │ atom_to_binary(Y) || + │ ^ Y. +Expression has type: number() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:205:14 + │ +205 │ <> <= L + │ ^ L. +Expression has type: [term()] +Context expected type: binary() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:207:3 + │ +207 │ Res. + │ ^^^ Res. +Expression has type: [binary()] +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:213:7 + │ +213 │ B + │ ^ B. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:215:3 + │ +215 │ Res. + │ ^^^ Res. +Expression has type: binary() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:221:13 + │ +221 │ #{id := a + 1}, + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:223:13 + │ +223 │ #{id := b + 2}, + │ ^ 'b'. +Expression has type: 'b' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:3 + │ +224 │ M1 + M2. + │ ^^ M1. +Expression has type: #{id := number(), dynamic() => dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:3 + │ +224 │ M1 + M2. + │ ^^^^^^^ _ + _. +Expression has type: number() +Context expected type: 'ok' + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:224:8 + │ +224 │ M1 + M2. + │ ^^ M2. +Expression has type: #{id := number(), dynamic() => dynamic()} +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:229:5 + │ +229 │ A: + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:230:5 + │ +230 │ S + │ ^ S. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:232:3 + │ +232 │ B + 1. + │ ^ B. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:232:3 + │ +232 │ B + 1. + │ ^^^^^ _ + _. +Expression has type: number() +Context expected type: atom() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:237:11 + │ +237 │ catch F(), + │ ^ F. +Expected fun type with arity 0 +Got: atom() + +error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:243:5 + │ +243 │ A() + │ ^ A. +Expected fun type with arity 0 +Got: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:247:9 + │ +247 │ 1 + a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:249:3 + │ +249 │ Res. + │ ^^^ + │ │ + │ Res. +Expression has type: dynamic() | [] +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: [] + 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 + │ +254 │ A() + │ ^ A. +Expected fun type with arity 0 +Got: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:257:13 + │ +257 │ A + 1 + │ ^ A. +Expression has type: atom() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:261:15 + │ +261 │ 1 + a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:263:3 + │ +263 │ Res. + │ ^^^ Res. +Expression has type: number() | [] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:273:10 + │ +273 │ id = Pid, + │ ^^^ Pid. +Expression has type: pid() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:274:11 + │ +274 │ pid = Id + │ ^^ Id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:276:3 + │ +276 │ Res. + │ ^^^ Res. +Expression has type: #rec{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:281:11 + │ +281 │ pid = Id + │ ^^ Id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:283:3 + │ +283 │ Res#rec.id. + │ ^^^^^^^^^^ ...#rec.id. +Expression has type: number() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:288:9 + │ +288 │ _ = <<>> + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:288:9 + │ +288 │ _ = <<>> + │ ^^^^ <<..>>. +Expression has type: binary() +Context expected type: pid() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:290:3 + │ +290 │ Res. + │ ^^^ Res. +Expression has type: #rec{} +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:297:7 + │ +297 │ ╭ atom_to_binary( +298 │ │ [N] +299 │ │ ++ a +300 │ │ ); + │ ╰───────^ erlang:atom_to_binary(_ ++ _). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:298:9 + │ +298 │ ╭ [N] +299 │ │ ++ a + │ ╰──────────────^ _ ++ _. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:299:14 + │ +299 │ ++ a + │ ^ 'a'. +Expression has type: 'a' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:302:7 + │ +302 │ ╭ atom_to_binary( +303 │ │ <> +304 │ │ ++ b +305 │ │ ) + │ ╰───────^ erlang:atom_to_binary(_ ++ _). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:303:9 + │ +303 │ <> + │ ^^^^^ <<..>>. +Expression has type: binary() +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:303:9 + │ +303 │ ╭ <> +304 │ │ ++ b + │ ╰──────────────^ _ ++ _. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:304:14 + │ +304 │ ++ b + │ ^ 'b'. +Expression has type: 'b' +Context expected type: [term()] + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:313:7 + │ +313 │ ╭ atom_to_binary( +314 │ │ [N] +315 │ │ ); + │ ╰───────^ erlang:atom_to_binary([N]). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:314:9 + │ +314 │ [N] + │ ^^^ [N]. +Expression has type: [dynamic()] +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:317:7 + │ +317 │ ╭ atom_to_binary( +318 │ │ <> +319 │ │ ) + │ ╰───────^ erlang:atom_to_binary(<<..>>). +Expression has type: binary() +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:318:9 + │ +318 │ <> + │ ^^^^^ <<..>>. +Expression has type: binary() +Context expected type: atom() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:321:5 + │ +321 │ [] + │ ^^ []. +Expression has type: [] +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:326:3 + │ +326 │ a + b. + │ ^ 'a'. +Expression has type: 'a' +Context expected type: number() + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:326:7 + │ +326 │ a + b. + │ ^ 'b'. +Expression has type: 'b' +Context expected type: number() + +error: reference_to_invalid_type (See https://fb.me/eqwalizer_errors#reference_to_invalid_type) + ┌─ fault_tolerance/src/fault_tolerance.erl:331:1 + │ +331 │ -spec use_invalid(loop()) -> none(). + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use_invalid/1 references type with invalid definition: loop/0 + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:348:3 + │ +348 │ error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +error: redundant_fixme (See https://fb.me/eqwalizer_errors#redundant_fixme) + ┌─ fault_tolerance/src/fault_tolerance.erl:352:3 + │ +352 │ % eqwalizer:fixme + │ ^^^^^^^^^^^^^^^^^ redundant fixme + +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ fault_tolerance/src/fault_tolerance.erl:364:17 + │ +364 │ map_pair(L, fun make_pair/2). + │ ^^^^^^^^^^^^^^^ Arg 2 of 'make_pair/2'. +Expression has type: term() +Context expected type: number() + +109 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-27.pretty new file mode 100644 index 0000000000..6c7920b6af --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-27.pretty @@ -0,0 +1,15 @@ +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ options/src/bad_maps.erl:10:24 + │ +10 │ -type bad_map_1() :: #{atom() := integer()}. + │ ^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ options/src/bad_maps.erl:14:32 + │ +14 │ -type bad_map_3() :: #{a => b, atom() := integer()}. + │ ^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-28.pretty new file mode 100644 index 0000000000..6c7920b6af --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/bad_maps-OTP-28.pretty @@ -0,0 +1,15 @@ +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ options/src/bad_maps.erl:10:24 + │ +10 │ -type bad_map_1() :: #{atom() := integer()}. + │ ^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +error: bad_map_key (See https://fb.me/eqwalizer_errors#bad_map_key) + ┌─ options/src/bad_maps.erl:14:32 + │ +14 │ -type bad_map_3() :: #{a => b, atom() := integer()}. + │ ^^^^^^^^^^^^^^^^^ Map type will be approximated to #{dynamic() => dynamic()}, suppressing potential errors. +Use => instead of := here. Required map key should always be composed of statically defined atoms or tuples. + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-27.pretty new file mode 100644 index 0000000000..01c204c224 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-27.pretty @@ -0,0 +1,37 @@ +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:11:11 + │ +11 │ Fun = fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:16:11 + │ +16 │ Fun = fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:23:9 + │ +23 │ fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:24:9 + │ +24 │ fun(#{}=M) -> M#{a => b} end + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:31:14 + │ +31 │ a => fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:32:14 + │ +32 │ b => fun(#{}=M) -> M#{a => b} end + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-28.pretty new file mode 100644 index 0000000000..01c204c224 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/dynamic_lambdas-OTP-28.pretty @@ -0,0 +1,37 @@ +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:11:11 + │ +11 │ Fun = fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:16:11 + │ +16 │ Fun = fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:23:9 + │ +23 │ fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:24:9 + │ +24 │ fun(#{}=M) -> M#{a => b} end + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:31:14 + │ +31 │ a => fun(X) -> X end, + │ ^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +error: dynamic_lambda (See https://fb.me/eqwalizer_errors#dynamic_lambda) + ┌─ options/src/dynamic_lambdas.erl:32:14 + │ +32 │ b => fun(#{}=M) -> M#{a => b} end + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lambda without context: parameters are dynamic() + +6 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/overloaded_specs_dynamic_result-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-26.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-26.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-27.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-27.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-28.pretty new file mode 100644 index 0000000000..89dc33db74 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/redundant_guards-OTP-28.pretty @@ -0,0 +1 @@ +NO ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-26.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-26.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-27.pretty new file mode 100644 index 0000000000..0304f7976a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-27.pretty @@ -0,0 +1,43 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:11:1 + │ +11 │ uncovered_1(A) when A == a -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:15:1 + │ +15 │ uncovered_2(_, {a, A}) -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:20:1 + │ +20 │ uncovered_3({ok, A}) -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:28:1 + │ +28 │ uncovered_5({err, _A}) -> ok; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:32:1 + │ +32 │ uncovered_6(#{a := V}) when is_integer(V) -> V; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:46:1 + │ +46 │ uncovered_single(#{a_typo := A}) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:57:1 + │ +57 │ uncovered_last_3({err, _A}) -> ok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +7 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-28.pretty new file mode 100644 index 0000000000..0304f7976a --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/options/uncovered_clauses-OTP-28.pretty @@ -0,0 +1,43 @@ +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:11:1 + │ +11 │ uncovered_1(A) when A == a -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:15:1 + │ +15 │ uncovered_2(_, {a, A}) -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:20:1 + │ +20 │ uncovered_3({ok, A}) -> A; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:28:1 + │ +28 │ uncovered_5({err, _A}) -> ok; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:32:1 + │ +32 │ uncovered_6(#{a := V}) when is_integer(V) -> V; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:46:1 + │ +46 │ uncovered_single(#{a_typo := A}) -> A. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) + ┌─ options/src/uncovered_clauses.erl:57:1 + │ +57 │ uncovered_last_3({err, _A}) -> ok. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clause is not covered by spec + +7 ERRORS diff --git a/crates/elp/src/resources/test/glean_help.stdout b/crates/elp/src/resources/test/glean_help.stdout index c4e938e143..bdde2ec540 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] +Usage: [--project PROJECT] [--module MODULE] [--to TO] [--v2] [--pretty] [--multi] [--prefix ARG] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) @@ -7,4 +7,5 @@ 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 1510d5bddb..67fa0d9498 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] [[--color WHEN]] [COMMAND ...] +Usage: [--log-file LOG_FILE] [--erl ERL] [--escript ESCRIPT] [--no-log-buffering] [--no-buck-generated] [COMMAND ...] Available options: --log-file @@ -6,27 +6,24 @@ Available options: --escript --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: - version Print version - server Run lsp server - shell Starts an interactive ELP shell eqwalize Eqwalize specified module eqwalize-all Eqwalize all opted-in modules in a project eqwalize-app Eqwalize all opted-in modules in specified application eqwalize-target Eqwalize all opted-in modules in specified buck target 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. + server Run lsp server + generate-completions Generate shell completions 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 build-info Generate build info JSON file + version Print version + shell Starts an interactive ELP shell + eqwalize-stats Return statistics about code quality for eqWAlizer + explain Explain a diagnostic code project-info Generate project info file glean Glean indexer - generate-completions Generate shell completions config Dump a JSON config stanza suitable for use in VS Code project.json diff --git a/crates/elp/src/resources/test/hierarchical_config/basic.stdout b/crates/elp/src/resources/test/hierarchical_config/basic.stdout deleted file mode 100644 index de8cb63308..0000000000 --- a/crates/elp/src/resources/test/hierarchical_config/basic.stdout +++ /dev/null @@ -1,5 +0,0 @@ -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 bffdd1fe91..8d03db6ee3 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]] [--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] ... +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] ... Available positional items: Rest of args are space separated list of apps to ignore @@ -14,6 +14,7 @@ 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 @@ -37,5 +38,4 @@ 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 c552fa7dfa..08c0321771 100644 --- a/crates/elp/src/resources/test/linter/custom_function_matches.stdout +++ b/crates/elp/src/resources/test/linter/custom_function_matches.stdout @@ -1,4 +1,5 @@ Reporting all diagnostics codes module specified: custom_function_matches -Diagnostics reported: -app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. +Diagnostics reported in 1 modules: + custom_function_matches: 1 + 13:4-13:25::[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 471efc7900..db2b6e3afa 100644 --- a/crates/elp/src/resources/test/linter/elp_lint_ct.stdout +++ b/crates/elp/src/resources/test/linter/elp_lint_ct.stdout @@ -1,2 +1,3 @@ -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 +Diagnostics reported in 1 modules: + app_a_unreachable_test_SUITE: 1 + 7:0-7:1::[Error] [W0008] Unreachable test (b/1) 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 a667df3930..022b3b8972 100644 --- a/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout +++ b/crates/elp/src/resources/test/linter/elp_lint_edoc.stdout @@ -1,2 +1,3 @@ -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 +Diagnostics reported in 1 modules: + app_a_edoc: 1 + 4:0-5:0::[Warning] [O0039] tag @docc not recognized. 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 47dc6d4a5e..b0cc8a1337 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: -app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' - 7:1-7:5: Mismatched clause name +Diagnostics reported in 1 modules: + app_a: 1 + 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' 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 0d0d56491d..5adeaace66 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,12 +1,13 @@ module specified: app_b -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 +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 --------------------------------------------- Applying fixes in module 'app_b' for - 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 + 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 @@ -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 edd7c5cf1c..a5c27f4367 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: -app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' - 7:1-7:5: Mismatched clause name +Diagnostics reported in 1 modules: + app_a: 1 + 8:0-8:4::[Error] [P1700] head mismatch 'fooX' vs 'food' 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 6daa076a31..8b6d166b16 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,10 +1,13 @@ - 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 +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 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 90ccbcb5c0..50166d0687 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,5 +1,9 @@ -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 +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 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 eeb41e5adf..071903fae8 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,3 +1,4 @@ module specified: expression_updates_literal -Diagnostics reported: -app_a/src/expression_updates_literal.erl:8:7-9:16::[Warning] [L1318] expression updates a literal +Diagnostics reported in 1 modules: + expression_updates_literal: 1 + 7:6-8:15::[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 1d7771b3b9..6304537096 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,12 +1,17 @@ -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 +Diagnostics reported in 6 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_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. 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 3e4a25f329..4c42e88eec 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,10 +1,11 @@ module specified: app_b -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` +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` --------------------------------------------- Applying fix in module 'app_b' for - 5:5-5:35::[Warning] [W0011] module `app_b` belongs to app `app_b`, but reads env for `misc` + 4:4-4:34::[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 2cec0678d2..fcba7f3aee 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,10 +1,11 @@ module specified: spelling -Diagnostics reported: -app_a/src/spelling.erl:2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' +Diagnostics reported in 1 modules: + spelling: 1 + 1:1-1:9::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' --------------------------------------------- Applying fix in module 'spelling' for - 2:2-2:10::[Error] [W0013] misspelled attribute, saw 'dyalizer' but expected 'dialyzer' + 1:1-1:9::[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 1e7e049554..07a81b80e2 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,6 +1,9 @@ -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 +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 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 4b8d3cc466..e7a35d0262 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,2 +1,3 @@ -Diagnostics reported: -app_b/src/app_b_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused +Diagnostics reported in 1 modules: + app_b_unused_param: 1 + 4:4-4:5::[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 5a42009118..00d4e2355e 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,3 +1,5 @@ -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 +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 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 5d6b5e78bc..4fb3478926 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"} \ No newline at end of file +{"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"} 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 new file mode 100644 index 0000000000..7389cc926a --- /dev/null +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_json_output_prefix.stdout @@ -0,0 +1,3 @@ +{"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 7b6a1e705c..008b2b5469 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,18 +1,28 @@ -{"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.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":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_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.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":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_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_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_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 +{"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"} 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 5bcd503e10..01ac36d828 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,21 +1,38 @@ - 7:1-7:5: Mismatched clause name -Diagnostics reported: Reporting all diagnostics codes -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 +Diagnostics reported in 8 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_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. diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout deleted file mode 100644 index 7ded0e8d2f..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc.stdout +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index e673a7be31..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli.stdout +++ /dev/null @@ -1,5 +0,0 @@ - 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 deleted file mode 100644 index 880b8f3473..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_dump_config.stdout +++ /dev/null @@ -1,12 +0,0 @@ - -# 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 deleted file mode 100644 index 78f52012ac..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_expand.stdout +++ /dev/null @@ -1,4 +0,0 @@ - app_a_ssr: 1 - 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 deleted file mode 100644 index f5a1e98710..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_no_expand.stdout +++ /dev/null @@ -1,4 +0,0 @@ - app_a_ssr: 1 - 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 deleted file mode 100644 index 6f49895580..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_macros_visible_expand.stdout +++ /dev/null @@ -1,5 +0,0 @@ - app_a_ssr: 2 - 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 deleted file mode 100644 index a71ebede29..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_multiple.stdout +++ /dev/null @@ -1,5 +0,0 @@ - app_a_ssr: 2 - 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 deleted file mode 100644 index 6938fabb87..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_invisible.stdout +++ /dev/null @@ -1,4 +0,0 @@ - app_a_ssr: 1 - 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 deleted file mode 100644 index a43317a2cf..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parens_visible.stdout +++ /dev/null @@ -1,6 +0,0 @@ - app_a_ssr: 3 - 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_ad_hoc_cli_parse_error.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parse_error.stdout deleted file mode 100644 index 7db783fb72..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_cli_parse_error.stdout +++ /dev/null @@ -1 +0,0 @@ -invalid SSR pattern 'ssr: {_@A, = _@B}.': Parse error: Could not lower rule diff --git a/crates/elp/src/resources/test/linter/ssr_ad_hoc_parse_fail.stdout b/crates/elp/src/resources/test/linter/ssr_ad_hoc_parse_fail.stdout deleted file mode 100644 index 4957343496..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_ad_hoc_parse_fail.stdout +++ /dev/null @@ -1 +0,0 @@ -errors parsing "{project_path}/elp_lint_ssr_adhoc_parse_fail.toml": invalid SSR pattern 'ssr: {_@A, = 10}.': Parse error: Could not lower rule for key `ad_hoc_lints.lints` at line 3 column 1 diff --git a/crates/elp/src/resources/test/linter/ssr_as_cli_arg.stdout b/crates/elp/src/resources/test/linter/ssr_as_cli_arg.stdout deleted file mode 100644 index 55650830ba..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_as_cli_arg.stdout +++ /dev/null @@ -1,3 +0,0 @@ -Diagnostics reported in 1 modules: - app_a: 1 - 15:12-15:17::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: {_@A, _@B}. diff --git a/crates/elp/src/resources/test/linter/ssr_context_separator.stdout b/crates/elp/src/resources/test/linter/ssr_context_separator.stdout deleted file mode 100644 index f791ea85bf..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_context_separator.stdout +++ /dev/null @@ -1,20 +0,0 @@ - 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 deleted file mode 100644 index 119d62a52b..0000000000 --- a/crates/elp/src/resources/test/linter/ssr_context_separator_color.stdout +++ /dev/null @@ -1,18 +0,0 @@ - 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 01007e94c4..8ffe5b1201 100644 --- a/crates/elp/src/resources/test/linter/warnings_as_errors.stdout +++ b/crates/elp/src/resources/test/linter/warnings_as_errors.stdout @@ -1,21 +1,38 @@ - 7:1-7:5: Mismatched clause name -Diagnostics reported: Reporting all diagnostics codes -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 +Diagnostics reported in 8 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_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. diff --git a/crates/elp/src/resources/test/linter_config/basic.stdout b/crates/elp/src/resources/test/linter_config/basic.stdout deleted file mode 100644 index 946fd7dc9a..0000000000 --- a/crates/elp/src/resources/test/linter_config/basic.stdout +++ /dev/null @@ -1,5 +0,0 @@ -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 75cc38354e..1457aed5c5 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] [[--severity SEVERITY]] +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] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) @@ -15,5 +15,4 @@ 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/project_info_help.stdout b/crates/elp/src/resources/test/project_info_help.stdout index 2f761a19fa..637b221d66 100644 --- a/crates/elp/src/resources/test/project_info_help.stdout +++ b/crates/elp/src/resources/test/project_info_help.stdout @@ -1,8 +1,7 @@ -Usage: [--project PROJECT] [--to TO] [--buck-query] [--target-types] +Usage: [--project PROJECT] [--to TO] [--buck-query] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --to Path to a directory where to dump wa.build_info --buck-query Include the buck uquery results in the output - --target-types Dump a list of targets and their types -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 deleted file mode 100644 index d51bdf1627..0000000000 --- a/crates/elp/src/resources/test/ssr_help.stdout +++ /dev/null @@ -1,26 +0,0 @@ -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. - --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 1d868bbbc6..fc5bc27296 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,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -153,6 +179,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -170,6 +202,12 @@ 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 │ @@ -192,14 +230,6 @@ 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 │ @@ -208,4 +238,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -21 ERRORS +20 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 b311705d68..89db77919f 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> [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/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/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,10 +12,9 @@ {"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> [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_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```\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_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 1d868bbbc6..fc5bc27296 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty @@ -44,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -153,6 +179,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -170,6 +202,12 @@ 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 │ @@ -192,14 +230,6 @@ 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 │ @@ -208,4 +238,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -21 ERRORS +20 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 b311705d68..89db77919f 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> [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/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/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,10 +12,9 @@ {"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> [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_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```\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_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 943c4a231d..f56ea404dc 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> [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/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} 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 f5f3065414..3cfdbd0dc2 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_a.pretty @@ -44,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,4 +100,15 @@ 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 8c93bd01fe..6bb5a7421b 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,6 +50,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -67,4 +73,10 @@ 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 482195c7ab..5aba82bc71 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics.pretty @@ -44,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -145,6 +171,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -162,6 +194,12 @@ 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 482195c7ab..5aba82bc71 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,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -145,6 +171,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -162,6 +194,12 @@ 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 043aa00af0..b72b8d1178 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,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -153,6 +179,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -170,6 +202,12 @@ 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 │ @@ -192,12 +230,4 @@ 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' - -20 ERRORS +19 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 043aa00af0..b72b8d1178 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,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -153,6 +179,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -170,6 +202,12 @@ 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 │ @@ -192,12 +230,4 @@ 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' - -20 ERRORS +19 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 482195c7ab..5aba82bc71 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_target_diagnostics.pretty @@ -44,6 +44,10 @@ 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 │ @@ -64,6 +68,17 @@ 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 │ @@ -85,6 +100,17 @@ 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 │ @@ -145,6 +171,12 @@ 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 + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_a/src/app_a_lists.erl:1305:15 │ @@ -162,6 +194,12 @@ 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 deleted file mode 100644 index 688be17de3..0000000000 --- a/crates/elp/src/resources/test/xref/unavailable_type.stdout +++ /dev/null @@ -1,5 +0,0 @@ -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 98890640bc..43406fd200 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_biased; +use crossbeam_channel::select; use dispatch::NotificationDispatcher; use elp_eqwalizer::ast::Pos; use elp_eqwalizer::types::Type; @@ -62,7 +62,6 @@ use elp_log::timeit; use elp_log::timeit_exceeds; use elp_project_model::ElpConfig; use elp_project_model::Project; -use elp_project_model::ProjectBuildData; use elp_project_model::ProjectManifest; use elp_project_model::buck::BuckQueryConfig; use elp_project_model::buck::BuckQueryError; @@ -351,7 +350,7 @@ impl Server { projects: Arc::new(vec![]), project_loader: Arc::new(Mutex::new(ProjectLoader::new())), initial_load_status: InitialLoading::Initial, - reload_manager: Arc::new(Mutex::new(ReloadManager::new(config.buck_quick_start()))), + reload_manager: Arc::new(Mutex::new(ReloadManager::new())), dynamic_registrations_done: false, unresolved_app_id_paths: Arc::new(FxHashMap::default()), generated_app_inputs: Arc::new(FxHashMap::default()), @@ -434,28 +433,27 @@ impl Server { } fn next_event(&self) -> Option { - // 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())) - } - + 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())) } + recv(self.progress.receiver()) -> msg => { + Some(Event::Task(Task::Progress(msg.unwrap()))) + } + + recv(telemetry::receiver()) -> msg => { + Some(Event::Telemetry(msg.unwrap())) + } + recv (self.project_pool.receiver) -> msg => { Some(Event::Task(msg.unwrap())) } @@ -468,16 +466,6 @@ 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())) - } - - } } @@ -511,17 +499,9 @@ impl Server { Event::Task(task) => match task { Task::Response(response) => self.send_response(response), Task::FetchProject(spinner, projects) => { - let mut a_file_per_project = extract_dirs_for_buck_projects(&projects); - if !self.fetch_project_completed(&spinner, projects)? { - // We already have changed files, will restart loading. - // So no point heading to generate targets yet - a_file_per_project = FxHashSet::default(); - } + self.fetch_project_completed(&spinner, projects)?; spinner.end(); - log::info!(target: FILE_WATCH_LOGGER_NAME, "Project reloading complete"); - self.reload_manager - .lock() - .set_reload_done(a_file_per_project); + self.reload_manager.lock().set_reload_done(); } Task::NativeDiagnostics(diags) => self.native_diagnostics_completed(diags), Task::EqwalizerDiagnostics(spinner, diags_types) => { @@ -570,9 +550,8 @@ 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); + self.reload_manager.lock().set_reload_active(); + self.reload_project(to_reload); } let (changed, highest_file_id) = self.process_changes_to_vfs_store(); @@ -635,16 +614,7 @@ impl Server { .diagnostics .diagnostics_for(*file_id) .iter() - .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)) - }) - }) + .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) .collect(); self.send_notification::( @@ -754,12 +724,13 @@ impl Server { this.ct_diagnostics_requested = true; this.native_diagnostics_requested = true; if let Ok(path) = convert::abs_path(¶ms.text_document.uri) { - let vfs_path = VfsPath::from(path.clone()); + this.fetch_projects_if_needed(&path); + let path = VfsPath::from(path); let already_exists = this .mem_docs .write() .insert( - vfs_path.clone(), + path.clone(), DocumentData::new( params.text_document.version, params.text_document.text.clone().into_bytes(), @@ -767,31 +738,24 @@ impl Server { ) .is_err(); if already_exists { - tracing::error!("duplicate DidOpenTextDocument: {}", &vfs_path); - log::error!("duplicate DidOpenTextDocument: {vfs_path}"); + tracing::error!("duplicate DidOpenTextDocument: {}", path); + log::error!("duplicate DidOpenTextDocument: {path}"); } - let file_id = { - let mut vfs = this.vfs.write(); - let bytes = params.text_document.text.into_bytes(); - vfs.set_file_contents(vfs_path.clone(), Some(bytes.clone())); + let mut vfs = this.vfs.write(); + let bytes = params.text_document.text.into_bytes(); + vfs.set_file_contents(path.clone(), Some(bytes.clone())); - // Until we bring over the full rust-analyzer - // style change processing, make a list of files - // that are freshly opened so diagnostics are - // generated for them, despite no changes being - // registered in vfs. - let (file_id, _) = vfs.file_id(&vfs_path).unwrap(); - this.newly_opened_documents.push(ChangedFile { - file_id, - change: Change::Modify(bytes, params.text_document.version as u64), - }); - file_id - }; - if this.status != Status::Running || this.analysis_host.raw_database().app_data_id_by_file(file_id).is_none() { - // We do not have a project associated with this file - this.fetch_projects_if_needed(&path); - } + // Until we bring over the full rust-analyzer + // style change processing, make a list of files + // that are freshly opened so diagnostics are + // generated for them, despite no changes being + // registered in vfs. + let (file_id, _) = vfs.file_id(&path).unwrap(); + this.newly_opened_documents.push(ChangedFile { + file_id, + change: Change::Modify(bytes, params.text_document.version as u64), + }); } else { log::error!( "DidOpenTextDocument: could not get vfs path for {}", @@ -848,20 +812,11 @@ 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| { - 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)) - }) - }) + .map(|d| ide_to_lsp_diagnostic(&line_index, &url, d)) .collect() } } @@ -883,14 +838,19 @@ impl Server { if let Ok(path) = convert::abs_path(&change.uri) { // We only put .erl/.hrl files into the // `mem_docs` store, but will get changes for - // other kinds of files. So we need to do + // other kinds of files. So we neede to do // this check. - // Note: we only receive DidSaveTextDocument notifications for files that pass the filter - // in the TextDocumentRegistrationOptions setting at the beginning of main_loop. let opened = convert::vfs_path(&change.uri) .map(|vfs_path| this.mem_docs.read().contains(&vfs_path)) .unwrap_or(false); if opened { + if this.should_reload_config_for_path(&path) { + // e.g. `.elp_lint.toml` + this.refresh_config(); + } + if this.should_reload_project_for_path(&path, &change) { + this.reload_manager.lock().add(path.clone()); + } this.eqwalizer_and_erlang_service_diagnostics_requested = true; if this.config.eqwalizer().all { this.eqwalizer_project_diagnostics_requested = true; @@ -907,31 +867,22 @@ impl Server { Ok(()) })? .on::(|this, params| { - // For some reason, we are seeing duplicate notifications for the same file. - // But we cannot use FxHashSet because change does not implement Hash. let changes: &[FileEvent] = ¶ms.changes; let mut refresh_config = false; - let mut seen: FxHashMap = FxHashMap::default(); for change in changes { - if seen.get(&change.uri).is_some_and(|prev_typ| prev_typ == &change.typ) { - continue; - } - seen.insert(change.uri.clone(), change.typ); if let Ok(path) = convert::abs_path(&change.uri) { let opened = convert::vfs_path(&change.uri) .map(|vfs_path| this.mem_docs.read().contains(&vfs_path)) .unwrap_or(false); log::info!(target: FILE_WATCH_LOGGER_NAME, "DidChangeWatchedFiles:{}:{}", &opened, &path); - // We process config changes here, because we do not register to receive - // DidSaveTextDocument notifiactions for these kinds of files - if this.should_reload_project_for_path(&path, change) { - this.reload_manager.lock().add(path.clone()); - } - if this.should_reload_config_for_path(&path) { - // e.g. `.elp_lint.toml` - refresh_config = true; - } if !opened { + if this.should_reload_project_for_path(&path, change) { + this.reload_manager.lock().add(path.clone()); + } + if this.should_reload_config_for_path(&path) { + // e.g. `.elp_lint.toml` + refresh_config = true; + } this.vfs_loader.handle.invalidate(path); } } @@ -1002,14 +953,9 @@ impl Server { always!(config_version <= self.vfs_config_version); match progress { LoadingProgress::Started => { - let pb_message = if self.status != Status::Running { - "Loading source files" - } else { - "Reloading source files" - }; let pb = self .progress - .begin_bar_with_telemetry(pb_message.into(), Some(n_total)); + .begin_bar_with_telemetry("Loading source files".into(), Some(n_total)); self.transition(Status::Loading(pb)); } LoadingProgress::Progress(n_done) => { @@ -1026,8 +972,7 @@ impl Server { self.show_message(params); } self.transition(Status::Running); - self.telemetry_manager - .operational(self.config.buck_quick_start()); + self.telemetry_manager.operational(); self.initial_load_status = InitialLoading::DoneButVfsChanges; self.schedule_compile_deps(); self.schedule_cache(); @@ -1053,10 +998,8 @@ 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`, only if the new - // content is different from the current, checked via - // a hash. + // This call will add the file to the changed_files, picked + // up in `process_changes`, if it has changed. vfs.set_file_contents(path, contents); } } @@ -1429,11 +1372,9 @@ 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 { @@ -1464,38 +1405,12 @@ 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, }; - let registration_id = "workspace/didChangeWatchedFiles".to_string(); - - // Unregister the old watcher - let unregistrations = vec![lsp_types::Unregistration { - id: registration_id.clone(), - method: notification::DidChangeWatchedFiles::METHOD.to_string(), - }]; - - self.send_request::( - lsp_types::UnregistrationParams { - unregisterations: unregistrations, - }, - |_, _| Ok(()), - ); - - // Register the new watcher let registrations = vec![lsp_types::Registration { - id: registration_id, + id: "workspace/didChangeWatchedFiles".to_string(), method: notification::DidChangeWatchedFiles::METHOD.to_string(), register_options: Some(serde_json::to_value(register_options).unwrap()), }]; @@ -1510,7 +1425,6 @@ impl Server { watch: vec![], version: 0, }; - spinner.report("Starting file loader".to_string()); self.vfs_loader.handle.set_config(vfs_loader_config); self.projects = Arc::new(projects); @@ -1567,18 +1481,12 @@ impl Server { fn update_configuration(&mut self, config: Config) { let _p = tracing::info_span!("Server::update_configuration").entered(); - let old_config = mem::replace(&mut self.config, Arc::new(config)); + let _old_config = mem::replace(&mut self.config, Arc::new(config)); self.logger .reconfigure(LOGGER_NAME, self.config.log_filter()); self.logger.reconfigure("default", self.config.log_filter()); - if old_config.buck_quick_start() != self.config.buck_quick_start() { - self.reload_manager - .lock() - .set_buck_quickstart(self.config.buck_quick_start()); - } - // Read the lint config file let loader = self.project_loader.clone(); let loader = loader.lock(); @@ -1727,15 +1635,14 @@ impl Server { .register(request.id.clone(), (request.method.clone(), received_timer)) } - fn reload_project(&mut self, paths: FxHashSet, query_config: BuckQueryConfig) { + fn reload_project(&mut self, paths: FxHashSet) { if !paths.is_empty() { let loader = self.project_loader.clone(); + let query_config = self.config.buck_query(); let paths = self.resolve_generated_project_paths(paths); - let spinner_message = match query_config { - BuckQueryConfig::BuildGeneratedCode => "ELP generating projects".to_string(), - _ => "ELP reloading projects".to_string(), - }; - let spinner = self.progress.begin_spinner_with_telemetry(spinner_message); + let spinner = self + .progress + .begin_spinner_with_telemetry("ELP reloading project config".to_string()); self.project_pool.handle.spawn_with_sender({ move |sender| { let mut loader = loader.lock(); @@ -1817,9 +1724,12 @@ impl Server { }; spinner.report("Loading project".to_string()); - let mut project = Project::load(&manifest, &elp_config, query_config, &|message| { - spinner.report(message.to_string()) - }); + let mut project = Project::load( + &manifest, + elp_config.eqwalizer.clone(), + query_config, + &|message| spinner.report(message.to_string()), + ); if let Err(err) = &project { log::error!("Failed to load project for manifest {manifest:?}, error: {err:?}"); match err.downcast_ref::() { @@ -1835,9 +1745,10 @@ impl Server { } } if !fallback_used { - project = Project::load(&fallback, &elp_config, query_config, &|message| { - spinner.report(message.to_string()) - }); + project = + Project::load(&fallback, elp_config.eqwalizer, query_config, &|message| { + spinner.report(message.to_string()) + }); if let Err(err) = &project { log::error!( "Failed to load project for fallback manifest {manifest:?}, error: {err:?}" @@ -1848,13 +1759,9 @@ 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: error_message, + message: err, actions: Some(vec![MessageActionItem { title: "Open Buck UI".to_string(), properties: HashMap::from_iter(vec![( @@ -1877,11 +1784,6 @@ impl Server { } fn fetch_projects_if_needed(&mut self, path: &AbsPath) { - // We do not try to load a project for a file that is in the buck-out directory, - // as it will have been generated by buck from a file originating in a different directory. - if path.to_string().contains("/buck-out/") { - return; - } let path = path.to_path_buf(); let loader = self.project_loader.clone(); let query_config = self.config.buck_query(); @@ -1915,18 +1817,12 @@ impl Server { }) } - fn fetch_project_completed( - &mut self, - spinner: &Spinner, - projects: Vec, - ) -> Result { - if !self.reload_manager.lock().ok_to_switch_workspace() { + fn fetch_project_completed(&mut self, spinner: &Spinner, projects: Vec) -> Result<()> { + if self.reload_manager.lock().has_changed_files() { // 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); + return Ok(()); } - spinner.report("Switching to loaded projects".to_string()); if let Err(err) = self.switch_workspaces(spinner, projects) { let params = lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, @@ -1939,7 +1835,7 @@ impl Server { self.register_dynamic_now_operational(); self.dynamic_registrations_done = true; } - Ok(true) + Ok(()) } fn register_dynamic_now_operational(&mut self) { @@ -2073,7 +1969,7 @@ impl Server { }; for (_, _, file_id) in module_index.iter_own() { - match snapshot.analysis.should_eqwalize(file_id) { + match snapshot.analysis.should_eqwalize(file_id, false) { Ok(true) => { files.push(file_id); } @@ -2179,23 +2075,6 @@ impl Server { } } -/// For each buck project, extract a directory inside it, for possible -/// project reloading to generate targets -fn extract_dirs_for_buck_projects(projects: &[Project]) -> FxHashSet { - let files: FxHashSet = projects - .iter() - .filter_map(|p| { - if let ProjectBuildData::Buck(_) = &p.project_build_data { - // We just need a single path inside the project somewhere - Some(p.project_apps.first()?.dir.clone()) - } else { - None - } - }) - .collect(); - files -} - fn lsp_msg_for_context(message: &lsp_server::Message) -> String { match message { lsp_server::Message::Request(m) => m.method.clone(), diff --git a/crates/elp/src/server/setup.rs b/crates/elp/src/server/setup.rs index 4b8615e07c..6dade5c677 100644 --- a/crates/elp/src/server/setup.rs +++ b/crates/elp/src/server/setup.rs @@ -33,7 +33,6 @@ 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; @@ -126,9 +125,8 @@ impl ServerSetup { // Set up a logger for tracking down why we are seeing stale // results when branches are switched, as per T218973130 - // @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_dir = "/tmp/elp"; + let _ = fs::create_dir_all(log_dir); let log_file = format!( "{}/elp_file_watch_reports-{}.log", log_dir, diff --git a/crates/elp/src/server/telemetry_manager.rs b/crates/elp/src/server/telemetry_manager.rs index 5d4293d5ec..b1cb2d3ac9 100644 --- a/crates/elp/src/server/telemetry_manager.rs +++ b/crates/elp/src/server/telemetry_manager.rs @@ -26,23 +26,17 @@ use crate::memory_usage::MemoryUsage; const PERIODIC_INTERVAL_MS: Duration = Duration::from_millis(60_000); -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum OperationalState { - NotStarted, - Started, - Restarted, -} pub(crate) struct TelemetryManager { last_periodic_processed: Instant, server_started_at: SystemTime, - operational_state: OperationalState, + already_operational: bool, } impl TelemetryManager { pub(crate) fn new() -> Self { Self { last_periodic_processed: Instant::now(), server_started_at: SystemTime::now(), - operational_state: OperationalState::NotStarted, + already_operational: false, } } @@ -76,48 +70,32 @@ impl TelemetryManager { } } - pub(crate) fn operational(&mut self, buck_quick_start: bool) { - let title = if buck_quick_start { - match self.operational_state { - OperationalState::NotStarted => "ELP operational (quick start)", - OperationalState::Started => "ELP operational (fully)", - OperationalState::Restarted => return, - } - } else { - match self.operational_state { - OperationalState::NotStarted => "ELP operational", - OperationalState::Started => return, - OperationalState::Restarted => return, - } - }; + pub(crate) fn operational(&mut self) { + if !self.already_operational { + let duration = self + .server_started_at + .elapsed() + .map(|e| e.as_millis()) + .unwrap_or(0) as u32; - let duration = self - .server_started_at - .elapsed() - .map(|e| e.as_millis()) - .unwrap_or(0) as u32; - - #[derive(Serialize)] - struct Operational { - title: String, + #[derive(Serialize)] + struct Operational { + title: String, + } + let data = Operational { + title: "ELP operational".to_string(), + }; + let data = serde_json::to_value(data).unwrap_or_else(|err| { + serde_json::Value::String(format!("JSON serialization failed: {err}")) + }); + telemetry::send_with_duration( + String::from("telemetry"), + data, + duration, + self.server_started_at, + ); + self.already_operational = true; } - let data = Operational { - title: title.to_string(), - }; - let data = serde_json::to_value(data).unwrap_or_else(|err| { - serde_json::Value::String(format!("JSON serialization failed: {err}")) - }); - telemetry::send_with_duration( - String::from("telemetry"), - data, - duration, - self.server_started_at, - ); - self.operational_state = if self.operational_state == OperationalState::NotStarted { - OperationalState::Started - } else { - OperationalState::Restarted - }; } } @@ -278,14 +256,12 @@ mod test { // Control lock scope by having it in braces let mut telemetry = TEST_TELEMETRY.lock(); telemetry.next_segment(token.clone(), None); - let new = telemetry.active.get_mut(&token).unwrap(); + let new = telemetry.active.get(&token).unwrap(); assert_ne!(orig.segment_start_time, new.segment_start_time); assert_eq!(orig.start_time, new.start_time); - // We sometimes run on a rollover boundary, set the value to zero to avoid flakiness - for seg in &mut new.segments { - seg.1 = 0; - } let expected = format!("{:#?}\n", &new.segments); + // We sometimes run on a rollover boundary + let expected = expected.replace("1", "0"); expect![[r#" [ ( diff --git a/crates/elp/src/snapshot.rs b/crates/elp/src/snapshot.rs index 69ea6b97e0..e79251f73a 100644 --- a/crates/elp/src/snapshot.rs +++ b/crates/elp/src/snapshot.rs @@ -36,11 +36,9 @@ 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; @@ -188,14 +186,6 @@ 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, @@ -203,7 +193,7 @@ impl Snapshot { ) -> Result<()> { let _ = self.analysis.def_map(file_id)?; if optimize_for_eqwalizer { - let should_eqwalize = self.analysis.should_eqwalize(file_id)?; + let should_eqwalize = self.analysis.should_eqwalize(file_id, false)?; if should_eqwalize { let _ = self.analysis.module_ast(file_id)?; } @@ -252,7 +242,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) { + if let Ok(true) = self.analysis.should_eqwalize(file_id, false) { Some(file_id) } else { None diff --git a/crates/elp/src/to_proto.rs b/crates/elp/src/to_proto.rs index 3e5b1fd1e1..f9263cfcab 100644 --- a/crates/elp/src/to_proto.rs +++ b/crates/elp/src/to_proto.rs @@ -10,7 +10,6 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. -use std::mem; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -48,11 +47,10 @@ 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; @@ -123,9 +121,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 @@ -133,131 +131,34 @@ 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, - mut source_change: SourceChange, + source_change: SourceChange, ) -> Result { - 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); - } - } - - 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); - } - } - + let mut edits: Vec<_> = vec![]; 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(), - }, - )); + // 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(), + }); } - - // 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 document_changes = lsp_types::DocumentChanges::Edits(edits); let workspace_edit = lsp_types::WorkspaceEdit { changes: None, - document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), + document_changes: Some(document_changes), change_annotations: None, }; Ok(workspace_edit) @@ -281,6 +182,10 @@ 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 91f2b234d9..45b24c99f6 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/test_projects/buck_tests"; + let path_str = "../../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) + .is_eqwalizer_enabled(file_id, false) .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,14 +76,19 @@ mod tests { #[test] #[ignore] fn test_load_buck_targets() { - let path_str = "../../test/test_projects/buck_tests"; + let path_str = "../../test_projects/buck_tests"; let path: PathBuf = path_str.into(); let (elp_config, buck_config) = ProjectManifest::discover(&to_abs_path_buf(&path).unwrap()).unwrap(); - let project = - Project::load(&buck_config, &elp_config, &BUCK_QUERY_CONFIG, &|_| {}).unwrap(); + let project = Project::load( + &buck_config, + elp_config.eqwalizer, + &BUCK_QUERY_CONFIG, + &|_| {}, + ) + .unwrap(); let project_data: Vec = project .non_otp_apps() diff --git a/crates/elp/tests/slow-tests/main.rs b/crates/elp/tests/slow-tests/main.rs index e256ce2c62..98455e8850 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/test_projects/end_to_end"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/end_to_end"), ); // Sanity check @@ -70,7 +70,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/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/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/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/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/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/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/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/test_projects/standard"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("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 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\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", "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 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\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", "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 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\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", "range": { "end": { "character": 5, @@ -321,7 +321,7 @@ fn test_e2e_eqwalizer_module() { "source": "eqWAlizer" } ], - "uri": "file:///[..]/test/test_projects/standard/app_a/src/app_a.erl", + "uri": "file:///[..]/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/test_projects/standard")); +// AbsPathBuf::assert(Path::new(env!("CARGO_WORKSPACE_DIR")).join("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 2c794f6246..bf482a7816 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_mins(1); + let timeout = Duration::from_secs(60); select! { recv(receiver) -> msg => Ok(msg.ok()), recv(after(timeout)) -> _ => Err(Timeout), diff --git a/crates/elp_log/src/telemetry.rs b/crates/elp_log/src/telemetry.rs index 86da912528..4132de6102 100644 --- a/crates/elp_log/src/telemetry.rs +++ b/crates/elp_log/src/telemetry.rs @@ -20,18 +20,18 @@ use std::time::SystemTime; -pub use humantime::format_rfc3339_millis; +use humantime::format_rfc3339_millis; use lazy_static::lazy_static; use serde::Deserialize; use serde::Serialize; pub type TelemetryData = serde_json::Value; -pub type DurationMs = u32; +pub type Duration = u32; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TelemetryMessage { #[serde(rename = "type")] pub typ: String, - pub duration_ms: Option, + pub duration_ms: Option, pub start_time: Option, pub start_time_string: Option, pub end_time_string: Option, @@ -56,7 +56,7 @@ pub fn receiver() -> &'static TelemetryReceiver { fn build_message( typ: String, data: TelemetryData, - duration_ms: Option, + duration_ms: Option, start_time: Option, ) -> TelemetryMessage { let start_time_string = start_time.map(|time| format_rfc3339_millis(time).to_string()); @@ -84,7 +84,7 @@ fn build_message( fn do_send( typ: String, data: serde_json::Value, - duration: Option, + duration: Option, start_time: Option, ) { let message = build_message(typ, data, duration, start_time); @@ -98,18 +98,12 @@ pub fn send(typ: String, data: serde_json::Value) { pub fn send_with_duration( typ: String, data: serde_json::Value, - duration: DurationMs, + duration: Duration, start_time: SystemTime, ) { do_send(typ, data, Some(duration), Some(start_time)); } -pub fn report_elapsed_time(what: &str, start_time: SystemTime) { - let data = serde_json::Value::String(what.to_string()); - let duration = start_time.elapsed().map(|e| e.as_millis()).unwrap_or(0) as u32; - send_with_duration("telemetry".to_string(), data, duration, start_time); -} - #[cfg(test)] mod tests { use std::time::SystemTime; diff --git a/crates/eqwalizer/build.rs b/crates/eqwalizer/build.rs index 2fb342b00e..f7e9a0acaa 100644 --- a/crates/eqwalizer/build.rs +++ b/crates/eqwalizer/build.rs @@ -11,20 +11,16 @@ use std::env; use std::fs; use std::path::Path; -use std::path::PathBuf; use std::process::Command; fn main() { let source_directory = Path::new("../../../eqwalizer/eqwalizer"); - let tools_dir = source_directory.join("tools"); let out_dir = env::var_os("OUT_DIR").unwrap(); let eqwalizer_out_dir = Path::new("../../../../../buck-out/eqwalizer/scala-3.6.4"); let dest_path = Path::new(&out_dir).join("eqwalizer"); let extension; - let java; if let Some(path) = env::var_os("ELP_EQWALIZER_PATH") { - java = "java".into(); let from = Path::new(&path); extension = from .extension() @@ -34,75 +30,60 @@ fn main() { .to_string(); fs::copy(from, dest_path).expect("Copying precompiled eqwalizer failed"); } else { - let jar = build_jar(source_directory, eqwalizer_out_dir); + extension = "".to_string(); + // Use the sbt wrapper on linux or otherwise require sbt to be installed + let sbt = fs::canonicalize(source_directory.join("./meta/sbt.sh")).unwrap(); + let output = Command::new(sbt) + .arg("assembly") + .current_dir(source_directory) + .env("EQWALIZER_USE_BUCK_OUT", "true") + .output() + .expect("failed to execute sbt assembly"); - if env::var("OPT_LEVEL").unwrap_or_default() == "0" { - extension = "jar".to_string(); - java = if env::var("CARGO_CFG_TARGET_OS").unwrap() == "linux" { - fs::canonicalize(tools_dir.join("graalvm/bin/java")).unwrap() - } else { - "java".into() - }; - fs::copy(jar, dest_path).expect("Copying fresh eqwalizer failed"); - } else { - extension = "".to_string(); - java = "java".into(); - let image_path = build_native_image(source_directory, eqwalizer_out_dir, jar); - fs::copy(image_path, dest_path).expect("Copying fresh eqwalizer failed"); + if !output.status.success() { + let stdout = + String::from_utf8(output.stdout).expect("valid utf8 output from sbt assembly"); + let stderr = + String::from_utf8(output.stderr).expect("valid utf8 output from sbt assembly"); + panic!("sbt assembly failed with stdout:\n{stdout}\n\nstderr:\n{stderr}"); } + let jar = fs::canonicalize(eqwalizer_out_dir.join("eqwalizer.jar")).unwrap(); + + let native_image = + fs::canonicalize(source_directory.join("./meta/native-image.sh")).unwrap(); + let image_path = fs::canonicalize(eqwalizer_out_dir) + .unwrap() + .join("eqwalizer"); + let output = Command::new(native_image) + .current_dir(source_directory) + .arg("-H:IncludeResources=application.conf") + .arg("--no-server") + .arg("--no-fallback") + .arg("-jar") + .arg(jar) + .arg(&image_path) + .output() + .expect("failed to execute native-image"); + + if !output.status.success() { + let stdout = + String::from_utf8(output.stdout).expect("valid utf8 output from native-image"); + let stderr = + String::from_utf8(output.stderr).expect("valid utf8 output from native-image"); + panic!("native-image failed with stdout:\n{stdout}\n\nstderr:\n{stderr}"); + } + + fs::copy(image_path, dest_path).expect("Copying fresh eqwalizer failed"); + rerun_if_changed(source_directory.join("build.sbt")); rerun_if_changed(source_directory.join("src")); } println!("cargo:rustc-env=ELP_EQWALIZER_EXT={extension}"); - println!("cargo:rustc-env=ELP_EQWALIZER_JAVA={}", java.display()); println!("cargo:rerun-if-env-changed=ELP_EQWALIZER_PATH"); } -fn build_native_image(source_directory: &Path, eqwalizer_out_dir: &Path, jar: PathBuf) -> PathBuf { - let native_image = fs::canonicalize(source_directory.join("./meta/native-image.sh")).unwrap(); - let image_path = fs::canonicalize(eqwalizer_out_dir) - .unwrap() - .join("eqwalizer"); - let output = Command::new(native_image) - .current_dir(source_directory) - .arg("-H:IncludeResources=application.conf") - .arg("--no-server") - .arg("--no-fallback") - .arg("-jar") - .arg(jar) - .arg(&image_path) - .output() - .expect("failed to execute native-image"); - - if !output.status.success() { - let stdout = String::from_utf8(output.stdout).expect("valid utf8 output from native-image"); - let stderr = String::from_utf8(output.stderr).expect("valid utf8 output from native-image"); - panic!("native-image failed with stdout:\n{stdout}\n\nstderr:\n{stderr}"); - } - image_path -} - -fn build_jar(source_directory: &Path, eqwalizer_out_dir: &Path) -> PathBuf { - // Use the sbt wrapper on linux or otherwise require sbt to be installed - let sbt = fs::canonicalize(source_directory.join("./meta/sbt.sh")).unwrap(); - let output = Command::new(sbt) - .arg("assembly") - .current_dir(source_directory) - .env("EQWALIZER_USE_BUCK_OUT", "true") - .output() - .expect("failed to execute sbt assembly"); - - if !output.status.success() { - let stdout = String::from_utf8(output.stdout).expect("valid utf8 output from sbt assembly"); - let stderr = String::from_utf8(output.stderr).expect("valid utf8 output from sbt assembly"); - panic!("sbt assembly failed with stdout:\n{stdout}\n\nstderr:\n{stderr}"); - } - - fs::canonicalize(eqwalizer_out_dir.join("eqwalizer.jar")).unwrap() -} - fn rerun_if_changed(path: impl AsRef) { println!("cargo:rerun-if-changed={}", path.as_ref().display()); } diff --git a/crates/eqwalizer/src/ast/convert_types.rs b/crates/eqwalizer/src/ast/convert_types.rs index ccfc635cdc..e097e1a028 100644 --- a/crates/eqwalizer/src/ast/convert_types.rs +++ b/crates/eqwalizer/src/ast/convert_types.rs @@ -258,15 +258,9 @@ impl TypeConverter { ExtType::ListExtType(ty) => Ok(self .convert_type(sub, *ty.t)? .map(|t| Type::ListType(ListType { t: Box::new(t) }))), - // unions with NoneType are meaningless, we can remove them early - ExtType::UnionExtType(tys) => Ok(self.convert_types(sub, tys.tys)?.map(|mut tys| { - tys.retain(|ty| *ty != Type::NoneType); - match tys.len() { - 0 => Type::NoneType, - 1 => tys.into_iter().next().unwrap(), - _ => Type::UnionType(UnionType { tys }), - } - })), + ExtType::UnionExtType(tys) => Ok(self + .convert_types(sub, tys.tys)? + .map(|tys| Type::UnionType(UnionType { tys }))), ExtType::RemoteExtType(ty) if ty.id.module == "eqwalizer" && ty.id.name == "refinable" diff --git a/crates/eqwalizer/src/ipc.rs b/crates/eqwalizer/src/ipc.rs index 4d10d663ec..30b1dae582 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_mins(4); -const READ_TIMEOUT: Duration = Duration::from_mins(4); +const WRITE_TIMEOUT: Duration = Duration::from_secs(240); +const READ_TIMEOUT: Duration = Duration::from_secs(240); impl IpcHandle { fn spawn_cmd(cmd: &mut Command) -> Result { diff --git a/crates/eqwalizer/src/lib.rs b/crates/eqwalizer/src/lib.rs index 54333570fd..e8c7b18b92 100644 --- a/crates/eqwalizer/src/lib.rs +++ b/crates/eqwalizer/src/lib.rs @@ -220,7 +220,7 @@ impl EqwalizerExe { let (cmd, args) = match ext.as_str() { "jar" => ( - env!("ELP_EQWALIZER_JAVA").into(), + "java".into(), vec!["-Xss20M".into(), "-jar".into(), path.into()], ), "" => (path, vec![]), @@ -244,9 +244,7 @@ impl EqwalizerExe { impl Eqwalizer { fn cmd(&self) -> Command { let mut exe = EQWALIZER_EXE.lock(); - // 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() { + if !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 072f439db3..16020d78f6 100644 --- a/crates/erlang_service/src/lib.rs +++ b/crates/erlang_service/src/lib.rs @@ -59,13 +59,12 @@ lazy_static! { pub static ref ESCRIPT: RwLock = RwLock::new("escript".to_string()); } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum DiagnosticLocation { Normal(TextRange), Included { - 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 + directive_location: TextRange, // Location of include directive in the file compiled + error_location: TextRange, // Location of the error in the included file }, } @@ -736,8 +735,7 @@ fn decode_errors(buf: &[u8]) -> Result> { eetf::Term::decode(buf)? .as_match(pattern::VarList(( - Str, // path - Str, // error path + Str, pattern::Or(( (pattern::U32, pattern::U32), // Normal location pattern::FixList(((pattern::U32, pattern::U32), (pattern::U32, pattern::U32))), // Location in include file @@ -749,7 +747,7 @@ fn decode_errors(buf: &[u8]) -> Result> { .map_err(|err| anyhow!("Failed to decode errors: {:?}", err)) .map(|res| { res.into_iter() - .map(|(path, error_path, position, msg, code)| ParseError { + .map(|(path, position, msg, code)| ParseError { path: path.into(), location: match position { pattern::Union3::A((a, b)) => Some(DiagnosticLocation::Normal( @@ -757,8 +755,7 @@ fn decode_errors(buf: &[u8]) -> Result> { )), pattern::Union3::B(((a, b), (c, d))) => { Some(DiagnosticLocation::Included { - file_attribute_location: safe_textrange(a.into(), b.into()), - error_path: error_path.into(), + directive_location: safe_textrange(a.into(), b.into()), error_location: safe_textrange(c.into(), d.into()), }) } diff --git a/crates/hir/src/body.rs b/crates/hir/src/body.rs index 9ebac17c66..e4731ddc87 100644 --- a/crates/hir/src/body.rs +++ b/crates/hir/src/body.rs @@ -43,7 +43,6 @@ use crate::FormList; use crate::FunctionClause; use crate::FunctionClauseId; use crate::InFile; -use crate::IncludeAttributeId; use crate::Literal; use crate::Name; use crate::NameArity; @@ -662,15 +661,6 @@ impl FunctionClauseBody { tree_print::print_function_clause_with_strategy(db, self, strategy) } - pub fn tree_print_with_range( - &self, - db: &dyn InternDatabase, - strategy: Strategy, - source_map: &BodySourceMap, - ) -> String { - tree_print::print_function_clause_with_strategy_and_range(db, self, strategy, source_map) - } - pub fn fold(&self, strategy: Strategy, initial: T, callback: AnyCallBack<'_, T>) -> T { match &self.from_macro { Some(from_macro) if strategy.macros == MacroStrategy::DoNotExpand => { @@ -1066,12 +1056,8 @@ impl Index for FoldBody<'_> { type Output = TypeExpr; fn index(&self, index: TypeExprId) -> &Self::Output { - // Apply the visibility strategies to macros and parens + // Do not "look through" macro expansion match &self.body.type_exprs[index] { - TypeExpr::Paren { ty } => match self.parens { - ParenStrategy::VisibleParens => &self.body.type_exprs[index], - ParenStrategy::InvisibleParens => self.index(*ty), - }, type_expr @ TypeExpr::MacroCall { expansion, .. } => match self.macros { VisibleMacros::Yes => type_expr, VisibleMacros::No => self.index(*expansion), @@ -1156,24 +1142,6 @@ 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 @@ -1250,7 +1218,6 @@ pub struct BodySourceMap { term_map: FxHashMap, term_map_back: ArenaMap, macro_map: FxHashMap, - diagnostics: Vec, } impl BodySourceMap { @@ -1312,10 +1279,6 @@ 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 4abf0737b2..f6a03b8a2c 100644 --- a/crates/hir/src/body/lower.rs +++ b/crates/hir/src/body/lower.rs @@ -38,7 +38,6 @@ use crate::AttributeBody; use crate::BasedInteger; use crate::BinarySeg; use crate::Body; -use crate::BodyDiagnostic; use crate::BodySourceMap; use crate::CRClause; use crate::CallTarget; @@ -117,10 +116,6 @@ 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)] @@ -157,8 +152,6 @@ impl<'a> Ctx<'a> { starting_stack_size: 1, macro_source_map: FxHashMap::default(), in_ssr: false, - in_macro_rhs: false, - diagnostics: Vec::new(), } } @@ -223,7 +216,6 @@ 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) } @@ -409,9 +401,7 @@ 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) } @@ -822,9 +812,6 @@ 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() @@ -860,8 +847,11 @@ impl<'a> Ctx<'a> { self.alloc_pat(Pat::Missing, Some(expr)) } ast::ExprMax::ParenExpr(paren_expr) => { - if let Some(inner_paren_expr) = paren_expr.expr() { - let pat_id = self.lower_pat(&inner_paren_expr); + if let Some(paren_expr) = paren_expr.expr() { + let pat_id = self.lower_pat(&paren_expr); + let ptr = AstPtr::new(&paren_expr); + let source = InFileAstPtr::new(self.curr_file_id(), ptr); + self.record_pat_source(pat_id, source); self.alloc_pat(Pat::Paren { pat: pat_id }, Some(expr)) } else { self.alloc_pat(Pat::Missing, Some(expr)) @@ -1182,9 +1172,6 @@ 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()) @@ -1416,8 +1403,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); @@ -1480,9 +1467,6 @@ 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() @@ -1499,12 +1483,14 @@ 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() { - let expr_id = self.lower_expr(&inner_expr); + if let Some(paren_expr) = paren_expr.expr() { + let expr_id = self.lower_expr(&paren_expr); + let ptr = AstPtr::new(expr); + let source = InFileAstPtr::new(self.curr_file_id(), ptr); + self.record_expr_source(expr_id, source); self.alloc_expr(Expr::Paren { expr: expr_id }, Some(expr)) } else { self.alloc_expr(Expr::Missing, Some(expr)) @@ -2010,9 +1996,6 @@ 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()) @@ -2193,9 +2176,6 @@ 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() @@ -2215,9 +2195,12 @@ impl<'a> Ctx<'a> { }), ast::ExprMax::MacroString(_) => self.alloc_type_expr(TypeExpr::Missing, Some(expr)), ast::ExprMax::ParenExpr(paren_expr) => { - if let Some(inner_expr) = paren_expr.expr() { - let type_expr_id = self.lower_type_expr(&inner_expr); - self.alloc_type_expr(TypeExpr::Paren { ty: type_expr_id }, Some(expr)) + if let Some(expr) = paren_expr.expr() { + let type_expr_id = self.lower_type_expr(&expr); + let ptr = AstPtr::new(&expr); + let source = InFileAstPtr::new(self.curr_file_id(), ptr); + self.record_type_source(type_expr_id, source); + type_expr_id } else { self.alloc_type_expr(TypeExpr::Missing, Some(expr)) } @@ -2562,9 +2545,6 @@ 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() @@ -3026,23 +3006,6 @@ 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/pretty.rs b/crates/hir/src/body/pretty.rs index 5b84b4e8ef..e19c97e637 100644 --- a/crates/hir/src/body/pretty.rs +++ b/crates/hir/src/body/pretty.rs @@ -776,7 +776,6 @@ impl<'a> Printer<'a> { macro_def: _, macro_name: _, } => self.print_type(&self.body[*expansion]), - TypeExpr::Paren { ty } => self.print_type(&self.body[*ty]), TypeExpr::SsrPlaceholder(ssr) => self.print_ssr_placeholder(ssr), } } diff --git a/crates/hir/src/body/scope.rs b/crates/hir/src/body/scope.rs index 4ac4e366d8..dd1da23e94 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 eab603a76d..dd5e5e4699 100644 --- a/crates/hir/src/body/tests.rs +++ b/crates/hir/src/body/tests.rs @@ -8,8 +8,6 @@ * above-listed licenses. */ -use std::fmt::Write; - use elp_base_db::fixture::WithFixture; use expect_test::Expect; use expect_test::expect; @@ -448,7 +446,6 @@ 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 = }. "#, @@ -474,7 +471,6 @@ foo2(#record{field}) -> #record{field = }. fn record_update() { check( r#" -//- expect_parse_errors foo1() -> Expr#record{field = undefined}. foo2() -> Expr#record{field = ok, missing = }. "#, @@ -559,7 +555,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. "#, @@ -568,7 +564,7 @@ foo() -> case (1 + 2) of X when (X andalso true); - (X =< 100), + (X < 100), (X >= 5) -> ok; @@ -838,7 +834,6 @@ foo() -> fn parens() { check( r#" -//- expect_parse_errors foo((ok), ()) -> (ok), (). @@ -998,7 +993,6 @@ foo(fun() -> ok end) -> ok. fn invalid_comprehension() { check( r#" -//- expect_parse_errors foo(<>, [Byte || Byte <- List]]) -> ok. "#, expect![[r#" @@ -1396,7 +1390,6 @@ 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 ::}. @@ -1534,7 +1527,6 @@ fn record_definition() { fn simple_term() { check( r#" -//- expect_parse_errors -foo(ok). -missing_value(). "#, @@ -2699,7 +2691,6 @@ 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"). "#, @@ -2730,7 +2721,6 @@ fn verbatim_binary_sigil_in_term() { fn lowering_with_error_nodes() { check( r#" - //- expect_parse_errors f(1a) -> ok begin 1 end. "#, expect![[r#" @@ -2769,74 +2759,6 @@ fn tree_print_function() { ); } -#[test] -fn tree_print_function_with_ranges() { - let (db, file_ids, _) = TestDB::with_many_files( - r#" - foo( (((A))) ) -> ((3)). - "#, - ); - let file_id = file_ids[0]; - let form_list = db.file_form_list(file_id); - let strategy = Strategy { - macros: MacroStrategy::ExpandButIncludeMacroCall, - parens: ParenStrategy::VisibleParens, - }; - let pretty = form_list - .forms() - .iter() - .flat_map(|&form_idx| match form_idx { - FormIdx::FunctionClause(function_id) => { - let def_map = db.def_map(file_id); - let function_def_id = InFile::new(file_id, FunctionDefId::new(function_id)); - if let Some(_fun_def) = def_map.get_by_function_id(&function_def_id) { - let (body, source_maps) = db.function_body_with_source(function_def_id); - let mut out = String::new(); - if let Some((_, clause)) = body.clauses.iter().next() { - if let Some(na) = clause.name.clone() { - writeln!(out, "function: {na}").ok(); - } - if let Some(source_map) = source_maps.first() { - write!( - out, - "{}", - clause.tree_print_with_range(&db, strategy, source_map) - ) - .ok(); - } - } - writeln!(out, ".").ok(); - Some(out) - } else { - None - } - } - _ => None, - }) - .collect::>() - .join(""); - expect![[r#" - function: foo/1 - - Clause { - pats - Pat<3>:Pat::Paren { (range: 5..12) - Pat<2>:Pat::Paren { (range: 6..11) - Pat<1>:Pat::Paren { (range: 7..10) - Pat<0>:Pat::Var(A)}}}, - guards - exprs - Expr<3>:Expr::Paren { (range: 18..23) - Expr<2>:Expr::Paren { (range: 19..22) - Expr<1>:Literal(Integer(3)) - } - }, - } - . - "#]] - .assert_eq(pretty.trim_start()); -} - #[test] fn tree_print_type_body() { check_ast( @@ -2997,10 +2919,8 @@ 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 d6b956690f..ff89265c10 100644 --- a/crates/hir/src/body/tree_print.rs +++ b/crates/hir/src/body/tree_print.rs @@ -15,15 +15,12 @@ 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; use super::SpecBody; use super::SpecOrCallback; use crate::AnyAttribute; -use crate::AnyExprId; use crate::AttributeBody; use crate::BinarySeg; use crate::CRClause; @@ -35,15 +32,11 @@ 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; @@ -56,7 +49,6 @@ 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; @@ -183,23 +175,6 @@ pub(crate) fn print_function_clause_with_strategy( out } -pub(crate) fn print_function_clause_with_strategy_and_range( - db: &dyn InternDatabase, - clause: &FunctionClauseBody, - strategy: Strategy, - source_map: &crate::BodySourceMap, -) -> String { - let mut out = String::new(); - - let fold_body = fold_body(strategy, &clause.body); - let mut printer = Printer::new_with_strategy_and_range(db, &fold_body, strategy, source_map); - printer.print_clause(&clause.clause); - write!(out, "{}", printer.result_raw()).unwrap(); - writeln!(out).unwrap(); - - out -} - pub(crate) fn print_type_alias( db: &dyn InternDatabase, body: &crate::TypeBody, @@ -339,63 +314,6 @@ 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>, @@ -404,8 +322,6 @@ struct Printer<'a> { indent_level: usize, needs_indent: bool, include_id: bool, - source_map: Option<&'a crate::BodySourceMap>, - current: Option, } impl<'a> Printer<'a> { @@ -418,8 +334,6 @@ impl<'a> Printer<'a> { indent_level: 0, needs_indent: true, include_id: true, - source_map: None, - current: None, } } @@ -436,27 +350,6 @@ impl<'a> Printer<'a> { indent_level: 0, needs_indent: true, include_id: true, - source_map: None, - current: None, - } - } - - fn new_with_strategy_and_range( - db: &'a dyn InternDatabase, - body: &'a FoldBody, - strategy: Strategy, - source_map: &'a crate::BodySourceMap, - ) -> Self { - Printer { - db, - body, - expand_macros: strategy.macros != MacroStrategy::DoNotExpand, - buf: String::new(), - indent_level: 0, - needs_indent: true, - include_id: true, - source_map: Some(source_map), - current: None, } } @@ -480,7 +373,6 @@ impl<'a> Printer<'a> { } fn print_expr(&mut self, expr_id: &ExprId) { - self.current = Some(AnyExprId::Expr(*expr_id)); if self.include_id { write!(self, "Expr<{}>:", expr_id.into_raw().into_u32()).ok(); } @@ -840,7 +732,6 @@ impl<'a> Printer<'a> { Expr::Paren { expr } => { self.print_herald("Expr::Paren", &mut |this| { this.print_expr(expr); - writeln!(this).ok(); }); } Expr::SsrPlaceholder(ssr) => self.print_ssr_placeholder(ssr), @@ -848,7 +739,6 @@ impl<'a> Printer<'a> { } fn print_pat(&mut self, pat: &PatId) { - self.current = Some(AnyExprId::Pat(*pat)); if self.include_id { write!(self, "Pat<{}>:", pat.into_raw().into_u32()).ok(); } @@ -981,7 +871,6 @@ impl<'a> Printer<'a> { } fn print_term(&mut self, term: &TermId) { - self.current = Some(AnyExprId::Term(*term)); match &self.body[*term] { Term::Missing => { write!(self, "Term::Missing").ok(); @@ -1083,7 +972,6 @@ impl<'a> Printer<'a> { } fn print_type(&mut self, ty: &TypeExprId) { - self.current = Some(AnyExprId::TypeExpr(*ty)); match &self.body[*ty] { TypeExpr::AnnType { var, ty } => { self.print_herald("TypeExpr::AnnType", &mut |this| { @@ -1252,11 +1140,6 @@ impl<'a> Printer<'a> { } }); } - TypeExpr::Paren { ty } => { - self.print_herald("TypeExpr::Paren", &mut |this| { - this.print_type(ty); - }); - } TypeExpr::SsrPlaceholder(ssr) => self.print_ssr_placeholder(ssr), } } @@ -1351,16 +1234,7 @@ impl<'a> Printer<'a> { } fn print_herald(&mut self, label: &str, print: &mut dyn FnMut(&mut Self)) { - let range = self - .current - .and_then(|id| self.source_map.and_then(|sm| sm.any(id))) - .map(|es| es.range().range); - - if let Some(r) = range { - writeln!(self, "{label} {{ (range: {r:?})").ok(); - } else { - writeln!(self, "{label} {{").ok(); - } + writeln!(self, "{label} {{").ok(); self.indent(); print(self); self.dedent(); @@ -1587,8 +1461,13 @@ 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::body::tree_print::print_form_list; + use crate::db::DefDatabase; use crate::fold::MacroStrategy; use crate::fold::ParenStrategy; use crate::test_db::TestDB; @@ -1605,7 +1484,52 @@ mod tests { #[track_caller] fn check_with_strategy(strategy: Strategy, fixture: &str, expect: Expect) { let (db, file_id) = TestDB::with_single_file(fixture); - let pretty = print_form_list(&db, file_id, strategy); + 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(""); expect.assert_eq(pretty.trim_start()); } @@ -2013,16 +1937,6 @@ 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 @@ -2340,7 +2254,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. "#, @@ -2381,7 +2295,7 @@ mod tests { rhs Expr<8>:Literal(Integer(100)) op - CompOp(Ord { ordering: Less, strict: false }), + CompOp(Ord { ordering: Less, strict: true }), }, Expr<12>:Expr::BinaryOp { lhs @@ -3114,7 +3028,6 @@ 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 ::}. @@ -3388,9 +3301,7 @@ mod tests { exprs Expr<3>:Expr::Paren { Expr<2>:Expr::Paren { - Expr<1>:Literal(Integer(3)) - } - }, + Expr<1>:Literal(Integer(3))}}, }. "#]], ); @@ -3437,111 +3348,10 @@ mod tests { Expr<4>:Expr::Tuple { Expr<3>:Expr::Paren { Expr<2>:Expr::Paren { - Expr<1>:Literal(Integer(3)) - } - }, + Expr<1>:Literal(Integer(3))}}, }, }. "#]], ); } - - #[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 114930efe3..9defb2b071 100644 --- a/crates/hir/src/expr.rs +++ b/crates/hir/src/expr.rs @@ -581,26 +581,6 @@ 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 { @@ -928,13 +908,6 @@ pub enum TypeExpr { macro_def: Option>, macro_name: MacroCallName, }, - Paren { - // This constructor allows us to analyze the usage of parens - // when deciding on assists. - // Much like `TypeExpr::MacroCall`, it is normally hidden during a - // `fold`, but can be made visible if needed. - ty: TypeExprId, - }, SsrPlaceholder(SsrPlaceholder), } @@ -963,7 +936,6 @@ impl TypeExpr { TypeExpr::UnaryOp { .. } => "TypeExpr::UnaryOp", TypeExpr::Var(_) => "TypeExpr::Var", TypeExpr::MacroCall { .. } => "TypeExpr::MacroCall", - TypeExpr::Paren { .. } => "TypeExpr::Paren", TypeExpr::SsrPlaceholder(_) => "TypeExpr::SsrPlaceholder", } } diff --git a/crates/hir/src/fold.rs b/crates/hir/src/fold.rs index 59b12be6fd..e28a81d0fe 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<(HirIdx, Option>)>, + pub in_macro: 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<(HirIdx, Option>)>, + macro_stack: Vec, 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<(HirIdx, Option>)> { + fn in_macro(&self) -> Option { self.macro_stack.first().copied() } @@ -752,19 +752,16 @@ 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), - }, - *macro_def, - )); + self.macro_stack.push(HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Expr(expr_id), + }); let e = self.do_fold_expr(*expansion, acc); self.macro_stack.pop(); e @@ -953,19 +950,16 @@ 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), - }, - *macro_def, - )); + self.macro_stack.push(HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Pat(pat_id), + }); let e = self.do_fold_pat(*expansion, acc); self.macro_stack.pop(); e @@ -1171,25 +1165,21 @@ 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), - }, - *macro_def, - )); + self.macro_stack.push(HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::TypeExpr(type_expr_id), + }); let e = self.do_fold_type_expr(*expansion, acc); self.macro_stack.pop(); e } } - TypeExpr::Paren { ty } => self.do_fold_type_expr(*ty, acc), TypeExpr::SsrPlaceholder(_) => acc, }; self.parents.pop(); @@ -1626,7 +1616,7 @@ bar() -> -define(AA, {foo,foo,foo}). bar(XX) -> begin %% clause.exprs[0] - case XX of + case XX of ?AA -> ok end, {fo~o} @@ -1688,7 +1678,7 @@ bar() -> -define(AA, {foo,foo,foo}). bar(XX) -> begin %% clause.exprs[0] - case XX of + case XX of ?AA -> ok end, {fo~o} @@ -2221,9 +2211,7 @@ 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}). "#; @@ -2663,56 +2651,6 @@ bar() -> count_parens(fixture_str, 3); } - #[test] - fn parens_in_type_expr() { - let fixture_str = r#" - -type foo() :: ((integer())). - "#; - count_parens_in_type_expr(fixture_str, 2); - } - - #[test] - fn parens_in_type_expr_complex() { - let fixture_str = r#" - -type bar() :: {((atom())), (integer() | (binary()))}. - "#; - count_parens_in_type_expr(fixture_str, 4); - } - - #[track_caller] - fn count_parens_in_type_expr(fixture_str: &str, n: u32) { - let (db, fixture) = TestDB::with_fixture(fixture_str); - let file_id = fixture.files[0]; - let sema = Semantic::new(&db); - - let r = do_count_parens_in_type_expr(&sema, ParenStrategy::InvisibleParens, file_id); - assert_eq!(r, 0); - - let r = do_count_parens_in_type_expr(&sema, ParenStrategy::VisibleParens, file_id); - assert_eq!(r, n); - } - - fn do_count_parens_in_type_expr( - sema: &Semantic, - parens: ParenStrategy, - file_id: FileId, - ) -> u32 { - fold_file( - sema, - Strategy { - macros: MacroStrategy::Expand, - parens, - }, - file_id, - 0, - &mut |acc, ctx| match ctx.item { - AnyExpr::TypeExpr(TypeExpr::Paren { .. }) => acc + 1, - _ => acc, - }, - &mut |acc, _on, _form_id| acc, - ) - } - // End of testing paren visibility // ----------------------------------------------------------------- } diff --git a/crates/hir/src/form_list.rs b/crates/hir/src/form_list.rs index a1431d7506..560befbb4b 100644 --- a/crates/hir/src/form_list.rs +++ b/crates/hir/src/form_list.rs @@ -188,11 +188,6 @@ 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() } @@ -655,23 +650,6 @@ 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 79f542fd0d..00a5ec4020 100644 --- a/crates/hir/src/form_list/tests.rs +++ b/crates/hir/src/form_list/tests.rs @@ -318,7 +318,6 @@ 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 751a2f4907..11d4b40971 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -35,7 +35,6 @@ 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; @@ -155,7 +154,7 @@ pub use name::MacroName; pub use name::Name; pub use name::NameArity; pub use name::known; -// @fb-only: pub use name::meta_only; +// @fb-only pub use sema::AtomDef; pub use sema::CallDef; pub use sema::DefinitionOrReference; @@ -232,10 +231,6 @@ 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 0420e19bb6..b081cbd539 100644 --- a/crates/hir/src/module_data.rs +++ b/crates/hir/src/module_data.rs @@ -615,9 +615,8 @@ impl TypeAliasDef { } pub fn name_range(&self, db: &dyn SourceDatabase) -> Option { - let type_name = self.source(db).type_name()?; - let name = type_name.name()?; - Some(name.syntax().text_range()) + let range = self.source(db).type_name()?.syntax().text_range(); + Some(range) } /// This information is used for completion. diff --git a/crates/hir/src/name.rs b/crates/hir/src/name.rs index f94f139d38..00ce2f89f1 100644 --- a/crates/hir/src/name.rs +++ b/crates/hir/src/name.rs @@ -10,7 +10,7 @@ //! See [`Name`]. -// @fb-only: pub mod meta_only; +// @fb-only use std::borrow::Cow; use std::collections::HashSet; diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index 68577d2a2e..78a7db7c3c 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: pub mod meta_only; +// @fb-only pub mod to_def; pub struct ModuleIter(Arc); @@ -1006,28 +1006,6 @@ 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 01d17278a2..fcefa17929 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: use crate::sema::meta_only; +// @fb-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: meta_only::resolve_handle_call_target(sema, arity, file_id, &module_name, &fn_name); + // @fb-only if let Some(r) = mo { r } else { @@ -786,20 +786,19 @@ use lazy_static::lazy_static; /// Pattern for matching dynamic call expressions (apply, rpc calls, etc.) #[derive(Debug, Clone)] -pub(crate) struct DynamicCallPattern { +struct DynamicCallPattern { /// Index of the target module argument (None when target function is in same module) - pub(crate) module_arg_index: Option, + module_arg_index: Option, /// Index of the target function argument - pub(crate) function_arg_index: usize, + function_arg_index: usize, /// Index of the arguments list or arity argument - pub(crate) args_list_index: usize, + args_list_index: usize, /// Whether to extract arity directly from an integer argument (true) or from list length (false) - pub(crate) direct_arity: bool, + direct_arity: bool, } -pub(crate) type PatternKey = (Option<&'static str>, &'static str, usize); +type PatternKey = (Option<&'static str>, &'static str, usize); -#[macro_export] macro_rules! add_patterns { ($patterns:ident, [$(($module:expr, $func:literal, $arity:literal, $module_idx:expr, $func_idx:literal, $args_idx:literal, $direct:expr)),* $(,)?]) => { $( @@ -816,7 +815,9 @@ macro_rules! add_patterns { }; } -fn add_dynamic_call_patterns(patterns: &mut FxHashMap) { +fn create_dynamic_call_patterns() -> FxHashMap { + let mut patterns = FxHashMap::default(); + // Each entry follows the format: // // (module, function, arity, module_arg_index, function_arg_index, args_list_index, direct_arity) @@ -883,197 +884,20 @@ 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 initialization for the patterns map lazy_static! { - static ref DYNAMIC_CALL_PATTERNS: FxHashMap = { - let mut patterns = FxHashMap::default(); - add_dynamic_call_patterns(&mut patterns); - // @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 - }; + static ref DYNAMIC_CALL_PATTERNS: FxHashMap = + create_dynamic_call_patterns(); } fn get_dynamic_call_patterns() -> &'static FxHashMap { &DYNAMIC_CALL_PATTERNS } -pub fn get_module_arg_patterns() -> &'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 acba8ef642..aba9731379 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -10,11 +10,12 @@ workspace = true elp_eqwalizer.workspace = true elp_erlang_service.workspace = true elp_ide_assists.workspace = true -elp_ide_db.workspace = true elp_ide_completion.workspace = true +elp_ide_db.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 d0ae18c158..6cbdd3469c 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: use crate::meta_only; +// @fb-only use crate::runnables::Runnable; use crate::runnables::runnables; @@ -57,11 +57,11 @@ pub struct Link { } #[rustfmt::skip] -// @fb-only: pub(crate) fn annotations(db: &RootDatabase, file_id: FileId) -> Vec { +// @fb-only pub(crate) fn annotations(_db: &RootDatabase, _file_id: FileId) -> Vec { // @oss-only - // @fb-only: let mut annotations = Vec::default(); + // @fb-only let annotations = Vec::default(); // @oss-only - // @fb-only: meta_only::annotations(db, file_id, &mut annotations); + // @fb-only annotations } diff --git a/crates/ide/src/codemod_helpers.rs b/crates/ide/src/codemod_helpers.rs index 2ecd21e82c..d964a58420 100644 --- a/crates/ide/src/codemod_helpers.rs +++ b/crates/ide/src/codemod_helpers.rs @@ -439,14 +439,13 @@ impl TryFrom<&str> for MFA { } } -#[allow(unused)] pub struct CheckCallCtx<'a, T> { - pub sema: &'a Semantic<'a>, + #[allow(unused)] pub mfa: &'a FunctionMatch, pub parents: &'a Vec, pub target: &'a CallTarget, pub t: &'a T, - pub args: &'a Args, + pub args: Args, pub in_clause: &'a InFunctionClauseBody<'a, &'a FunctionDef>, } @@ -471,11 +470,10 @@ impl Args { } } - pub fn as_slice(&self) -> &[ExprId] { - const EMPTY_SLICE: &[ExprId] = &[]; + pub fn as_vec(&self) -> Vec { match self { - Args::Args(args) => args, - Args::Arity(_arity) => EMPTY_SLICE, + Args::Args(args) => args.to_vec(), + Args::Arity(_arity) => vec![], } } } @@ -487,7 +485,7 @@ pub struct MatchCtx<'a, Extra> { pub sema: &'a Semantic<'a>, pub def_fb: &'a InFunctionClauseBody<'a, &'a FunctionDef>, pub target: &'a CallTarget, - pub args: &'a Args, + pub args: Args, /// Range of the module:fun part of an MFA, if not defined in a /// macro. pub range_surface_mf: Option, @@ -551,30 +549,29 @@ pub(crate) fn find_call_in_function( } && let None = excluded_matcher.get_match( &target, args.arity(), - Some(args.as_slice()), + Some(&args.as_vec()), sema, &def_fb.body(clause_id), ) && let Some((mfa, t)) = matcher.get_match( &target, args.arity(), - Some(args.as_slice()), + Some(&args.as_vec()), sema, &def_fb.body(clause_id), ) { let in_clause = &def_fb.in_clause(clause_id); let context = CheckCallCtx { - sema, mfa, parents: ctx.parents, t, target: &target, - args: &args, + args: args.clone(), in_clause, }; if let Some(extra) = check_call(context) { // Got one. - let call_expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { - hir_idx.idx + let call_expr_id = if let Some(expr_id) = ctx.in_macro { + expr_id.idx } else { ctx.item_id }; @@ -589,7 +586,7 @@ pub(crate) fn find_call_in_function( sema, def_fb: in_clause, target: &target, - args: &args, + args, extra: &extra, range_surface_mf, range: *range, @@ -791,9 +788,9 @@ mod tests { bar(Config) -> foo:fire_bombs(Config), - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message foo:fire_bombs(Config, zz). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -807,9 +804,9 @@ mod tests { bar(Config) -> foo:fire_bombs(Config), - %% ^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message foo:fire_bombs(Config, zz). - %% ^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -825,7 +822,7 @@ mod tests { bar(Config) -> ?MY_MACRO(Config). - %% ^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -842,7 +839,7 @@ mod tests { bar(Config) -> ?MY_MACRO(Config). - %% ^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message //- /src/inc.hrl -define(MY_MACRO(A), fun() -> foo:fire_bombs(A) end). @@ -859,7 +856,7 @@ mod tests { bar(Node) -> spawn(Node, mod, fff, []). - %% ^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -875,13 +872,13 @@ mod tests { erlang:spawn(fun() -> ok end), spawn(fun() -> ok end), erlang:spawn(Node, fun() -> ok end), - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message spawn(Node, fun() -> ok end), - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message erlang:spawn(Node, mod, fff, []), - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message spawn(Node, mod, fff, []). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -896,7 +893,7 @@ mod tests { bar() -> erlang:spawn(), - %% ^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message spawn(). "#, ) @@ -911,11 +908,11 @@ mod tests { bar() -> foo:bar(), - %% ^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^ 💡 warning: Diagnostic Message foo:bar(x), - %% ^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^ 💡 warning: Diagnostic Message foo:bar(x,y), - %% ^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^ 💡 warning: Diagnostic Message baz:bar(). "#, ) @@ -930,10 +927,10 @@ mod tests { bar() -> foo:bar(), - %% ^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^ 💡 warning: Diagnostic Message baz:bar(x), foo:florgle(x,y). - %% ^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, ) } @@ -947,11 +944,11 @@ mod tests { bar() -> foo:bar(), - %% ^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^ 💡 warning: Diagnostic Message baz:bar(x), - %% ^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^ 💡 warning: Diagnostic Message local(x,y). - %% ^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^ 💡 warning: Diagnostic Message local(A,B) -> {A,B}. "#, ) @@ -966,7 +963,7 @@ mod tests { bar() -> fo~o:bar(). - %% ^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^ 💡 warning: Diagnostic Message "#, expect![[r#" -module(main). @@ -986,7 +983,7 @@ mod tests { -module(main). foo(Node, M,F,A) -> rpc:c~all(Node, M, F, A). - %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, expect![[r#" -module(main). @@ -1005,7 +1002,7 @@ mod tests { -module(main). foo(Node, M,F,A) -> baz(rpc:c~all(Node, M, F, A)). - %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: ad-hoc: test: Diagnostic Message + %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Diagnostic Message "#, expect![[r#" -module(main). diff --git a/crates/ide/src/common_test.rs b/crates/ide/src/common_test.rs index a5441efd28..eea87c4422 100644 --- a/crates/ide/src/common_test.rs +++ b/crates/ide/src/common_test.rs @@ -346,7 +346,7 @@ mod tests { a(_Config) -> ok. b(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (b/1) +%% ^ 💡 error: Unreachable test (b/1) ok. "#, ); @@ -368,7 +368,7 @@ mod tests { a(_Config) -> ok. b(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (b/1) +%% ^ 💡 error: Unreachable test (b/1) ok. "#, ); @@ -391,7 +391,7 @@ mod tests { a(_Config) -> ok. b(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (b/1) +%% ^ 💡 error: Unreachable test (b/1) ok. "#, ); @@ -413,7 +413,7 @@ mod tests { b(_Config) -> ok. c(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (c/1) +%% ^ 💡 error: Unreachable test (c/1) ok. "#, ); @@ -434,7 +434,7 @@ mod tests { b(_Config) -> ok. c(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (c/1) +%% ^ 💡 error: Unreachable test (c/1) ok. "#, ); @@ -453,10 +453,10 @@ mod tests { a(_Config) -> ok. b(_Config) -> - %% ^ 💡 error: W0008: Unreachable test (b/1) + %% ^ 💡 error: Unreachable test (b/1) ok. c(_Config) -> - %% ^ 💡 error: W0008: Unreachable test (c/1) + %% ^ 💡 error: Unreachable test (c/1) ok. "#, ); @@ -501,7 +501,7 @@ c(_Config) -> -export([all/0]). -export([a/1, b/1, c/1]). all() -> my_external_helper:all(). -%% ^^^ warning: W0021: Could not evaluate function. No code lenses for tests will be available. +%% ^^^ warning: Could not evaluate function. No code lenses for tests will be available. a(_Config) -> ok. b(_Config) -> @@ -522,9 +522,9 @@ c(_Config) -> -export([all/0, groups/0]). -export([a/1, b/1, c/1]). all() -> [a]. -%% ^^^ warning: W0021: Could not evaluate function. No code lenses for tests will be available. +%% ^^^ warning: Could not evaluate function. No code lenses for tests will be available. groups() -> my_external_helper:groups(). -%% ^^^^^^ warning: W0021: Could not evaluate function. No code lenses for tests will be available. +%% ^^^^^^ warning: Could not evaluate function. No code lenses for tests will be available. a(_Config) -> ok. b(_Config) -> @@ -550,7 +550,7 @@ c(_Config) -> a(_Config) -> ok. b(_Config) -> -%% ^ 💡 error: W0008: Unreachable test (b/1) +%% ^ 💡 error: Unreachable test (b/1) ok. //- /my_app/src/my_behaviour.erl -module(my_behaviour). diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index eb6bae611b..c4c90ae527 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -11,7 +11,6 @@ use std::borrow::Cow; use std::collections::BTreeSet; use std::fmt; -use std::path::Path; use std::sync::Arc; use anyhow::Result; @@ -39,18 +38,15 @@ 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; @@ -67,12 +63,11 @@ 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; @@ -97,13 +92,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; @@ -113,14 +108,12 @@ 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: mod meta_only; +// @fb-only mod missing_compile_warn_missing_spec; -mod missing_module; mod missing_separator; mod misspelled_attribute; mod module_mismatch; @@ -132,7 +125,6 @@ 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; @@ -140,7 +132,6 @@ 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; @@ -158,7 +149,6 @@ mod unused_record_field; pub use elp_ide_db::DiagnosticCode; pub use from_config::Lint; pub use from_config::LintsFromConfig; -pub use from_config::MatchSsr; pub use from_config::ReplaceCall; pub use from_config::ReplaceCallAction; pub use replace_call::Replacement; @@ -233,9 +223,8 @@ impl Diagnostic { self } - pub(crate) fn as_related(&self, file_id: FileId) -> RelatedInformation { + pub(crate) fn as_related(&self) -> RelatedInformation { RelatedInformation { - file_id, range: self.range, message: self.message.clone(), } @@ -386,10 +375,10 @@ impl Diagnostic { format!( "{}:{}-{}:{}::[{:?}] [{}] {}", - start.line + 1, - start.col_utf16 + 1, - end.line + 1, - end.col_utf16 + 1, + start.line, + start.col_utf16, + end.line, + end.col_utf16, self.severity(use_cli_severity), self.code, self.message @@ -454,7 +443,6 @@ impl Diagnostic { #[derive(Debug, Clone)] pub struct RelatedInformation { - pub file_id: FileId, pub range: TextRange, pub message: String, } @@ -469,10 +457,9 @@ impl fmt::Display for Diagnostic { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] 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 @@ -482,6 +469,12 @@ 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, @@ -511,13 +504,13 @@ pub(crate) trait Linter { fn description(&self) -> &'static str; // The severity for the lint issue. It defaults to `Warning`. - fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::Warning } // For CLI, when using the --use-cli-severity flag. It defaults to `severity()` - fn cli_severity(&self, sema: &Semantic, file_id: FileId) -> Severity { - self.severity(sema, file_id) + fn cli_severity(&self) -> Severity { + self.severity() } // Specify if the linter issues can be suppressed via a `% elp:ignore` comment. @@ -551,37 +544,12 @@ 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()) @@ -892,7 +860,6 @@ pub(crate) trait GenericLinter: Linter { fn fixes( &self, _context: &Self::Context, - _range: TextRange, _sema: &Semantic, _file_id: FileId, ) -> Option> { @@ -926,7 +893,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, matched.range, sema, file_id); + let fixes = self.fixes(&matched.context, sema, file_id); let tag = self.tag(&matched.context); let mut d = Diagnostic::new(self.id(), message, matched.range) .with_fixes(fixes) @@ -1081,7 +1048,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.clone()); + allowed_diagnostics.insert(diagnostic_filter); } // Make sure the enabled ones win out over disabled if a lint appears in both @@ -1099,13 +1066,6 @@ 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) } @@ -1142,31 +1102,15 @@ impl DiagnosticsConfig { } pub fn enable(mut self, code: DiagnosticCode) -> DiagnosticsConfig { - self.enabled.enable(code.clone()); - self.set_linter_enabled(code, true); + self.enabled.enable(code); self } pub fn disable(mut self, code: DiagnosticCode) -> DiagnosticsConfig { - self.disabled.insert(code.clone()); - self.set_linter_enabled(code, false); + self.disabled.insert(code); 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, @@ -1196,29 +1140,6 @@ 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 @@ -1244,16 +1165,6 @@ 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, @@ -1305,43 +1216,9 @@ 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 { @@ -1349,25 +1226,6 @@ 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 { @@ -1377,33 +1235,10 @@ 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 @@ -1555,8 +1390,14 @@ 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() @@ -1564,7 +1405,7 @@ pub fn native_diagnostics( config .lints_from_config .get_diagnostics(&mut res, &sema, file_id); - // @fb-only: meta_only::diagnostics(&mut res, &sema, file_id, file_kind, config); + // @fb-only syntax_diagnostics(&sema, &parse, &mut res, file_id); diagnostics_from_descriptors( &mut res, @@ -1593,7 +1434,6 @@ 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 @@ -1602,7 +1442,6 @@ 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 { @@ -1631,12 +1470,22 @@ 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, ] } @@ -1653,20 +1502,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) { - 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); + 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); + } } + } else { + (descriptor.checker)(res, sema, file_id, file_kind); } } }); @@ -1708,28 +1557,10 @@ 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, - &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, -]; +const GENERIC_LINTERS: &[&dyn GenericDiagnostics] = &[&unused_macro::LINTER]; /// Unified registry for all types of linters pub(crate) fn linters() -> Vec { @@ -1757,7 +1588,7 @@ pub(crate) fn linters() -> Vec { ); // Add meta-only linters - // @fb-only: all_linters.extend(meta_only::linters()); + // @fb-only all_linters } @@ -1774,26 +1605,25 @@ 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, &app_name, is_generated, is_test) + && should_run(linter, config, 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(sema, file_id)) + .unwrap_or_else(|| linter.severity()) } else { - linter.severity(sema, file_id) + linter.severity() }; 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(sema, file_id)) + .unwrap_or_else(|| linter.cli_severity()) } else { - linter.cli_severity(sema, file_id) + linter.cli_severity() }; match l { DiagnosticLinter::FunctionCall(function_linter) => { @@ -1893,12 +1723,13 @@ fn widen_range(range: TextRange) -> TextRange { } } -fn syntax_diagnostics( +pub 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); @@ -1909,180 +1740,32 @@ pub fn filter_diagnostics(diagnostics: Vec, code: DiagnosticCode) -> diagnostics.into_iter().filter(|d| d.code == code).collect() } -/// 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) +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 } - 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::FileAttribute(_) => { + continue; // skip } - FormIdx::Spec(spec_id) => { - let (_, body_map) = sema - .db - .spec_body_with_source(InFile::new(file_id, *spec_id)); - Some(body_map) + ast::Form::ModuleAttribute(_) => { + break; } - FormIdx::Callback(callback_id) => { - let (_, body_map) = sema - .db - .callback_body_with_source(InFile::new(file_id, *callback_id)); - Some(body_map) + other_form => { + report(other_form.syntax().text_range()); + break; } - 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 { @@ -2203,47 +1886,6 @@ pub(crate) fn make_unexpected_diagnostic( .with_severity(Severity::Warning) } -/// Deduplicate diagnostics by line start and add ignore fix only to the first diagnostic on each line. -/// This prevents duplicate ignore fix suggestions when multiple diagnostics occur on the same line. -pub(crate) fn add_ignore_fix_deduplicated( - diagnostics: &mut Vec, - temp_diagnostics: Vec, - sema: &Semantic, - file_id: FileId, -) { - let parsed = sema.parse(file_id); - let mut diagnostics_by_line_start: FxHashMap> = FxHashMap::default(); - - for diagnostic in temp_diagnostics { - let line_start_offset = if let Some(token) = parsed - .value - .syntax() - .token_at_offset(diagnostic.range.start()) - .right_biased() - { - start_of_line(&token).into() - } else { - diagnostic.range.start().into() - }; - - diagnostics_by_line_start - .entry(line_start_offset) - .or_default() - .push(diagnostic); - } - - for (_, mut diags_on_line) in diagnostics_by_line_start { - if let Some(first_diag) = diags_on_line.first_mut() { - *first_diag = std::mem::replace( - first_diag, - Diagnostic::new(DiagnosticCode::default(), "", TextRange::default()), - ) - .with_ignore_fix(sema, file_id); - } - diagnostics.extend(diags_on_line); - } -} - #[derive(Debug, PartialEq, Eq)] pub enum RemoveElpReported { Yes, @@ -2277,54 +1919,51 @@ 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, &mut related_info_map)) + .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d)) .for_each(|val| { error_info.insert(val); }); res.warnings .iter() - .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d, &mut related_info_map)) + .filter_map(|d| parse_error_to_diagnostic_info(db, file_id, d)) .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)| { - to_diagnostic(file_id, start, end, code, msg, Severity::Error) + ( + file_id, + tag_erlang_service_diagnostic( + Diagnostic::new( + DiagnosticCode::ErlangService(code), + msg, + TextRange::new(start, end), + ) + .with_severity(Severity::Error), + ), + ) }) .chain( warning_info .into_iter() .map(|(file_id, start, end, code, msg)| { - to_diagnostic(file_id, start, end, code, msg, warning_severity) + ( + file_id, + tag_erlang_service_diagnostic( + Diagnostic::new( + DiagnosticCode::ErlangService(code), + msg, + TextRange::new(start, end), + ) + .with_severity(warning_severity), + ), + ) }), ) .collect(); @@ -2341,14 +1980,11 @@ 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) - && should_process_app(&app_name, config, &d.code) + !d.should_be_suppressed(&metadata, config) && !config.disabled.contains(&d.code) }) .map(|(file_id, d)| { ( @@ -2639,7 +2275,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: meta_only::ct_diagnostics(&mut res, &sema, file_id, testcases); + // @fb-only } CommonTestInfo::EvalError(_error) => { // The error currently does not contain anything useful, so we ignore it @@ -2716,68 +2352,20 @@ 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 { - file_attribute_location, - error_path, + directive_location, error_location, - }) => { - // 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(), - ) - }, - ) - } - } + }) => 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(), + ) + }), Some(DiagnosticLocation::Normal(range)) => { let default_range = ( file_id, @@ -2787,13 +2375,11 @@ 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(), @@ -2806,7 +2392,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(), @@ -2817,7 +2403,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(), @@ -2827,7 +2413,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(), @@ -2837,7 +2423,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(), @@ -2847,7 +2433,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(), @@ -2870,22 +2456,6 @@ 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, @@ -2970,13 +2540,13 @@ fn type_alias_name_range( pub fn included_file_file_id( db: &RootDatabase, file_id: FileId, - file_attribute_location: TextRange, + directive_range: 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().start() >= file_attribute_location.start() { + if form.syntax().text_range().contains(directive_range.start()) { db.resolve_include(InFile::new(file_id, idx)) } else { None @@ -2985,30 +2555,6 @@ 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 @@ -3031,7 +2577,6 @@ 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 { @@ -3067,7 +2612,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(file_id)).collect_vec(); + let related_info = related.iter().map(|d| d.as_related()).collect_vec(); syntax_error_diags .iter() .map(|d| d.clone().with_related(Some(related_info.clone()))) @@ -3088,90 +2633,10 @@ pub fn attach_related_diagnostics( .filter(|(k, _)| !undefineds_to_remove.contains(k)) .flat_map(|(_, v)| v); - // 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) + native + .normal + .into_iter() + .chain(erlang_service.normal) .chain(syntax_errors_with_related) .chain(erlang_service_undefined_not_related.cloned()) // TODO:AZ: consider returning an iterator @@ -3236,10 +2701,9 @@ mod tests { fn syntax_error() { check_diagnostics( r#" -//- expect_parse_errors -module(main). foo() -> XX 3.0. - %% ^^ error: P1711: Syntax Error + %% ^^ error: Syntax Error "#, ); } @@ -3250,7 +2714,7 @@ foo() -> XX 3.0. r#" -module(main). -export([foo/0 bar/1]). - %% ^ warning: W0004: Missing ',' + %% ^ warning: Missing ',' "#, ); } @@ -3261,7 +2725,7 @@ foo() -> XX 3.0. r#" -module(main). -export_type([foo/0 bar/1]). - %% ^ warning: W0004: Missing ',' + %% ^ warning: Missing ',' "#, ); } @@ -3272,7 +2736,7 @@ foo() -> XX 3.0. r#" -module(main). -import(bb, [foo/0 bar/1]). - %% ^ warning: W0004: Missing ',' + %% ^ warning: Missing ',' "#, ); } @@ -3283,7 +2747,7 @@ foo() -> XX 3.0. r#" -module(main). -type foo(A B) :: [A,B]. - %% ^ warning: W0004: Missing ',' + %% ^ warning: Missing ',' "#, ); } @@ -3294,7 +2758,7 @@ foo() -> XX 3.0. r#" -module(main). -record(foo {f1, f2 = 3}). - %% ^^^ warning: W0004: Missing ',' + %% ^^^ warning: Missing ',' main(X) -> {X#foo.f1, X#foo.f2}. "#, @@ -3326,6 +2790,53 @@ 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: no module definition +"#, + ); + } + + #[test] + fn fun_decl_module_decl_missing_2() { + check_diagnostics( + r#" + baz(1)->4. +%%^^^^^^^^^^ error: 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()); @@ -3388,7 +2899,7 @@ main(X) -> do_foo() -> X = foo:bar(), - %% ^^^^^^^^^ 💡 weak: ad-hoc: foo:bar/0: 'foo:bar/0' called + %% ^^^^^^^^^ 💡 weak: 'foo:bar/0' called X. //- /src/foo.erl -module(foo). @@ -3414,11 +2925,10 @@ main(X) -> #[test] fn label_syntax_error_not_function() { let fixture_str = r#" - //- expect_parse_errors - -module(main). - -record(person, {(name + XXX)}). - %% ^^^^^^^ error: P1711: Syntax Error - %% ^ error: P1711: Syntax Error + -module(main). + -record(person, {(name + XXX)}). +%% ^^^^^^^ error: Syntax Error +%% ^ error: Syntax Error "#; check_diagnostics(fixture_str); let diagnostic = Diagnostic::error( @@ -3430,7 +2940,7 @@ main(X) -> expect![[r#" Some( Range( - 5..45, + 20..52, ), ) "#]] @@ -3442,7 +2952,7 @@ main(X) -> check_diagnostics( r#" baz(1)->4. -%%^^^^^^^^^^ 💡 error: L1201: no module definition +%%^^^^^^^^^^ error: no module definition foo(2)->3. "#, ); @@ -3466,7 +2976,7 @@ main(X) -> %% elp:ignore L1201 baz(1)->4. -%%^^^^^^^^^^ 💡 error: L1201: no module definition +%%^^^^^^^^^^ error: no module definition foo(2)->3. "#, ); @@ -3480,7 +2990,7 @@ main(X) -> baz()-> Foo = 1, - %%^^^ 💡 warning: W0007: match is redundant + %%^^^ 💡 warning: match is redundant % elp:ignore W0007 Bar = 2, ok. @@ -3496,11 +3006,11 @@ main(X) -> baz()-> Foo = 1, - %%^^^ 💡 warning: W0007: match is redundant + %%^^^ 💡 warning: match is redundant % elp:ignore W0007 Bar = 2, - %%^^^ 💡 warning: W0007: match is redundant + %%^^^ 💡 warning: match is redundant ok. "#, ); @@ -3513,7 +3023,7 @@ main(X) -> //- edoc //- /main/src/main_edoc.erl app:main % @unknown - %%<^^^^^^^^ warning: O0039: tag @unknown not recognized. + %%<^^^^^^^^ warning: tag @unknown not recognized. -module(main_edoc). "#, @@ -3589,7 +3099,6 @@ main(X) -> config, &extra_diags, r#" - //- expect_parse_errors -module(main). -export([foo/0,bar/0]). @@ -3599,10 +3108,7 @@ main(X) -> -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 + %% ^ error: Missing ')' "#, ); } @@ -3610,7 +3116,7 @@ main(X) -> #[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/test_projects/diagnostics/app_a/src/syntax.erl + // spec, same code as in test_projects/diagnostics/app_a/src/syntax.erl check_diagnostics( r#" -module(main). @@ -3627,14 +3133,12 @@ main(X) -> 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 + %% ^^^^^ error: syntax error before: error "#, ); } @@ -3646,7 +3150,6 @@ main(X) -> check_diagnostics( r#" //- erlang_service - //- expect_parse_errors //- native //- /src/a_mod.erl app:app_a -module(a_mod). @@ -3654,10 +3157,10 @@ main(X) -> foo() -> bar(). - %% ^^^^^ error: L1227: function bar/0 undefined + %% ^^^^^ error: function bar/0 undefined bar() -> !!! %% syntax error - %%<^^^^^^^^^ error: P1711: Syntax Error + %%<^^^^^^^^^ error: Syntax Error "#, ); } @@ -3671,7 +3174,7 @@ main(X) -> baz()-> Fo~o = 1. - %%^^^ 💡 warning: W0007: match is redundant + %%^^^ 💡 warning: match is redundant "#, expect![[r#" -module(main). @@ -3715,7 +3218,7 @@ main(X) -> -spec baz() -> ok. baz() -> something_else. - %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types "#, ); } @@ -3725,7 +3228,6 @@ main(X) -> fn test_nested_syntax_errors() { check_diagnostics( r#" - //- expect_parse_errors -module(main). run() -> ExitCode = @@ -3734,9 +3236,9 @@ main(X) -> to_exit_code(run1(Root)), catch _:Reason -> to_exit_code(Reason) - %% ^^^^^^^^^^^ error: P1711: Syntax Error + %% ^^^^^^^^^^^ error: Syntax Error end, - %% ^^^ error: P1711: Syntax Error + %% ^^^ error: Syntax Error erlang:halt(ExitCode). "#, @@ -3750,7 +3252,7 @@ main(X) -> //- erlang_service -module(main). -export([foo/0, bar/0]). -%% ^^^^^ error: L1227: function bar/0 undefined +%% ^^^^^ error: function bar/0 undefined foo() -> ok. "#, ); @@ -3763,7 +3265,7 @@ main(X) -> //- erlang_service -module(main). -export_type([foo/0, bar/3]). -%% ^^^^^ error: L1295: type bar/3 undefined +%% ^^^^^ error: type bar/3 undefined -type foo() :: integer(). "#, ); @@ -3777,7 +3279,7 @@ main(X) -> -module(main). -export([foo/0]). -spec bar() -> ok. -%% ^^^ error: L1308: spec for undefined function bar/0 +%% ^^^ error: spec for undefined function bar/0 foo() -> ok. "#, ); @@ -3791,7 +3293,7 @@ main(X) -> -module(main). -type foo() :: ok. -%% ^^^ warning: L1296: type foo() is unused +%% ^^^ warning: type foo() is unused "#, ); @@ -3801,7 +3303,7 @@ main(X) -> -module(main). -type foo(A, B) :: {A, B}. -%% ^^^ warning: L1296: type foo(_,_) is unused +%% ^^^ warning: type foo(_,_) is unused "#, ); } @@ -3869,7 +3371,7 @@ main(X) -> -module(a_file). -doc {file,"../../doc/src/info.md"}. - %% ^^^^^^^^^^^^^^^^^^^^^^^ warning: E1599: can't find doc file "../../doc/src/info.md" + %% ^^^^^^^^^^^^^^^^^^^^^^^ warning: can't find doc file "../../doc/src/info.md" "#, ); @@ -3885,7 +3387,7 @@ main(X) -> -module(erlang). -doc {file,"../../doc/src/info.md"}. - %% ^^^^^^^^^^^^^^^^^^^^^^^ warning: E1599: can't find doc file "../../doc/src/info.md" + %% ^^^^^^^^^^^^^^^^^^^^^^^ warning: can't find doc file "../../doc/src/info.md" "#, ); } @@ -3908,37 +3410,6 @@ main(X) -> ); } - #[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( @@ -3970,8 +3441,7 @@ main(X) -> foo() -> \~"\"\\µA\"" = \~/"\\µA"/ X = 3. - %% ^ error: P1711: syntax error before: X - %% | Related info: 0:32-37 function foo/0 undefined + %% ^ error: syntax error before: X "#, ); } @@ -4007,7 +3477,7 @@ main(X) -> -export([foo/0]). % @docc - %%<^^^^^ warning: O0039: tag @docc not recognized. + %%<^^^^^ warning: tag @docc not recognized. foo() -> \~"foo". "#, ); @@ -4039,7 +3509,6 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, - exclude_apps: None, config: None, }, ); @@ -4062,7 +3531,7 @@ main(X) -> -spec error() -> ok. error() -> erlang:garbage_collect(). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0047: Avoid forcing garbage collection. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 error: 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]). @@ -4082,7 +3551,6 @@ main(X) -> include_tests: Some(true), include_generated: None, experimental: None, - exclude_apps: None, config: None, }, ); @@ -4104,7 +3572,7 @@ main(X) -> warning() -> erlang:garbage_collect(). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0047: Avoid forcing garbage collection. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: 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]). @@ -4124,7 +3592,6 @@ main(X) -> include_tests: None, include_generated: Some(true), experimental: None, - exclude_apps: None, config: None, }, ); @@ -4147,7 +3614,7 @@ main(X) -> warning() -> erlang:garbage_collect(). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0047: Avoid forcing garbage collection. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: 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]). @@ -4167,7 +3634,6 @@ main(X) -> include_tests: None, include_generated: None, experimental: Some(true), - exclude_apps: None, config: None, }, ); @@ -4212,7 +3678,6 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, - exclude_apps: None, config: None, }, ); @@ -4234,7 +3699,6 @@ main(X) -> 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]). @@ -4242,203 +3706,4 @@ main(X) -> "#, ); } - - #[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 615b7801f9..dac88b260c 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: use crate::diagnostics; +// @fb-only 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: false, + include_tests: true, 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: diagnostics::meta_only::application_env_bad_matches(), + // @fb-only ] .into_iter() .flatten() @@ -265,11 +265,11 @@ mod tests { get_mine() -> application:get_env(misc, key). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0011: module `main` belongs to app `my_app`, but reads env for `misc` + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: module `main` belongs to app `my_app`, but reads env for `misc` get_mine3() -> application:get_env(misc, key, def). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0011: module `main` belongs to app `my_app`, but reads env for `misc` + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: module `main` belongs to app `my_app`, but reads env for `misc` //- /my_app/src/application.erl -module(application). @@ -302,7 +302,7 @@ mod tests { steal() -> application:get_env(debug, key). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0011: module `app_env` belongs to app `misc`, but reads env for `debug` + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: module `app_env` belongs to app `misc`, but reads env for `debug` //- /misc/src/application.erl app:misc -module(application). @@ -324,7 +324,7 @@ mod tests { get_mine() -> ?get(misc, key). - %% ^^^^^^^^^^^^^^^ 💡 warning: W0011: module `main` belongs to app `my_app`, but reads env for `misc` + %% ^^^^^^^^^^^^^^^ 💡 warning: module `main` belongs to app `my_app`, but reads env for `misc` //- /my_app/include/my_header.hrl app:my_app -define(get(K,V), application:get_env(K,V)). diff --git a/crates/ide/src/diagnostics/atoms_exhaustion.rs b/crates/ide/src/diagnostics/atoms_exhaustion.rs index 56a43a50ca..bb15bebc2c 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: use crate::diagnostics; +// @fb-only 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: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { + // @fb-only fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only: diagnostics::meta_only::is_relevant_file(sema.db.upcast(), file_id) + // @fb-only true // @oss-only } } @@ -56,19 +56,19 @@ impl FunctionCallLinter for AtomsExhaustionLinter { // FunctionMatch::mfa("erlang", "binary_to_term", 2), ] .into_iter() - // @fb-only: .chain(diagnostics::meta_only::atoms_exhaustion_matches().into_iter()) + // @fb-only .collect::>() ] } fn check_match(&self, context: &CheckCallCtx<'_, ()>) -> Option { #[rustfmt::skip] - // @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); + // @fb-only + // @fb-only + // @fb-only let is_safe = false; // @oss-only if !is_safe { - match context.args.as_slice() { + match context.args.as_vec()[..] { [_, options] => { let body = context.in_clause.body(); match &body[options].literal_list_contains_atom(context.in_clause, "safe") { @@ -100,9 +100,9 @@ mod tests { -export([main/0]). main() -> erlang:list_to_atom(foo), -%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0023: Risk of atoms exhaustion. +%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: Risk of atoms exhaustion. list_to_atom(foo). -%% ^^^^^^^^^^^^ 💡 warning: W0023: Risk of atoms exhaustion. +%% ^^^^^^^^^^^^ 💡 warning: Risk of atoms exhaustion. //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 -module(erlang). @@ -121,9 +121,9 @@ mod tests { -export([main/0]). main() -> erlang:binary_to_atom(foo), -%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0023: Risk of atoms exhaustion. +%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Risk of atoms exhaustion. binary_to_atom(foo). -%% ^^^^^^^^^^^^^^ 💡 warning: W0023: Risk of atoms exhaustion. +%% ^^^^^^^^^^^^^^ 💡 warning: Risk of atoms exhaustion. //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 -module(erlang). diff --git a/crates/ide/src/diagnostics/binary_string_to_sigil.rs b/crates/ide/src/diagnostics/binary_string_to_sigil.rs index 91b08f9d1d..d87d206583 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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::WeakWarning } @@ -143,7 +143,7 @@ mod tests { -module(binary_string_to_sigil). fn() -> <<"hello">>. - %% ^^^^^^^^^^^ 💡 weak: W0051: Binary string can be written using sigil syntax. + %% ^^^^^^^^^^^ 💡 weak: Binary string can be written using sigil syntax. "#, ) } diff --git a/crates/ide/src/diagnostics/boolean_precedence.rs b/crates/ide/src/diagnostics/boolean_precedence.rs index f8f7851f8a..86650651f7 100644 --- a/crates/ide/src/diagnostics/boolean_precedence.rs +++ b/crates/ide/src/diagnostics/boolean_precedence.rs @@ -15,7 +15,6 @@ // 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; @@ -25,11 +24,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; @@ -46,107 +45,29 @@ use hir::fold::MacroStrategy; use hir::fold::ParenStrategy; use hir::fold::ParentId; -use super::GenericLinter; -use super::GenericLinterMatchContext; -use super::Linter; -use crate::Assist; +use super::Diagnostic; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use super::Severity; use crate::fix; -pub(crate) struct BooleanPrecedenceLinter; +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); + }, +}; -impl Linter for BooleanPrecedenceLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::BooleanPrecedence - } - - fn description(&self) -> &'static str { - "boolean precedence" - } +fn boolean_precedence(diagnostics: &mut Vec, sema: &Semantic, file_id: FileId) { + sema.for_each_function(file_id, |def| check_function(diagnostics, sema, def)); } -#[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, -) { +fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &FunctionDef) { let def_fb = def.in_function_body(sema, def); def_fb.clone().fold_function( Strategy { @@ -170,20 +91,28 @@ fn check_function( _ => None, }; if let Some(op) = op { - collect_match(matches, sema, &def_fb, def.file.file_id, clause_id, ctx, op); + report( + sema, + &def_fb, + def.file.file_id, + clause_id, + ctx, + op, + diagnostics, + ); } }, ) } -fn collect_match( - matches: &mut Vec>, +fn report( 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) @@ -227,25 +156,39 @@ fn collect_match( let (_op, token) = binop_ast.op()?; let range = token.text_range(); let preceding_ws_range = include_preceding_whitespace(&token); - - matches.push(GenericLinterMatchContext { - range, - context: Context { - preceding_ws_range, - op: binop, - lhs_complex, - rhs_complex, - add_parens_range, - }, - }); + 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); } }; Some(()) } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +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)] enum Op { - #[default] And, AndInGuard, Or, @@ -280,7 +223,31 @@ impl Op { } } -pub static LINTER: BooleanPrecedenceLinter = BooleanPrecedenceLinter; +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, + )])) +} #[cfg(test)] mod tests { @@ -308,7 +275,7 @@ mod tests { foo() -> F = fun(X) -> my_is_integer(X) and X > 0 end, - %% ^^^ 💡 warning: W0025: Consider using the short-circuit expression 'andalso' instead of 'and'. + %% ^^^ 💡 warning: Consider using the short-circuit expression 'andalso' instead of 'and'. %% | Or add parentheses to avoid potential ambiguity. F. @@ -350,7 +317,7 @@ mod tests { -module(main). foo(X) -> predicate(X) or X > 10. - %% ^^ 💡 warning: W0025: Consider using the short-circuit expression 'orelse' instead of 'or'. + %% ^^ 💡 warning: Consider using the short-circuit expression 'orelse' instead of 'or'. %% | Or add parentheses to avoid potential ambiguity. predicate(_X) -> false. @@ -365,7 +332,7 @@ mod tests { -module(main). foo(S,P) -> (S or P > 3). - %% ^^ 💡 warning: W0025: Consider using the short-circuit expression 'orelse' instead of 'or'. + %% ^^ 💡 warning: Consider using the short-circuit expression 'orelse' instead of 'or'. %% | Or add parentheses to avoid potential ambiguity. "#, ) @@ -378,7 +345,7 @@ mod tests { -module(main). foo(S,P) -> ((S or P > 3)). - %% ^^ 💡 warning: W0025: Consider using the short-circuit expression 'orelse' instead of 'or'. + %% ^^ 💡 warning: Consider using the short-circuit expression 'orelse' instead of 'or'. %% | Or add parentheses to avoid potential ambiguity. "#, ) @@ -393,7 +360,7 @@ mod tests { foo() -> F = fun(X) -> my_is_integer(X) a~nd X > 0 end, - %% ^^^ 💡 warning: W0025: Consider using the short-circuit expression 'andalso' instead of 'and'. + %% ^^^ 💡 warning: Consider using the short-circuit expression 'andalso' instead of 'and'. %% | Or add parentheses to avoid potential ambiguity. F. @@ -421,7 +388,7 @@ mod tests { foo() -> F = fun(X) -> my_is_integer(X) a~nd X > 0 end, - %% ^^^ 💡 warning: W0025: Consider using the short-circuit expression 'andalso' instead of 'and'. + %% ^^^ 💡 warning: Consider using the short-circuit expression 'andalso' instead of 'and'. %% | Or add parentheses to avoid potential ambiguity. F. @@ -445,7 +412,7 @@ mod tests { -module(main). foo(X) when X < 10 a~nd X > 0 -> X + 1. - %% ^^^ 💡 warning: W0025: Consider using the short-circuit expression ',' instead of 'and'. + %% ^^^ 💡 warning: Consider using the short-circuit expression ',' instead of 'and'. %% | Or add parentheses to avoid potential ambiguity. "#, expect![[r#" @@ -465,7 +432,7 @@ mod tests { foo() -> F = fun(X) -> my_is_integer(X) a~nd X > 0 end, - %% ^^^ 💡 warning: W0025: Consider using the short-circuit expression 'andalso' instead of 'and'. + %% ^^^ 💡 warning: Consider using the short-circuit expression 'andalso' instead of 'and'. %% | Or add parentheses to avoid potential ambiguity. F. @@ -490,7 +457,7 @@ mod tests { foo() -> F = fun(X) -> X < 0 o~r X > 10 end, - %% ^^ 💡 warning: W0025: Consider using the short-circuit expression 'orelse' instead of 'or'. + %% ^^ 💡 warning: Consider using the short-circuit expression 'orelse' instead of 'or'. %% | Or add parentheses to avoid potential ambiguity. F. @@ -514,7 +481,7 @@ mod tests { -module(main). foo(X) -> predicate(X) o~r X > 10. - %% ^^ 💡 warning: W0025: Consider using the short-circuit expression 'orelse' instead of 'or'. + %% ^^ 💡 warning: Consider using the short-circuit expression 'orelse' instead of 'or'. %% | Or add parentheses to avoid potential ambiguity. predicate(_X) -> false."#, diff --git a/crates/ide/src/diagnostics/bound_variable.rs b/crates/ide/src/diagnostics/bound_variable.rs deleted file mode 100644 index 92f0edadfa..0000000000 --- a/crates/ide/src/diagnostics/bound_variable.rs +++ /dev/null @@ -1,178 +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. - */ - -// 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 03ea0e6274..73b596e0c9 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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::Information } } @@ -265,7 +265,7 @@ mod tests { -module(main). fn() -> list_to_binary("foo"). - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a binary string literal. + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a binary string literal. //- /src/erlang.erl -module(erlang). @@ -293,7 +293,7 @@ mod tests { -module(main). fn() -> ~"foo". - + "#]], ) } @@ -306,7 +306,7 @@ mod tests { -module(main). fn() -> erlang:list_to_binary("foo"). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a binary string literal. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a binary string literal. //- /src/erlang.erl -module(erlang). @@ -347,7 +347,7 @@ mod tests { -module(main). fn() -> list_to_atom("foo"). - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as an atom literal. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as an atom literal. //- /src/erlang.erl -module(erlang). @@ -388,7 +388,7 @@ mod tests { -module(main). fn() -> erlang:list_to_atom("foo"). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as an atom literal. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as an atom literal. //- /src/erlang.erl -module(erlang). @@ -429,7 +429,7 @@ mod tests { -module(main). fn() -> atom_to_binary(foo). - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a binary string literal. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a binary string literal. //- /src/erlang.erl -module(erlang). @@ -470,7 +470,7 @@ mod tests { -module(main). fn() -> erlang:atom_to_binary(foo). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a binary string literal. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a binary string literal. //- /src/erlang.erl -module(erlang). @@ -511,7 +511,7 @@ mod tests { -module(main). fn() -> atom_to_list(foo). - %% ^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a string literal. + %% ^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a string literal. //- /src/erlang.erl -module(erlang). @@ -552,7 +552,7 @@ mod tests { -module(main). fn() -> erlang:atom_to_list(foo). - %% ^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0055: Could be rewritten as a string literal. + %% ^^^^^^^^^^^^^^^^^^^^^^^^ 💡 information: Could be rewritten as a string literal. //- /src/erlang.erl -module(erlang). diff --git a/crates/ide/src/diagnostics/cross_node_eval.rs b/crates/ide/src/diagnostics/cross_node_eval.rs index f50ca663e3..a61c058a40 100644 --- a/crates/ide/src/diagnostics/cross_node_eval.rs +++ b/crates/ide/src/diagnostics/cross_node_eval.rs @@ -12,9 +12,6 @@ //! //! 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; @@ -31,7 +28,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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::Error } fn should_process_test_files(&self) -> bool { @@ -114,7 +111,7 @@ mod tests { foo(Node) -> erlang:spawn(Node, fun() -> ok end). - %% ^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) } @@ -127,7 +124,7 @@ mod tests { foo(Node) -> spawn(Node, fun() -> ok end). - %% ^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -141,7 +138,7 @@ mod tests { foo(Node) -> erlang:spawn(Node, modu, ff, []). - %% ^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -155,7 +152,7 @@ mod tests { foo(Node) -> spawn(Node, modu, ff, []). - %% ^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -171,7 +168,7 @@ mod tests { foo(Node) -> erlang:spawn_link(Node, fun() -> ok end). - %% ^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) } @@ -184,7 +181,7 @@ mod tests { foo(Node) -> spawn_link(Node, fun() -> ok end). - %% ^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -198,7 +195,7 @@ mod tests { foo(Node) -> erlang:spawn_link(Node, modu, ff, []). - %% ^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -212,7 +209,7 @@ mod tests { foo(Node) -> spawn_link(Node, modu, ff, []). - %% ^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -228,7 +225,7 @@ mod tests { foo(Node) -> erlang:spawn_monitor(Node, fun() -> ok end). - %% ^^^^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) } @@ -241,7 +238,7 @@ mod tests { foo(Node) -> spawn_monitor(Node, fun() -> ok end). - %% ^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -255,7 +252,7 @@ mod tests { foo(Node) -> erlang:spawn_monitor(Node, modu, ff, []). - %% ^^^^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -269,7 +266,7 @@ mod tests { foo(Node) -> spawn_monitor(Node, modu, ff, []). - %% ^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -285,7 +282,7 @@ mod tests { foo(Node) -> erlang:spawn_opt(Node, fun() -> ok end, []). - %% ^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) } @@ -298,7 +295,7 @@ mod tests { foo(Node) -> spawn_opt(Node, fun() -> ok end, []). - %% ^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -312,7 +309,7 @@ mod tests { foo(Node) -> erlang:spawn_opt(Node, modu, ff, [], []). - %% ^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -326,7 +323,7 @@ mod tests { foo(Node) -> spawn_opt(Node, modu, ff, [], []). - %% ^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -340,9 +337,9 @@ mod tests { foo(Node) -> erts_internal_dist:dist_spawn_request(Node, fun() -> ok end), - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) erts_internal_dist:dist_spawn_request(Node, modu, ff, [], []). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -356,9 +353,9 @@ mod tests { foo(Node) -> rpc:call(Node, mod, func, []), - %% ^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) rpc:multicall([Node], mod, func, []). - %% ^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -372,9 +369,9 @@ mod tests { foo(Name, FuncSpec) -> sys:install(Name, FuncSpec), - %% ^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) sys:install(Name, FuncSpec, 500). - %% ^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, ) @@ -404,7 +401,7 @@ mod tests { foo(Name, FuncSpec) -> sys:inst~all(Name, FuncSpec). - %% ^^^^^^^^^^^^^ 💡 error: W0014: Production code must not use cross node eval (e.g. `rpc:call()`) + %% ^^^^^^^^^^^^^ 💡 error: Production code must not use cross node eval (e.g. `rpc:call()`) "#, expect![[r#" diff --git a/crates/ide/src/diagnostics/debugging_function.rs b/crates/ide/src/diagnostics/debugging_function.rs index 1f0099781d..cdeb147754 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_ide_db::text_edit::TextRange; +use elp_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: use crate::diagnostics::meta_only; +// @fb-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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::WeakWarning } - fn cli_severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + fn cli_severity(&self) -> 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: .chain(meta_only::debugging_function_matches().into_iter()) + // @fb-only .collect::>() ] } @@ -113,9 +113,9 @@ mod tests { error() -> redbug:start("io:format/2->return", []), -%% ^^^^^^^^^^^^ 💡 weak: W0041: Debugging functions should only be used during local debugging and usages should not be checked in. +%% ^^^^^^^^^^^^ 💡 weak: Debugging functions should only be used during local debugging and usages should not be checked in. redbug:stop(). -%% ^^^^^^^^^^^ 💡 weak: W0041: Debugging functions should only be used during local debugging and usages should not be checked in. +%% ^^^^^^^^^^^ 💡 weak: Debugging functions should only be used during local debugging and usages should not be checked in. noerror() -> redbug(), @@ -127,7 +127,7 @@ redbug() -> start(_) -> ok. - + //- /src/redbug.erl -module(redbug). -export([start/2, stop/0]). @@ -159,7 +159,7 @@ redbug(_) -> main() -> re~dbug:start("io:format/2->return", []). -%% ^^^^^^^^^^^^ 💡 weak: W0041: Debugging functions should only be used during local debugging and usages should not be checked in. +%% ^^^^^^^^^^^^ 💡 weak: Debugging functions should only be used during local debugging and usages should not be checked in. //- /src/redbug.erl -module(redbug). -export([start/2]). @@ -191,7 +191,7 @@ main() -> main() -> re~dbug:start("io:format/2->return", []), -%% ^^^^^^^^^^^^ 💡 weak: W0041: Debugging functions should only be used during local debugging and usages should not be checked in. +%% ^^^^^^^^^^^^ 💡 weak: Debugging functions should only be used during local debugging and usages should not be checked in. ok. //- /src/redbug.erl -module(redbug). diff --git a/crates/ide/src/diagnostics/dependent_header.rs b/crates/ide/src/diagnostics/dependent_header.rs index 4729f05872..d3b786e240 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; @@ -119,7 +119,7 @@ mod tests { r#" //- /include/main.hrl -define(MY_MACRO, #my_record{}). -%% ^^^^^^^^^^ warning: W0015: Record 'my_record' not defined in this context +%% ^^^^^^^^^^ warning: Record 'my_record' not defined in this context "#, ) } @@ -154,7 +154,7 @@ mod tests { r#" //- /include/main.hrl -define(MY_MACRO(Record), Record#my_record.my_field). -%% ^^^^^^^^^^ warning: W0015: Record 'my_record' not defined in this context +%% ^^^^^^^^^^ warning: Record 'my_record' not defined in this context "#, ) } @@ -165,7 +165,7 @@ mod tests { r#" //- /include/main.hrl -define(MY_MACRO, #my_record.my_field). -%% ^^^^^^^^^^ warning: W0015: Record 'my_record' not defined in this context +%% ^^^^^^^^^^ warning: Record 'my_record' not defined in this context "#, ) } @@ -176,7 +176,7 @@ mod tests { r#" //- /include/main.hrl -define(MY_MACRO(Record), Record#my_record{my_field = 42}). -%% ^^^^^^^^^^ warning: W0015: Record 'my_record' not defined in this context +%% ^^^^^^^^^^ warning: Record 'my_record' not defined in this context "#, ) } diff --git a/crates/ide/src/diagnostics/deprecated_function.rs b/crates/ide/src/diagnostics/deprecated_function.rs index b353d824d2..65b06006dc 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: use crate::diagnostics; +// @fb-only 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: diagnostics::meta_only::deprecated_function_matches(), + // @fb-only ]; 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((hir_idx, _macro_def)) = ctx.in_macro { - hir_idx.idx + let expr_id = if let Some(expr_id) = ctx.in_macro { + expr_id.idx } else { ctx.item_id }; @@ -262,7 +262,7 @@ mod tests { ok. main() -> not_ok_to_use(). -%% ^^^^^^^^^^^^^ 💡 warning: W0016: Function 'not_ok_to_use/0' is deprecated. +%% ^^^^^^^^^^^^^ 💡 warning: Function 'not_ok_to_use/0' is deprecated. "#, ) } @@ -282,7 +282,7 @@ mod tests { main() -> b:not_ok_to_use(). -%% ^^^^^^^^^^^^^^^ 💡 warning: W0016: Function 'not_ok_to_use/0' is deprecated. +%% ^^^^^^^^^^^^^^^ 💡 warning: Function 'not_ok_to_use/0' is deprecated. "#, ) } @@ -296,9 +296,9 @@ mod tests { -deprecated({do, 0}). main() -> do(), -%% ^^ 💡 warning: W0016: Function 'do/0' is deprecated. +%% ^^ 💡 warning: Function 'do/0' is deprecated. ?LAZY(do()). -%% ^^^^^^^^^^^ 💡 warning: W0016: Function 'do/0' is deprecated. +%% ^^^^^^^^^^^ 💡 warning: Function 'do/0' is deprecated. do() -> ok. "#, @@ -320,7 +320,7 @@ mod tests { main() -> b:not_ok_to_use(). -%% ^^^^^^^^^^^^^^^ 💡 warning: W0016: Function 'not_ok_to_use/0' is deprecated. +%% ^^^^^^^^^^^^^^^ 💡 warning: Function 'not_ok_to_use/0' is deprecated. %% | Cause I said so. "#, ) diff --git a/crates/ide/src/diagnostics/duplicate_module.rs b/crates/ide/src/diagnostics/duplicate_module.rs index 09eb548b31..de4009cfb8 100644 --- a/crates/ide/src/diagnostics/duplicate_module.rs +++ b/crates/ide/src/diagnostics/duplicate_module.rs @@ -12,58 +12,51 @@ // // 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 crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; +use super::Diagnostic; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use crate::diagnostics::Severity; -pub(crate) struct DuplicateModuleLinter; +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); + }, +}; -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 +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), + ); } + 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; @@ -74,11 +67,11 @@ mod test { r#" //- /src/dup_mod.erl -module(dup_mod). - %% ^^^^^^^ 💡 warning: W0045: A module with this name exists elsewhere + %% ^^^^^^^ warning: Duplicate module name //- /src/sub/dup_mod.erl -module(dup_mod). - %% ^^^^^^^ 💡 warning: W0045: A module with this name exists elsewhere + %% ^^^^^^^ warning: Duplicate module name "#, ) } diff --git a/crates/ide/src/diagnostics/old_edoc_syntax.rs b/crates/ide/src/diagnostics/edoc.rs similarity index 90% rename from crates/ide/src/diagnostics/old_edoc_syntax.rs rename to crates/ide/src/diagnostics/edoc.rs index da4c3f2430..1f708b51ea 100644 --- a/crates/ide/src/diagnostics/old_edoc_syntax.rs +++ b/crates/ide/src/diagnostics/edoc.rs @@ -8,138 +8,96 @@ * above-listed licenses. */ -// Diagnostic: old_edoc_syntax +// Diagnostic: edoc -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_syntax::ast; +use elp_text_edit::TextRange; +use elp_text_edit::TextSize; 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::GenericLinter; -use super::GenericLinterMatchContext; -use super::Linter; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use super::Severity; -pub(crate) struct OldEdocSyntaxLinter; +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"; -impl Linter for OldEdocSyntaxLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::OldEdocSyntax - } +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); + }, +}; - fn description(&self) -> &'static str { - "EDoc style comments are deprecated. Please use Markdown instead." - } - - fn should_process_test_files(&self) -> bool { - false - } -} - -#[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, - }, - }); +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, + )); } } - 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( +fn old_edoc_syntax_diagnostic( sema: &Semantic, file_id: FileId, + show_range: TextRange, header: &EdocHeader, start_offset: TextSize, - range: TextRange, -) -> Assist { +) -> Diagnostic { let eep59_insert_offset = match header.kind { EdocHeaderKind::Module => { helpers::moduledoc_insert_offset(sema, file_id).unwrap_or(start_offset) @@ -177,12 +135,17 @@ fn old_edoc_syntax_fix( } builder.insert(eep59_insert_offset, header.to_eep59()); let source_change = builder.finish(); - crate::fix( - "convert_to_markdown", - "Convert to Markdown", - source_change, - range, - ) + 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, + } } fn author_exists(author: &str, authors: &FxHashSet) -> bool { @@ -263,7 +226,7 @@ mod tests { %% This is some license text. %%%------------------------------------------------------------------- %% @doc This is the module documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. %% With some more text. %% And some more lines. %% @end @@ -285,7 +248,23 @@ mod tests { r#" -module(main). %% @doc This is the main function documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. + main() -> + dep(). + + dep() -> ok. + "#, + ) + } + + #[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: EDoc style comments are deprecated. Please use Markdown instead. main() -> dep(). @@ -302,14 +281,14 @@ mod tests { -export([main/0, main/2]). %% @doc This is the main function documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. %% @see main/2 for more information. -spec main() -> tuple(). main() -> main([], []). %% @doc This is the main function with two arguments documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. -spec main(any(), any()) -> tuple(). main(A, B) -> {A, B}. @@ -326,7 +305,7 @@ mod tests { -export_type([my_integer/0]). %% @doc This is an incorrect type doc - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. -type my_integer() :: integer(). -type my_integer2() :: integer(). @@ -354,7 +333,7 @@ mod tests { -type my_integer2() :: integer(). %% @doc These are docs for the main function - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. -spec main(any(), any()) -> ok. main(A, B) -> dep(). @@ -372,7 +351,7 @@ mod tests { -export([main/0, main/2]). %% @doc This is the main function documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. %% @see main/2 which is a great function to look at %% with a very long description that goes on and on -spec main() -> tuple(). @@ -380,7 +359,7 @@ mod tests { main([], []). %% @doc This is the main function with two arguments documentation. - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. -spec main(any(), any()) -> tuple(). main(A, B) -> {A, B}. @@ -625,7 +604,7 @@ dep() -> ok. check_fix( r#" -module(main). -%% @d~oc +%% @d~oc %% This is some doc %% @end main() -> diff --git a/crates/ide/src/diagnostics/effect_free_statement.rs b/crates/ide/src/diagnostics/effect_free_statement.rs index 9aec815fbb..a5a1a138a9 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; @@ -275,10 +275,10 @@ mod tests { test_foo(_Config) -> do_something(), ok, - %% ^^ 💡 warning: W0006: this statement has no effect + %% ^^ 💡 warning: this statement has no effect do_something_else(), bar, - %% ^^^ 💡 warning: W0006: this statement has no effect + %% ^^^ 💡 warning: this statement has no effect ok. do_something() -> ok. "#, @@ -293,7 +293,7 @@ mod tests { test_foo(_Config) -> X = 42, X, - %% ^ 💡 warning: W0006: this statement has no effect + %% ^ 💡 warning: this statement has no effect ok. "#, ); @@ -307,16 +307,16 @@ mod tests { test_foo(_Config) -> do_something(), 42, - %% ^^ 💡 warning: W0006: this statement has no effect + %% ^^ 💡 warning: this statement has no effect 41.9999, - %% ^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^ 💡 warning: this statement has no effect do_something_else(), "foo", - %% ^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^ 💡 warning: this statement has no effect <<"foo">>, - %% ^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^ 💡 warning: this statement has no effect 'A', - %% ^^^ 💡 warning: W0006: this statement has no effect + %% ^^^ 💡 warning: this statement has no effect ok. do_something() -> 42. "#, @@ -332,13 +332,13 @@ mod tests { test_foo(_Config) -> do_something(), fun() -> do_something() end, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect F = fun() -> do_something() end, F(), fun do_something/0, - %% ^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect fun erlang:length/1, - %% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect ok. do_something() -> 42. //- /src/erlang.erl @@ -357,7 +357,7 @@ mod tests { test_foo(_Config) -> (do_something()), (blah), - %% ^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^ 💡 warning: this statement has no effect ok. do_something() -> (abc). "#, @@ -372,13 +372,13 @@ mod tests { test_foo(_Config) -> do_something(), begin abc, blah, ("foo") end, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect - %% ^^^ 💡 warning: W0006: this statement has no effect - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect + %% ^^^ 💡 warning: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect begin do_something(), blah, - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect ok end, ok. @@ -395,10 +395,10 @@ mod tests { test_foo(_Config) -> do_something(), [42, blah, ("foo")], - %% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect [42, do_something(), blah], [], - %% ^^ 💡 warning: W0006: this statement has no effect + %% ^^ 💡 warning: this statement has no effect ok. do_something() -> []. "#, @@ -413,10 +413,10 @@ mod tests { test_foo(_Config) -> do_something(), {42, [blah], {"foo"}}, - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect {42, do_something(), blah}, {}, - %% ^^ 💡 warning: W0006: this statement has no effect + %% ^^ 💡 warning: this statement has no effect ok. do_something() -> []. "#, @@ -432,11 +432,11 @@ mod tests { test_foo(P) -> do_something(), #person{name="Bob", age=42}, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect #person{name=get_name(), age=42}, P#person{name="Alice"}, #person.name, - %% ^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^ 💡 warning: this statement has no effect P#person.name, ok. get_name() -> "bob". @@ -452,7 +452,7 @@ mod tests { test_foo(P) -> do_something(), #{name => "Bob", age => 42}, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect #{name => get_name(), age => 42}, #{get_key() => "Bob", age => 42}, P#{name=>"Alice"}, @@ -471,10 +471,10 @@ mod tests { test_foo(_P) -> catch do_something(), catch ok, - %% ^^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^ 💡 warning: this statement has no effect try does, nothing catch _ -> do_stuff() end, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect try does_nothing of _ -> ok @@ -486,20 +486,20 @@ mod tests { ok end, try does, nothing after blah, ok end, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0006: this statement has no effect - %% ^^^^ 💡 warning: W0006: this statement has no effect - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect try does, nothing - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect of _ -> foo, bar - %% ^^^ 💡 warning: W0006: this statement has no effect + %% ^^^ 💡 warning: this statement has no effect catch _ -> 42, not_ok - %% ^^ 💡 warning: W0006: this statement has no effect + %% ^^ 💡 warning: this statement has no effect after [1,2,3], - %% ^^^^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^^^^ 💡 warning: this statement has no effect ok end, ok. @@ -522,7 +522,7 @@ mod tests { blah() -> noop, - %% ^^^^ 💡 warning: W0006: this statement has no effect + %% ^^^^ 💡 warning: this statement has no effect do_something(), ?included_noop(42), do_something(), diff --git a/crates/ide/src/diagnostics/equality_check_with_unnecessary_operator.rs b/crates/ide/src/diagnostics/equality_check_with_unnecessary_operator.rs index 92f205f894..803f89b886 100644 --- a/crates/ide/src/diagnostics/equality_check_with_unnecessary_operator.rs +++ b/crates/ide/src/diagnostics/equality_check_with_unnecessary_operator.rs @@ -619,7 +619,7 @@ mod tests { fn(A, Same, Diff) -> case A =:= b of true -> Same; false -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -633,7 +633,7 @@ mod tests { fn(A, Same, Diff) -> case A =:= b of false -> Diff; true -> Same end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -724,7 +724,7 @@ mod tests { fn(B, Same, Diff) -> case a =:= B of true -> Same; _ -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -740,7 +740,7 @@ mod tests { fn(Same, Diff) -> case get_a() =:= foo of true -> Same; _ -> Diff end. - %% ^^^^^^^^^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^^^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -756,7 +756,7 @@ mod tests { fn(Same, Diff) -> case foo =:= get_b() of true -> Same; _ -> Diff end. - %% ^^^^^^^^^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^^^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -908,7 +908,7 @@ mod tests { fn(A, Same, Diff) -> case A =/= b of false -> Same; true -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); check_diagnostics( @@ -918,7 +918,7 @@ mod tests { fn(B, Same, Diff) -> case a =/= B of false -> Same; true -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -932,7 +932,7 @@ mod tests { fn(A, Same, Diff) -> case A =/= b of true -> Diff; false -> Same end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); check_diagnostics( @@ -942,7 +942,7 @@ mod tests { fn(B, Same, Diff) -> case a =/= B of true -> Diff; false -> Same end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -956,7 +956,7 @@ mod tests { fn(A, Same, Diff) -> case A =/= b of false -> Same; _ -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); check_diagnostics( @@ -966,7 +966,7 @@ mod tests { fn(B, Same, Diff) -> case a =/= B of false -> Same; _ -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); } @@ -982,7 +982,7 @@ mod tests { fn(Same, Diff) -> case get_a() =/= bar of false -> Same; _ -> Diff end. - %% ^^^^^^^^^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^^^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -998,7 +998,7 @@ mod tests { fn(Same, Diff) -> case bar =/= get_b() of false -> Same; _ -> Diff end. - %% ^^^^^^^^^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^^^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } @@ -1504,7 +1504,7 @@ mod tests { fn(A, Same, Diff) -> if A =:= b -> Same; true -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); check_diagnostics( @@ -1514,7 +1514,7 @@ mod tests { fn(B, Same, Diff) -> if a =:= B -> Same; true -> Diff end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); } @@ -1528,7 +1528,7 @@ mod tests { fn(A, Same, Diff) -> if A =/= b -> Diff; true -> Same end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ); check_diagnostics( @@ -1538,7 +1538,7 @@ mod tests { fn(B, Same, Diff) -> if a =/= B -> Diff; true -> Same end. - %% ^^^^^^^💡 information: W0042: Consider rewriting to an equality match. + %% ^^^^^^^💡 information: Consider rewriting to an equality match. "#, ) } diff --git a/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs b/crates/ide/src/diagnostics/eqwalizer_assists/expected_type.rs index 081f6e43ae..ff8226b611 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_ide_db::text_edit::TextEdit; -use elp_ide_db::text_edit::TextSize; +use elp_text_edit::TextEdit; +use elp_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; @@ -192,7 +192,7 @@ mod tests { -spec baz() -> spec_atom. baz() -> something_else. - %%% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %%% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types "#, ) } @@ -210,7 +210,7 @@ mod tests { -spec baz() -> spec_atom. baz() -> somet~hing_else. - %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types "#, expect![[r#" -module(bar3e). @@ -234,7 +234,7 @@ mod tests { -spec baz() -> spec_atom. baz() -> somethin~g_else. - %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: incompatible_types "#, expect![[r#" -module(bar4e). @@ -258,7 +258,7 @@ mod tests { -spec baz() -> {ok, number()}. baz() -> 5~3. - %% ^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^ 💡 error: eqwalizer: incompatible_types "#, expect![[r#" -module(bar5e). @@ -282,7 +282,7 @@ mod tests { -spec baz() -> {ok, number()}. baz() -> 5~3. - %% ^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^ 💡 error: eqwalizer: incompatible_types "#, expect![[r#" -module(bar6e). @@ -306,7 +306,7 @@ mod tests { -spec foo() -> integer(). foo() -> o~k. - %% ^^ 💡 error: eqwalizer: incompatible_types: eqwalizer: incompatible_types + %% ^^ 💡 error: eqwalizer: incompatible_types "#, expect![[r#" -module(bar). diff --git a/crates/ide/src/diagnostics/eqwalizer_assists/unexported_type.rs b/crates/ide/src/diagnostics/eqwalizer_assists/unexported_type.rs index f83361eaf0..61b4bfee32 100644 --- a/crates/ide/src/diagnostics/eqwalizer_assists/unexported_type.rs +++ b/crates/ide/src/diagnostics/eqwalizer_assists/unexported_type.rs @@ -92,7 +92,7 @@ mod tests { -module(bar). -spec baz() -> other:a_type(). - %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: non_exported_id: eqwalizer: non_exported_id + %% ^^^^^^^^^^^^^^ 💡 error: eqwalizer: non_exported_id baz() -> ok. //- /play/src/other.erl app:play diff --git a/crates/ide/src/diagnostics/expression_can_be_simplified.rs b/crates/ide/src/diagnostics/expression_can_be_simplified.rs index 4c9144e5c0..f941ff1738 100644 --- a/crates/ide/src/diagnostics/expression_can_be_simplified.rs +++ b/crates/ide/src/diagnostics/expression_can_be_simplified.rs @@ -261,19 +261,10 @@ 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_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); - } + use crate::tests::check_fix; #[test] fn test_generates_diagnostics() { @@ -282,32 +273,32 @@ mod tests { -module(main). list_ops(X) -> f([] ++ [1]), - %% ^^^^^^^^^ 💡 warning: W0019: Can be simplified to `[1]`. + %% ^^^^^^^^^ 💡 warning: Can be simplified to `[1]`. f([2] ++ [1]), f(X ++ [1]), ok. arith_ops(X) -> f(0 + 42), - %% ^^^^^^ 💡 warning: W0019: Can be simplified to `42`. + %% ^^^^^^ 💡 warning: Can be simplified to `42`. f(40 + 2), f(X + 42), ok. short_circuit_boolean_ops(X) -> f(true andalso X), - %% ^^^^^^^^^^^^^^ 💡 warning: W0019: Can be simplified to `X`. + %% ^^^^^^^^^^^^^^ 💡 warning: Can be simplified to `X`. f(false orelse X), - %% ^^^^^^^^^^^^^^ 💡 warning: W0019: Can be simplified to `X`. + %% ^^^^^^^^^^^^^^ 💡 warning: Can be simplified to `X`. f(not false), - %% ^^^^^^^^^ 💡 warning: W0019: Can be simplified to `true`. + %% ^^^^^^^^^ 💡 warning: Can be simplified to `true`. f(not true), - %% ^^^^^^^^ 💡 warning: W0019: Can be simplified to `false`. + %% ^^^^^^^^ 💡 warning: Can be simplified to `false`. true andalso X, - %% ^^^^^^^^^^^^^^ 💡 warning: W0019: Can be simplified to `X`. + %% ^^^^^^^^^^^^^^ 💡 warning: Can be simplified to `X`. ok. - + f(X) -> X. "#, diff --git a/crates/ide/src/diagnostics/from_config.rs b/crates/ide/src/diagnostics/from_config.rs index 250fa4422d..f40a8e769e 100644 --- a/crates/ide/src/diagnostics/from_config.rs +++ b/crates/ide/src/diagnostics/from_config.rs @@ -10,21 +10,12 @@ //! Read a section of the config file and generate diagnostics from it. -use elp_ide_db::DiagnosticCode; -use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::FileId; -use elp_ide_ssr::SsrRule; -use elp_ide_ssr::SsrSearchScope; -use elp_ide_ssr::match_pattern; use hir::Semantic; -use hir::Strategy; -use hir::fold::MacroStrategy; -use hir::fold::ParenStrategy; use serde::Deserialize; use serde::Serialize; use super::Diagnostic; -use super::Severity; use super::TypeReplacement; use super::replace_call; use super::replace_call::Replacement; @@ -50,7 +41,6 @@ impl LintsFromConfig { pub enum Lint { ReplaceCall(ReplaceCall), ReplaceInSpec(ReplaceInSpec), - LintMatchSsr(MatchSsr), } impl Lint { @@ -58,7 +48,6 @@ impl Lint { match self { Lint::ReplaceCall(l) => l.get_diagnostics(acc, sema, file_id), Lint::ReplaceInSpec(l) => l.get_diagnostics(acc, sema, file_id), - Lint::LintMatchSsr(l) => l.get_diagnostics(acc, sema, file_id), } } } @@ -126,167 +115,12 @@ impl ReplaceInSpec { // --------------------------------------------------------------------- -#[derive(Debug, Clone)] -pub struct MatchSsr { - pub ssr_pattern: String, - pub message: Option, - pub strategy: Option, - pub severity: Option, -} - -impl Serialize for MatchSsr { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - - 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 - && strategy.parens == ParenStrategy::InvisibleParens; - - // Only serialize strategy if it's not the default - if !is_default { - // Serialize macro strategy - let macro_strategy = match strategy.macros { - MacroStrategy::DoNotExpand => "no-expand", - MacroStrategy::Expand => "expand", - MacroStrategy::ExpandButIncludeMacroCall => "visible-expand", - }; - state.serialize_field("macro_strategy", macro_strategy)?; - - // Serialize paren strategy - let paren_strategy = match strategy.parens { - ParenStrategy::VisibleParens => "visible", - ParenStrategy::InvisibleParens => "invisible", - }; - state.serialize_field("paren_strategy", paren_strategy)?; - } - } - state.end() - } -} - -impl<'de> Deserialize<'de> for MatchSsr { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - - #[derive(Deserialize)] - struct MatchSsrHelper { - ssr_pattern: String, - #[serde(default)] - message: Option, - #[serde(default)] - severity: Option, - #[serde(default)] - macro_strategy: Option, - #[serde(default)] - paren_strategy: Option, - } - - let helper = MatchSsrHelper::deserialize(deserializer)?; - - // Validate the SSR pattern by trying to parse it - // Use a minimal database for validation - let db = RootDatabase::default(); - SsrRule::parse_str(&db, &helper.ssr_pattern).map_err(|e| { - D::Error::custom(format!( - "invalid SSR pattern '{}': {}", - helper.ssr_pattern, e - )) - })?; - - // Parse strategy from strings if provided - let strategy = if helper.macro_strategy.is_some() || helper.paren_strategy.is_some() { - let macros = match helper.macro_strategy.as_deref() { - Some("no-expand") => MacroStrategy::DoNotExpand, - Some("expand") | None => MacroStrategy::Expand, - Some("visible-expand") => MacroStrategy::ExpandButIncludeMacroCall, - Some(s) => { - return Err(D::Error::custom(format!( - "invalid macro strategy '{}'. Valid options are: expand, no-expand, visible-expand", - s - ))); - } - }; - - let parens = match helper.paren_strategy.as_deref() { - Some("visible") => ParenStrategy::VisibleParens, - Some("invisible") | None => ParenStrategy::InvisibleParens, - Some(s) => { - return Err(D::Error::custom(format!( - "invalid paren strategy '{}'. Valid options are: visible, invisible", - s - ))); - } - }; - - Some(Strategy { macros, parens }) - } else { - None - }; - - Ok(MatchSsr { - ssr_pattern: helper.ssr_pattern, - message: helper.message, - strategy, - severity: helper.severity, - }) - } -} - -impl MatchSsr { - pub fn get_diagnostics(&self, acc: &mut Vec, sema: &Semantic, file_id: FileId) { - let strategy = self.strategy.unwrap_or(Strategy { - macros: MacroStrategy::Expand, - parens: ParenStrategy::InvisibleParens, - }); - - let scope = SsrSearchScope::WholeFile(file_id); - let matches = match_pattern(sema, strategy, &self.ssr_pattern, scope); - - for matched in matches.matches { - let message = self - .message - .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); - acc.push(diag); - } - } -} - -// --------------------------------------------------------------------- - #[cfg(test)] mod tests { use expect_test::expect; - use hir::Strategy; - use hir::fold::MacroStrategy; - use hir::fold::ParenStrategy; use super::Lint; use super::LintsFromConfig; - use super::MatchSsr; use super::ReplaceCall; use super::ReplaceCallAction; use super::ReplaceInSpec; @@ -658,326 +492,4 @@ mod tests { "#]] .assert_eq(&result); } - - #[test] - fn serde_serialize_match_ssr() { - let result = toml::to_string::(&MatchSsr { - ssr_pattern: "ssr: _@A = 10.".to_string(), - message: Some("Found pattern".to_string()), - strategy: None, - severity: None, - }) - .unwrap(); - expect![[r#" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - "#]] - .assert_eq(&result); - } - - #[test] - fn serde_deserialize_match_ssr() { - let match_ssr: MatchSsr = toml::from_str( - r#" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - "#, - ) - .unwrap(); - - expect![[r#" - MatchSsr { - ssr_pattern: "ssr: _@A = 10.", - message: Some( - "Found pattern", - ), - strategy: None, - severity: None, - } - "#]] - .assert_debug_eq(&match_ssr); - } - - #[test] - fn serde_serialize_lint_match_ssr() { - 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: None, - severity: None, - })], - }) - .unwrap(); - expect![[r#" - [[lints]] - type = "LintMatchSsr" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - "#]] - .assert_eq(&result); - } - - #[test] - fn serde_serialize_lint_match_ssr_with_strategy() { - 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::Expand, - parens: ParenStrategy::InvisibleParens, - }), - severity: None, - })], - }) - .unwrap(); - expect![[r#" - [[lints]] - type = "LintMatchSsr" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - "#]] - .assert_eq(&result); - } - - #[test] - fn serde_serialize_lint_match_ssr_with_non_default_strategy() { - 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: None, - })], - }) - .unwrap(); - expect![[r#" - [[lints]] - type = "LintMatchSsr" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - macro_strategy = "no-expand" - paren_strategy = "visible" - "#]] - .assert_eq(&result); - } - - #[test] - fn serde_deserialize_lint_match_ssr_with_strategy() { - let match_ssr: LintsFromConfig = toml::from_str( - r#" - [[lints]] - type = "LintMatchSsr" - ssr_pattern = "ssr: _@A = 10." - message = "Found pattern" - macro_strategy = "visible-expand" - paren_strategy = "visible" - "#, - ) - .unwrap(); - - expect![[r#" - LintsFromConfig { - lints: [ - LintMatchSsr( - MatchSsr { - ssr_pattern: "ssr: _@A = 10.", - message: Some( - "Found pattern", - ), - strategy: Some( - Strategy { - macros: ExpandButIncludeMacroCall, - parens: VisibleParens, - }, - ), - severity: None, - }, - ), - ], - } - "#]] - .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 edfb1d8518..259ed2853c 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,7 +270,6 @@ impl Validate for Name { attr_loc, ) .with_related(Some(vec![RelatedInformation { - file_id, range: ref_loc, message: "Mismatched clause name".to_string(), }])) @@ -294,7 +293,7 @@ impl Validate for Arity { fn make_diagnostic( self, - file_id: FileId, + _file_id: FileId, attr: &usize, hattr: &usize, attr_loc: TextRange, @@ -306,7 +305,6 @@ impl Validate for Arity { attr_loc, ) .with_related(Some(vec![RelatedInformation { - file_id, range: ref_loc, message: "Mismatched clause".to_string(), }])) @@ -395,8 +393,7 @@ mod tests { -module(main). foo(0) -> 1; boo(1) -> 2. - %% ^^^ 💡 error: P1700: head mismatch 'boo' vs 'foo' - %% | Related info: 0:21-24 Mismatched clause name + %% ^^^ 💡 error: head mismatch 'boo' vs 'foo' "#, ); check_fix( @@ -421,8 +418,7 @@ mod tests { food(0) -> ok; fooX(_X) -> - %% ^^^^ 💡 error: P1700: head mismatch 'fooX' vs 'food' - %% | Related info: 0:21-25 Mismatched clause name + %% ^^^^ 💡 error: head mismatch 'fooX' vs 'food' no. bar() -> @@ -451,8 +447,7 @@ mod tests { r#" -module(main). foo(0) -> 1; - %% ^^^ 💡 error: P1700: head mismatch 'foo' vs 'boo' - %% | Related info: 0:37-40 Mismatched clause name + %% ^^^ 💡 error: head mismatch 'foo' vs 'boo' boo(1) -> 2; boo(2) -> 3. "#, @@ -480,8 +475,7 @@ mod tests { -module(main). foo(0) -> 1; foo(1,0) -> 2. - %% ^^^^^^^^^^^^^ error: P1700: head arity mismatch 2 vs 1 - %% | Related info: 0:21-32 Mismatched clause + %% ^^^^^^^^^^^^^ error: head arity mismatch 2 vs 1 "#, ); } @@ -493,8 +487,7 @@ mod tests { -module(main). foo(2,0) -> 3; foo(0) -> 1; - %% ^^^^^^^^^^^ error: P1700: head arity mismatch 1 vs 2 - %% | Related info: 0:21-34 Mismatched clause + %% ^^^^^^^^^^^ error: head arity mismatch 1 vs 2 foo(1,0) -> 2. "#, ); @@ -520,8 +513,7 @@ mod tests { F = fun (0) -> ok; A(N) -> ok - %% ^ 💡 error: P1700: head mismatch 'A' vs '' - %% | Related info: 0:44-53 Mismatched clause name + %% ^ 💡 error: head mismatch 'A' vs '' end, F(). "#, diff --git a/crates/ide/src/diagnostics/inefficient_enumerate.rs b/crates/ide/src/diagnostics/inefficient_enumerate.rs index 01891974e3..f09cffae61 100644 --- a/crates/ide/src/diagnostics/inefficient_enumerate.rs +++ b/crates/ide/src/diagnostics/inefficient_enumerate.rs @@ -261,7 +261,7 @@ mod tests { -module(inefficient_enumerate). fn(List) -> lists:zip(lists:seq(1,length(List)),List). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0033: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } @@ -305,7 +305,7 @@ mod tests { -module(inefficient_enumerate). fn(N, List) -> lists:zip(lists:seq(N,length(List)),List). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0033: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } @@ -337,7 +337,7 @@ mod tests { -module(inefficient_enumerate). fn(N, Step, List) -> lists:zip(lists:seq(N,Step,length(List)),List). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0033: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } diff --git a/crates/ide/src/diagnostics/inefficient_flatlength.rs b/crates/ide/src/diagnostics/inefficient_flatlength.rs index 7cc2dd14e7..29d2088ffc 100644 --- a/crates/ide/src/diagnostics/inefficient_flatlength.rs +++ b/crates/ide/src/diagnostics/inefficient_flatlength.rs @@ -138,7 +138,7 @@ mod tests { -module(inefficient_flatlength). fn(NestedList) -> length(lists:flatten(NestedList)). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0028: Unnecessary intermediate flat-list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate flat-list allocated. "#, ) } diff --git a/crates/ide/src/diagnostics/inefficient_last.rs b/crates/ide/src/diagnostics/inefficient_last.rs index c120d7ca7f..e068ef614c 100644 --- a/crates/ide/src/diagnostics/inefficient_last.rs +++ b/crates/ide/src/diagnostics/inefficient_last.rs @@ -38,7 +38,6 @@ 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); }, }; @@ -63,24 +62,6 @@ 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, @@ -211,7 +192,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> hd(lists:reverse(List)). - %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0029: Unnecessary intermediate reverse list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unnecessary intermediate reverse list allocated. "#, ) } @@ -244,7 +225,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> [LastElem|_] = lists:reverse(List), LastElem. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0029: Unnecessary intermediate reverse list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unnecessary intermediate reverse list allocated. "#, ) } @@ -267,50 +248,4 @@ 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 deleted file mode 100644 index 96743b6f28..0000000000 --- a/crates/ide/src/diagnostics/lists_reverse_append.rs +++ /dev/null @@ -1,187 +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. - */ - -//! 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 6cd5fe2429..eb982359e2 100644 --- a/crates/ide/src/diagnostics/macro_precedence_suprise.rs +++ b/crates/ide/src/diagnostics/macro_precedence_suprise.rs @@ -15,12 +15,14 @@ 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_ide_db::text_edit::TextRange; +use elp_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; @@ -28,105 +30,91 @@ use hir::fold::ParenStrategy; use hir::fold::ParentId; use hir::fold::fold_file_functions; -use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; +use super::Diagnostic; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use super::Severity; use crate::fix; -#[derive(Debug, Default, Clone, PartialEq)] -pub(crate) struct MacroPrecedenceContext; +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); + }, +}; -pub(crate) struct MacroPrecedenceSupriseLinter; +/// 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, + }; -impl Linter for MacroPrecedenceSupriseLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::MacroPrecedenceEscape - } - - fn description(&self) -> &'static str { - "The macro expansion can have unexpected precedence here" - } - - fn should_process_generated_files(&self) -> bool { - true - } -} - -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) + 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 { - 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, - }); - } - } + // 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); } - }; + } }; - } - }); - 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, +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])), ); - Some(vec![fix]) } } -pub static LINTER: MacroPrecedenceSupriseLinter = MacroPrecedenceSupriseLinter; +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, + ) +} #[cfg(test)] mod tests { @@ -138,7 +126,6 @@ mod tests { use crate::tests::check_diagnostics_with_config; use crate::tests::check_fix; - #[track_caller] fn check_diagnostics(fixture: &str) { let config = DiagnosticsConfig::default() .enable(DiagnosticCode::OldEdocSyntax) @@ -153,7 +140,7 @@ mod tests { -module(main). -define(MYOP(B,C), B + C). foo(A,B,C) -> A * ?MYOP(B,C). - %% ^^^^^^^^^^ 💡 warning: W0039: The macro expansion can have unexpected precedence here + %% ^^^^^^^^^^ 💡 warning: The macro expansion can have unexpected precedence here bar(B,C) -> ?MYOP(B,C). "#, ) @@ -190,7 +177,7 @@ mod tests { -define(MYOPB(B,C), ?MYOPA(B, C)). -define(MYOPC(B,C), ?MYOPB(B, C)). foo(A,B,C) -> A * ?MYOPC(B,C). - %% ^^^^^^^^^^^ 💡 warning: W0039: The macro expansion can have unexpected precedence here + %% ^^^^^^^^^^^ 💡 warning: The macro expansion can have unexpected precedence here "#, ) } diff --git a/crates/ide/src/diagnostics/map_find_to_syntax.rs b/crates/ide/src/diagnostics/map_find_to_syntax.rs index 0f4b9afe72..2bb80e9307 100644 --- a/crates/ide/src/diagnostics/map_find_to_syntax.rs +++ b/crates/ide/src/diagnostics/map_find_to_syntax.rs @@ -548,7 +548,7 @@ mod tests { fn(Found, NotFound, M) -> % elp:ignore W0017 (undefined_function) case maps:find(my_key,M) of {ok, V} -> Found; error -> NotFound end. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: W0032: Unnecessary allocation of result tuple when the key is found. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: Unnecessary allocation of result tuple when the key is found. "#, ) } @@ -563,7 +563,7 @@ mod tests { fn(Found, NotFound, M) -> % elp:ignore W0017 (undefined_function) case maps:find({key_a,key_b},M) of {ok, V} -> Found; error -> NotFound end. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: W0032: Unnecessary allocation of result tuple when the key is found. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: Unnecessary allocation of result tuple when the key is found. "#, ) } @@ -578,7 +578,7 @@ mod tests { fn(K, Found, NotFound, M) -> % elp:ignore W0017 (undefined_function) case maps:find(K,M) of {ok, V} -> Found; error -> NotFound end. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: W0032: Unnecessary allocation of result tuple when the key is found. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: Unnecessary allocation of result tuple when the key is found. "#, ) } @@ -593,7 +593,7 @@ mod tests { fn(K, Found, NotFound, M) -> % elp:ignore W0017 (undefined_function) case maps:find(K,M) of {ok, V} -> Found; _ -> NotFound end. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: W0032: Unnecessary allocation of result tuple when the key is found. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^💡 warning: Unnecessary allocation of result tuple when the key is found. "#, ) } diff --git a/crates/ide/src/diagnostics/map_insertion_to_syntax.rs b/crates/ide/src/diagnostics/map_insertion_to_syntax.rs index 5f8c8a5398..20fef36dab 100644 --- a/crates/ide/src/diagnostics/map_insertion_to_syntax.rs +++ b/crates/ide/src/diagnostics/map_insertion_to_syntax.rs @@ -234,7 +234,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(K, V, Map) -> maps:put(K, V, Map). - %% ^^^^^^^^^^^^^^^^^^^💡 weak: W0030: Consider using map syntax rather than a function call. + %% ^^^^^^^^^^^^^^^^^^^💡 weak: Consider using map syntax rather than a function call. "#, ) } @@ -248,7 +248,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(K, V, Map) -> maps:update(K, V, Map). - %% ^^^^^^^^^^^^^^^^^^^^^^💡 weak: W0031: Consider using map syntax rather than a function call. + %% ^^^^^^^^^^^^^^^^^^^^^^💡 weak: Consider using map syntax rather than a function call. "#, ) } diff --git a/crates/ide/src/diagnostics/meck.rs b/crates/ide/src/diagnostics/meck.rs index 8919ddb770..fb79f39b57 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_ide_db::text_edit::TextRange; -use elp_ide_db::text_edit::TextSize; +use elp_text_edit::TextRange; +use elp_text_edit::TextSize; use hir::AnyExprId; use hir::Expr; use hir::FunctionDef; @@ -75,7 +75,7 @@ pub(crate) fn check_function(diags: &mut Vec, sema: &Semantic, def: if in_anonymous_fun(def_fb, parents) { return None; } - match args.as_slice() { + match args.as_vec()[..] { [_module] => Some(()), [_module, options] => { let body = def_fb.body(); @@ -96,7 +96,7 @@ pub(crate) fn check_function(diags: &mut Vec, sema: &Semantic, def: args, range, .. - }| match args.as_slice()[..] { + }| match args.as_vec()[..] { [module] => { if let Some(module_range) = def_fb.range_for_expr(module) { if def.file.file_id == range.file_id { @@ -253,7 +253,7 @@ mod tests { all() -> [a]. init_per_suite(Config) -> meck:new(my_module). -%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0022: Missing no_link option. +%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: Missing no_link option. a(_Config) -> ok. @@ -277,7 +277,7 @@ mod tests { all() -> [a]. init_per_group(_Group, Config) -> meck:new(my_module), -%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: W0022: Missing no_link option. +%% ^^^^^^^^^^^^^^^^^^^ 💡 warning: Missing no_link option. Config. a(_Config) -> @@ -326,7 +326,7 @@ mod tests { all() -> [a]. init_per_suite(Config) -> meck:new(my_module, [passthrough, link]). -%% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0022: Missing no_link option. +%% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Missing no_link option. a(_Config) -> ok. 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 dd0b677dce..308d2890c6 100644 --- a/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs +++ b/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs @@ -1,11 +1,10 @@ /* * 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 + * 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 * 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. + * of this source tree. */ //! Lint/fix: missing_compile_warn_missing_spec @@ -16,12 +15,11 @@ 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; @@ -38,160 +36,94 @@ use hir::known; use lazy_static::lazy_static; use super::DIAGNOSTIC_WHOLE_FILE_RANGE; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; -use crate::diagnostics::Severity; +use super::Diagnostic; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; use crate::fix; -pub(crate) struct MissingCompileWarnMissingSpec; +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); + }, +}; -impl Linter for MissingCompileWarnMissingSpec { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::MissingCompileWarnMissingSpec - } - 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." - } - 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, +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; } + _ => {} } -} -#[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 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); + } + 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, }, - }); - } - 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 - } + &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) - } + } + _ => acc, + }, + ); + (is_present, compile_attribute) + }) + .collect::>(); - 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 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) } - 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)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] enum Found { - #[default] No, WarnMissingSpec, WarnMissingSpecAll, @@ -214,6 +146,43 @@ 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 { @@ -228,9 +197,8 @@ mod tests { #[track_caller] pub(crate) fn check_fix(fixture_before: &str, fixture_after: Expect) { - let config = DiagnosticsConfig::default() - .disable(DiagnosticCode::NoNoWarnSuppressions) - .enable(DiagnosticCode::MissingCompileWarnMissingSpec); + let config = + DiagnosticsConfig::default().enable(DiagnosticCode::MissingCompileWarnMissingSpec); check_fix_with_config(config, fixture_before, fixture_after) } @@ -240,17 +208,17 @@ mod tests { fixture_before: &str, fixture_after: Expect, ) { - let config = DiagnosticsConfig::default() - .disable(DiagnosticCode::NoNoWarnSuppressions) - .enable(DiagnosticCode::MissingCompileWarnMissingSpec); + let config = + DiagnosticsConfig::default().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) - .enable(DiagnosticCode::MissingCompileWarnMissingSpec); + .disable(DiagnosticCode::UnspecificInclude); check_diagnostics_with_config(config, fixture) } @@ -265,7 +233,7 @@ mod tests { check_diagnostics( r#" //- /erl/my_app/src/main.erl - %% <<< 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + %% <<< 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. -module(main). @@ -281,7 +249,7 @@ mod tests { -module(main). -compile([export_all, nowarn_export_all]). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. "#, ) @@ -307,7 +275,7 @@ mod tests { -module(main). -compile(warn_missing_spec). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. "#, ) @@ -321,7 +289,7 @@ mod tests { -module(main). -compile(nowarn_missing_spec). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. "#, ) @@ -374,7 +342,7 @@ mod tests { -module(main). -compile(export_all). - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. -compile(nowarn_export_all). "#, ) @@ -399,20 +367,19 @@ mod tests { #[test] fn not_in_generated_file() { - check_diagnostics(&format!( + check_diagnostics( r#" //- /erl/my_app/src/main.erl %% -*- coding: utf-8 -*- %% Automatically generated, do not edit - %% @{} from blah + %% @generated 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] @@ -549,8 +516,8 @@ mod tests { "Ignore problem", r#" //- /erl/my_app/src/main.erl - ~%% <<< 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. - + ~%% <<< 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + -module(main). "#, @@ -568,7 +535,7 @@ mod tests { "Ignore problem", r#" //- /erl/my_app/src/main.erl - ~%% <<< 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + ~%% <<< 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. %% a comment at the %% top of the file @@ -592,7 +559,7 @@ mod tests { "Ignore problem", r#" //- /erl/my_app/src/main.erl - ~%% <<< 💡 error: W0012: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. + ~%% <<< 💡 error: Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced. %% a comment at the %% top of the file diff --git a/crates/ide/src/diagnostics/missing_module.rs b/crates/ide/src/diagnostics/missing_module.rs deleted file mode 100644 index 3fcbbf0fb7..0000000000 --- a/crates/ide/src/diagnostics/missing_module.rs +++ /dev/null @@ -1,132 +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. - */ - -// 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/missing_separator.rs b/crates/ide/src/diagnostics/missing_separator.rs index 1783b6d3d8..f5c8713cae 100644 --- a/crates/ide/src/diagnostics/missing_separator.rs +++ b/crates/ide/src/diagnostics/missing_separator.rs @@ -148,7 +148,7 @@ mod tests { r#" -module(main). foo(1)->2 - %% ^ warning: W0004: Missing ';' + %% ^ warning: Missing ';' foo(2)->3. "#, ); @@ -161,7 +161,7 @@ mod tests { -module(main). foo(1)->2; foo(2)->3 - %% ^ warning: W0004: Missing ';' + %% ^ warning: Missing ';' foo(3)->4. "#, ); @@ -174,7 +174,7 @@ mod tests { -module(main). foo(1)->2; foo(2)->3 - %% ^ warning: W0004: Missing '.' + %% ^ warning: Missing '.' "#, ); } @@ -186,7 +186,7 @@ mod tests { -module(main). foo(1)->2; foo(2)->3. - %% ^ warning: W0018: Unexpected '.' + %% ^ warning: Unexpected '.' foo(3)->4. "#, ); @@ -199,7 +199,7 @@ mod tests { -module(main). foo(1)->2; foo(2)->3; - %% ^ warning: W0018: Unexpected ';' + %% ^ warning: Unexpected ';' "#, ); } diff --git a/crates/ide/src/diagnostics/misspelled_attribute.rs b/crates/ide/src/diagnostics/misspelled_attribute.rs index 15ad1dd9d8..b61d3ac635 100644 --- a/crates/ide/src/diagnostics/misspelled_attribute.rs +++ b/crates/ide/src/diagnostics/misspelled_attribute.rs @@ -10,18 +10,16 @@ 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::DiagnosticCode; -use super::GenericLinter; -use super::GenericLinterMatchContext; -use super::Linter; -use crate::Assist; +use super::Diagnostic; use crate::TextRange; use crate::TextSize; -use crate::diagnostics::Severity; +use crate::diagnostics::RelatedInformation; use crate::fix; // Diagnostic: misspelled_attribute @@ -36,91 +34,26 @@ use crate::fix; // ``` // -include_lib("/foo/bar/baz.hrl"). // ``` - -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); +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)| { let parsed_file = sema.db.parse(file_id); - 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, - )]) - } + let attr_form = attr.form_id.get(&parsed_file.tree()); + diagnostics.push(make_diagnostic( + sema, + file_id, + attr, + attr_form, + suggested_rename, + )) + }) } const KNOWN_ATTRIBUTES: &[&str] = &[ @@ -152,7 +85,7 @@ const KNOWN_ATTRIBUTES: &[&str] = &[ "doc", ]; -fn looks_like_misspelling(attr: &hir::Attribute) -> Option<&str> { +fn looks_like_misspelling(attr: &Attribute) -> Option<&str> { let mut suggestions: Vec<(&str, f64)> = KNOWN_ATTRIBUTES .iter() .filter(|&known| &attr.name != known) @@ -170,7 +103,47 @@ fn looks_like_misspelling(attr: &hir::Attribute) -> Option<&str> { .copied() } -pub static LINTER: MisspelledAttributeLinter = MisspelledAttributeLinter; +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) +} // To run the tests via cargo // cargo test --package elp_ide --lib @@ -189,7 +162,7 @@ mod tests { r#" -module(main). -dyalizer({nowarn_function, f/0}). - %%% ^^^^^^^^ 💡 error: W0013: misspelled attribute, saw 'dyalizer' but expected 'dialyzer' + %%% ^^^^^^^^ 💡 error: misspelled attribute, saw 'dyalizer' but expected 'dialyzer' "#, ); check_fix( @@ -249,7 +222,7 @@ mod tests { r#" -module(main). -module_doc """ -%%% ^^^^^^^^^^ 💡 error: W0013: misspelled attribute, saw 'module_doc' but expected 'moduledoc' +%%% ^^^^^^^^^^ 💡 error: misspelled attribute, saw 'module_doc' but expected 'moduledoc' Hola """. "#, @@ -262,7 +235,7 @@ mod tests { r#" -module(main). -docs """ -%%% ^^^^ 💡 error: W0013: misspelled attribute, saw 'docs' but expected 'doc' +%%% ^^^^ 💡 error: misspelled attribute, saw 'docs' but expected 'doc' Hola """. foo() -> ok. diff --git a/crates/ide/src/diagnostics/module_mismatch.rs b/crates/ide/src/diagnostics/module_mismatch.rs index 77181b55ef..f6f7a2405c 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; @@ -79,7 +79,7 @@ mod tests { r#" //- /src/foo.erl -module(bar). -%% ^^^ 💡 error: W0001: Module name (bar) does not match file name (foo) +%% ^^^ 💡 error: Module name (bar) does not match file name (foo) "#, ); check_fix( diff --git a/crates/ide/src/diagnostics/mutable_variable.rs b/crates/ide/src/diagnostics/mutable_variable.rs index 6878c90d57..42a698eaa4 100644 --- a/crates/ide/src/diagnostics/mutable_variable.rs +++ b/crates/ide/src/diagnostics/mutable_variable.rs @@ -27,8 +27,12 @@ // 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; @@ -56,7 +60,21 @@ fn mutable_variable_bug( sema: &Semantic, file_id: FileId, ) -> Option<()> { - let bound_vars_by_function = sema.bound_vars_by_function(file_id); + 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 + }); + }); sema.def_map(file_id) .get_function_clauses() .for_each(|(_, def)| { @@ -111,7 +129,7 @@ test() -> One = 1, Result = One = Zero, -%% ^^^^^^^^^^^^^^^^^^^ error: W0005: Possible mutable variable bug +%% ^^^^^^^^^^^^^^^^^^^ error: Possible mutable variable bug Result. "#, @@ -119,7 +137,7 @@ test() -> } #[test] - fn mutable_variable_multiple_clauses() { + fn mutable_variable_mutliple_clauses() { check_diagnostics( r#" //- /src/test.erl @@ -137,8 +155,8 @@ push_eligible(ProductPlatform, _Pu) -> false; push_eligible(_ProductPlatform, Pu) -> AppVersion = ABUserInfo = Pu, -%% ^^^^^^^^^^ 💡 warning: W0007: match is redundant -%% ^^^^^^^^^^ 💡 warning: W0007: match is redundant +%% ^^^^^^^^^^ 💡 warning: match is redundant +%% ^^^^^^^^^^ 💡 warning: match is redundant false. "#, diff --git a/crates/ide/src/diagnostics/no_catch.rs b/crates/ide/src/diagnostics/no_catch.rs index a804f248bd..eeffcc002e 100644 --- a/crates/ide/src/diagnostics/no_catch.rs +++ b/crates/ide/src/diagnostics/no_catch.rs @@ -8,8 +8,7 @@ * above-listed licenses. */ -// Diagnostic: no-catch -use elp_ide_db::elp_base_db::FileId; +use elp_ide_db::DiagnosticCode; use elp_syntax::AstNode; use hir::AnyExpr; use hir::AnyExprId; @@ -21,48 +20,28 @@ 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; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use crate::diagnostics::Diagnostic; +use crate::diagnostics::Severity; -pub(crate) struct NoCatchLinter; +const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoCatch; +const DIAGNOSTIC_MESSAGE: &str = "Avoid `catch`."; +const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; -impl Linter for NoCatchLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::NoCatch - } +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)); + }, +}; - 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, -) { +fn check_function(diagnostics: &mut Vec, sema: &Semantic, def: &FunctionDef) { let def_fb = def.in_function_body(sema, def); def_fb.fold_function( Strategy { @@ -73,19 +52,15 @@ fn check_function( &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(match_context) = make_match_context(sema, &map, ctx.item_id) { - matches.push(match_context); + if let Some(diagnostic) = make_diagnostic(sema, &map, ctx.item_id) { + diagnostics.push(diagnostic); } }; }, ) } -fn make_match_context( - sema: &Semantic, - map: &BodySourceMap, - item_id: AnyExprId, -) -> Option> { +fn make_diagnostic(sema: &Semantic, map: &BodySourceMap, item_id: AnyExprId) -> Option { match item_id { AnyExprId::Expr(expr_id) => { let ast_ptr = map.expr(expr_id)?; @@ -93,7 +68,9 @@ fn make_match_context( elp_syntax::ast::Expr::CatchExpr(catch_expr) => { let catch_keyword = catch_expr.syntax().first_token()?; let range = catch_keyword.text_range(); - Some(GenericLinterMatchContext { range, context: () }) + let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, range) + .with_severity(DIAGNOSTIC_SEVERITY); + Some(diagnostic) } _ => None, } @@ -116,7 +93,7 @@ mod tests { catcher(X,Y) -> case catch X/Y of - %% ^^^^^ 💡 warning: W0052: Avoid `catch`. + %% ^^^^^ warning: 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 9a671e6c65..ec67123aa9 100644 --- a/crates/ide/src/diagnostics/no_dialyzer_attribute.rs +++ b/crates/ide/src/diagnostics/no_dialyzer_attribute.rs @@ -8,55 +8,50 @@ * above-listed licenses. */ -// Diagnostic: no-dialyzer-attribute +use elp_ide_db::DiagnosticCode; use elp_ide_db::elp_base_db::FileId; use hir::Semantic; use hir::known; -use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; +use crate::diagnostics::Diagnostic; +use crate::diagnostics::DiagnosticConditions; +use crate::diagnostics::DiagnosticDescriptor; +use crate::diagnostics::Severity; -pub(crate) struct NoDialyzerAttributeLinter; +const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoDialyzerAttribute; +const DIAGNOSTIC_MESSAGE: &str = "Avoid -dialyzer attribute."; +const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; -impl Linter for NoDialyzerAttributeLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::NoDialyzerAttribute - } +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); + }, +}; - fn description(&self) -> &'static str { - "Avoid using the -dialyzer attribute." - } - - fn should_process_test_files(&self) -> bool { - false - } +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); + } + }); } -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) - } +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) } -pub static LINTER: NoDialyzerAttributeLinter = NoDialyzerAttributeLinter; - #[cfg(test)] mod tests { @@ -68,7 +63,7 @@ mod tests { r#" -module(main). -dialyzer({nowarn_function, foo/0}). - %% ^^^^^^^^^ 💡 warning: W0048: Avoid using the -dialyzer attribute. + %% ^^^^^^^^^ warning: Avoid -dialyzer attribute. "#, ) } diff --git a/crates/ide/src/diagnostics/no_error_logger.rs b/crates/ide/src/diagnostics/no_error_logger.rs index 46f3d15e3e..b48ccf115c 100644 --- a/crates/ide/src/diagnostics/no_error_logger.rs +++ b/crates/ide/src/diagnostics/no_error_logger.rs @@ -8,9 +8,6 @@ * 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; @@ -26,12 +23,9 @@ impl Linter for NoErrorLoggerLinter { fn description(&self) -> &'static str { "The `error_logger` module is deprecated." } - fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::Error } - fn should_process_test_files(&self) -> bool { - false - } } impl FunctionCallLinter for NoErrorLoggerLinter { @@ -56,7 +50,7 @@ mod tests { //- /src/main.erl -module(main). foo() -> error_logger:error_msg("ops"). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 error: W0053: The `error_logger` module is deprecated. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 error: The `error_logger` module is deprecated. //- /src/error_logger.erl -module(error_logger). -export([error_msg/1]). @@ -73,7 +67,7 @@ mod tests { -module(main). -import(error_logger, [error_msg/1]). foo() -> error_msg("ops"). - %% ^^^^^^^^^^^^^^^^ 💡 error: W0053: The `error_logger` module is deprecated. + %% ^^^^^^^^^^^^^^^^ 💡 error: The `error_logger` module is deprecated. //- /src/error_logger.erl -module(error_logger). -export([error_msg/1]). diff --git a/crates/ide/src/diagnostics/no_garbage_collect.rs b/crates/ide/src/diagnostics/no_garbage_collect.rs index 4fcfa57257..e24ef7c9a9 100644 --- a/crates/ide/src/diagnostics/no_garbage_collect.rs +++ b/crates/ide/src/diagnostics/no_garbage_collect.rs @@ -50,10 +50,10 @@ mod tests { //- /src/main.erl -module(main). -export([error/0]). - + error() -> erlang:garbage_collect(). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0047: Avoid forcing garbage collection. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: 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]). @@ -69,10 +69,10 @@ mod tests { //- /src/main.erl -module(main). -export([error/0]). - + error() -> garbage_collect(). - %% ^^^^^^^^^^^^^^^ 💡 warning: W0047: Avoid forcing garbage collection. + %% ^^^^^^^^^^^^^^^ 💡 warning: 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]). diff --git a/crates/ide/src/diagnostics/no_nowarn_suppressions.rs b/crates/ide/src/diagnostics/no_nowarn_suppressions.rs index a0b36f87b8..f0484f0256 100644 --- a/crates/ide/src/diagnostics/no_nowarn_suppressions.rs +++ b/crates/ide/src/diagnostics/no_nowarn_suppressions.rs @@ -16,63 +16,62 @@ use hir::Semantic; use lazy_static::lazy_static; use regex::Regex; +use crate::diagnostics::Diagnostic; use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; +use crate::diagnostics::DiagnosticConditions; +use crate::diagnostics::DiagnosticDescriptor; +use crate::diagnostics::Severity; -pub(crate) struct NoNoWarnSuppressionsLinter; +const DIAGNOSTIC_CODE: DiagnosticCode = DiagnosticCode::NoNoWarnSuppressions; +const DIAGNOSTIC_MESSAGE: &str = "Do not suppress compiler warnings at module level."; +const DIAGNOSTIC_SEVERITY: Severity = Severity::Warning; -impl Linter for NoNoWarnSuppressionsLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::NoNoWarnSuppressions +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(); } - 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: () }); - } - }, - _ => {} - } + 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); + } + }, + _ => {} } } } } - Some(res) } } -pub static LINTER: NoNoWarnSuppressionsLinter = NoNoWarnSuppressionsLinter; - #[cfg(test)] mod tests { use crate::tests; @@ -83,7 +82,7 @@ mod tests { r#" -module(main). -compile(nowarn_export_all). - %% ^^^^^^^^^^^^^^^^^ 💡 warning: W0054: Do not suppress compiler warnings at module level. + %% ^^^^^^^^^^^^^^^^^ 💡 warning: Do not suppress compiler warnings at module level. "#, ) } @@ -94,7 +93,7 @@ mod tests { r#" -module(main). -compile({nowarn_unused_function, {unused_function, 1}}). - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0054: Do not suppress compiler warnings at module level. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Do not suppress compiler warnings at module level. "#, ) } @@ -106,9 +105,9 @@ mod tests { -module(main). -compile([ nowarn_export_all, - %% ^^^^^^^^^^^^^^^^^ 💡 warning: W0054: Do not suppress compiler warnings at module level. + %% ^^^^^^^^^^^^^^^^^ 💡 warning: Do not suppress compiler warnings at module level. {nowarn_unused_function, {unused_function, 1}} - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0054: Do not suppress compiler warnings at module level. + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Do not suppress compiler warnings at module level. ]). "#, ) diff --git a/crates/ide/src/diagnostics/no_size.rs b/crates/ide/src/diagnostics/no_size.rs index 87df996048..d298b49cfa 100644 --- a/crates/ide/src/diagnostics/no_size.rs +++ b/crates/ide/src/diagnostics/no_size.rs @@ -53,10 +53,10 @@ mod tests { //- /src/main.erl -module(main). foo() -> erlang:size({}). - %% ^^^^^^^^^^^ 💡 warning: W0050: Avoid using the `size/1` BIF. - + %% ^^^^^^^^^^^ 💡 warning: Avoid using the `size/1` BIF. + bar() -> size(<<>>). - %% ^^^^ 💡 warning: W0050: Avoid using the `size/1` BIF. + %% ^^^^ 💡 warning: Avoid using the `size/1` BIF. //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 -module(erlang). -export([size/1]). @@ -72,7 +72,7 @@ mod tests { //- /src/main.erl -module(main). foo() -> er~lang:size({}). - + bar() -> size(<<>>). //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 -module(erlang). diff --git a/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs b/crates/ide/src/diagnostics/nonstandard_integer_formatting.rs index 63b0a450cb..cb353cc63a 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; @@ -285,7 +285,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, 10000}. - %% ^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -315,7 +315,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, 16#4865316F774F6C64}. - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -345,7 +345,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, 2#1011011110010001}. - %% ^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -375,7 +375,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -10000}. - %% ^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -435,7 +435,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -16#4865316F774F6C64}. - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -465,7 +465,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -2#1011011110010001}. - %% ^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -512,7 +512,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, 16#4865316F7_74F6_C64}. - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -542,7 +542,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, 2#1011011_110010001}. - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -572,7 +572,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -1000_0}. - %% ^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -602,7 +602,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -16#4865316F7_74F6C64}. - %% ^^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -632,7 +632,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, -2#101_1011110010001}. - %% ^^^^^^^^^^^^^^^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^^^^^^^^^^^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -662,7 +662,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, +10000}. - %% ^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^ 💡 information: Non-standard integer formatting. "#, ) } @@ -692,7 +692,7 @@ mod tests { -module(integer_literal_format). fn() -> {val, - 10000}. - %% ^^^^^ 💡 information: W0043: Non-standard integer formatting. + %% ^^^^^ 💡 information: Non-standard integer formatting. "#, ) } diff --git a/crates/ide/src/diagnostics/record_tuple_match.rs b/crates/ide/src/diagnostics/record_tuple_match.rs index 9756bfeb73..019d6a57f5 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_ide_db::text_edit::TextRange; +use elp_text_edit::TextRange; use hir::AnyExpr; use hir::FunctionDef; use hir::Literal; @@ -108,7 +108,7 @@ mod tests { tt(X) -> A = #my_rec{field1 = 4, field2 = 2}, {my_rec, Field1, _} = X, - %% ^^^^^^ warning: W0027: matching record 'my_rec' as a tuple. + %% ^^^^^^ warning: matching record 'my_rec' as a tuple. {A, Field1}. "#, ); @@ -137,7 +137,7 @@ mod tests { -record(my_rec, {field1, field2}). tt({my_rec, Field1, _}) -> - %% ^^^^^^ warning: W0027: matching record 'my_rec' as a tuple. + %% ^^^^^^ warning: matching record 'my_rec' as a tuple. A = #my_rec{field1 = 4, field2 = 2}, {A, Field1}. "#, diff --git a/crates/ide/src/diagnostics/redundant_assignment.rs b/crates/ide/src/diagnostics/redundant_assignment.rs index 6351335533..1521d4117c 100644 --- a/crates/ide/src/diagnostics/redundant_assignment.rs +++ b/crates/ide/src/diagnostics/redundant_assignment.rs @@ -200,10 +200,10 @@ mod tests { do_foo() -> X = 42, Y = X, - %% ^^^^^ 💡 weak: W0009: assignment is redundant + %% ^^^^^ 💡 weak: assignment is redundant bar(Y), Z = Y, - %% ^^^^^ 💡 weak: W0009: assignment is redundant + %% ^^^^^ 💡 weak: assignment is redundant g(Z), case Y of [A] -> C = A; diff --git a/crates/ide/src/diagnostics/replace_call.rs b/crates/ide/src/diagnostics/replace_call.rs index fa16acf356..e27592dd3a 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; @@ -109,7 +109,7 @@ pub fn replace_call_site_if_args_match( sema, def_fb, file_id, - args.as_slice(), + &args.as_vec(), target, &range, ) { @@ -536,7 +536,7 @@ mod tests { foo:fire_bombs(Config, 44), foo:fire_bombs(Config, 43), foo:fire_bombs(Config, 42), - %%% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: ad-hoc: foo:fire_bombs/2: 'foo:fire_bombs/2' called with 42 + %%% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: 'foo:fire_bombs/2' called with 42 foo:fire_bombs(Config, 41), foo:fire_bombs(Config, 40). //- /src/foo.erl diff --git a/crates/ide/src/diagnostics/replace_in_spec.rs b/crates/ide/src/diagnostics/replace_in_spec.rs index a5df5217e2..0c43154064 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; @@ -185,7 +185,7 @@ mod tests { -type one() :: one. -spec fn(integer()) -> modu:one(). - %% ^^^^^^^^^^ 💡 weak: ad-hoc: modu:one/0: Replace 'modu:one/0' with 'modu:other()' + %% ^^^^^^^^^^ 💡 weak: Replace 'modu:one/0' with 'modu:other()' fn(0) -> one. "#, diff --git a/crates/ide/src/diagnostics/sets_version_2.rs b/crates/ide/src/diagnostics/sets_version_2.rs index 4535d6839a..dcd4c41a9f 100644 --- a/crates/ide/src/diagnostics/sets_version_2.rs +++ b/crates/ide/src/diagnostics/sets_version_2.rs @@ -53,11 +53,11 @@ mod tests { //- /src/main.erl -module(main). foo() -> sets:new(). - %% ^^^^^^^^ 💡 warning: W0049: Prefer `[{version, 2}]` when constructing a set. - + %% ^^^^^^^^ 💡 warning: Prefer `[{version, 2}]` when constructing a set. + bar() -> sets:from_list([]). - %% ^^^^^^^^^^^^^^ 💡 warning: W0049: Prefer `[{version, 2}]` when constructing a set. - + %% ^^^^^^^^^^^^^^ 💡 warning: Prefer `[{version, 2}]` when constructing a set. + //- /src/sets.erl -module(sets). -export([new/0, from_list/1]). diff --git a/crates/ide/src/diagnostics/simplify_negation.rs b/crates/ide/src/diagnostics/simplify_negation.rs index bb45c582c9..7f381adc4d 100644 --- a/crates/ide/src/diagnostics/simplify_negation.rs +++ b/crates/ide/src/diagnostics/simplify_negation.rs @@ -246,7 +246,7 @@ mod tests { fn(A, TrueBranch, FalseBranch) -> case not A of true -> FalseBranch; false -> TrueBranch end. - %% ^ 💡 information: W0044: Consider rewriting to match directly on the negated expression. + %% ^ 💡 information: Consider rewriting to match directly on the negated expression. "#, ) } @@ -260,7 +260,7 @@ mod tests { fn(A, TrueBranch, FalseBranch) -> case not A of false -> TrueBranch; true -> FalseBranch end. - %% ^ 💡 information: W0044: Consider rewriting to match directly on the negated expression. + %% ^ 💡 information: Consider rewriting to match directly on the negated expression. "#, ) } @@ -274,7 +274,7 @@ mod tests { fn(A, TrueBranch, FalseBranch) -> if not A -> FalseBranch; true -> TrueBranch end. - %% ^ 💡 information: W0044: Consider rewriting to match directly on the negated expression. + %% ^ 💡 information: Consider rewriting to match directly on the negated expression. "#, ) } diff --git a/crates/ide/src/diagnostics/trivial_match.rs b/crates/ide/src/diagnostics/trivial_match.rs index 705cc22d7e..ab94c0c9ad 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; @@ -348,16 +348,16 @@ mod tests { do_foo() -> 42 = 42, - %% ^^ 💡 warning: W0007: match is redundant + %% ^^ 💡 warning: match is redundant 42 = 43, "blah" = "blah", - %% ^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^ 💡 warning: match is redundant "blah" = "bleh", 'x' = 'x', - %% ^^^ 💡 warning: W0007: match is redundant + %% ^^^ 💡 warning: match is redundant 'x' = 'X', true = true, - %% ^^^^ 💡 warning: W0007: match is redundant + %% ^^^^ 💡 warning: match is redundant true = false, ok. "#, @@ -374,12 +374,12 @@ mod tests { X = 42, Y = 42, X = X, - %% ^ 💡 warning: W0007: match is redundant + %% ^ 💡 warning: match is redundant X = Y, {Z} = {Y}, - %% ^^^ 💡 warning: W0007: match is redundant + %% ^^^ 💡 warning: match is redundant [W, ok] = [ok, ok], - %% ^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^ 💡 warning: match is redundant [_W, ok] = [ok, ok], ok. "#, @@ -395,9 +395,9 @@ mod tests { do_foo() -> X = 42, <<"foo", 42>> = <<"foo", 42>>, - %% ^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^ 💡 warning: match is redundant <<"foo", X>> = <<"foo", X>>, - %% ^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^ 💡 warning: match is redundant <<"foo", Y>> = <<"foo", 42>>, Y. "#, @@ -412,12 +412,12 @@ mod tests { do_foo() -> X = 42, {X, "foo", {foo, bar}} = {X, "foo", {foo, bar}}, - %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant {X, foo} = {X, bar}, {X, "foo", {foo, bar}} = {X, "foo", {foo, pub}}, {X, "foo", {foo, bar}} = {X, "foo", {foo, bar, hey}}, {} = {}, - %% ^^ 💡 warning: W0007: match is redundant + %% ^^ 💡 warning: match is redundant ok. "#, ) @@ -432,12 +432,12 @@ mod tests { do_foo() -> X = 42, [X, ["foo"], [foo, bar]] = [X, ["foo"], [foo, bar]], - %% ^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant [X, foo] = [X, bar], [X, "foo", [foo, bar]] = [X, "foo", [foo, pub]], [X, "foo", [foo, bar]] = [X, "foo", [foo, bar, hey]], [] = [], - %% ^^ 💡 warning: W0007: match is redundant + %% ^^ 💡 warning: match is redundant ok. "#, ) @@ -453,12 +453,12 @@ mod tests { do_foo() -> #person{name = "Joe", age = 42} = #person{age = 42, name = "Joe"}, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant #person{name = "Joe", age = 43} = #person{age = 42, name = "Joe"}, #person{name = "Joe"} = #person{age = 42, name = "Joe"}, - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant #person{age = 42} = #person{age = 42, name = "Joe"}, - %% ^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant ok. "#, ) @@ -472,12 +472,12 @@ mod tests { do_foo() -> #{name := "Joe", age := 42} = #{age => 42, name => "Joe"}, - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: match is redundant #{name := "Joe", age := 43} = #{age => 42, name => "Joe"}, #{name := "Joe"} = #{age => 42, name => "Joe"}, - %% ^^^^^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^^^^^ 💡 warning: match is redundant #{age := 42} = #{age => 42, name => "Joe"}, - %% ^^^^^^^^^^^^ 💡 warning: W0007: match is redundant + %% ^^^^^^^^^^^^ 💡 warning: match is redundant ok. "#, ) diff --git a/crates/ide/src/diagnostics/unavailable_type.rs b/crates/ide/src/diagnostics/unavailable_type.rs deleted file mode 100644 index 6f2f52c122..0000000000 --- a/crates/ide/src/diagnostics/unavailable_type.rs +++ /dev/null @@ -1,340 +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. - */ - -// 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 52fa2cfa3c..5d898417d3 100644 --- a/crates/ide/src/diagnostics/undefined_function.rs +++ b/crates/ide/src/diagnostics/undefined_function.rs @@ -43,13 +43,6 @@ 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 { @@ -87,45 +80,19 @@ impl FunctionCallLinter for UndefinedFunctionLinter { return None; } - if sema.is_atom_named(name, &known::module_info) && (arity == 0 || arity == 1) { + 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)) + { return None; } - - // 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 - } - } + 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())), } } // Diagnostic L1227 already covers the case for local calls, so avoid double-reporting @@ -207,7 +174,7 @@ mod tests { main() -> _T0 = erlang:monotonic_time(milliseconds), _T2 = erlang:monitonic_time(milliseconds), -%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'erlang:monitonic_time/1' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'erlang:monitonic_time/1' is undefined. exists(). exists() -> ok. @@ -228,7 +195,7 @@ mod tests { main() -> dependency:exists(), dependency:not_exists(). -%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'dependency:not_exists/0' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:not_exists/0' is undefined. exists() -> ok. //- /src/dependency.erl -module(dependency). @@ -251,9 +218,9 @@ mod tests { main:behaviour_info(callbacks), hascallback:behaviour_info(callbacks), nocallback:behaviour_info(callbacks), -%% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'nocallback:behaviour_info/1' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'nocallback:behaviour_info/1' is undefined. nonexisting:behaviour_info(callbacks), -%% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'nonexisting:behaviour_info/1' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'nonexisting:behaviour_info/1' is undefined. behaviour_info(callbacks). //- /src/hascallback.erl @@ -294,7 +261,7 @@ mod tests { main() -> dependency:exists(), dependency:module_info(a, b). -%% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'dependency:module_info/2' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:module_info/2' is undefined. exists() -> ok. //- /src/dependency.erl -module(dependency). @@ -313,9 +280,9 @@ mod tests { -module(main). main() -> erlang:get_stacktrace(), -%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'erlang:get_stacktrace/0' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'erlang:get_stacktrace/0' is undefined. dependency:get_stacktrace(). -%% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'dependency:get_stacktrace/0' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:get_stacktrace/0' is undefined. "#, ) } @@ -329,7 +296,7 @@ mod tests { -define(MY_MACRO, fun() -> dep:exists(), dep:not_exists() end). main() -> ?MY_MACRO(). -%% ^^^^^^^^^^^ 💡 warning: W0017: Function 'dep:not_exists/0' is undefined. +%% ^^^^^^^^^^^ 💡 warning: Function 'dep:not_exists/0' is undefined. exists() -> ok. //- /src/dep.erl -module(dep). @@ -399,9 +366,9 @@ exists() -> ok. main() -> {fun dependency:exists/0, fun dependency:not_exists/1, -%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'dependency:not_exists/1' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:not_exists/1' is undefined. fun dependency:module_info/2}. -%% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0017: Function 'dependency:module_info/2' is undefined. +%% ^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:module_info/2' is undefined. exists() -> ok. //- /src/dependency.erl -module(dependency). @@ -446,29 +413,4 @@ 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 51ee0ceaff..b4be2ac306 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_ide_db::text_edit::TextEdit; +use elp_text_edit::TextEdit; use fxhash::FxHashSet; use hir::Semantic; use lazy_static::lazy_static; @@ -121,7 +121,7 @@ mod tests { -module(main). foo(X) -> ?assertEqual(X,2). - %% ^^^^^^^^^^^^ 💡 error: E1508: undefined macro 'assertEqual/2' + %% ^^^^^^^^^^^^ 💡 error: undefined macro 'assertEqual/2' //- /another-app/include/inc.hrl app:another include_path:/another-app/include -define(assertEqual(A,B), A =:= B). "#, @@ -159,7 +159,7 @@ mod tests { -module(main). foo(X) -> ?LIFE. - %% ^^^^^ 💡 error: E1507: undefined macro 'LIFE' + %% ^^^^^ 💡 error: undefined macro 'LIFE' //- /another-app/include/inc.hrl app:another include_path:/another-app/include -define(LIFE, 42). "#, @@ -175,7 +175,7 @@ mod tests { -module(main). foo(X) -> ?'LIFE/42'. - %% ^^^^^^^^^^ 💡 error: E1507: undefined macro 'LIFE/42' + %% ^^^^^^^^^^ 💡 error: undefined macro 'LIFE/42' //- /another-app/include/inc.hrl app:another include_path:/another-app/include -define('LIFE/42', 42). "#, @@ -191,7 +191,7 @@ mod tests { -module(main). foo(X) -> ?'LIFE/42'(42). - %% ^^^^^^^^^^ 💡 error: E1508: undefined macro 'LIFE/42/1' + %% ^^^^^^^^^^ 💡 error: undefined macro 'LIFE/42/1' //- /another-app/include/inc.hrl app:another include_path:/another-app/include -define('LIFE/42(X)', X). "#, @@ -208,7 +208,7 @@ mod tests { -module(main). foo(X) -> ?assert~Equal(X,2). - %% ^^^^^^^^^^^^ 💡 error: E1508: undefined macro 'assertEqual/2' + %% ^^^^^^^^^^^^ 💡 error: undefined macro 'assertEqual/2' //- /app_a/include/inc.hrl app:app_a include_path:/app_a/include -define(assertEqual(A,B), A =:= B). @@ -236,7 +236,7 @@ mod tests { -module(main). foo(X) -> ?assert~Equal(X,2). - %% ^^^^^^^^^^^^ 💡 error: E1508: undefined macro 'assertEqual/2' + %% ^^^^^^^^^^^^ 💡 error: undefined macro 'assertEqual/2' //- /app_a/include/inc.hrl app:app_a include_path:/app_a/include -define(assertEqual(A,B), A =:= B). @@ -264,7 +264,7 @@ mod tests { -module(main). foo(X) -> ?assert~Equal(X,2). - %% ^^^^^^^^^^^^ 💡 error: E1508: undefined macro 'assertEqual/2' + %% ^^^^^^^^^^^^ 💡 error: undefined macro 'assertEqual/2' //- /app_a/include/inc.hrl app:app_a include_path:/app_a/include diff --git a/crates/ide/src/diagnostics/undocumented_function.rs b/crates/ide/src/diagnostics/undocumented_function.rs index f1d0aa06b5..353e79fee3 100644 --- a/crates/ide/src/diagnostics/undocumented_function.rs +++ b/crates/ide/src/diagnostics/undocumented_function.rs @@ -8,10 +8,9 @@ * above-listed licenses. */ -// Diagnostic: undocumented-function use elp_ide_assists::helpers::unwrap_parens; +// Diagnostic: undocumented-function 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; @@ -22,66 +21,37 @@ use hir::Semantic; use hir::form_list::ModuleDocAttribute; use hir::known; -use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; -use crate::diagnostics::Severity; +use super::Diagnostic; +use super::DiagnosticCode; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use super::Severity; -pub(crate) struct UndocumentedFunctionLinter; +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; -impl Linter for UndocumentedFunctionLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::UndocumentedFunction - } +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 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 +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)); } } -#[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() @@ -117,22 +87,20 @@ 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) { - Some(GenericLinterMatchContext { - range: name_range, - context: Context { range: name_range }, - }) - } else { - None + let diagnostic = Diagnostic::new(DIAGNOSTIC_CODE, DIAGNOSTIC_MESSAGE, name_range) + .with_severity(DIAGNOSTIC_SEVERITY); + diagnostics.push(diagnostic); } } @@ -150,7 +118,6 @@ mod tests { .disable(DiagnosticCode::OldEdocSyntax) } - #[track_caller] fn check_diagnostics(fixture: &str) { tests::check_diagnostics_with_config(config(), fixture); } @@ -162,7 +129,7 @@ mod tests { -module(main). -export([main/0]). main() -> - %% ^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. + %% ^^^^ weak: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -179,7 +146,7 @@ mod tests { -module(main). -export([main/0]). main() -> - %% ^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. + %% ^^^^ weak: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -273,7 +240,7 @@ mod tests { -module(main). -moduledoc false. -export([main/0]). - + main() -> [ok, ok, @@ -291,7 +258,7 @@ mod tests { -module(main). -moduledoc(false). -export([main/0]). - + main() -> [ok, ok, @@ -309,7 +276,7 @@ mod tests { -module(main). -moduledoc hidden. -export([main/0]). - + main() -> [ok, ok, @@ -327,7 +294,7 @@ mod tests { -module(main). -moduledoc(hidden). -export([main/0]). - + main() -> [ok, ok, @@ -347,9 +314,9 @@ mod tests { -behaviour(gen_server). -export([main/0]). -export([handle_call/1]). - + main() -> - %%<^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. + %%<^ weak: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -376,12 +343,12 @@ mod tests { r#" -module(main). -export([simple/0, complex/0]). - + simple() -> ok. complex() -> - %%<^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. + %%<^^^^ weak: The function is non-trivial, exported, but not documented. [ok, ok, ok, @@ -397,12 +364,12 @@ mod tests { r#" -module(main). -export([simple/0, complex/1]). - + simple() -> ok. complex(a) -> - %%<^^^^ 💡 weak: W0040: The function is non-trivial, exported, but not documented. + %%<^^^^ weak: 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 1b2c80cfc9..1e4659c1f8 100644 --- a/crates/ide/src/diagnostics/undocumented_module.rs +++ b/crates/ide/src/diagnostics/undocumented_module.rs @@ -1,105 +1,74 @@ /* * 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 + * 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 * 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. + * of this source tree. */ 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 crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; -use crate::diagnostics::Severity; -use crate::fix; +use super::Diagnostic; +use super::DiagnosticCode; +use super::DiagnosticConditions; +use super::DiagnosticDescriptor; +use super::Severity; -pub(crate) struct UndocumentedModuleLinter; +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"; -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 +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); } + Some(()) } -#[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]) - } +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] } -pub static LINTER: UndocumentedModuleLinter = UndocumentedModuleLinter; - #[cfg(test)] mod tests { @@ -114,7 +83,6 @@ mod tests { DiagnosticsConfig::default().enable(DiagnosticCode::UndocumentedModule) } - #[track_caller] fn check_diagnostics(fixture: &str) { tests::check_diagnostics_with_config(config(), fixture); } @@ -128,7 +96,7 @@ mod tests { check_diagnostics( r#" -module(main). - %% ^^^^ 💡 weak: W0046: The module is not documented. + %% ^^^^ 💡 weak: The module is not documented. "#, ) } @@ -173,7 +141,7 @@ mod tests { check_diagnostics( r#" % @doc - %% ^^^^ 💡 warning: W0038: EDoc style comments are deprecated. Please use Markdown instead. + %% ^^^^ 💡 warning: EDoc style comments are deprecated. Please use Markdown instead. % This is a module -module(main). "#, diff --git a/crates/ide/src/diagnostics/unexported_function.rs b/crates/ide/src/diagnostics/unexported_function.rs index 2e1ebf7f54..4985b3c4e3 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: use crate::diagnostics::meta_only; +// @fb-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: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { + // @fb-only true // @oss-only - // @fb-only: meta_only::should_check_for_unexported(sema, file_id) + // @fb-only } } @@ -127,7 +127,6 @@ mod tests { use crate::tests::check_diagnostics_with_config; use crate::tests::check_nth_fix; - #[track_caller] pub(crate) fn check_diagnostics(fixture: &str) { let config = DiagnosticsConfig::default().disable(elp_ide_db::DiagnosticCode::NoSize); check_diagnostics_with_config(config, fixture) @@ -142,7 +141,7 @@ mod tests { main() -> dependency:exists(), dependency:private(). -%% ^^^^^^^^^^^^^^^^^^ 💡 warning: W0026: Function 'dependency:private/0' is not exported. +%% ^^^^^^^^^^^^^^^^^^ 💡 warning: Function 'dependency:private/0' is not exported. exists() -> ok. //- /src/dependency.erl -module(dependency). @@ -161,9 +160,9 @@ mod tests { -module(main). main() -> ?MODULE:private(), -%% ^^^^^^^^^^^^^^^ 💡 warning: W0026: Function 'main:private/0' is not exported. +%% ^^^^^^^^^^^^^^^ 💡 warning: Function 'main:private/0' is not exported. main:private(). -%% ^^^^^^^^^^^^ 💡 warning: W0026: Function 'main:private/0' is not exported. +%% ^^^^^^^^^^^^ 💡 warning: Function 'main:private/0' is not exported. private() -> ok. "#, @@ -179,7 +178,7 @@ mod tests { -include("header.hrl"). foo() -> main:bar(). -%% ^^^^^^^^ 💡 warning: W0026: Function 'main:bar/0' is not exported. +%% ^^^^^^^^ 💡 warning: Function 'main:bar/0' is not exported. //- /src/header.hrl bar() -> ok. 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 40f16da4d8..9fabb2f21d 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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::WeakWarning } } @@ -260,7 +260,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> lists:foldl(fun(K, Acc) -> Acc#{K => []} end, #{}, List). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0035: Unnecessary explicit fold to construct map from keys. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary explicit fold to construct map from keys. "#, ) } @@ -274,7 +274,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> lists:foldl(fun({K,V}, Acc) -> Acc#{K => V} end, #{}, List). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0035: Unnecessary explicit fold to construct map from list. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary explicit fold to construct map from list. "#, ) } diff --git a/crates/ide/src/diagnostics/unnecessary_map_from_list_around_comprehension.rs b/crates/ide/src/diagnostics/unnecessary_map_from_list_around_comprehension.rs index d2378528ce..9d337dda7d 100644 --- a/crates/ide/src/diagnostics/unnecessary_map_from_list_around_comprehension.rs +++ b/crates/ide/src/diagnostics/unnecessary_map_from_list_around_comprehension.rs @@ -160,7 +160,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> maps:from_list([{K + 1, V + 2} || {K,V} <- List]). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0036: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } @@ -174,7 +174,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(List) -> maps:from_list([{element(1, Pair) + 1, element(2, Pair) + 2} || Pair <- List]). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0036: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } 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 0c2ed5e140..99859c225d 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, _sema: &Semantic, _file_id: FileId) -> Severity { + fn severity(&self) -> Severity { Severity::WeakWarning } } @@ -137,7 +137,7 @@ mod tests { % elp:ignore W0017 (undefined_function) fn(Map) -> [K + V + 1 || {K,V} <- maps:to_list(Map)]. - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0034: Unnecessary intermediate list allocated. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unnecessary intermediate list allocated. "#, ) } diff --git a/crates/ide/src/diagnostics/unspecific_include.rs b/crates/ide/src/diagnostics/unspecific_include.rs index 431740c085..b6306c399f 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,12 +152,10 @@ 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 { @@ -169,13 +167,9 @@ mod tests { tests::check_filtered_diagnostics(fixture, &filter) } - #[rustfmt::skip] #[track_caller] fn check_fix(fixture_before: &str, fixture_after: Expect) { - let config = DiagnosticsConfig::default() - // @fb-only: .disable(DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::MalformedInclude)) - .disable(DiagnosticCode::UnusedInclude); - tests::check_fix_with_config(config, fixture_before, fixture_after) + tests::check_fix(fixture_before, fixture_after) } #[test] @@ -185,7 +179,7 @@ mod tests { //- /app_a/src/unspecific_include.erl -module(unspecific_include). -include("some_header_from_app_a.hrl"). - %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: W0037: Unspecific include. + %% ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: Unspecific include. //- /app_a/include/some_header_from_app_a.hrl include_path:/app_a/include -define(A,3). diff --git a/crates/ide/src/diagnostics/unused_function_args.rs b/crates/ide/src/diagnostics/unused_function_args.rs index ec4825e522..dbe7cd6152 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; @@ -210,8 +210,8 @@ mod tests { r#" -module(main). do_something(Unused, Used, _AlsoUsed, AlsoUnused, _UnusedButOk) -> - %%% ^^^^^^ 💡 warning: W0010: this variable is unused - %%% ^^^^^^^^^^ 💡 warning: W0010: this variable is unused + %%% ^^^^^^ 💡 warning: this variable is unused + %%% ^^^^^^^^^^ 💡 warning: this variable is unused case Used of undefined -> ok; @@ -227,8 +227,8 @@ mod tests { r#" -module(main). do_something([Unused | Used], #{foo := {_AlsoUsed, AlsoUnused, _UnusedButOk}}) -> - %%% ^^^^^^ 💡 warning: W0010: this variable is unused - %%% ^^^^^^^^^^ 💡 warning: W0010: this variable is unused + %%% ^^^^^^ 💡 warning: this variable is unused + %%% ^^^^^^^^^^ 💡 warning: this variable is unused case Used of undefined -> ok; @@ -302,7 +302,7 @@ mod tests { -module(main). foo(Args) -> {foo, Args}; foo(Args2) -> ok. - %% ^^^^^ 💡 warning: W0010: this variable is unused + %% ^^^^^ 💡 warning: this variable is unused "#, ); } @@ -314,7 +314,7 @@ mod tests { -module(main). foo(X, _Y = [_Z = {X, _, _} | _]) -> bar; foo(X, _Y) -> pub. - %% ^ 💡 warning: W0010: this variable is unused + %% ^ 💡 warning: this variable is unused "#, ); } diff --git a/crates/ide/src/diagnostics/unused_include.rs b/crates/ide/src/diagnostics/unused_include.rs index eb26bcd923..08f23af1d5 100644 --- a/crates/ide/src/diagnostics/unused_include.rs +++ b/crates/ide/src/diagnostics/unused_include.rs @@ -12,17 +12,14 @@ // // 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; @@ -34,11 +31,9 @@ use hir::db::DefDatabase; use hir::known; use lazy_static::lazy_static; -use crate::Assist; +use super::Diagnostic; use crate::diagnostics::DiagnosticCode; -use crate::diagnostics::GenericLinter; -use crate::diagnostics::GenericLinterMatchContext; -use crate::diagnostics::Linter; +use crate::diagnostics::Severity; use crate::fix; lazy_static! { @@ -48,114 +43,56 @@ lazy_static! { .collect(); } -pub(crate) struct UnusedIncludeLinter; - -impl Linter for UnusedIncludeLinter { - fn id(&self) -> DiagnosticCode { - DiagnosticCode::UnusedInclude - } - - fn description(&self) -> &'static str { - "Unused include file" - } - - 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() - } - - 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, - }, - }); +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; } + + 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(); + + 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, + )])); + + log::debug!("Found unused include {path:?}"); + + diagnostics.push(diagnostic); } } - 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, @@ -328,7 +265,7 @@ mod tests { //- /src/foo.erl -module(foo). -include("foo.hrl"). -%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0020: Unused file: foo.hrl +%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused file: foo.hrl "#, ); } @@ -356,7 +293,7 @@ mod tests { //- /src/foo.erl -module(foo). -include("foo.hrl"). -%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0020: Unused file: foo.hrl +%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused file: foo.hrl "#, ); } @@ -384,7 +321,7 @@ mod tests { //- /src/foo.erl -module(foo). -include("foo.hrl"). -%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0020: Unused file: foo.hrl +%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused file: foo.hrl "#, ); } @@ -413,7 +350,7 @@ mod tests { //- /src/foo.erl -module(foo). -include("foo.hrl"). -%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0020: Unused file: foo.hrl +%%^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused file: foo.hrl "#, ); } @@ -643,7 +580,7 @@ foo() -> ok. //- /src/header.hrl %% The following shows up as a wild attribute, which we regard as being used. -defin e(X, 1). -%% ^^^^^ 💡 error: W0013: misspelled attribute, saw 'defin' but expected 'define' +%% ^^^^^ 💡 error: misspelled attribute, saw 'defin' but expected 'define' -def ine(Y, 2). "#, @@ -657,7 +594,7 @@ foo() -> ok. //- /src/main.erl -module(main). -include("header.hrl"). -%%^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0020: Unused file: header.hrl +%%^^^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused file: header.hrl foo() -> ok. diff --git a/crates/ide/src/diagnostics/unused_macro.rs b/crates/ide/src/diagnostics/unused_macro.rs index 6ea8756b91..99af05370d 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,13 +88,7 @@ impl GenericLinter for UnusedMacroLinter { Some(DiagnosticTag::Unused) } - fn fixes( - &self, - context: &Context, - _range: TextRange, - _sema: &Semantic, - file_id: FileId, - ) -> Option> { + fn fixes(&self, context: &Context, _sema: &Semantic, file_id: FileId) -> Option> { Some(vec![delete_unused_macro( file_id, context.delete_range, @@ -129,9 +123,7 @@ mod tests { #[track_caller] pub(crate) fn check_diagnostics(fixture: &str) { - let config = DiagnosticsConfig::default() - .disable(DiagnosticCode::UndefinedFunction) - .disable(DiagnosticCode::HirUnresolvedMacro); + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); check_diagnostics_with_config(config, fixture) } @@ -141,7 +133,7 @@ mod tests { r#" -module(main). -define(MEANING_OF_LIFE, 42). - %% ^^^^^^^^^^^^^^^ 💡 warning: W0002: Unused macro (MEANING_OF_LIFE) + %% ^^^^^^^^^^^^^^^ 💡 warning: Unused macro (MEANING_OF_LIFE) "#, ); check_fix( @@ -184,9 +176,9 @@ main() -> -module(main). -define(USED_MACRO, used_macro). -define(UNUSED_MACRO, unused_macro). - %% ^^^^^^^^^^^^ 💡 warning: W0002: Unused macro (UNUSED_MACRO) + %% ^^^^^^^^^^^^ 💡 warning: Unused macro (UNUSED_MACRO) -define(UNUSED_MACRO_WITH_ARG(C), C). - %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: W0002: Unused macro (UNUSED_MACRO_WITH_ARG/1) + %% ^^^^^^^^^^^^^^^^^^^^^ 💡 warning: Unused macro (UNUSED_MACRO_WITH_ARG/1) main() -> ?MOD:foo(), @@ -219,7 +211,7 @@ main() -> -module(foo). -include("foo.hrl"). -define(BAR, 42). - %% ^^^ 💡 warning: W0002: Unused macro (BAR) + %% ^^^ 💡 warning: Unused macro (BAR) main() -> ?A. "#, diff --git a/crates/ide/src/diagnostics/unused_record_field.rs b/crates/ide/src/diagnostics/unused_record_field.rs index cbce6009bf..33f5bbaac3 100644 --- a/crates/ide/src/diagnostics/unused_record_field.rs +++ b/crates/ide/src/diagnostics/unused_record_field.rs @@ -100,7 +100,7 @@ mod tests { -record(used_field, {field_a, field_b = 42}). -record(unused_field, {field_c, field_d}). - %% ^^^^^^^ warning: W0003: Unused record field (unused_field.field_d) + %% ^^^^^^^ warning: Unused record field (unused_field.field_d) main(#used_field{field_a = A, field_b = B}) -> {A, B}; @@ -120,7 +120,7 @@ main(R) -> -record(used_field, {field_a, field_b = 42}). -record(unused_field, {field_c :: atom(), field_d :: number()}). - %% ^^^^^^^ warning: W0003: Unused record field (unused_field.field_d) + %% ^^^^^^^ warning: Unused record field (unused_field.field_d) main(#used_field{field_a = A, field_b = B}) -> {A, B}; @@ -189,9 +189,9 @@ main(#used_field{field_a = A}) -> r#" -module(main). -record(a, {a1, a2}). - %% ^^ warning: W0003: Unused record field (a.a2) + %% ^^ warning: Unused record field (a.a2) -record(b, {b1, b2}). - %% ^^ warning: W0003: Unused record field (b.b1) + %% ^^ warning: Unused record field (b.b1) main(#a{a1 = #b{b2 = B2}} = A) -> {A, B2}. "#, diff --git a/crates/ide/src/diagnostics_collection.rs b/crates/ide/src/diagnostics_collection.rs index fe1fc759aa..d944e223da 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(file_id, native.clone(), erlang_service.clone()); + attach_related_diagnostics(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(file_id, diagnostics, extra_diags.clone()); + let combined = attach_related_diagnostics(diagnostics, extra_diags.clone()); let expected = fixture.annotations_by_file_id(&file_id); let mut actual = combined .into_iter() @@ -415,7 +415,6 @@ 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 6a85ab3dc3..d13befe8bb 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: use crate::meta_only::exdoc_links; +// @fb-only -// @fb-only: mod meta_only; +// @fb-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: exdoc_links::links(&mut doc_links, &sema, &def); + // @fb-only }); } - // @fb-only: meta_only::links(&mut doc_links, node, position); + // @fb-only Some(doc_links) } diff --git a/crates/ide/src/document_symbols.rs b/crates/ide/src/document_symbols.rs index daed2c6368..390a975132 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 a5a6f759fc..95735a904d 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 @@ bar() -> ?L~INE. 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 88a95e86d7..cf16198dfe 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -259,7 +259,6 @@ 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 ad5c1bb680..1facbcaedb 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: pub mod meta_only; +// @fb-only pub use annotations::Annotation; pub use annotations::AnnotationKind; @@ -251,9 +251,9 @@ impl Analysis { }) } - pub fn should_eqwalize(&self, file_id: FileId) -> Cancellable { + pub fn should_eqwalize(&self, file_id: FileId, include_tests: bool) -> 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)?) + Ok(is_in_app && self.is_eqwalizer_enabled(file_id, include_tests)?) } /// 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) -> Cancellable { - self.with_db(|db| db.is_eqwalizer_enabled(file_id)) + 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)) } /// ETF for the module's abstract forms @@ -566,16 +566,6 @@ impl Analysis { self.with_db(|db| db.is_otp(file_id)) } - /// Validates an SSR pattern string by attempting to parse it. - /// Returns `Ok(())` if the pattern is valid, or an error message if parsing fails. - pub fn validate_ssr_pattern(&self, pattern: &str) -> Cancellable> { - self.with_db(|db| { - elp_ide_ssr::SsrRule::parse_str(db, pattern) - .map(|_| ()) - .map_err(|e| e.to_string()) - }) - } - /// Search symbols. Only module names are currently supported. pub fn symbol_search( &self, diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index ee5bfb7219..23e9ceca89 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -77,28 +77,6 @@ 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 { @@ -193,54 +171,36 @@ pub fn rename_var( } #[cfg(test)] -pub(crate) mod tests { - +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 fxhash::FxHashSet; + use elp_text_edit::TextEdit; 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) { + fn check(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 (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 (analysis, position, _) = fixture::position(fixture_before); 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); } @@ -250,82 +210,6 @@ 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:") { @@ -341,24 +225,14 @@ 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."#); + check("Y", r#"main() -> I~ = 1."#, r#"main() -> Y = 1."#); } #[test] fn test_rename_var_2() { - check_rename( + check( "Y", r#"main() -> I~ = 1, @@ -371,7 +245,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_3() { - check_rename( + check( "Y", r#"main(X) -> case X of @@ -390,7 +264,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_4() { - check_rename( + check( "Y", r#"testz() -> case rand:uniform(2) of @@ -417,7 +291,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_5() { - check_rename( + check( "YY", r#"main(_) -> Y = 5, @@ -442,7 +316,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_6() { - check_rename( + check( "ZZ", r#"main(_) -> Z = 2, @@ -459,7 +333,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_7() { - check_rename( + check( "Y", r#"main() -> I = 1, @@ -472,7 +346,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_name_clash_1() { - check_rename( + check( "Y", r#"main(Y) -> I~ = 1, @@ -483,7 +357,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_but_not_shadowed() { - check_rename( + check( "Z", r#"triples( Self, X, Y, none )-> [ Result || Result = { X~, Y, _} <- Self ]."#, @@ -496,7 +370,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_no_calls() { - check_rename( + check( "new_fun", r#"trip~les( Self, X, Y, none )-> [ Result || Result = { X, Y, _} <- Self ]."#, @@ -507,7 +381,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_with_calls_1() { - check_rename( + check( "new_fun", r#"fo~o() -> ok. bar() -> foo()."#, @@ -518,7 +392,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_with_calls_2() { - check_rename( + check( "new_fun", r#"fo~o() -> ok. bar() -> baz(),foo()."#, @@ -529,7 +403,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_with_calls_3() { - check_rename( + check( "new_fun", r#"fo~o(0) -> 0; foo(X) -> foo(X - 1). @@ -542,7 +416,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_with_calls_4() { - check_rename( + check( "new_fun", r#"test1() -> ok. @@ -555,7 +429,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_name_clash_1() { - check_rename( + check( "new_fun", r#"fo~o() -> ok. new_fun() -> ok."#, @@ -565,7 +439,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_name_clash_2() { - check_rename( + check( "foo", r#"foo() -> ok. b~ar() -> ok."#, @@ -575,7 +449,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_name_clash_checks_arity() { - check_rename( + check( "new_fun", r#"fo~o() -> ok. new_fun(X) -> ok."#, @@ -586,7 +460,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_name_clash_imported_function() { - check_rename( + check( "new_fun", r#"-import(bar, [new_fun/0]). fo~o() -> ok."#, @@ -596,7 +470,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_name_clash_erlang_function() { - check_rename( + check( "alias", r#"fo~o() -> ok."#, r#"error: Function 'alias/0' already in scope"#, @@ -605,7 +479,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_also_name_in_macro() { - check_rename( + check( "new", r#"-define(FOO, foo). fo~o() -> ok. @@ -618,17 +492,17 @@ pub(crate) mod tests { #[test] fn test_rename_local_var_trims_surrounding_spaces() { - check_rename(" Aaa ", r#"foo() -> V~ar = 3."#, r#"foo() -> Aaa = 3."#); + check(" Aaa ", r#"foo() -> V~ar = 3."#, r#"foo() -> Aaa = 3."#); } #[test] fn test_rename_local_function_trims_surrounding_spaces() { - check_rename(" aaa ", r#"fo~o() -> Var = 3."#, r#"aaa() -> Var = 3."#); + check(" aaa ", r#"fo~o() -> Var = 3."#, r#"aaa() -> Var = 3."#); } #[test] fn test_rename_local_var_fails_invalid_var_name() { - check_rename( + check( "aaa", r#"foo() -> V~ar = 3."#, r#"error: Invalid new variable name: 'aaa'"#, @@ -637,7 +511,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_invalid_function_name_1() { - check_rename( + check( "Foo", r#"fo~o() -> ok."#, r#"error: Invalid new function name: 'Foo'"#, @@ -646,7 +520,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_invalid_function_name_2() { - check_rename( + check( "TT TTT", r#"fo~o() -> ok."#, r#"error: Invalid new function name: 'TT TTT'"#, @@ -655,7 +529,7 @@ pub(crate) mod tests { #[test] fn test_rename_local_function_fails_invalid_function_name_3() { - check_rename( + check( "TTT", r#"fo~o() -> ok."#, r#"error: Invalid new function name: 'TTT'"#, @@ -664,7 +538,7 @@ pub(crate) mod tests { #[test] fn test_rename_var_d39578003_case_5() { - check_rename( + check( "_G", r#"main(_) -> fun F() -> @@ -687,7 +561,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_1() { - check_rename( + check( "new_name", r#"foo() -> Pid = erlang:spawn(fun noop_~group_leader/0), @@ -704,7 +578,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_2() { - check_rename( + check( "new_name", r#"foo() -> Pid = erlang:spawn(fun noop_group_leader/0), @@ -721,7 +595,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_1() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -743,7 +617,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_2() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -763,7 +637,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_args_1() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -787,7 +661,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_args_2() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -804,7 +678,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_3() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -826,7 +700,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -848,7 +722,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_apply_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -863,7 +737,7 @@ pub(crate) mod tests { #[test] fn test_rename_function_with_spec_1() { - check_rename( + check( "new_name", r#" -spec foo() -> ok. @@ -878,7 +752,7 @@ pub(crate) mod tests { #[test] fn test_rename_function_with_spec_2() { - check_rename( + check( "new_name", r#" -spec f~oo() -> ok. @@ -893,7 +767,7 @@ pub(crate) mod tests { #[test] fn test_rename_function_with_spec_3() { - check_rename( + check( "new_name", r#" -spec f~oo(any()) -> ok. @@ -908,7 +782,7 @@ pub(crate) mod tests { #[test] fn test_rename_underscore_1() { - check_rename( + check( "NewName", r#" foo() -> @@ -920,7 +794,7 @@ pub(crate) mod tests { #[test] fn test_rename_underscore_2() { - check_rename( + check( "NewName", r#" foo(X) -> @@ -933,7 +807,7 @@ pub(crate) mod tests { #[test] fn test_rename_case_1() { - check_rename( + check( "XX", r#" foo(X) -> @@ -954,7 +828,7 @@ pub(crate) mod tests { #[test] fn test_rename_case_2() { - check_rename( + check( "XX", r#" foo() -> @@ -971,7 +845,7 @@ pub(crate) mod tests { #[test] fn test_rename_case_3() { - check_rename( + check( "XX", r#" foo() -> @@ -986,7 +860,7 @@ pub(crate) mod tests { #[test] fn rename_export_function() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1019,7 +893,7 @@ pub(crate) mod tests { #[test] fn rename_export_function_2() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1054,7 +928,7 @@ pub(crate) mod tests { #[test] fn rename_export_function_fails() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1077,7 +951,7 @@ pub(crate) mod tests { #[test] fn rename_export_function_ok_no_local_import() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1114,7 +988,7 @@ pub(crate) mod tests { fn rename_function_in_include() { // We do not have functions defined in our header files, but // confirm it does the right thing anyway - check_rename( + check( "new_name", r#" //- /src/main.hrl @@ -1167,7 +1041,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_macro_rhs_1() { - check_rename( + check( "new_name", r#" -define(BAR(X), foo(X)). @@ -1184,7 +1058,7 @@ pub(crate) mod tests { #[test] fn rename_with_macro() { - check_rename( + check( "NewName", r#" //- /src/main.hrl @@ -1220,7 +1094,7 @@ pub(crate) mod tests { // See T157498333 #[test] fn rename_function_with_macro_type() { - check_rename( + check( "newFoo", r#" -module(main). @@ -1239,326 +1113,6 @@ 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] @@ -1629,261 +1183,9 @@ 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( + check( "new_name", r#" //- /src/baz.erl @@ -1905,7 +1207,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_call_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1927,7 +1229,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_async_call() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1949,7 +1251,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_cast() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1971,7 +1273,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_multicall_3() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -1993,7 +1295,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_multicall_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2015,7 +1317,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_rpc_multicall_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2037,7 +1339,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_function_exported() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2059,7 +1361,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_function_exported() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2081,7 +1383,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_is_builtin() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2103,7 +1405,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_is_builtin() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2125,7 +1427,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_hibernate() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2147,7 +1449,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_hibernate() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2169,7 +1471,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2191,7 +1493,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2213,7 +1515,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2235,7 +1537,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2257,7 +1559,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_link() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2279,7 +1581,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_link() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2301,7 +1603,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_link_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2323,7 +1625,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_link_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2345,7 +1647,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_monitor() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2367,7 +1669,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_monitor() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2389,7 +1691,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_monitor_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2411,7 +1713,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_monitor_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2433,7 +1735,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_opt() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2455,7 +1757,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_opt() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2477,7 +1779,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_opt_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2499,7 +1801,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_opt_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2521,7 +1823,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_spawn_request() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2543,7 +1845,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erlang_spawn_request() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2565,7 +1867,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_call_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2587,7 +1889,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_call_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2609,7 +1911,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_cast() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2631,7 +1933,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_multicall_4() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2653,7 +1955,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_multicall_5() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2675,7 +1977,7 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_multicast() { - check_rename( + check( "new_name", r#" //- /src/baz.erl @@ -2697,406 +1999,23 @@ pub(crate) mod tests { #[test] fn test_rename_in_erpc_send_request() { - check_rename( + check( "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."#, - ); - } - - // --------------------------------- - // 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. - "#, + new_name() -> + ok."#, ); } } diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 0aa731c8c9..4b2348f610 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/0]). + -export([all/]). main() -> ok. "#, diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index d09ec201f8..fba6d12d73 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -311,7 +311,6 @@ mod tests { fn test_fn_signature_local_two_args() { check( r#" -//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -344,7 +343,6 @@ main() -> ); check( r#" -//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -377,7 +375,6 @@ main() -> ); check( r#" -//- expect_parse_errors -module(main). -spec add(integer(), integer()) -> integer(). @@ -414,7 +411,6 @@ main() -> fn test_fn_signature_remote_two_args() { check( r#" -//- expect_parse_errors //- /one.erl -module(one). @@ -453,7 +449,6 @@ main() -> ); check( r#" -//- expect_parse_errors //- /one.erl -module(one). @@ -492,7 +487,6 @@ main() -> ); check( r#" -//- expect_parse_errors //- /one.erl -module(one). @@ -535,7 +529,6 @@ main() -> fn test_fn_signature_quoted_remote_two_args() { check( r#" -//- expect_parse_errors //- /Elixir.One.erl -module('Elixir.One'). @@ -583,7 +576,6 @@ main() -> fn test_fn_signature_unclosed_call() { check( r#" -//- expect_parse_errors -module(main). -compile(export_all). @@ -634,7 +626,6 @@ main() -> fn test_fn_signature_doc() { check( r#" -//- expect_parse_errors -module(main). -compile(export_all). @@ -694,7 +685,6 @@ main() -> if supports_eep59_doc_attributes() { check( r#" -//- expect_parse_errors -module(main). -compile(export_all). @@ -763,7 +753,6 @@ main() -> fn test_fn_signature_local_imported() { check( r#" -//- expect_parse_errors //- /one.erl -module(one). -compile(export_all). @@ -805,7 +794,6 @@ 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 55e9494e48..418adf88da 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,10 +376,7 @@ pub(crate) fn check_diagnostics(fixture: &str) { let config = DiagnosticsConfig::default() .set_experimental(true) .disable(DiagnosticCode::UnspecificInclude) - .disable(DiagnosticCode::BinaryStringToSigil) - .disable(DiagnosticCode::HirUnresolvedMacro) - .disable(DiagnosticCode::BoundVarInLhs) - .disable(DiagnosticCode::HirUnresolvedInclude); + .disable(DiagnosticCode::BinaryStringToSigil); check_diagnostics_with_config(config, fixture) } @@ -399,26 +396,7 @@ fn convert_diagnostics_to_annotations(diagnostics: Vec) -> Vec<(Text Severity::Information => "information", }); annotation.push_str(": "); - 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::>(); @@ -531,8 +509,7 @@ 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(file_id, diagnostics, extra_diags.clone()); + let diagnostics = diagnostics::attach_related_diagnostics(diagnostics, extra_diags.clone()); let expected = fixture.annotations_by_file_id(&file_id); let actual = convert_diagnostics_to_annotations(diagnostics); @@ -545,7 +522,6 @@ 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![]); } @@ -696,12 +672,11 @@ mod test { fn filtered_diagnostics_passes_syntax_errors() { check_filtered_diagnostics( r#" - //- expect_parse_errors - %%<^^^^^^^^^^^^ 💡 error: L1201: no module definition + %%<^^^^^^^^^^^^ error: no module definition foo() -> bug bug. - %% ^^^^ error: P1711: Syntax Error - + %% ^^^^ error: Syntax Error + "#, &filter, ) diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml index b2277cd12d..167e1f4b11 100644 --- a/crates/ide_assists/Cargo.toml +++ b/crates/ide_assists/Cargo.toml @@ -9,6 +9,7 @@ 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 87a511fbff..b1f51a7b90 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 65ae663518..662e31b3e9 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 2821e9214e..384ac8a65b 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 055395af52..69dbb1c55d 100644 --- a/crates/ide_assists/src/handlers/inline_function.rs +++ b/crates/ide_assists/src/handlers/inline_function.rs @@ -22,7 +22,6 @@ 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; @@ -33,6 +32,7 @@ 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 121b1dda41..bb2a38cf1b 100644 --- a/crates/ide_assists/src/helpers.rs +++ b/crates/ide_assists/src/helpers.rs @@ -20,8 +20,6 @@ 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; @@ -36,6 +34,8 @@ 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 f381df147d..fc97832d30 100644 --- a/crates/ide_completion/src/attributes.rs +++ b/crates/ide_completion/src/attributes.rs @@ -166,7 +166,6 @@ mod test { fn test_error_recovery() { check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample1). % U.S. English @@ -181,7 +180,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample1). % U.K. English @@ -199,7 +197,6 @@ mod test { fn test_typing_attribute() { check( r#" - //- expect_parse_errors -module(sample). -typ~ "#, @@ -214,7 +211,6 @@ mod test { fn test_module_attribute() { check( r#" - //- expect_parse_errors -mod~ "#, None, @@ -228,7 +224,6 @@ mod test { fn test_module_attribute_hyphen() { check( r#" - //- expect_parse_errors //- /src/my-module.erl -mod~ "#, @@ -243,7 +238,6 @@ mod test { fn test_module_attribute_at() { check( r#" - //- expect_parse_errors //- /src/my@module.erl -mod~ "#, @@ -258,7 +252,6 @@ mod test { fn test_module_attribute_underscore() { check( r#" - //- expect_parse_errors //- /src/my_module.erl -mod~ "#, @@ -273,7 +266,6 @@ mod test { fn test_module_attribute_uppercase() { check( r#" - //- expect_parse_errors //- /src/Module.erl -mod~ "#, @@ -288,7 +280,6 @@ 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 0d9381e7ec..22e385e3fa 100644 --- a/crates/ide_completion/src/ctx.rs +++ b/crates/ide_completion/src/ctx.rs @@ -281,7 +281,6 @@ mod ctx_tests { fn expr_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> ~X. @@ -291,7 +290,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> case 1 of. @@ -303,7 +301,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> fun(_) -> ~X end. @@ -313,7 +310,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> try 1 @@ -357,7 +353,6 @@ mod ctx_tests { fn ctx_pattern() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(Y, X) -> ~Y = X. @@ -367,7 +362,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(X) -> case rand:uniform(1) of @@ -379,7 +373,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(X) -> fun(X~) -> 1 end. @@ -389,7 +382,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> receive @@ -401,7 +393,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> try [1] @@ -416,7 +407,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(X) -> if @@ -439,7 +429,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -451,7 +440,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -469,7 +457,6 @@ mod ctx_tests { fn ctx_pattern_error_recovery_wip() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -482,7 +469,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test(Y, X) -> try ok of @@ -499,7 +485,6 @@ mod ctx_tests { fn test_type_param_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -type ty(s~) :: ok. "#), @@ -511,7 +496,6 @@ mod ctx_tests { fn test_export_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -export([ f~ @@ -525,7 +509,6 @@ mod ctx_tests { fn test_export_type_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -export_type([ t~ @@ -539,7 +522,6 @@ mod ctx_tests { fn test_spec_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -spec t~ table() -> ok. @@ -553,7 +535,6 @@ mod ctx_tests { fn test_type_ctx() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -spec test() -> ~ test() -> ok. @@ -563,7 +544,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -spec test() -> o~k test() -> ok. @@ -573,7 +553,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -spec test(o~) -> ok. test() -> ok. @@ -583,7 +562,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -record(foo, {field1, field2 :: X~}). "#), @@ -592,7 +570,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -opaque test() :: ~. "#), @@ -601,7 +578,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -nominal test() :: ~. "#), @@ -610,7 +586,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -type test() :: m~ "#), @@ -619,7 +594,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -spec test() -> ~ok. "#), @@ -631,7 +605,6 @@ mod ctx_tests { fn test_ctx_error_recovery() { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> ~ @@ -641,7 +614,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> X + ~ @@ -651,7 +623,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> X + ~. @@ -661,7 +632,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> case rand:uniform(1) of @@ -673,7 +643,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> (erlang:term_to_binary(~ @@ -684,7 +653,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). test() -> (erlang:term_to_binary(~. @@ -695,7 +663,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -type ty() :: ~ "#), @@ -704,7 +671,6 @@ mod ctx_tests { assert_eq!( ctx(r#" - //- expect_parse_errors -module(sample). -type ty() :: l~. "#), @@ -713,7 +679,6 @@ 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 ef803eb9ab..f362ea830a 100644 --- a/crates/ide_completion/src/export_functions.rs +++ b/crates/ide_completion/src/export_functions.rs @@ -83,7 +83,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -export([ foo~ @@ -107,7 +106,6 @@ 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 c21a37339e..9e80828b66 100644 --- a/crates/ide_completion/src/export_types.rs +++ b/crates/ide_completion/src/export_types.rs @@ -79,7 +79,6 @@ 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 eab033765f..4ce641d0e9 100644 --- a/crates/ide_completion/src/functions.rs +++ b/crates/ide_completion/src/functions.rs @@ -243,7 +243,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -266,7 +265,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -340,7 +338,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -363,7 +360,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -383,14 +379,12 @@ 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() -> @@ -419,7 +413,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -448,7 +441,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -474,7 +466,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -593,7 +584,6 @@ mod test { fn test_remote_fun_exprs_with_trigger() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). main(_) -> @@ -617,7 +607,6 @@ mod test { fn test_local_fun_exprs_with_trigger() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -633,7 +622,6 @@ mod test { fn test_local_fun_exprs_no_trigger() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -649,7 +637,6 @@ mod test { fn function_error_recovery() { check( r#" - //- expect_parse_errors -module(sample1). foo() -> b~ @@ -663,7 +650,6 @@ mod test { ); check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -682,7 +668,6 @@ mod test { fn test_local_and_remote() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). samba() -> ok. @@ -703,7 +688,6 @@ mod test { fn test_remote_call_broken_case() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). test() -> @@ -725,7 +709,6 @@ mod test { fn test_local_call_broken_case() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). foo() -> ok. @@ -819,7 +802,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -838,7 +820,6 @@ mod test { ); check( r#" -//- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -858,7 +839,6 @@ foo(X, Y) -> ok. ); check( r#" -//- expect_parse_errors //- /src/sample1.erl -module(sample1). local() -> @@ -922,7 +902,6 @@ 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"}). @@ -943,7 +922,6 @@ foo(X, Y) -> ok. fn test_in_dialyzer_attribute() { check( r#" - //- expect_parse_errors -module(main). -export([ foo/1, @@ -964,7 +942,6 @@ 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 dc60ae8158..734afc80a5 100644 --- a/crates/ide_completion/src/keywords.rs +++ b/crates/ide_completion/src/keywords.rs @@ -141,7 +141,6 @@ mod test { fn test_no_keywords() { check( r#" - //- expect_parse_errors -module(sample). test(X) -> a~ @@ -151,7 +150,6 @@ mod test { ); check( r#" - //- expect_parse_errors -module(sample). test(~X) -> X. @@ -162,7 +160,6 @@ mod test { check( r#" - //- expect_parse_errors -module(m~). "#, None, @@ -171,7 +168,6 @@ mod test { check( r#" - //- expect_parse_errors -module(m). -~ "#, @@ -181,7 +177,6 @@ mod test { check( r#" - //- expect_parse_errors -module(m). -type foo() :: ~. "#, @@ -196,7 +191,6 @@ mod test { fn test_keywords_error_recovery() { check( r#" - //- expect_parse_errors -module(sample). test(X) -> X ~ @@ -240,7 +234,6 @@ 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 f41aecdd67..71b4ff02c0 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: mod meta_only; +// @fb-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: || meta_only::add_completions(&mut acc, ctx) + // @fb-only || 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 e86cf3ba93..3c4b77cfad 100644 --- a/crates/ide_completion/src/macros.rs +++ b/crates/ide_completion/src/macros.rs @@ -253,7 +253,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample1). -define(FOO, 1). -define(FOO(), 1). @@ -272,7 +271,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample1). -define(FOO, 1). -define(BAR, 1). @@ -284,7 +282,6 @@ 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 e232a7d8bc..4371793356 100644 --- a/crates/ide_completion/src/maps.rs +++ b/crates/ide_completion/src/maps.rs @@ -214,7 +214,6 @@ mod test { fn test_local_type() { check( r#" - //- expect_parse_errors -module(test_local_type). -type my_map() :: #{field1 := integer(), field2 => boolean()}. foo(X) -> #~ @@ -230,7 +229,6 @@ 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 @@ -252,7 +250,6 @@ mod test { fn test_empty_map_type() { check( r#" - //- expect_parse_errors -module(test_empty_map_type). -type my_map() :: #{}. foo(X) -> #~ @@ -268,7 +265,6 @@ 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) -> #~ @@ -284,7 +280,6 @@ 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}). @@ -302,7 +297,6 @@ 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()}}. @@ -321,12 +315,11 @@ 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()). @@ -345,12 +338,11 @@ 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()). @@ -367,7 +359,6 @@ 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 3823339f78..820d04cfe8 100644 --- a/crates/ide_completion/src/modules.rs +++ b/crates/ide_completion/src/modules.rs @@ -132,7 +132,6 @@ 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 d66e054423..cf8e0abc7c 100644 --- a/crates/ide_completion/src/records.rs +++ b/crates/ide_completion/src/records.rs @@ -320,7 +320,6 @@ mod test { fn test_record_name() { check( r#" - //- expect_parse_errors -module(sample). -record(this_record, {field1=1, field2=2}). -record(that_record, {}). @@ -335,7 +334,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -record(this_record, {field1=1, field2=2}). -record(that_record, {}). @@ -355,7 +353,6 @@ mod test { // Irregular names are quoted. check( r#" - //- expect_parse_errors -module(sample). -record('this.record', {field1=1, field2=2}). -record('that$record', {}). @@ -372,7 +369,6 @@ 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~. @@ -385,7 +381,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> X#rec{field1 = 1, field2~. @@ -398,7 +393,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -410,7 +404,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -424,7 +417,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). -record(rec, {field1=1, field2=2}). foo(X) -> case ok of @@ -441,7 +433,6 @@ 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, {}). @@ -459,7 +450,6 @@ 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 cc853af865..6a0061e914 100644 --- a/crates/ide_completion/src/spec.rs +++ b/crates/ide_completion/src/spec.rs @@ -109,7 +109,6 @@ mod test { check( r#" - //- expect_parse_errors -module(sample). frog() -> ok. @@ -130,7 +129,6 @@ 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 e232ab2944..3800a97b5c 100644 --- a/crates/ide_completion/src/types.rs +++ b/crates/ide_completion/src/types.rs @@ -169,7 +169,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample). -type alias() :: ok. @@ -198,7 +197,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample). -type alias() :: ok. @@ -227,7 +225,6 @@ mod test { check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample). -spec foo() -> sample2:~. @@ -251,7 +248,6 @@ mod test { // something reasonable (show nothing) check( r#" - //- expect_parse_errors //- /src/sample.erl -module(sample). -spec foo() -> sample~:. @@ -267,7 +263,6 @@ 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 5544475ed3..65740d56a4 100644 --- a/crates/ide_completion/src/vars.rs +++ b/crates/ide_completion/src/vars.rs @@ -100,7 +100,6 @@ mod test { fn test_local_variables_1() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). test(AnArg1,Blah) -> @@ -118,7 +117,6 @@ mod test { fn test_local_variables_limit_to_current_function() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). another(AnArgNotMatched) -> @@ -144,7 +142,6 @@ mod test { fn test_local_variables_none_if_space() { check( r#" - //- expect_parse_errors //- /src/sample1.erl -module(sample1). test(AnArg1,Blah) -> @@ -160,7 +157,6 @@ 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 927391ba93..babeee31b9 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml @@ -12,16 +12,15 @@ 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 @@ -36,7 +35,6 @@ 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 ec1e1d76cf..d3e73636bd 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: use crate::meta_only::MetaOnlyDiagnosticCode; +// @fb-only -// @fb-only: pub const BASE_URL: &str = crate::meta_only::BASE_URL; +// @fb-only pub const BASE_URL: &str = "https://whatsapp.github.io/erlang-language-platform/docs"; // @oss-only #[derive(Clone, Debug, PartialEq, Eq, Hash, EnumIter)] @@ -52,7 +52,6 @@ pub enum DiagnosticCode { DependentHeader, DeprecatedFunction, UndefinedFunction, - UnavailableType, Unexpected(String), ExpressionCanBeSimplified, CannotEvaluateCTCallbacks, @@ -90,10 +89,6 @@ pub enum DiagnosticCode { NoErrorLogger, NoNoWarnSuppressions, CouldBeAStringLiteral, - ListsReverseAppend, - HirUnresolvedMacro, - HirUnresolvedInclude, - BoundVarInLhs, // Wrapper for erlang service diagnostic codes ErlangService(String), @@ -101,7 +96,7 @@ pub enum DiagnosticCode { Eqwalizer(String), // Used for ad-hoc diagnostics via lints/codemods AdHoc(String), - // @fb-only: MetaOnly(MetaOnlyDiagnosticCode), + // @fb-only } // These namespaces map the error codes returned by the Erlang Service. @@ -117,7 +112,7 @@ pub enum Namespace { Parser, EDoc, WhatsApp, - // @fb-only: MetaOnly, + // @fb-only } impl fmt::Display for Namespace { @@ -132,7 +127,7 @@ impl fmt::Display for Namespace { Namespace::Parser => "p", Namespace::EDoc => "o", Namespace::WhatsApp => "w", - // @fb-only: Namespace::MetaOnly => "meta_only", + // @fb-only }; write!(f, "{namespace}") } @@ -165,7 +160,7 @@ impl Namespace { pub fn supports_doc_path(&self) -> bool { match self { Namespace::WhatsApp => true, - // @fb-only: Namespace::MetaOnly => true, + // @fb-only _ => false, } } @@ -253,15 +248,10 @@ 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: DiagnosticCode::MetaOnly(c) => c.as_code(), + // @fb-only } } @@ -273,7 +263,6 @@ 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(), @@ -355,15 +344,11 @@ 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: DiagnosticCode::MetaOnly(c) => c.as_label(), + // @fb-only } } @@ -374,7 +359,7 @@ impl DiagnosticCode { pub fn maybe_from_string(s: &str) -> Option { DIAGNOSTIC_CODE_LOOKUPS .get(s).cloned() - // @fb-only: .or_else(|| MetaOnlyDiagnosticCode::from_str(s).ok().map(DiagnosticCode::MetaOnly)) + // @fb-only .or_else( || // Look for ErlangService and AdHoc if let Some(code) = Self::is_adhoc(s) { @@ -391,7 +376,7 @@ impl DiagnosticCode { match self { DiagnosticCode::DefaultCodeForEnumIter => None, DiagnosticCode::AdHoc(_) => None, - // @fb-only: DiagnosticCode::MetaOnly(_) => Some(Namespace::MetaOnly), + // @fb-only DiagnosticCode::ErlangService(code) => Namespace::from_str(code).ok(), _ => Namespace::from_str(&self.as_code()).ok(), } @@ -400,7 +385,7 @@ impl DiagnosticCode { pub fn supports_doc_path(&self) -> bool { match self { DiagnosticCode::DefaultCodeForEnumIter => false, - // @fb-only: DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::DefaultCodeForEnumIter) => false, + // @fb-only _ => true, } } @@ -489,7 +474,6 @@ impl DiagnosticCode { DiagnosticCode::ModuleMismatch => false, DiagnosticCode::UnusedInclude => false, DiagnosticCode::BoundVarInPattern => false, - DiagnosticCode::BoundVarInLhs => false, DiagnosticCode::UnusedMacro => false, DiagnosticCode::UnusedRecordField => false, DiagnosticCode::MutableVarBug => false, @@ -504,7 +488,6 @@ impl DiagnosticCode { DiagnosticCode::DependentHeader => false, DiagnosticCode::DeprecatedFunction => false, DiagnosticCode::UndefinedFunction => false, - DiagnosticCode::UnavailableType => false, DiagnosticCode::Unexpected(_) => false, DiagnosticCode::ExpressionCanBeSimplified => false, DiagnosticCode::UnnecessaryFlatteningToFindFlatLength => false, @@ -537,15 +520,12 @@ 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: DiagnosticCode::MetaOnly(code) => code.allows_fixme_comment(), + // @fb-only } } diff --git a/crates/ide_db/src/eqwalizer.rs b/crates/ide_db/src/eqwalizer.rs index fca577bb21..4deed79778 100644 --- a/crates/ide_db/src/eqwalizer.rs +++ b/crates/ide_db/src/eqwalizer.rs @@ -12,6 +12,7 @@ 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; @@ -88,7 +89,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) -> bool; + fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> bool; } pub fn eqwalizer_diagnostics_by_project( @@ -113,7 +114,7 @@ fn type_at_position( db: &dyn EqwalizerDatabase, range: FileRange, ) -> Option> { - if !db.is_eqwalizer_enabled(range.file_id) { + if !db.is_eqwalizer_enabled(range.file_id, false) { return None; } let project_id = db.file_app_data(range.file_id)?.project_id; @@ -148,7 +149,7 @@ fn type_at_position( } fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option>> { - if !db.is_eqwalizer_enabled(file_id) { + if !db.is_eqwalizer_enabled(file_id, false) { return None; } let project_id = db.file_app_data(file_id)?.project_id; @@ -161,7 +162,7 @@ fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option bool { +fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId, include_tests: bool) -> bool { if !otp_supported_by_eqwalizer() { return false; } @@ -177,8 +178,11 @@ fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId) -> bool { 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 || db.has_eqwalizer_module_marker(file_id); + let opt_in = + (global_opt_in && (is_src || is_test_opted_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 038b41f0be..cad66d9e48 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,22 +43,11 @@ 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 { - 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) + if let Some(call) = ast::Call::cast(syntax.parent()?) { + Some(call) } else { - ast::ExternalFun::cast(syntax.parent()?.parent()?) + ast::Call::cast(syntax.parent()?.parent()?) } } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index aecfd86473..0240d8498d 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -57,17 +57,15 @@ pub mod docs; pub mod eqwalizer; mod erl_ast; mod line_index; -// @fb-only: pub mod meta_only; +// @fb-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; @@ -385,7 +383,7 @@ impl TypedSemantic for RootDatabase { let project_id = app_data.project_id; - let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id); + let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id, false); if !eqwalizer_enabled { return Some(vec![]); } @@ -421,8 +419,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] @@ -448,17 +446,4 @@ 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 bd5a49f8cf..c25ee41e2a 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -15,24 +15,19 @@ 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; @@ -84,45 +79,6 @@ 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, @@ -139,10 +95,7 @@ impl SymbolDefinition { ) -> RenameResult { match self.clone() { SymbolDefinition::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) + rename_error!("Cannot rename module") } SymbolDefinition::Function(fun) => { if safety_check == SafetyChecks::Yes && !is_valid_function_name(new_name) { @@ -164,58 +117,14 @@ impl SymbolDefinition { SymbolDefinition::RecordField(_) => { rename_error!("Cannot rename record field") } - 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::Type(_) => { + rename_error!("Cannot rename type") } SymbolDefinition::Callback(_) => { rename_error!("Cannot rename callback") } - 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::Define(_) => { + rename_error!("Cannot rename define") } SymbolDefinition::Header(_) => { rename_error!("Cannot rename header") @@ -242,27 +151,6 @@ 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, } } @@ -327,40 +215,6 @@ 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 { @@ -395,7 +249,6 @@ 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 _ => { @@ -403,184 +256,6 @@ 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( @@ -713,32 +388,6 @@ 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 1e66598359..20b4f050b4 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, PartialEq, Eq)] +#[derive(Debug, Clone)] 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 60b62279fe..bb64431772 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs @@ -20,46 +20,19 @@ use std::mem; use elp_base_db::AnchoredPathBuf; use elp_base_db::FileId; use elp_syntax::SyntaxNode; -use elp_syntax::TextRange; -use elp_syntax::TextSize; +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 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, } @@ -73,7 +46,6 @@ impl SourceChange { ) -> Self { SourceChange { source_file_edits, - new_file_edits: FxHashMap::default(), file_system_edits, is_snippet: false, } @@ -102,22 +74,6 @@ 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); } @@ -129,15 +85,12 @@ 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.new_file_edits.is_empty() + self.source_file_edits.is_empty() && self.file_system_edits.is_empty() } pub fn text_range(&self, file_id: FileId) -> Option { @@ -163,18 +116,10 @@ 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, } @@ -226,7 +171,7 @@ impl SourceChangeBuilder { fn commit(&mut self) { if let Some(tm) = self.mutated_tree.take() { - diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) + algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) } let edit = mem::take(&mut self.edit).finish(); @@ -320,7 +265,6 @@ 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/ide_db/src/tree_diff.rs b/crates/ide_db/src/tree_diff.rs deleted file mode 100644 index 5bca5c2ec2..0000000000 --- a/crates/ide_db/src/tree_diff.rs +++ /dev/null @@ -1,507 +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. - */ - -//! 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 881a5ac123..108680853e 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 45c6b7f732..8289482442 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -12,7 +12,6 @@ use core::str; use std::borrow::Cow; use std::convert::TryFrom; use std::fmt; -use std::path::Path; use std::path::PathBuf; use std::process; use std::process::Command; @@ -35,7 +34,6 @@ use paths::RelPathBuf; use paths::Utf8PathBuf; use regex::Regex; use serde::Deserialize; -use serde::Deserializer; use serde::Serialize; use thiserror::Error; @@ -45,7 +43,6 @@ use crate::CommandProxy; use crate::ElpConfig; use crate::ProjectAppData; use crate::ProjectModelError; -use crate::json; use crate::otp::Otp; pub type TargetFullName = String; @@ -58,7 +55,6 @@ lazy_static! { } const ERL_EXT: &str = "erl"; -const BUCK_ISOLATION_DIR: &str = "lsp"; #[derive( Debug, @@ -89,14 +85,6 @@ 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 { @@ -109,7 +97,7 @@ impl BuckConfig { cmd.env_remove("RUST_BACKTRACE") .env_remove("RUST_LIB_BACKTRACE"); cmd.arg("--isolation-dir"); - cmd.arg(BUCK_ISOLATION_DIR); + cmd.arg("lsp"); cmd.current_dir(self.buck_root()); CommandProxy::new(guard, cmd) } @@ -338,7 +326,6 @@ impl IncludeMapping { impl BuckProject { pub fn load_from_config( buck_conf: &BuckConfig, - elp_config: &ElpConfig, query_config: &BuckQueryConfig, report_progress: &impl Fn(&str), ) -> Result< @@ -362,7 +349,7 @@ impl BuckProject { }; let otp_root = Otp::find_otp()?; // TODO: we now get these twice. Perhaps they should be cached? - let (_otp, otp_project_apps) = Otp::discover(otp_root.clone(), &elp_config.otp); + let (_otp, otp_project_apps) = Otp::discover(otp_root.clone()); include_mapping.add_otp(&otp_project_apps); Ok(( project, @@ -461,6 +448,8 @@ pub struct BuckTarget { /// to build the graph. #[serde(default)] extra_includes: Vec, + #[serde(default)] + origin: BuckTargetOrigin, } impl BuckTarget { @@ -471,155 +460,6 @@ impl BuckTarget { AppName(self.name.clone()) } } - - fn is_generated(&self) -> bool { - self.labels.contains("generated") - } -} - -#[derive(Debug, PartialEq, Eq)] -enum BuckTargetType { - App, - Test, - Other(String), -} - -impl<'de> Deserialize<'de> for BuckTargetType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - match s.as_str() { - "prelude//rules.bzl:erlang_app" => Ok(BuckTargetType::App), - "prelude//rules.bzl:erlang_test" => Ok(BuckTargetType::Test), - other => { - log::info!("Ignoring buck target type: {}", &other); - Ok(BuckTargetType::Other(other.to_string())) - } - } - } -} - -/// The interface type used in reading the output of running `buck2 target --json` -#[derive(Deserialize, Debug)] -pub struct BuckTargetBare { - // Note that some of the fields are of type `serde_json::Value`. - // - // This is because for some target types that we are not - // interested in they are not simple lists of strings, so fail - // serialization, and there is no point having a complex rust type - // for a thing we don't care about. - // - // We can only make this decision after extracting the `buck_type` - // field and filtering for the target types we care about, so we - // initially deserialize the problematic fields to `Value`, and - // complete it when we use them. - // - // This also gives much better error messages, since `serde_json` - // is optimised for this, whereas deserializing from a `Value` - // gives no useful information. - name: String, - #[serde(default)] - app_name: Option, - - #[serde(rename = "buck.package")] - buck_package: String, - - // Note: we are not using the following available fields - // _includes_target - // buck.deps - // buck.inputs - #[serde(rename = "buck.type")] - buck_type: BuckTargetType, - #[serde(default)] - labels: serde_json::Value, - - #[serde(default)] - applications: serde_json::Value, - #[serde(default)] - deps: serde_json::Value, - #[serde(default)] - extra_includes: Vec, - #[serde(default)] - included_applications: Vec, - #[serde(default)] - includes: Vec, - #[serde(default)] - srcs: serde_json::Value, - #[serde(default)] - suite: Option, -} - -impl BuckTargetBare { - fn target_full_name(&self) -> String { - if let Some(name) = &self.app_name { - format!("{}:{}", &self.buck_package, &name) - } else { - format!("{}:{}", &self.buck_package, &self.name) - } - } - - fn as_buck_target(bare: Self, cells: &BuckCellInfo) -> BuckTarget { - let srcs = match bare.srcs { - serde_json::Value::Array(values) => values - .iter() - .flat_map(|v| match v { - serde_json::Value::String(s) => Some(cells.make_absolute(s)), - _ => None, - }) - .collect(), - _ => vec![], - }; - let deps = Self::json_value_to_strings(&bare.deps); - let apps = Self::json_value_to_strings(&bare.applications); - let labels = Self::json_value_to_strings(&bare.labels) - .into_iter() - .collect(); - BuckTarget { - name: bare.name, - app_name: bare.app_name, - suite: bare.suite.map(|tgt| cells.make_absolute(&tgt)), - srcs, - includes: includes_directories_only(&cells.make_all_absolute(&bare.includes)), - labels, - gen_srcs: vec![], - deps, - apps, - included_apps: bare.included_applications, - extra_includes: bare - .extra_includes - .iter() - .map(|inc| { - inc.strip_suffix("_includes_only") - .unwrap_or(inc) - .to_string() - }) - .collect::>(), - } - } - - fn json_value_to_strings(val: &serde_json::Value) -> Vec { - match val { - serde_json::Value::Array(values) => values - .iter() - .flat_map(|v| match v { - serde_json::Value::String(s) => Some(s.clone()), - _ => None, - }) - .collect(), - _ => vec![], - } - } - - fn skipped_target_type(&self) -> bool { - match self.buck_type { - BuckTargetType::App => false, - BuckTargetType::Test => false, - - BuckTargetType::Other(_) => true, - } - } } #[derive(Debug, Eq, PartialEq, Clone)] @@ -639,8 +479,6 @@ pub struct Target { pub target_type: TargetType, /// true if there are .hrl files in the src dir pub private_header: bool, - /// true if generated via elp.bxl - pub buck_generated: bool, } impl Target { @@ -653,20 +491,16 @@ impl Target { } } - fn include_dirs(&self) -> Vec { - let mut include_dirs = FxHashSet::from_iter( - self.include_files - .iter() - .map(|path| include_path_from_file(path)), - ); + fn include_files(&self) -> Vec { + let mut include_files = self.include_files.clone(); if self.private_header { self.src_files.iter().for_each(|path| { if Some("hrl") == path.extension() { - include_dirs.insert(include_path_from_file(path)); + include_files.push(include_path_from_file(path)); } }); } - include_dirs.iter().cloned().collect() + include_files } } @@ -679,23 +513,9 @@ pub enum TargetType { ErlangTestUtils, } -fn includes_directories_only(includes: &[String]) -> Vec { - let mut set = FxHashSet::default(); - for inc in includes { - let full_path = Path::new(inc); - let path = full_path - .extension() - .and_then(|_| full_path.parent()) - .and_then(|p| p.to_str()) - .unwrap_or(inc); - set.insert(path); - } - set.iter().map(|s| s.to_string()).collect() -} - fn load_buck_targets_bxl( buck_config: &BuckConfig, - query_config: &BuckQueryConfig, + build: &BuckQueryConfig, report_progress: &impl Fn(&str), ) -> Result { let _timer = timeit!("loading info from buck"); @@ -708,35 +528,26 @@ fn load_buck_targets_bxl( FxHashMap::default() }; - match query_config { + match build { BuckQueryConfig::BuildGeneratedCode => { report_progress("Querying and generating buck targets") } - BuckQueryConfig::NoBuildGeneratedCode => { - report_progress("Querying buck targets without codegen") - } - BuckQueryConfig::BuckTargetsOnly => report_progress("Querying buck targets, phase 1"), + BuckQueryConfig::NoBuildGeneratedCode => report_progress("Querying buck targets"), } - let result = query_buck_targets(buck_config, query_config)?; - let buck_targets = filter_buck_targets(buck_config, result)?; + let buck_targets = query_buck_targets(buck_config, build)?; report_progress("Making target info"); let mut target_info = TargetInfo::default(); let mut used_deps = FxHashSet::default(); - let targets_include_prelude = buck_config - .included_targets - .iter() - .any(|t| t.starts_with("prelude//")); for (name, buck_target) in &buck_targets { if let Ok(target) = make_buck_target( root, name, buck_target, - buck_config, - targets_include_prelude, + buck_config.build_deps, &mut dep_path, &mut target_info, ) { @@ -756,54 +567,55 @@ fn make_buck_target( root: &AbsPathBuf, name: &String, target: &BuckTarget, - buck_config: &BuckConfig, - targets_include_prelude: bool, + build_deps: bool, dep_path: &mut FxHashMap, target_info: &mut TargetInfo, ) -> Result { let dir = find_app_root_bxl(root, name, target).expect("could not find app root"); - let (src_files, target_type, private_header, ebin) = if let Some(ref suite) = target.suite { - let src_file = json::canonicalize(buck_path_to_abs_path(root, suite)?)?; - let src = vec![src_file.clone()]; - target_info - .path_to_target_name - .insert(src_file, name.clone()); - (src, TargetType::ErlangTest, false, None) - } else { - let mut private_header = false; - 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())?; - if Some("hrl") == src.extension() { - private_header = true; + let (src_files, include_files, target_type, private_header, ebin) = + if let Some(ref suite) = target.suite { + let src_file = buck_path_to_abs_path(root, suite)?; + let src = vec![src_file.clone()]; + target_info + .path_to_target_name + .insert(src_file, name.clone()); + let mut include_files = vec![]; + for include in &target.includes { + if let Ok(inc) = AbsPathBuf::try_from(include.as_str()) { + include_files.push(inc); + } + } + (src, include_files, TargetType::ErlangTest, false, None) + } else { + let mut private_header = false; + let target_type = compute_target_type(name, target); + let mut src_files = vec![]; + for src in &target.srcs { + let src = buck_path_to_abs_path(root, src).unwrap(); + if Some("hrl") == src.extension() { + private_header = true; + } + src_files.push(src); + } + let mut include_files = vec![]; + for include in &target.includes { + let mut inc = buck_path_to_abs_path(root, include).unwrap(); + if inc.extension().is_some() { + inc.pop(); + } + include_files.push(inc); } - src_files.push(src); - } - let ebin = match target_type { - TargetType::ThirdParty if buck_config.build_deps => dep_path - .remove(name) - .map(|dir| dir.join(Utf8PathBuf::from("ebin"))), - TargetType::ThirdParty => Some(dir.clone()), - _ => None, + let ebin = match target_type { + TargetType::ThirdParty if build_deps => dep_path + .remove(name) + .map(|dir| dir.join(Utf8PathBuf::from("ebin"))), + TargetType::ThirdParty => Some(dir.clone()), + _ => None, + }; + (src_files, include_files, target_type, private_header, ebin) }; - (src_files, target_type, private_header, ebin) - }; - let mut include_files = vec![]; - for include in &target.includes { - if let Ok(inc_abs) = buck_path_to_abs_path(root, include) - && let Ok(inc) = json::canonicalize(inc_abs) - { - include_files.push(inc); - } - } let gen_src_files = target .gen_srcs .iter() @@ -823,28 +635,22 @@ fn make_buck_target( ebin, target_type, private_header, - buck_generated: target.is_generated(), }) } -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") { +fn compute_target_type(name: &TargetFullName, target: &BuckTarget) -> TargetType { + if target.origin != BuckTargetOrigin::App && (name.starts_with("prelude//")) + || name.contains("//third-party") + { TargetType::ThirdParty } else { - let is_test_application = test_application_labels - .iter() - .any(|label| target.labels.contains(label)); - if is_test_application { - TargetType::ErlangTestUtils - } else { - TargetType::ErlangApp + 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, } } } @@ -884,24 +690,15 @@ fn find_root(buck_config: &BuckConfig) -> Result { pub enum BuckQueryConfig { BuildGeneratedCode, NoBuildGeneratedCode, - /// Instead of using elp.bxl, use `buck2 targets`. - 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( +fn query_buck_targets( buck_config: &BuckConfig, - result: FxHashMap, + query_config: &BuckQueryConfig, ) -> Result> { + let _timer = timeit!("load buck targets"); + let result = query_buck_targets_bxl(buck_config, query_config)?; + let result = result .into_iter() .filter(|(name, _)| { @@ -921,21 +718,7 @@ fn filter_buck_targets( Ok(result) } -pub fn query_buck_targets( - buck_config: &BuckConfig, - query_config: &BuckQueryConfig, -) -> Result> { - let _timer = timeit!("load buck targets"); - let result = match query_config { - BuckQueryConfig::BuildGeneratedCode | BuckQueryConfig::NoBuildGeneratedCode => { - query_buck_targets_bxl(buck_config, query_config)? - } - BuckQueryConfig::BuckTargetsOnly => query_buck_targets_bare(buck_config)?, - }; - Ok(result) -} - -fn query_buck_targets_bxl( +pub fn query_buck_targets_bxl( buck_config: &BuckConfig, build: &BuckQueryConfig, ) -> Result> { @@ -971,12 +754,6 @@ struct BuckCellInfo { } impl BuckCellInfo { - fn make_all_absolute(&self, list: &[String]) -> Vec { - list.iter() - .map(|buck_path| self.make_absolute(buck_path)) - .collect() - } - fn make_absolute(&self, target: &str) -> String { self.make_absolute_maybe(target) .unwrap_or(target.to_string()) @@ -1057,41 +834,6 @@ fn run_buck_command(mut command: CommandProxy<'_>) -> Result { Ok(output) } -fn query_buck_targets_bare(buck_config: &BuckConfig) -> Result> { - let cells = buck_cell_info()?; - - let bare_targets_apps = do_buck_query_bare(buck_config, &buck_config.included_targets)?; - let bare_targets_deps = do_buck_query_bare(buck_config, &buck_config.deps_targets)?; - - let mut result = FxHashMap::default(); - - let mut do_one = |bare: BuckTargetBare| { - if !bare.skipped_target_type() { - let target_full_name = bare.target_full_name(); - let target: BuckTarget = BuckTargetBare::as_buck_target(bare, &cells); - result.insert(target_full_name, target); - } - }; - for bare in bare_targets_apps { - do_one(bare); - } - for bare in bare_targets_deps { - do_one(bare); - } - Ok(result) -} - -fn do_buck_query_bare(buck_config: &BuckConfig, targets: &[String]) -> Result> { - let mut command = buck_config.buck_command(); - command.arg("targets").arg("--json").args(targets); - let output = run_buck_command(command)?; - let string = String::from_utf8(output.stdout)?; - let bare_targets: Vec = serde_json::from_str(&string)?; - Ok(bare_targets) -} - -// --------------------------------------------------------------------- - #[derive(Error, Debug)] pub struct BuckQueryError { pub command: String, @@ -1319,22 +1061,15 @@ fn targets_to_project_data_bxl( dir: target.dir.clone(), ebin: None, extra_src_dirs: extra_src_dirs.into_iter().collect(), - include_dirs: target.include_dirs(), + include_dirs: target.include_files(), abs_src_dirs: abs_src_dirs.into_iter().collect(), macros, parse_transforms: vec![], app_type: target.app_type(), include_path: vec![], gen_src_files: Some(FxHashSet::from_iter(target.gen_src_files.clone())), - applicable_files: Some(FxHashSet::from_iter( - target - .src_files - .iter() - .cloned() - .chain(target.include_files.iter().cloned()), - )), + applicable_files: Some(FxHashSet::from_iter(target.src_files.clone())), is_test_target: Some(target.target_type == TargetType::ErlangTest), - is_buck_generated: Some(target.buck_generated), }; // Do not bother with applications that exist purely as a // handle on dependencies for include_path. @@ -1363,56 +1098,36 @@ 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 mut command = buck_config.buck_command(); - command + let output = buck_config + .buck_command() .arg("audit") .arg("cell") .arg("prelude") - .arg("--json"); - let raw_output = check_buck_output_success(command)?; - - 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) + .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)?; -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()) + lazy_static! { + static ref RE: Regex = Regex::new(r"^prelude: ([^\s]+)").unwrap(); + } + let string = RE + .captures_iter(&raw_output) + .next() + .map(|c| c[1].to_string()) + .unwrap(); + Ok(string) } #[cfg(test)] @@ -1426,14 +1141,13 @@ mod tests { use parking_lot::MutexGuard; use paths::AbsPath; use paths::Utf8Path; - use serde_json::Value; use super::BUCK_CELL_INFO; use super::BuckCellInfo; use crate::buck::BuckConfig; use crate::buck::BuckQueryError; use crate::buck::BuckTarget; - use crate::buck::BuckTargetBare; + use crate::buck::BuckTargetOrigin; use crate::buck::buck_path_to_abs_path; use crate::buck::find_app_root_bxl; use crate::buck::get_prelude_cell; @@ -1453,10 +1167,6 @@ 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(), @@ -1470,6 +1180,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1485,10 +1196,6 @@ 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(), @@ -1502,6 +1209,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1517,10 +1225,6 @@ 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(), @@ -1534,6 +1238,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1570,6 +1275,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1603,6 +1309,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1619,10 +1326,6 @@ 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(), @@ -1636,6 +1339,7 @@ mod tests { apps: vec![], included_apps: vec![], extra_includes: vec![], + origin: BuckTargetOrigin::App, }; let actual = find_app_root_bxl(root, &target_name, &target); @@ -1643,66 +1347,71 @@ 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) { - 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]/"); + 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 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 cfg!(feature = "buck") { + if BUCK_TESTS_ENABLED { check_buck_bxl_query( false, expect![[r#" { - "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1712,7 +1421,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, @@ -1855,23 +1564,23 @@ mod tests { #[test] #[ignore] fn build_info_buck_bxl_generated_query() { - if cfg!(feature = "buck") { + if BUCK_TESTS_ENABLED { // Note that there is now a value for `srcs` in the - // "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs" + // "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target check_buck_bxl_query( true, expect![[r#" { - "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1881,12 +1590,12 @@ mod tests { "included_apps": [], "origin": "app" }, - "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, "srcs": [ - "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" + "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" ], "includes": [], "labels": [ @@ -2111,195 +1820,4 @@ mod tests { "#]] .assert_debug_eq(&cells.make_absolute("cell2//a/b/c.erl")); } - - #[test] - fn deserialize_buck_target_bare() { - let string = r#" - [ - { - "_includes_target": "cell//linter:app_a_includes_only", - "app_src": "cell//linter/app_a/src/app_a.app.src", - "applications": [], - "buck.deps": [ - "cell//linter:app_a_includes_only", - "prelude//erlang/shell:buck2_shell_utils", - "toolchains//:erlang-default" - ], - "buck.inputs": [ - "cell//linter/app_a/src/app_a.app.src", - "cell//linter/app_a/include/app_a.hrl", - "cell//linter/app_a/src/app_a.erl", - "cell//linter/app_a/src/app_a_edoc.erl", - "cell//linter/app_a/src/app_a_unused_param.erl", - "cell//linter/app_a/src/expression_updates_literal.erl", - "cell//linter/app_a/src/spelling.erl" - ], - "buck.oncall": "vscode_erlang", - "buck.package": "cell//linter", - "buck.type": "prelude//rules.bzl:erlang_app", - "extra_includes": [], - "included_applications": [], - "includes": [ - "cell//linter/app_a/include/app_a.hrl" - ], - "labels": [ - "user_application" - ], - "name": "app_a_target", - "app_name":"app_a", - "srcs": [ - "cell//linter/app_a/src/app_a.erl", - "cell//linter/app_a/src/app_a_edoc.erl", - "cell//linter/app_a/src/app_a_unused_param.erl", - "cell//linter/app_a/src/expression_updates_literal.erl", - "cell//linter/app_a/src/spelling.erl" - ], - "version": "1.0.0", - "visibility": [], - "within_view": [ - "PUBLIC" - ] - } - ]"#; - let result: Vec = serde_json::from_str(string).unwrap(); - expect![[r#" - [ - BuckTargetBare { - name: "app_a_target", - app_name: Some( - "app_a", - ), - buck_package: "cell//linter", - buck_type: App, - labels: Array [ - String("user_application"), - ], - applications: Array [], - deps: Null, - extra_includes: [], - included_applications: [], - includes: [ - "cell//linter/app_a/include/app_a.hrl", - ], - srcs: Array [ - String("cell//linter/app_a/src/app_a.erl"), - String("cell//linter/app_a/src/app_a_edoc.erl"), - String("cell//linter/app_a/src/app_a_unused_param.erl"), - String("cell//linter/app_a/src/expression_updates_literal.erl"), - String("cell//linter/app_a/src/spelling.erl"), - ], - suite: None, - }, - ] - "#]] - .assert_debug_eq(&result); - - let full_target_names = result - .iter() - .map(|b| b.target_full_name()) - .collect::>(); - expect![[r#" - [ - "cell//linter:app_a", - ] - "#]] - .assert_debug_eq(&full_target_names); - - let cells = BuckCellInfo { - cells: FxHashMap::from_iter(vec![ - ("fbcode".to_string(), "/[fbcode]".to_string()), - ("prelude".to_string(), "/[prelude]".to_string()), - ]), - }; - - let converted: Vec = result - .into_iter() - .map(|bare| BuckTargetBare::as_buck_target(bare, &cells)) - .collect(); - expect![[r#" - [ - BuckTarget { - name: "app_a_target", - app_name: Some( - "app_a", - ), - suite: None, - srcs: [ - "cell//linter/app_a/src/app_a.erl", - "cell//linter/app_a/src/app_a_edoc.erl", - "cell//linter/app_a/src/app_a_unused_param.erl", - "cell//linter/app_a/src/expression_updates_literal.erl", - "cell//linter/app_a/src/spelling.erl", - ], - includes: [ - "cell//linter/app_a/include", - ], - gen_srcs: [], - labels: { - "user_application", - }, - deps: [], - apps: [], - included_apps: [], - extra_includes: [], - }, - ] - "#]] - .assert_debug_eq(&converted); - } - - #[test] - fn unusual_buck_target() { - let string = r#" - [ - - { - "buck.type":"prelude//rules.bzl:erlang_release", - "buck.deps":[ - "waserver//buck2/test/targets/apps:app_a", - "waserver//buck2/test/targets/apps:app_e", - "toolchains//:erlang-default" - ], - "buck.inputs":[], - "buck.package":"waserver//buck2/test/targets/releases", - "name":"rel4", - "visibility":["PUBLIC"], - "within_view":["PUBLIC"], - "applications":[ - "waserver//buck2/test/targets/apps:app_a", - ["waserver//buck2/test/targets/apps:app_e","load"] - ] - } - ]"#; - let v: Value = serde_json::from_str(string).unwrap(); - expect![[r#" - Array [ - Object { - "applications": Array [ - String("waserver//buck2/test/targets/apps:app_a"), - Array [ - String("waserver//buck2/test/targets/apps:app_e"), - String("load"), - ], - ], - "buck.deps": Array [ - String("waserver//buck2/test/targets/apps:app_a"), - String("waserver//buck2/test/targets/apps:app_e"), - String("toolchains//:erlang-default"), - ], - "buck.inputs": Array [], - "buck.package": String("waserver//buck2/test/targets/releases"), - "buck.type": String("prelude//rules.bzl:erlang_release"), - "name": String("rel4"), - "visibility": Array [ - String("PUBLIC"), - ], - "within_view": Array [ - String("PUBLIC"), - ], - }, - ] - "#]] - .assert_debug_eq(&v); - } } diff --git a/crates/project_model/src/eqwalizer_support.rs b/crates/project_model/src/eqwalizer_support.rs index 861e9af812..672b7c174d 100644 --- a/crates/project_model/src/eqwalizer_support.rs +++ b/crates/project_model/src/eqwalizer_support.rs @@ -49,7 +49,6 @@ pub(crate) fn eqwalizer_suppport_data(otp_root: &AbsPath) -> ProjectAppData { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, } } diff --git a/crates/project_model/src/json.rs b/crates/project_model/src/json.rs index c98f30e726..12b822ac39 100644 --- a/crates/project_model/src/json.rs +++ b/crates/project_model/src/json.rs @@ -105,7 +105,6 @@ impl JsonProjectAppData { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, }) } @@ -230,7 +229,7 @@ pub(crate) fn gen_app_data( (apps, deps) } -pub fn canonicalize(path: impl AsRef) -> Result { +fn canonicalize(path: impl AsRef) -> Result { let abs = fs::canonicalize(path.as_ref())?; Ok(AbsPathBuf::assert( Utf8PathBuf::from_path_buf(abs).expect("Could not convert to UTF8"), diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 233c687afc..73a46e7e6e 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -339,13 +339,7 @@ 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(elp_config_basedir) = path.parent() - && let Some(r) = Self::discover_rebar( - &elp_config_basedir.to_path_buf(), - Some(rebar_profile), - IncludeParentDirs::No, - )? - { + if let Some(r) = Self::discover_rebar(path, Some(rebar_profile), IncludeParentDirs::No)? { return Ok(r); } if let Some(s) = Self::discover_static(path, IncludeParentDirs::No)? { @@ -391,8 +385,6 @@ pub struct ElpConfig { pub eqwalizer: EqwalizerConfig, #[serde(default)] pub rebar: ElpRebarConfig, - #[serde(default)] - pub otp: OtpConfig, } #[derive( @@ -509,22 +501,6 @@ impl Default for ElpRebarConfig { } } -#[derive( - Debug, - Default, - Clone, - PartialEq, - Eq, - Hash, - Ord, - PartialOrd, - Deserialize, - Serialize -)] -pub struct OtpConfig { - pub exclude_apps: Vec, -} - impl ElpConfig { pub fn new( config_path: AbsPathBuf, @@ -539,7 +515,6 @@ impl ElpConfig { build_info, eqwalizer, rebar, - otp: OtpConfig::default(), } } pub fn try_parse(path: &AbsPath) -> Result { @@ -902,9 +877,6 @@ pub struct ProjectAppData { // data applies to. pub applicable_files: Option>, pub is_test_target: Option, - /// Generated as part of the buck build process, via elp.bxl - /// Specifically not related to any marker tags in files - pub is_buck_generated: Option, } impl ProjectAppData { @@ -930,7 +902,6 @@ impl ProjectAppData { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, } } @@ -958,7 +929,6 @@ impl ProjectAppData { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, } } @@ -1028,7 +998,7 @@ impl Project { pub fn load( manifest: &ProjectManifest, - elp_config: &ElpConfig, + eqwalizer_config: EqwalizerConfig, query_config: &BuckQueryConfig, report_progress: &impl Fn(&str), ) -> Result { @@ -1062,7 +1032,7 @@ impl Project { ProjectManifest::TomlBuck(buck) => { // We only select this manifest if buck is actually enabled let (project, apps, otp_root, include_mapping) = - BuckProject::load_from_config(buck, elp_config, query_config, report_progress)?; + BuckProject::load_from_config(buck, query_config, report_progress)?; ( ProjectBuildData::Buck(project), apps, @@ -1091,14 +1061,14 @@ impl Project { } }; - let (otp, otp_project_apps) = Otp::discover(otp_root, &elp_config.otp); + let (otp, otp_project_apps) = Otp::discover(otp_root); project_apps.extend(otp_project_apps); report_progress("Project info loaded"); Ok(Project { otp, project_build_data: project_build_info, project_apps, - eqwalizer_config: elp_config.eqwalizer.clone(), + eqwalizer_config, include_mapping, }) } @@ -1202,9 +1172,6 @@ mod tests { rebar: ElpRebarConfig { profile: "test", }, - otp: OtpConfig { - exclude_apps: [], - }, }, Rebar( RebarConfig { @@ -1270,9 +1237,6 @@ mod tests { rebar: ElpRebarConfig { profile: "test", }, - otp: OtpConfig { - exclude_apps: [], - }, }, Json( JsonConfig { @@ -1383,9 +1347,6 @@ mod tests { rebar: ElpRebarConfig { profile: "test", }, - otp: OtpConfig { - exclude_apps: [], - }, }, JsonConfig { apps: [ @@ -1541,9 +1502,6 @@ mod tests { rebar: ElpRebarConfig { profile: "test", }, - otp: OtpConfig { - exclude_apps: [], - }, }, NoManifest( NoManifestConfig { @@ -1577,60 +1535,6 @@ 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 @@ -1667,9 +1571,6 @@ mod tests { rebar: ElpRebarConfig { profile: "test", }, - otp: OtpConfig { - exclude_apps: [], - }, }, NoManifest( NoManifestConfig { @@ -1863,9 +1764,6 @@ mod tests { rebar: ElpRebarConfig { profile: "other", }, - otp: OtpConfig { - exclude_apps: [], - }, } "#]] .assert_eq(&debug_normalise_temp_dir(dir, &elp_config)); @@ -1899,7 +1797,6 @@ 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, @@ -1910,7 +1807,6 @@ mod tests { rebar: ElpRebarConfig { profile: "my_profile".to_string(), }, - otp: OtpConfig::default(), }) .unwrap(); expect![[r#" @@ -1925,7 +1821,6 @@ 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 @@ -1934,9 +1829,6 @@ mod tests { [rebar] profile = "my_profile" - - [otp] - exclude_apps = [] "#]] .assert_eq(&result); } @@ -1999,9 +1891,6 @@ mod tests { source_root: Some( "path/to/root", ), - test_application_labels: [ - "test_application", - ], }, ), eqwalizer: EqwalizerConfig { @@ -2013,9 +1902,6 @@ mod tests { rebar: ElpRebarConfig { profile: "my_profile", }, - otp: OtpConfig { - exclude_apps: [], - }, } "#]] .assert_debug_eq(&lints); @@ -2131,112 +2017,4 @@ mod tests { } } } - - #[test] - fn test_toml_otp_exclude_apps() { - let spec = r#" - //- /.elp.toml - [otp] - exclude_apps = ["megaco", "eunit"] - //- /app_a/src/app.erl - -module(app). - "#; - let dir = FixtureWithProjectMeta::gen_project(spec); - if let Ok((elp_config, ProjectManifest::NoManifest(_))) = ProjectManifest::discover( - &to_abs_path_buf(&dir.path().join("app_a/src/app.erl")).unwrap(), - ) { - expect![[r#" - ElpConfig { - config_path: Some( - AbsPathBuf( - "TMPDIR/.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: [ - "megaco", - "eunit", - ], - }, - } - "#]] - .assert_eq(&debug_normalise_temp_dir(dir, &elp_config)); - } else { - panic!() - } - } - - #[test] - fn serde_serialize_elp_toml_with_otp() { - let result = toml::to_string::(&ElpConfig { - config_path: None, - build_info: None, - buck: None, - eqwalizer: EqwalizerConfig::default(), - rebar: ElpRebarConfig::default(), - otp: OtpConfig { - exclude_apps: vec!["megaco".to_string(), "eunit".to_string()], - }, - }) - .unwrap(); - expect![[r#" - [eqwalizer] - enable_all = true - max_tasks = 4 - ignore_modules = [] - - [rebar] - profile = "test" - - [otp] - exclude_apps = ["megaco", "eunit"] - "#]] - .assert_eq(&result); - } - - #[test] - fn serde_deserialize_elp_toml_with_otp() { - let config: ElpConfig = toml::from_str( - r#" - [otp] - exclude_apps = ["megaco", "eunit"] - "#, - ) - .unwrap(); - - expect![[r#" - ElpConfig { - config_path: None, - 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: [ - "megaco", - "eunit", - ], - }, - } - "#]] - .assert_debug_eq(&config); - } } diff --git a/crates/project_model/src/no_manifest.rs b/crates/project_model/src/no_manifest.rs index 6a7e021342..a1de933260 100644 --- a/crates/project_model/src/no_manifest.rs +++ b/crates/project_model/src/no_manifest.rs @@ -71,7 +71,6 @@ impl NoManifestConfig { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, }; data.include_path.extend(data.include_dirs()); if let Some(path) = self.root_path.parent() { diff --git a/crates/project_model/src/otp.rs b/crates/project_model/src/otp.rs index d2cb3be6a2..3d561177d8 100644 --- a/crates/project_model/src/otp.rs +++ b/crates/project_model/src/otp.rs @@ -22,7 +22,6 @@ use paths::Utf8Path; use paths::Utf8PathBuf; use crate::AppName; -use crate::OtpConfig; use crate::ProjectAppData; #[derive(Debug, Clone, PartialEq, Eq)] @@ -71,7 +70,7 @@ pub fn supports_eep66_sigils() -> bool { } fn get_erts_dir() -> AbsPathBuf { - let (_otp, apps) = Otp::discover(OTP_ROOT.to_path_buf(), &OtpConfig::default()); + let (_otp, apps) = Otp::discover(OTP_ROOT.to_path_buf()); for app in apps { if app.name == AppName("erts".to_string()) { return app.dir; @@ -158,8 +157,8 @@ impl Otp { Ok(val) } - pub fn discover(path: Utf8PathBuf, otp_config: &OtpConfig) -> (Otp, Vec) { - let apps = Self::discover_otp_apps(&path, otp_config); + pub fn discover(path: Utf8PathBuf) -> (Otp, Vec) { + let apps = Self::discover_otp_apps(&path); ( Otp { lib_dir: AbsPathBuf::assert(path), @@ -168,8 +167,7 @@ impl Otp { ) } - fn discover_otp_apps(path: &Utf8Path, otp_config: &OtpConfig) -> Vec { - let exclude_apps = &otp_config.exclude_apps; + fn discover_otp_apps(path: &Utf8Path) -> Vec { log::info!("Loading OTP apps from {path:?}"); if let Ok(entries) = fs::read_dir(path) { entries @@ -177,24 +175,11 @@ impl Otp { .filter_map(|entry| { let entry = entry.ok()?; let name = entry.file_name(); - let name_str = name.to_str()?; - - // Extract app name from versioned directory name (e.g., "megaco-4.5" -> "megaco") - let app_name = name_str - .split_once('-') - .map_or(name_str, |(base, _version)| base); - - // Skip excluded applications - if exclude_apps.contains(&app_name.to_string()) { - log::info!("Excluding OTP app: {}", app_name); - return None; - } - let path = fs::canonicalize(entry.path()).expect("Could not canonicalize path"); let dir = AbsPathBuf::assert( Utf8PathBuf::from_path_buf(path).expect("Could not convert to Utf8PathBuf"), ); - Some(ProjectAppData::otp_app_data(name_str, &dir)) + Some(ProjectAppData::otp_app_data(name.to_str()?, &dir)) }) .collect() } else { @@ -202,59 +187,3 @@ impl Otp { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_discover_with_excluded_apps() { - // Create a mock OTP directory structure - let temp_dir = tempfile::tempdir().unwrap(); - let otp_lib_dir = temp_dir.path().join("lib"); - std::fs::create_dir_all(&otp_lib_dir).unwrap(); - - // Create mock OTP app directories - let apps = vec![ - "kernel-9.2", - "stdlib-5.2", - "megaco-4.5", - "eunit-2.8.2", - "common_test-1.25.1", - ]; - - for app in &apps { - let app_dir = otp_lib_dir.join(app); - std::fs::create_dir_all(app_dir.join("src")).unwrap(); - std::fs::create_dir_all(app_dir.join("include")).unwrap(); - std::fs::create_dir_all(app_dir.join("ebin")).unwrap(); - } - - let otp_path = Utf8PathBuf::from_path_buf(otp_lib_dir).unwrap(); - - // Test without exclusions - let (_otp, all_apps) = Otp::discover(otp_path.clone(), &OtpConfig::default()); - let app_names: Vec<&str> = all_apps.iter().map(|app| app.name.as_str()).collect(); - assert!(app_names.contains(&"kernel")); - assert!(app_names.contains(&"stdlib")); - assert!(app_names.contains(&"megaco")); - assert!(app_names.contains(&"eunit")); - assert!(app_names.contains(&"common_test")); - - // Test with exclusions - let otp_config = OtpConfig { - exclude_apps: vec!["megaco".to_string(), "eunit".to_string()], - }; - let (_otp, filtered_apps) = Otp::discover(otp_path, &otp_config); - let filtered_names: Vec<&str> = filtered_apps.iter().map(|app| app.name.as_str()).collect(); - assert!(filtered_names.contains(&"kernel")); - assert!(filtered_names.contains(&"stdlib")); - assert!(filtered_names.contains(&"common_test")); - assert!(!filtered_names.contains(&"megaco")); - assert!(!filtered_names.contains(&"eunit")); - - // Verify the filtered list is smaller - assert!(filtered_apps.len() < all_apps.len()); - assert_eq!(filtered_apps.len(), all_apps.len() - 2); - } -} diff --git a/crates/project_model/src/rebar.rs b/crates/project_model/src/rebar.rs index bd675ffc2e..64686c26a6 100644 --- a/crates/project_model/src/rebar.rs +++ b/crates/project_model/src/rebar.rs @@ -240,7 +240,6 @@ impl RebarProject { gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, }) } } diff --git a/crates/project_model/src/test_fixture.rs b/crates/project_model/src/test_fixture.rs index 140baec0ee..152595251b 100644 --- a/crates/project_model/src/test_fixture.rs +++ b/crates/project_model/src/test_fixture.rs @@ -187,7 +187,6 @@ impl DiagnosticsEnabled { pub struct FixtureWithProjectMeta { pub fixture: Vec, pub diagnostics_enabled: DiagnosticsEnabled, - pub expect_parse_errors: bool, } impl FixtureWithProjectMeta { @@ -205,7 +204,6 @@ 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 @@ -234,12 +232,6 @@ 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; @@ -285,12 +277,6 @@ 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:?}" - ); } } } @@ -312,7 +298,6 @@ impl FixtureWithProjectMeta { FixtureWithProjectMeta { fixture: res, diagnostics_enabled, - expect_parse_errors, } } @@ -466,34 +451,21 @@ 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 @@ -574,16 +546,10 @@ 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)); - // Try to find a previous annotation at the same offset - let idx = if let Some(&(_, idx)) = prev_line_annotations + let &(_, idx) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - { - idx - } else { - // If no exact offset match, append to the most recent annotation - res.len() - 1 - }; + .unwrap(); res[idx].1.push('\n'); res[idx].1.push_str(&content); } @@ -853,19 +819,6 @@ 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() { @@ -1013,7 +966,6 @@ bar() -> ok. gen_src_files: None, applicable_files: None, is_test_target: None, - is_buck_generated: None, }"#]] .assert_eq(format!("{:#?}", meta0.app_data).as_str()); } diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 1f8d1aaa63..081efa08d4 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -7,6 +7,9 @@ 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 08b7ed9a1f..af72d7de98 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs @@ -10,8 +10,12 @@ //! 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; @@ -174,6 +178,157 @@ 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. /// @@ -297,3 +452,337 @@ 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 781d35aaaf..716d46d25a 100644 --- a/crates/syntax/src/syntax_kind/generated.rs +++ b/crates/syntax/src/syntax_kind/generated.rs @@ -1,8 +1,7 @@ //! @generated file, do not edit by hand, see `xtask/src/codegen.rs` #![allow(bad_style, missing_docs, unreachable_pub)] -use num_derive::FromPrimitive; -use num_derive::ToPrimitive; +use num_derive::{FromPrimitive, 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 new file mode 100644 index 0000000000..59d333adc7 --- /dev/null +++ b/crates/text_edit/Cargo.toml @@ -0,0 +1,11 @@ +[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/crates/ide_db/src/text_edit.rs b/crates/text_edit/src/lib.rs similarity index 99% rename from crates/ide_db/src/text_edit.rs rename to crates/text_edit/src/lib.rs index 0410102569..42dac0f3ac 100644 --- a/crates/ide_db/src/text_edit.rs +++ b/crates/text_edit/src/lib.rs @@ -15,6 +15,7 @@ //! `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/editors/code/README.md b/editors/code/README.md index ff5663d5ca..b03ce757f2 100644 --- a/editors/code/README.md +++ b/editors/code/README.md @@ -1,82 +1,19 @@ # Erlang Language Platform -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. +Provide support for the [Erlang](https://www.erlang.org/) Programming Language. ## Features -### 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 +* Syntax Highlighting +* Go To Definition +* Find References +* Auto-completion +* Call Hierarchy +* Signature Help +* Diagnostics +* Inlay Hints +* ... ## Documentation -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 -

+See https://whatsapp.github.io/erlang-language-platform/ for more information. diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 6309d4c81e..74ee51b3f6 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1,12 +1,12 @@ { "name": "erlang-language-platform", - "version": "0.47.0", + "version": "0.43.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "erlang-language-platform", - "version": "0.47.0", + "version": "0.43.0", "hasInstallScript": true, "license": "Apache2", "devDependencies": { @@ -1448,11 +1448,10 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1625,9 +1624,9 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { @@ -3400,9 +3399,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -3533,9 +3532,9 @@ } }, "glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "requires": { "foreground-child": "^3.1.0", diff --git a/editors/code/package.json b/editors/code/package.json index eb7ced3148..ab784449ee 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1,10 +1,9 @@ { "name": "erlang-language-platform", - "displayName": "Erlang Language Platform", - "description": "Erlang Language Support for VS Code, by WhatsApp.", + "description": "Erlang language server", "author": "Meta Platforms, Inc", "license": "Apache2", - "version": "0.47.0", + "version": "0.43.0", "icon": "images/elp-logo-color.png", "homepage": "https://whatsapp.github.io/erlang-language-platform/", "repository": { @@ -21,8 +20,7 @@ "Testing" ], "keywords": [ - "elp", - "erlang" + "elp" ], "engines": { "vscode": "^1.75.0" diff --git a/editors/emacs/dotemacs.el b/elisp/dotemacs.el similarity index 100% rename from editors/emacs/dotemacs.el rename to elisp/dotemacs.el diff --git a/eqwalizer b/eqwalizer index 0f514eb389..060f43d17b 160000 --- a/eqwalizer +++ b/eqwalizer @@ -1 +1 @@ -Subproject commit 0f514eb3893fa7070835c83ecb49fbea31b0426d +Subproject commit 060f43d17b7d161601bb58bd341a8dc7381c0e7c diff --git a/erlang_service/src/elp_lint.erl b/erlang_service/src/elp_lint.erl index 31d2c70248..609e8c0712 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: is_format_function(wa_log, send_if) -> true; -% @fb-only: is_format_function(wa_string, format) -> true; +% @fb-only +% @fb-only is_format_function(M, F) when is_atom(M), is_atom(F) -> false. %% check_format_1([Arg]) -> ok | {warn,Level,Format,[Arg]}. -% @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]; +% @fb-only +% @fb-only 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 bc5a2faf3b..77223da49a 100644 --- a/erlang_service/src/erlang_service_lint.erl +++ b/erlang_service/src/erlang_service_lint.erl @@ -178,7 +178,6 @@ 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)]) @@ -190,7 +189,6 @@ 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. @@ -201,19 +199,18 @@ format_error(_Forms, SamePath, SamePath, {Location, Mod, Reason}) -> erlang_service_error_codes:make_code(Mod, Reason) } ]; -format_error(Forms, IncluderPath, ErrorPath, {Location, Mod, Reason}) -> +format_error(Forms, OriginalPath, Path, {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, ErrorPath), + IncludeLocation = inclusion_range(Forms, Path), %% 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(IncluderPath), - unicode:characters_to_list(ErrorPath), + unicode:characters_to_list(OriginalPath), % Location would be {Line, Col} for a erlc compiler error/warning, % but {ByteStart, ByteEnd} for an eqwalizer diagnostic. % This is deciphered on elp side. @@ -222,8 +219,7 @@ format_error(Forms, IncluderPath, ErrorPath, {Location, Mod, Reason}) -> erlang_service_error_codes:make_code(erlang_service_error_codes, "Issue in included file") }, { - unicode:characters_to_list(IncluderPath), - unicode:characters_to_list(ErrorPath), + unicode:characters_to_list(Path), % Location would be {Line, Col} for a erlc compiler error/warning, % but {ByteStart, ByteEnd} for an eqwalizer diagnostic. % This is deciphered on elp side. @@ -236,96 +232,13 @@ format_error(Forms, IncluderPath, ErrorPath, {Location, Mod, Reason}) -> ]. inclusion_range(Forms, Path) -> - %% 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; + case [Location || {attribute, Location, file, {FormPath, _}} <- Forms, FormPath == Path] of + [{Loc, _}] -> + {Loc, Loc}; _ -> {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/rustfmt.toml b/rustfmt.toml index 06998754bd..8f75064140 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,9 +1,3 @@ -# Get help on options with `rustfmt --help=config` -# Please keep these in alphabetical order. -edition = "2024" -format_code_in_doc_comments = true -group_imports = "StdExternalCrate" -imports_granularity = "Item" +edition = "2018" merge_derives = false -style_edition = "2024" use_field_init_shorthand = true diff --git a/test/test_projects/.buckconfig b/test/test_projects/.buckconfig deleted file mode 100644 index f14e564a7d..0000000000 --- a/test/test_projects/.buckconfig +++ /dev/null @@ -1,24 +0,0 @@ -[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/test_projects/.buckroot b/test/test_projects/.buckroot deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/test_projects/buck_bad_config/.elp.toml b/test/test_projects/buck_bad_config/.elp.toml deleted file mode 100644 index 963072284c..0000000000 --- a/test/test_projects/buck_bad_config/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[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 deleted file mode 100644 index 498c769dac..0000000000 --- a/test/test_projects/buck_bad_config/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -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/test_projects/buck_tests/.elp.toml b/test/test_projects/buck_tests/.elp.toml deleted file mode 100644 index b38d6f043f..0000000000 --- a/test/test_projects/buck_tests/.elp.toml +++ /dev/null @@ -1,9 +0,0 @@ -[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/test_projects/buck_tests_2/.elp.toml b/test/test_projects/buck_tests_2/.elp.toml deleted file mode 100644 index e71eb6a819..0000000000 --- a/test/test_projects/buck_tests_2/.elp.toml +++ /dev/null @@ -1,12 +0,0 @@ -[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 deleted file mode 100644 index b01fdb868c..0000000000 --- a/test/test_projects/buck_tests_2/BUCK +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index f021317020..0000000000 --- a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK +++ /dev/null @@ -1,25 +0,0 @@ -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/test_projects/buck_tests_2/check_include/src/top_includer.erl b/test/test_projects/buck_tests_2/check_include/src/top_includer.erl deleted file mode 100644 index 6f12fdb31d..0000000000 --- a/test/test_projects/buck_tests_2/check_include/src/top_includer.erl +++ /dev/null @@ -1,15 +0,0 @@ --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 deleted file mode 100644 index 82ab343676..0000000000 --- a/test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl +++ /dev/null @@ -1,5 +0,0 @@ -%% 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 deleted file mode 100644 index b67b5b8b2b..0000000000 --- a/test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl +++ /dev/null @@ -1,6 +0,0 @@ - --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 deleted file mode 100644 index 1e0c48bf56..0000000000 --- a/test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl +++ /dev/null @@ -1,2 +0,0 @@ - --define(SECOND, ok). diff --git a/test/test_projects/buck_tests_2/generated/BUCK b/test/test_projects/buck_tests_2/generated/BUCK deleted file mode 100644 index 8eec40830b..0000000000 --- a/test/test_projects/buck_tests_2/generated/BUCK +++ /dev/null @@ -1,12 +0,0 @@ -oncall("vscode_erlang") - -erlang_application( - name = "generated_headers", - includes = [ - "out/generated_header.hrl", - ], - labels = ["generated"], - visibility = [ - "PUBLIC", - ], -) diff --git a/test/test_projects/buck_tests_2/util/app_a/BUCK b/test/test_projects/buck_tests_2/util/app_a/BUCK deleted file mode 100644 index 4231c1c6e8..0000000000 --- a/test/test_projects/buck_tests_2/util/app_a/BUCK +++ /dev/null @@ -1,16 +0,0 @@ -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/test_projects/codegen_test/.elp.toml b/test/test_projects/codegen_test/.elp.toml deleted file mode 100644 index f21fe1bc73..0000000000 --- a/test/test_projects/codegen_test/.elp.toml +++ /dev/null @@ -1,7 +0,0 @@ -[buck] -enabled = true -build_deps = false -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 deleted file mode 100644 index 1df5f86eba..0000000000 --- a/test/test_projects/codegen_test/BUCK +++ /dev/null @@ -1,73 +0,0 @@ -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 deleted file mode 100644 index 1f73475d9d..0000000000 --- a/test/test_projects/codegen_test/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# 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 deleted file mode 100644 index 7a38d49a24..0000000000 --- a/test/test_projects/codegen_test/app_a/src/codegen_test.app.src +++ /dev/null @@ -1,14 +0,0 @@ -{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 deleted file mode 100644 index a624c2089b..0000000000 --- a/test/test_projects/codegen_test/app_a/src/example_usage.erl +++ /dev/null @@ -1,51 +0,0 @@ --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 deleted file mode 100644 index 3c0135dcee..0000000000 --- a/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl +++ /dev/null @@ -1,73 +0,0 @@ -%%% 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 deleted file mode 100644 index 6b38c39425..0000000000 --- a/test/test_projects/codegen_test/generated/example_service_client.erl +++ /dev/null @@ -1,37 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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 deleted file mode 100644 index 3f89adcba9..0000000000 --- a/test/test_projects/codegen_test/generated/example_service_types.erl +++ /dev/null @@ -1,55 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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 deleted file mode 100644 index 4c957a0906..0000000000 --- a/test/test_projects/codegen_test/generated/example_service_types.hrl +++ /dev/null @@ -1,27 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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 deleted file mode 100644 index ab30a89f74..0000000000 --- a/test/test_projects/codegen_test/templates/example_service_client.erl +++ /dev/null @@ -1,38 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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 deleted file mode 100644 index 833df167c6..0000000000 --- a/test/test_projects/codegen_test/templates/example_service_types.erl +++ /dev/null @@ -1,56 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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 deleted file mode 100644 index 88a2e7aed2..0000000000 --- a/test/test_projects/codegen_test/templates/example_service_types.hrl +++ /dev/null @@ -1,28 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @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/diagnostics/.elp.toml b/test/test_projects/diagnostics/.elp.toml deleted file mode 100644 index 504ef547af..0000000000 --- a/test/test_projects/diagnostics/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[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 deleted file mode 100644 index 19c15e5acf..0000000000 --- a/test/test_projects/diagnostics/BUCK +++ /dev/null @@ -1,20 +0,0 @@ -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/test_projects/end_to_end/assist_examples/BUCK b/test/test_projects/end_to_end/assist_examples/BUCK deleted file mode 100644 index 60d39de125..0000000000 --- a/test/test_projects/end_to_end/assist_examples/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -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/test_projects/end_to_end/definitions/BUCK b/test/test_projects/end_to_end/definitions/BUCK deleted file mode 100644 index c9e6b204f4..0000000000 --- a/test/test_projects/end_to_end/definitions/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -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/test_projects/end_to_end/docs/BUCK b/test/test_projects/end_to_end/docs/BUCK deleted file mode 100644 index 7429863b6e..0000000000 --- a/test/test_projects/end_to_end/docs/BUCK +++ /dev/null @@ -1,16 +0,0 @@ -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/test_projects/end_to_end/hover/BUCK b/test/test_projects/end_to_end/hover/BUCK deleted file mode 100644 index 7732dc473e..0000000000 --- a/test/test_projects/end_to_end/hover/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -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/test_projects/end_to_end/single_errors/BUCK b/test/test_projects/end_to_end/single_errors/BUCK deleted file mode 100644 index a8f6e478b0..0000000000 --- a/test/test_projects/end_to_end/single_errors/BUCK +++ /dev/null @@ -1,14 +0,0 @@ -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/test_projects/eqwalizer_callers/.elp.toml b/test/test_projects/eqwalizer_callers/.elp.toml deleted file mode 100644 index 68476bcf2e..0000000000 --- a/test/test_projects/eqwalizer_callers/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//eqwalizer_callers/..." ] -source_root = "eqwalizer_callers" diff --git a/test/test_projects/eqwalizer_callers/BUCK b/test/test_projects/eqwalizer_callers/BUCK deleted file mode 100644 index 32012a0dc0..0000000000 --- a/test/test_projects/eqwalizer_callers/BUCK +++ /dev/null @@ -1,19 +0,0 @@ -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/test_projects/eqwalizer_tests/.elp.toml b/test/test_projects/eqwalizer_tests/.elp.toml deleted file mode 100644 index 5dac3aa6a6..0000000000 --- a/test/test_projects/eqwalizer_tests/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//eqwalizer_tests/..." ] -source_root = "eqwalizer_tests" - -[eqwalizer] -enable_all = true diff --git a/test/test_projects/eqwalizer_tests/BUCK b/test/test_projects/eqwalizer_tests/BUCK deleted file mode 100644 index 0817873cb0..0000000000 --- a/test/test_projects/eqwalizer_tests/BUCK +++ /dev/null @@ -1,77 +0,0 @@ -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/test_projects/hierarchical_config/.elp.toml b/test/test_projects/hierarchical_config/.elp.toml deleted file mode 100644 index e4d04eb6e0..0000000000 --- a/test/test_projects/hierarchical_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index 6cc536f399..0000000000 --- a/test/test_projects/hierarchical_config/.elp_lint.toml +++ /dev/null @@ -1,5 +0,0 @@ -enabled_lints = [] -disabled_lints = [ - "W0012", - "W0046" -] diff --git a/test/test_projects/hierarchical_config/BUCK b/test/test_projects/hierarchical_config/BUCK deleted file mode 100644 index bc78a42d57..0000000000 --- a/test/test_projects/hierarchical_config/BUCK +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 7445514e73..0000000000 --- a/test/test_projects/hierarchical_config/app_a/.elp_lint.toml +++ /dev/null @@ -1,4 +0,0 @@ -enabled_lints = [] -disabled_lints = [ - "W0002" -] 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 deleted file mode 100644 index 5b856d4f02..0000000000 --- a/test/test_projects/hierarchical_config/app_a/src/app_a.erl +++ /dev/null @@ -1,7 +0,0 @@ --module(app_a). - --define(MACRO_A, 1). --define(MACRO_B, 1). - -main() -> - ?MACRO_A. 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 deleted file mode 100644 index ad2453d0de..0000000000 --- a/test/test_projects/hierarchical_config/app_b/src/app_b.erl +++ /dev/null @@ -1,7 +0,0 @@ --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 deleted file mode 100644 index 2dcacbc5b3..0000000000 --- a/test/test_projects/hierarchical_config/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{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 deleted file mode 100644 index 70edcc3f03..0000000000 --- a/test/test_projects/in_place_tests/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index f4f354e7ee..0000000000 --- a/test/test_projects/in_place_tests/BUCK +++ /dev/null @@ -1,19 +0,0 @@ -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/test_projects/include_lib_dependency_test/.elp.toml b/test/test_projects/include_lib_dependency_test/.elp.toml deleted file mode 100644 index 9d1c859339..0000000000 --- a/test/test_projects/include_lib_dependency_test/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index 54b81c47b5..0000000000 --- a/test/test_projects/include_lib_dependency_test/BUCK +++ /dev/null @@ -1,43 +0,0 @@ -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/test_projects/linter/.elp.toml b/test/test_projects/linter/.elp.toml deleted file mode 100644 index 4820625b2d..0000000000 --- a/test/test_projects/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//linter/..." ] -source_root = "linter" diff --git a/test/test_projects/linter/BUCK b/test/test_projects/linter/BUCK deleted file mode 100644 index 2db8750582..0000000000 --- a/test/test_projects/linter/BUCK +++ /dev/null @@ -1,41 +0,0 @@ -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/test_projects/linter/app_a/src/app_a_ssr.erl b/test/test_projects/linter/app_a/src/app_a_ssr.erl deleted file mode 100644 index a7f6970376..0000000000 --- a/test/test_projects/linter/app_a/src/app_a_ssr.erl +++ /dev/null @@ -1,11 +0,0 @@ --module(app_a_ssr). - --export([bar/1, bar2/0]). - -%% Used for testing visible parens matching -bar(X) -> - X = ((3)), - X = (4). - --define(BAR(X), {X}). -bar2() -> ?BAR(4). diff --git a/test/test_projects/linter/elp_lint_ssr_adhoc.toml b/test/test_projects/linter/elp_lint_ssr_adhoc.toml deleted file mode 100644 index 2aa4491ab2..0000000000 --- a/test/test_projects/linter/elp_lint_ssr_adhoc.toml +++ /dev/null @@ -1,5 +0,0 @@ -enabled_lints = ["ad-hoc: ssr-match"] - -[[ad_hoc_lints.lints]] -type = "LintMatchSsr" -ssr_pattern = "ssr: {_@A, _@B}." diff --git a/test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml b/test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml deleted file mode 100644 index c4bee7057f..0000000000 --- a/test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml +++ /dev/null @@ -1,5 +0,0 @@ -enabled_lints = ["ad-hoc: ssr-match"] - -[[ad_hoc_lints.lints]] -type = "LintMatchSsr" -ssr_pattern = "ssr: {_@A, = 10}." diff --git a/test/test_projects/linter_bad_config/.elp.toml b/test/test_projects/linter_bad_config/.elp.toml deleted file mode 100644 index 4820625b2d..0000000000 --- a/test/test_projects/linter_bad_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//linter/..." ] -source_root = "linter" diff --git a/test/test_projects/linter_bad_config/BUCK b/test/test_projects/linter_bad_config/BUCK deleted file mode 100644 index a0817a6ad6..0000000000 --- a/test/test_projects/linter_bad_config/BUCK +++ /dev/null @@ -1,12 +0,0 @@ -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/test_projects/linter_bad_config/linter/.elp.toml b/test/test_projects/linter_bad_config/linter/.elp.toml deleted file mode 100644 index 4820625b2d..0000000000 --- a/test/test_projects/linter_bad_config/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//linter/..." ] -source_root = "linter" diff --git a/test/test_projects/linter_bad_config/linter/BUCK b/test/test_projects/linter_bad_config/linter/BUCK deleted file mode 100644 index 2db8750582..0000000000 --- a/test/test_projects/linter_bad_config/linter/BUCK +++ /dev/null @@ -1,41 +0,0 @@ -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/test_projects/linter_config/.elp.toml b/test/test_projects/linter_config/.elp.toml deleted file mode 100644 index c490cc155e..0000000000 --- a/test/test_projects/linter_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index 47886dd507..0000000000 --- a/test/test_projects/linter_config/.elp_lint.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index c1101daea2..0000000000 --- a/test/test_projects/linter_config/BUCK +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index 1381435a46..0000000000 --- a/test/test_projects/linter_config/app_a/src/app_a.app.src +++ /dev/null @@ -1,3 +0,0 @@ -{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 deleted file mode 100644 index c374c0b518..0000000000 --- a/test/test_projects/linter_config/app_a/src/app_a.erl +++ /dev/null @@ -1,4 +0,0 @@ --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 deleted file mode 100644 index 4112b4129f..0000000000 --- a/test/test_projects/linter_config/app_b/src/app_b.app.src +++ /dev/null @@ -1,3 +0,0 @@ -{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 deleted file mode 100644 index ca7ecf985b..0000000000 --- a/test/test_projects/linter_config/app_b/src/app_b.erl +++ /dev/null @@ -1,4 +0,0 @@ --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 deleted file mode 100644 index c278bbce23..0000000000 --- a/test/test_projects/linter_config/app_c/src/app_c.app.src +++ /dev/null @@ -1,3 +0,0 @@ -{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 deleted file mode 100644 index 57573554b6..0000000000 --- a/test/test_projects/linter_config/app_c/src/app_c.erl +++ /dev/null @@ -1,4 +0,0 @@ --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 deleted file mode 100644 index d508bd24de..0000000000 --- a/test/test_projects/linter_config/rebar.config +++ /dev/null @@ -1,9 +0,0 @@ -{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 deleted file mode 100644 index 1a06a42f0d..0000000000 --- a/test/test_projects/parse_error/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//parse_error/..." ] -source_root = "parse_error" - -[eqwalizer] -enable_all = false diff --git a/test/test_projects/parse_error/BUCK b/test/test_projects/parse_error/BUCK deleted file mode 100644 index ed65c267d0..0000000000 --- a/test/test_projects/parse_error/BUCK +++ /dev/null @@ -1,9 +0,0 @@ -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/test_projects/standard/.elp.toml b/test/test_projects/standard/.elp.toml deleted file mode 100644 index f5e2ac1943..0000000000 --- a/test/test_projects/standard/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "root//standard/..." ] -source_root = "standard" diff --git a/test/test_projects/standard/BUCK b/test/test_projects/standard/BUCK deleted file mode 100644 index 3f1e9ee947..0000000000 --- a/test/test_projects/standard/BUCK +++ /dev/null @@ -1,49 +0,0 @@ -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/src/app_a.app.src b/test/test_projects/standard/app_a/src/app_a.app.src deleted file mode 100644 index 1381435a46..0000000000 --- a/test/test_projects/standard/app_a/src/app_a.app.src +++ /dev/null @@ -1,3 +0,0 @@ -{application, app_a, - [{description, "example app A"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] -}. 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 deleted file mode 100644 index 4112b4129f..0000000000 --- a/test/test_projects/standard/app_b/src/app_b.app.src +++ /dev/null @@ -1,3 +0,0 @@ -{application, app_b, - [{description, "example app B"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] -}. diff --git a/test/test_projects/toolchains/BUCK b/test/test_projects/toolchains/BUCK deleted file mode 100644 index 770ac61f67..0000000000 --- a/test/test_projects/toolchains/BUCK +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 87c2f17334..0000000000 --- a/test/test_projects/xref/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index bf44968f02..0000000000 --- a/test/test_projects/xref/BUCK +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index ddf4fc00d2..0000000000 --- a/test/test_projects/xref/app_a/src/unavailable_type.erl +++ /dev/null @@ -1,12 +0,0 @@ --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 deleted file mode 100644 index d56609b179..0000000000 --- a/test/test_projects/xref/app_b/src/app_b.erl +++ /dev/null @@ -1,8 +0,0 @@ --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 deleted file mode 100644 index 040f77f26f..0000000000 --- a/test/test_projects/xref/app_c/src/app_c.erl +++ /dev/null @@ -1,8 +0,0 @@ --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 deleted file mode 100644 index 629a5228bf..0000000000 --- a/test/test_projects/xref/elp_lint_unavailable_type.toml +++ /dev/null @@ -1,2 +0,0 @@ -[linters.unavailable_type] -enabled = true diff --git a/test/test_projects/.gitignore b/test_projects/.gitignore similarity index 85% rename from test/test_projects/.gitignore rename to test_projects/.gitignore index 2672c17cc4..b34578734c 100644 --- a/test/test_projects/.gitignore +++ b/test_projects/.gitignore @@ -3,4 +3,3 @@ *wa.build_info *_build/ *rebar.lock -buck-out/ diff --git a/test/test_projects/README.md b/test_projects/README.md similarity index 100% rename from test/test_projects/README.md rename to test_projects/README.md diff --git a/test_projects/buck_bad_config/.elp.toml b/test_projects/buck_bad_config/.elp.toml new file mode 100644 index 0000000000..a891a3dfc5 --- /dev/null +++ b/test_projects/buck_bad_config/.elp.toml @@ -0,0 +1,8 @@ +[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/test_projects/buck_bad_config/src/bad_app.erl b/test_projects/buck_bad_config/src/bad_app.erl similarity index 100% rename from test/test_projects/buck_bad_config/src/bad_app.erl rename to test_projects/buck_bad_config/src/bad_app.erl diff --git a/test_projects/buck_tests/.elp.toml b/test_projects/buck_tests/.elp.toml new file mode 100644 index 0000000000..acb0799975 --- /dev/null +++ b/test_projects/buck_tests/.elp.toml @@ -0,0 +1,9 @@ +[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/test_projects/buck_tests/TARGETS.v2_ b/test_projects/buck_tests/TARGETS.v2_ similarity index 100% rename from test/test_projects/buck_tests/TARGETS.v2_ rename to test_projects/buck_tests/TARGETS.v2_ diff --git a/test/test_projects/buck_tests/test_elp/TARGETS.v2_ b/test_projects/buck_tests/test_elp/TARGETS.v2_ similarity index 77% rename from test/test_projects/buck_tests/test_elp/TARGETS.v2_ rename to test_projects/buck_tests/test_elp/TARGETS.v2_ index 7e5bd95db5..83089813da 100644 --- a/test/test_projects/buck_tests/test_elp/TARGETS.v2_ +++ b/test_projects/buck_tests/test_elp/TARGETS.v2_ @@ -7,11 +7,11 @@ erlang_application( ]), app_src = "src/test_elp.app.src", applications = [ - "//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", + "//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", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test/test_projects/buck_tests/test_elp/include/test_elp.hrl b/test_projects/buck_tests/test_elp/include/test_elp.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp/include/test_elp.hrl rename to test_projects/buck_tests/test_elp/include/test_elp.hrl diff --git a/test/test_projects/buck_tests/test_elp/src/test_elp.app.src b/test_projects/buck_tests/test_elp/src/test_elp.app.src similarity index 100% rename from test/test_projects/buck_tests/test_elp/src/test_elp.app.src rename to test_projects/buck_tests/test_elp/src/test_elp.app.src diff --git a/test/test_projects/buck_tests/test_elp/src/test_elp.erl b/test_projects/buck_tests/test_elp/src/test_elp.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp/src/test_elp.erl rename to test_projects/buck_tests/test_elp/src/test_elp.erl diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl b/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl rename to test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json b/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json rename to test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json b/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json rename to test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl b/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl rename to test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl b/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl rename to test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl b/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl rename to test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl diff --git a/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl b/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl rename to test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl diff --git a/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ b/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ similarity index 62% rename from test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ rename to test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ index a752b99087..d0fea8149b 100644 --- a/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ +++ b/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ @@ -5,10 +5,10 @@ erlang_application( "src/*.hrl", ]), applications = [ - "//buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", + "//whatsapp/elp/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", ], extra_includes = [ - "//buck_tests/test_elp:test_elp", + "//whatsapp/elp/test_projects/buck_tests/test_elp:test_elp", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl b/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl rename to test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl diff --git a/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl b/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl rename to test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl diff --git a/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl b/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl rename to test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl diff --git a/test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ b/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ similarity index 100% rename from test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ rename to test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ diff --git a/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl b/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl rename to test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl diff --git a/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl b/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl rename to test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl diff --git a/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl b/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl rename to test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl diff --git a/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl b/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl rename to test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl diff --git a/test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl b/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl rename to test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl diff --git a/test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl b/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl rename to test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl diff --git a/test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl b/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl rename to test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl diff --git a/test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl b/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl rename to test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl diff --git a/test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ b/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ similarity index 100% rename from test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ rename to test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ diff --git a/test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl b/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl rename to test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl diff --git a/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl b/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl similarity index 100% rename from test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl rename to test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl diff --git a/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl b/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl similarity index 100% rename from test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl rename to test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl diff --git a/test_projects/buck_tests_2/.elp.toml b/test_projects/buck_tests_2/.elp.toml new file mode 100644 index 0000000000..4ea681809f --- /dev/null +++ b/test_projects/buck_tests_2/.elp.toml @@ -0,0 +1,9 @@ +[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/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl b/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl similarity index 100% rename from test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl rename to test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl diff --git a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl b/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl similarity index 100% rename from test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl rename to test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl diff --git a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl b/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl similarity index 100% rename from test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl rename to test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl diff --git a/test/test_projects/buck_tests_2/generated/out/generated_header.hrl b/test_projects/buck_tests_2/generated/out/generated_header.hrl similarity index 100% rename from test/test_projects/buck_tests_2/generated/out/generated_header.hrl rename to test_projects/buck_tests_2/generated/out/generated_header.hrl diff --git a/test/test_projects/buck_tests_2/util/app_a/include/junk.hrl b/test_projects/buck_tests_2/util/app_a/include/junk.hrl similarity index 100% rename from test/test_projects/buck_tests_2/util/app_a/include/junk.hrl rename to test_projects/buck_tests_2/util/app_a/include/junk.hrl diff --git a/test/test_projects/buck_tests_2/util/app_a/src/app_a.erl b/test_projects/buck_tests_2/util/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/buck_tests_2/util/app_a/src/app_a.erl rename to test_projects/buck_tests_2/util/app_a/src/app_a.erl diff --git a/test/test_projects/custom_build_tool/.elp.toml b/test_projects/custom_build_tool/.elp.toml similarity index 100% rename from test/test_projects/custom_build_tool/.elp.toml rename to test_projects/custom_build_tool/.elp.toml diff --git a/test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl b/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl rename to test_projects/custom_build_tool/apps/app_a/include/app_a.hrl diff --git a/test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl b/test_projects/custom_build_tool/apps/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl rename to test_projects/custom_build_tool/apps/app_a/src/app_a.erl diff --git a/test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl b/test_projects/custom_build_tool/apps/app_b/src/app_b.erl similarity index 100% rename from test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl rename to test_projects/custom_build_tool/apps/app_b/src/app_b.erl diff --git a/test_projects/diagnostics/.elp.toml b/test_projects/diagnostics/.elp.toml new file mode 100644 index 0000000000..e5ee70c992 --- /dev/null +++ b/test_projects/diagnostics/.elp.toml @@ -0,0 +1,8 @@ +[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/test_projects/diagnostics/README.md b/test_projects/diagnostics/README.md similarity index 100% rename from test/test_projects/diagnostics/README.md rename to test_projects/diagnostics/README.md diff --git a/test/test_projects/diagnostics/app_a/extra/app_a.erl b/test_projects/diagnostics/app_a/extra/app_a.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/extra/app_a.erl rename to test_projects/diagnostics/app_a/extra/app_a.erl diff --git a/test/test_projects/diagnostics/app_a/include/app_a.hrl b/test_projects/diagnostics/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/diagnostics/app_a/include/app_a.hrl rename to test_projects/diagnostics/app_a/include/app_a.hrl diff --git a/test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl b/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl rename to test_projects/diagnostics/app_a/include/broken_diagnostics.hrl diff --git a/test/test_projects/diagnostics/app_a/include/diagnostics.hrl b/test_projects/diagnostics/app_a/include/diagnostics.hrl similarity index 100% rename from test/test_projects/diagnostics/app_a/include/diagnostics.hrl rename to test_projects/diagnostics/app_a/include/diagnostics.hrl diff --git a/test/test_projects/diagnostics/app_a/src/app_a.app.src b/test_projects/diagnostics/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/diagnostics/app_a/src/app_a.app.src rename to test_projects/diagnostics/app_a/src/app_a.app.src diff --git a/test/test_projects/diagnostics/app_a/src/app_a.erl b/test_projects/diagnostics/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/app_a.erl rename to test_projects/diagnostics/app_a/src/app_a.erl diff --git a/test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl b/test_projects/diagnostics/app_a/src/broken_parse_trans.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl rename to test_projects/diagnostics/app_a/src/broken_parse_trans.erl diff --git a/test/test_projects/diagnostics/app_a/src/cascading.erl b/test_projects/diagnostics/app_a/src/cascading.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/cascading.erl rename to test_projects/diagnostics/app_a/src/cascading.erl diff --git a/test/test_projects/diagnostics/app_a/src/crlf.erl b/test_projects/diagnostics/app_a/src/crlf.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/crlf.erl rename to test_projects/diagnostics/app_a/src/crlf.erl diff --git a/test/test_projects/diagnostics/app_a/src/diagnostics.erl b/test_projects/diagnostics/app_a/src/diagnostics.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/diagnostics.erl rename to test_projects/diagnostics/app_a/src/diagnostics.erl diff --git a/test/test_projects/diagnostics/app_a/src/diagnostics.escript b/test_projects/diagnostics/app_a/src/diagnostics.escript similarity index 100% rename from test/test_projects/diagnostics/app_a/src/diagnostics.escript rename to test_projects/diagnostics/app_a/src/diagnostics.escript diff --git a/test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript b/test_projects/diagnostics/app_a/src/diagnostics_errors.escript similarity index 100% rename from test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript rename to test_projects/diagnostics/app_a/src/diagnostics_errors.escript diff --git a/test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript b/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript similarity index 100% rename from test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript rename to test_projects/diagnostics/app_a/src/diagnostics_warnings.escript diff --git a/test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl b/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl rename to test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl diff --git a/test/test_projects/diagnostics/app_a/src/file_attribute.erl b/test_projects/diagnostics/app_a/src/file_attribute.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/file_attribute.erl rename to test_projects/diagnostics/app_a/src/file_attribute.erl diff --git a/test/test_projects/diagnostics/app_a/src/lint_recursive.erl b/test_projects/diagnostics/app_a/src/lint_recursive.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/lint_recursive.erl rename to test_projects/diagnostics/app_a/src/lint_recursive.erl diff --git a/test/test_projects/diagnostics/app_a/src/lints.erl b/test_projects/diagnostics/app_a/src/lints.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/lints.erl rename to test_projects/diagnostics/app_a/src/lints.erl diff --git a/test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl b/test_projects/diagnostics/app_a/src/otp27_docstrings.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl rename to test_projects/diagnostics/app_a/src/otp27_docstrings.erl diff --git a/test/test_projects/diagnostics/app_a/src/otp27_sigils.erl b/test_projects/diagnostics/app_a/src/otp27_sigils.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/otp27_sigils.erl rename to test_projects/diagnostics/app_a/src/otp27_sigils.erl diff --git a/test/test_projects/diagnostics/app_a/src/otp_7655.erl b/test_projects/diagnostics/app_a/src/otp_7655.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/otp_7655.erl rename to test_projects/diagnostics/app_a/src/otp_7655.erl diff --git a/test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl b/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl rename to test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl diff --git a/test/test_projects/diagnostics/app_a/src/suppressed.erl b/test_projects/diagnostics/app_a/src/suppressed.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/suppressed.erl rename to test_projects/diagnostics/app_a/src/suppressed.erl diff --git a/test/test_projects/diagnostics/app_a/src/syntax.erl b/test_projects/diagnostics/app_a/src/syntax.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/src/syntax.erl rename to test_projects/diagnostics/app_a/src/syntax.erl diff --git a/test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl b/test_projects/diagnostics/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl rename to test_projects/diagnostics/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/diagnostics/erlang_ls.config b/test_projects/diagnostics/erlang_ls.config similarity index 100% rename from test/test_projects/diagnostics/erlang_ls.config rename to test_projects/diagnostics/erlang_ls.config diff --git a/test/test_projects/diagnostics/rebar.config b/test_projects/diagnostics/rebar.config similarity index 100% rename from test/test_projects/diagnostics/rebar.config rename to test_projects/diagnostics/rebar.config diff --git a/test/test_projects/diagnostics/test_build_info.json b/test_projects/diagnostics/test_build_info.json similarity index 100% rename from test/test_projects/diagnostics/test_build_info.json rename to test_projects/diagnostics/test_build_info.json diff --git a/test/test_projects/end_to_end/.elp.toml b/test_projects/end_to_end/.elp.toml similarity index 50% rename from test/test_projects/end_to_end/.elp.toml rename to test_projects/end_to_end/.elp.toml index acbcd6146f..f015956e45 100644 --- a/test/test_projects/end_to_end/.elp.toml +++ b/test_projects/end_to_end/.elp.toml @@ -1,7 +1,7 @@ [buck] enabled = true build_deps = false -included_targets = ["root//end_to_end/..."] +included_targets = ["fbcode//whatsapp/elp/test_projects/end_to_end/..."] [eqwalizer] enable_all = false diff --git a/test/test_projects/end_to_end/assist_examples/src/add_doc.erl b/test_projects/end_to_end/assist_examples/src/add_doc.erl similarity index 100% rename from test/test_projects/end_to_end/assist_examples/src/add_doc.erl rename to test_projects/end_to_end/assist_examples/src/add_doc.erl diff --git a/test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src b/test_projects/end_to_end/assist_examples/src/assist_examples.app.src similarity index 100% rename from test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src rename to test_projects/end_to_end/assist_examples/src/assist_examples.app.src diff --git a/test/test_projects/end_to_end/assist_examples/src/code_completion.erl b/test_projects/end_to_end/assist_examples/src/code_completion.erl similarity index 100% rename from test/test_projects/end_to_end/assist_examples/src/code_completion.erl rename to test_projects/end_to_end/assist_examples/src/code_completion.erl diff --git a/test/test_projects/end_to_end/assist_examples/src/extract_function.erl b/test_projects/end_to_end/assist_examples/src/extract_function.erl similarity index 100% rename from test/test_projects/end_to_end/assist_examples/src/extract_function.erl rename to test_projects/end_to_end/assist_examples/src/extract_function.erl diff --git a/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl b/test_projects/end_to_end/assist_examples/src/head_mismatch.erl similarity index 100% rename from test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl rename to test_projects/end_to_end/assist_examples/src/head_mismatch.erl diff --git a/test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl b/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl similarity index 100% rename from test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl rename to test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl diff --git a/test/test_projects/end_to_end/definitions/README.md b/test_projects/end_to_end/definitions/README.md similarity index 100% rename from test/test_projects/end_to_end/definitions/README.md rename to test_projects/end_to_end/definitions/README.md diff --git a/test/test_projects/end_to_end/definitions/src/definitions.app.src b/test_projects/end_to_end/definitions/src/definitions.app.src similarity index 100% rename from test/test_projects/end_to_end/definitions/src/definitions.app.src rename to test_projects/end_to_end/definitions/src/definitions.app.src diff --git a/test/test_projects/end_to_end/definitions/src/local_def.erl b/test_projects/end_to_end/definitions/src/local_def.erl similarity index 100% rename from test/test_projects/end_to_end/definitions/src/local_def.erl rename to test_projects/end_to_end/definitions/src/local_def.erl diff --git a/test/test_projects/end_to_end/docs/src/docs.app.src b/test_projects/end_to_end/docs/src/docs.app.src similarity index 100% rename from test/test_projects/end_to_end/docs/src/docs.app.src rename to test_projects/end_to_end/docs/src/docs.app.src diff --git a/test/test_projects/end_to_end/docs/src/docs.erl b/test_projects/end_to_end/docs/src/docs.erl similarity index 100% rename from test/test_projects/end_to_end/docs/src/docs.erl rename to test_projects/end_to_end/docs/src/docs.erl diff --git a/test/test_projects/end_to_end/erlang_ls.config b/test_projects/end_to_end/erlang_ls.config similarity index 100% rename from test/test_projects/end_to_end/erlang_ls.config rename to test_projects/end_to_end/erlang_ls.config diff --git a/test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt b/test_projects/end_to_end/fixtures/erlang-stacktrace.txt similarity index 100% rename from test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt rename to test_projects/end_to_end/fixtures/erlang-stacktrace.txt diff --git a/test/test_projects/end_to_end/hover/README.md b/test_projects/end_to_end/hover/README.md similarity index 100% rename from test/test_projects/end_to_end/hover/README.md rename to test_projects/end_to_end/hover/README.md diff --git a/test/test_projects/end_to_end/hover/src/doc_examples.erl b/test_projects/end_to_end/hover/src/doc_examples.erl similarity index 100% rename from test/test_projects/end_to_end/hover/src/doc_examples.erl rename to test_projects/end_to_end/hover/src/doc_examples.erl diff --git a/test/test_projects/end_to_end/hover/src/hover.app.src b/test_projects/end_to_end/hover/src/hover.app.src similarity index 100% rename from test/test_projects/end_to_end/hover/src/hover.app.src rename to test_projects/end_to_end/hover/src/hover.app.src diff --git a/test/test_projects/end_to_end/rebar.config b/test_projects/end_to_end/rebar.config similarity index 100% rename from test/test_projects/end_to_end/rebar.config rename to test_projects/end_to_end/rebar.config diff --git a/test/test_projects/end_to_end/single_errors/README.md b/test_projects/end_to_end/single_errors/README.md similarity index 100% rename from test/test_projects/end_to_end/single_errors/README.md rename to test_projects/end_to_end/single_errors/README.md diff --git a/test/test_projects/end_to_end/single_errors/src/as_you_type.erl b/test_projects/end_to_end/single_errors/src/as_you_type.erl similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/as_you_type.erl rename to test_projects/end_to_end/single_errors/src/as_you_type.erl diff --git a/test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl b/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl rename to test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl diff --git a/test/test_projects/end_to_end/single_errors/src/single_errors.app.src b/test_projects/end_to_end/single_errors/src/single_errors.app.src similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/single_errors.app.src rename to test_projects/end_to_end/single_errors/src/single_errors.app.src diff --git a/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl b/test_projects/end_to_end/single_errors/src/spec_mismatch.erl similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl rename to test_projects/end_to_end/single_errors/src/spec_mismatch.erl diff --git a/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 b/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 rename to test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 diff --git a/test/test_projects/end_to_end/single_errors/src/types_on_hover.erl b/test_projects/end_to_end/single_errors/src/types_on_hover.erl similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/types_on_hover.erl rename to test_projects/end_to_end/single_errors/src/types_on_hover.erl diff --git a/test/test_projects/end_to_end/single_errors/src/unused_macro.erl b/test_projects/end_to_end/single_errors/src/unused_macro.erl similarity index 100% rename from test/test_projects/end_to_end/single_errors/src/unused_macro.erl rename to test_projects/end_to_end/single_errors/src/unused_macro.erl diff --git a/test/test_projects/eqwalizer/src/eqwalizer.app.src b/test_projects/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test/test_projects/eqwalizer/src/eqwalizer.app.src rename to test_projects/eqwalizer/src/eqwalizer.app.src diff --git a/test/test_projects/eqwalizer/src/eqwalizer_specs.erl b/test_projects/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test/test_projects/eqwalizer/src/eqwalizer_specs.erl rename to test_projects/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/eqwalizer_callers/.elp.toml b/test_projects/eqwalizer_callers/.elp.toml new file mode 100644 index 0000000000..f4b7f6f6ac --- /dev/null +++ b/test_projects/eqwalizer_callers/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/eqwalizer_callers/.gitignore b/test_projects/eqwalizer_callers/.gitignore similarity index 100% rename from test/test_projects/eqwalizer_callers/.gitignore rename to test_projects/eqwalizer_callers/.gitignore diff --git a/test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl b/test_projects/eqwalizer_callers/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl rename to test_projects/eqwalizer_callers/app_a/include/app_a.hrl diff --git a/test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src b/test_projects/eqwalizer_callers/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src rename to test_projects/eqwalizer_callers/app_a/src/app_a.app.src diff --git a/test/test_projects/eqwalizer_callers/app_a/src/app_a.erl b/test_projects/eqwalizer_callers/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/eqwalizer_callers/app_a/src/app_a.erl rename to test_projects/eqwalizer_callers/app_a/src/app_a.erl diff --git a/test/test_projects/eqwalizer_callers/app_a/src/app_b.erl b/test_projects/eqwalizer_callers/app_a/src/app_b.erl similarity index 100% rename from test/test_projects/eqwalizer_callers/app_a/src/app_b.erl rename to test_projects/eqwalizer_callers/app_a/src/app_b.erl diff --git a/test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl b/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl rename to test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/eqwalizer_callers/rebar.config b/test_projects/eqwalizer_callers/rebar.config similarity index 100% rename from test/test_projects/eqwalizer_callers/rebar.config rename to test_projects/eqwalizer_callers/rebar.config diff --git a/test/test_projects/eqwalizer_ignore_modules/.elp.toml b/test_projects/eqwalizer_ignore_modules/.elp.toml similarity index 100% rename from test/test_projects/eqwalizer_ignore_modules/.elp.toml rename to test_projects/eqwalizer_ignore_modules/.elp.toml diff --git a/test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl b/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl rename to test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl diff --git a/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl b/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl similarity index 100% rename from test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl rename to test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl diff --git a/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl b/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl rename to test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl diff --git a/test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl b/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl similarity index 100% rename from test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl rename to test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl diff --git a/test_projects/eqwalizer_tests/.elp.toml b/test_projects/eqwalizer_tests/.elp.toml new file mode 100644 index 0000000000..ed5c60502f --- /dev/null +++ b/test_projects/eqwalizer_tests/.elp.toml @@ -0,0 +1,8 @@ +[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/test_projects/eqwalizer_tests/.gitignore b/test_projects/eqwalizer_tests/.gitignore similarity index 100% rename from test/test_projects/eqwalizer_tests/.gitignore rename to test_projects/eqwalizer_tests/.gitignore diff --git a/test/test_projects/eqwalizer_tests/.rebar.root b/test_projects/eqwalizer_tests/.rebar.root similarity index 100% rename from test/test_projects/eqwalizer_tests/.rebar.root rename to test_projects/eqwalizer_tests/.rebar.root diff --git a/test/test_projects/eqwalizer_tests/check/include/my_header.hrl b/test_projects/eqwalizer_tests/check/include/my_header.hrl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/include/my_header.hrl rename to test_projects/eqwalizer_tests/check/include/my_header.hrl diff --git a/test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl b/test_projects/eqwalizer_tests/check/src/any_fun_type.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl rename to test_projects/eqwalizer_tests/check/src/any_fun_type.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/apply_none.erl b/test_projects/eqwalizer_tests/check/src/apply_none.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/apply_none.erl rename to test_projects/eqwalizer_tests/check/src/apply_none.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/approx.erl b/test_projects/eqwalizer_tests/check/src/approx.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/approx.erl rename to test_projects/eqwalizer_tests/check/src/approx.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/as_pat.erl b/test_projects/eqwalizer_tests/check/src/as_pat.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/as_pat.erl rename to test_projects/eqwalizer_tests/check/src/as_pat.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/auto_imports.erl b/test_projects/eqwalizer_tests/check/src/auto_imports.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/auto_imports.erl rename to test_projects/eqwalizer_tests/check/src/auto_imports.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/behave.erl b/test_projects/eqwalizer_tests/check/src/behave.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/behave.erl rename to test_projects/eqwalizer_tests/check/src/behave.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/binaries.erl b/test_projects/eqwalizer_tests/check/src/binaries.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/binaries.erl rename to test_projects/eqwalizer_tests/check/src/binaries.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/booleans.erl b/test_projects/eqwalizer_tests/check/src/booleans.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/booleans.erl rename to test_projects/eqwalizer_tests/check/src/booleans.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl b/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl rename to test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl similarity index 70% rename from test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl rename to test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl index 854feec923..a8495b8755 100644 --- a/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl +++ b/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl @@ -3,12 +3,11 @@ %%% 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). % @OTP27Only +-module(callbacks3_neg). -export([ init/1, handle_call/3, - handle_cast/2, - handle_info/2 + handle_cast/2 ]). -behavior(gen_server). @@ -20,7 +19,3 @@ 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/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl b/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl rename to test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl b/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl rename to test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl b/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl rename to test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl b/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl rename to test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/case_predicates.erl b/test_projects/eqwalizer_tests/check/src/case_predicates.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/case_predicates.erl rename to test_projects/eqwalizer_tests/check/src/case_predicates.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/check.app.src b/test_projects/eqwalizer_tests/check/src/check.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/check.app.src rename to test_projects/eqwalizer_tests/check/src/check.app.src diff --git a/test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl b/test_projects/eqwalizer_tests/check/src/compiler_macro.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl rename to test_projects/eqwalizer_tests/check/src/compiler_macro.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/complex_maps.erl b/test_projects/eqwalizer_tests/check/src/complex_maps.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/complex_maps.erl rename to test_projects/eqwalizer_tests/check/src/complex_maps.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/comprehensions.erl b/test_projects/eqwalizer_tests/check/src/comprehensions.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/comprehensions.erl rename to test_projects/eqwalizer_tests/check/src/comprehensions.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/contravariant.erl b/test_projects/eqwalizer_tests/check/src/contravariant.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/contravariant.erl rename to test_projects/eqwalizer_tests/check/src/contravariant.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/custom.erl b/test_projects/eqwalizer_tests/check/src/custom.erl similarity index 99% rename from test/test_projects/eqwalizer_tests/check/src/custom.erl rename to test_projects/eqwalizer_tests/check/src/custom.erl index b6910b0fc7..77aef06bce 100644 --- a/test/test_projects/eqwalizer_tests/check/src/custom.erl +++ b/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). % @OTP27Only +-module(custom). -import(maps, [get/2, get/3]). -compile([export_all, nowarn_export_all]). @@ -2713,13 +2713,3 @@ maps_foreach_neg(M) -> end, M ). - --spec maps_map_2_13( - #{ka => va, kb => vb, kc => vc} -) -> - #{ka => va2, kb => vb, kc => vc}. -maps_map_2_13(M) -> - maps:map(fun - (ka, va) -> va2; - (_K, V) -> V - end, M). diff --git a/test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl b/test_projects/eqwalizer_tests/check/src/detached_specs1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl rename to test_projects/eqwalizer_tests/check/src/detached_specs1.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl b/test_projects/eqwalizer_tests/check/src/detached_specs2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl rename to test_projects/eqwalizer_tests/check/src/detached_specs2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl b/test_projects/eqwalizer_tests/check/src/dyn_calls.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl rename to test_projects/eqwalizer_tests/check/src/dyn_calls.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl b/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl rename to test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl b/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl b/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl b/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_calls.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl b/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_catch.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl b/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_fun.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl b/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_generics.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl b/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl b/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_receive.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl b/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_refine.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl b/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl rename to test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/elab_clause.erl b/test_projects/eqwalizer_tests/check/src/elab_clause.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/elab_clause.erl rename to test_projects/eqwalizer_tests/check/src/elab_clause.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/error_messages.erl b/test_projects/eqwalizer_tests/check/src/error_messages.erl similarity index 64% rename from test/test_projects/eqwalizer_tests/check/src/error_messages.erl rename to test_projects/eqwalizer_tests/check/src/error_messages.erl index 73f64ae814..edeab66990 100644 --- a/test/test_projects/eqwalizer_tests/check/src/error_messages.erl +++ b/test_projects/eqwalizer_tests/check/src/error_messages.erl @@ -31,11 +31,3 @@ no_record_conversion_2(Foo) -> Foo. -spec record_conversion(#foo{}) -> {foo, binary(), atom()}. record_conversion(Foo) -> Foo. - --spec no_good_map_candidate_1(#{foo => #{large_map_key_a => large_map_val_a, large_map_key_b => large_map_val_b}}) - -> #{foo => #{large_map_key_c => large_map_val_c} | #{large_map_key_d => large_map_val_d}}. -no_good_map_candidate_1(M) -> M. - --spec no_good_map_candidate_2(#{foo => #{large_map_key_a => large_map_val_a, large_map_key_b => large_map_val_b}}) - -> #{foo => #{large_map_key_c => large_map_val_c, large_map_key_d => large_map_val_d} | any}. -no_good_map_candidate_2(M) -> M. diff --git a/test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl b/test_projects/eqwalizer_tests/check/src/fancy_generics.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl rename to test_projects/eqwalizer_tests/check/src/fancy_generics.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/format.erl b/test_projects/eqwalizer_tests/check/src/format.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/format.erl rename to test_projects/eqwalizer_tests/check/src/format.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/fun_stats.erl b/test_projects/eqwalizer_tests/check/src/fun_stats.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/fun_stats.erl rename to test_projects/eqwalizer_tests/check/src/fun_stats.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl b/test_projects/eqwalizer_tests/check/src/fun_stats2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl rename to test_projects/eqwalizer_tests/check/src/fun_stats2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/funs.erl b/test_projects/eqwalizer_tests/check/src/funs.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/funs.erl rename to test_projects/eqwalizer_tests/check/src/funs.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/funs2.erl b/test_projects/eqwalizer_tests/check/src/funs2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/funs2.erl rename to test_projects/eqwalizer_tests/check/src/funs2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl b/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl rename to test_projects/eqwalizer_tests/check/src/funs_uncommon.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl b/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl rename to test_projects/eqwalizer_tests/check/src/generic_fun_application.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl b/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl rename to test_projects/eqwalizer_tests/check/src/generics_with_unions.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl b/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl rename to test_projects/eqwalizer_tests/check/src/gradual_bounded.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl b/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl rename to test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl b/test_projects/eqwalizer_tests/check/src/gradual_custom.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl rename to test_projects/eqwalizer_tests/check/src/gradual_custom.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl b/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl rename to test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl b/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl rename to test_projects/eqwalizer_tests/check/src/gradual_maybe.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl b/test_projects/eqwalizer_tests/check/src/gradual_misc.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl rename to test_projects/eqwalizer_tests/check/src/gradual_misc.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl b/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl rename to test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl b/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl rename to test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl b/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl rename to test_projects/eqwalizer_tests/check/src/gradual_untyped.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl b/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl rename to test_projects/eqwalizer_tests/check/src/guard_b_connections.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/guards.erl b/test_projects/eqwalizer_tests/check/src/guards.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/guards.erl rename to test_projects/eqwalizer_tests/check/src/guards.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/guards_logic.erl b/test_projects/eqwalizer_tests/check/src/guards_logic.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/guards_logic.erl rename to test_projects/eqwalizer_tests/check/src/guards_logic.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/guards_simple.erl b/test_projects/eqwalizer_tests/check/src/guards_simple.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/guards_simple.erl rename to test_projects/eqwalizer_tests/check/src/guards_simple.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/hints.erl b/test_projects/eqwalizer_tests/check/src/hints.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/hints.erl rename to test_projects/eqwalizer_tests/check/src/hints.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/index1.erl b/test_projects/eqwalizer_tests/check/src/index1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/index1.erl rename to test_projects/eqwalizer_tests/check/src/index1.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/index2.erl b/test_projects/eqwalizer_tests/check/src/index2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/index2.erl rename to test_projects/eqwalizer_tests/check/src/index2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/iolists.erl b/test_projects/eqwalizer_tests/check/src/iolists.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/iolists.erl rename to test_projects/eqwalizer_tests/check/src/iolists.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/kp_01.erl b/test_projects/eqwalizer_tests/check/src/kp_01.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/kp_01.erl rename to test_projects/eqwalizer_tests/check/src/kp_01.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/kp_02.erl b/test_projects/eqwalizer_tests/check/src/kp_02.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/kp_02.erl rename to test_projects/eqwalizer_tests/check/src/kp_02.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/lists_tests.erl b/test_projects/eqwalizer_tests/check/src/lists_tests.erl similarity index 66% rename from test/test_projects/eqwalizer_tests/check/src/lists_tests.erl rename to test_projects/eqwalizer_tests/check/src/lists_tests.erl index 56d03b7859..b50a598a24 100644 --- a/test/test_projects/eqwalizer_tests/check/src/lists_tests.erl +++ b/test_projects/eqwalizer_tests/check/src/lists_tests.erl @@ -14,15 +14,3 @@ lists_union_neg(V1, V2) -> [V1, V2]. -spec lists_union_2_neg (atom(), [atom()] | [binary()]) -> [atom()] | [binary()]. lists_union_2_neg(V, L) -> [V | L]. - --type union1() :: a | b. --type union2() :: union1() | c. - --spec unit_list1(union2()) -> [a] | [b] | [c]. -unit_list1(X) -> [X]. - --spec unit_list2(union2()) -> [union2()]. -unit_list2(X) -> [X]. - --spec unit_list3(union2()) -> [union1()] | [c]. -unit_list3(X) -> [X]. diff --git a/test/test_projects/eqwalizer_tests/check/src/misc.erl b/test_projects/eqwalizer_tests/check/src/misc.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/misc.erl rename to test_projects/eqwalizer_tests/check/src/misc.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/misc_lib.erl b/test_projects/eqwalizer_tests/check/src/misc_lib.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/misc_lib.erl rename to test_projects/eqwalizer_tests/check/src/misc_lib.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl b/test_projects/eqwalizer_tests/check/src/my_behaviour.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl rename to test_projects/eqwalizer_tests/check/src/my_behaviour.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl b/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl rename to test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/my_header.erl b/test_projects/eqwalizer_tests/check/src/my_header.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/my_header.erl rename to test_projects/eqwalizer_tests/check/src/my_header.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/neg.erl b/test_projects/eqwalizer_tests/check/src/neg.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/neg.erl rename to test_projects/eqwalizer_tests/check/src/neg.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl b/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl rename to test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl b/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl rename to test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/nowarn.erl b/test_projects/eqwalizer_tests/check/src/nowarn.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/nowarn.erl rename to test_projects/eqwalizer_tests/check/src/nowarn.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl b/test_projects/eqwalizer_tests/check/src/number_comparisons.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl rename to test_projects/eqwalizer_tests/check/src/number_comparisons.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/numbers.erl b/test_projects/eqwalizer_tests/check/src/numbers.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/numbers.erl rename to test_projects/eqwalizer_tests/check/src/numbers.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/opaque.erl b/test_projects/eqwalizer_tests/check/src/opaque.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/opaque.erl rename to test_projects/eqwalizer_tests/check/src/opaque.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/other.erl b/test_projects/eqwalizer_tests/check/src/other.erl similarity index 88% rename from test/test_projects/eqwalizer_tests/check/src/other.erl rename to test_projects/eqwalizer_tests/check/src/other.erl index 7ac109e673..edb0c04186 100644 --- a/test/test_projects/eqwalizer_tests/check/src/other.erl +++ b/test_projects/eqwalizer_tests/check/src/other.erl @@ -52,9 +52,3 @@ elab_var_scope(_) -> -spec map_with_dup_keys(#{ak => av1, bk => bv1, ak => av2}) -> #{ak => av1, bk => bv1}. map_with_dup_keys(M) -> M. - -% none() unions are simplified from literals too --spec with_none(integer() | none()) -> ok. -with_none(X) -> - eqwalizer:reveal_type(X), - ok. diff --git a/test/test_projects/eqwalizer_tests/check/src/otp28.erl b/test_projects/eqwalizer_tests/check/src/otp28.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/otp28.erl rename to test_projects/eqwalizer_tests/check/src/otp28.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl b/test_projects/eqwalizer_tests/check/src/otp_opaques.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl rename to test_projects/eqwalizer_tests/check/src/otp_opaques.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/overloaded.erl b/test_projects/eqwalizer_tests/check/src/overloaded.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/overloaded.erl rename to test_projects/eqwalizer_tests/check/src/overloaded.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl b/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl rename to test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/parametricity.erl b/test_projects/eqwalizer_tests/check/src/parametricity.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/parametricity.erl rename to test_projects/eqwalizer_tests/check/src/parametricity.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/pats.erl b/test_projects/eqwalizer_tests/check/src/pats.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/pats.erl rename to test_projects/eqwalizer_tests/check/src/pats.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/pinned.erl b/test_projects/eqwalizer_tests/check/src/pinned.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/pinned.erl rename to test_projects/eqwalizer_tests/check/src/pinned.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/pos.erl b/test_projects/eqwalizer_tests/check/src/pos.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/pos.erl rename to test_projects/eqwalizer_tests/check/src/pos.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/records.erl b/test_projects/eqwalizer_tests/check/src/records.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/records.erl rename to test_projects/eqwalizer_tests/check/src/records.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl b/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl rename to test_projects/eqwalizer_tests/check/src/recursive_aliases.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/refine.erl b/test_projects/eqwalizer_tests/check/src/refine.erl similarity index 94% rename from test/test_projects/eqwalizer_tests/check/src/refine.erl rename to test_projects/eqwalizer_tests/check/src/refine.erl index 4140ba4495..a887d82535 100644 --- a/test/test_projects/eqwalizer_tests/check/src/refine.erl +++ b/test_projects/eqwalizer_tests/check/src/refine.erl @@ -170,6 +170,33 @@ exploit_recursive() -> refine_recursive_neg({cons, nil}) + 1, ok. +% returns a number +-spec opaque1_neg( + gb_sets:set(atom())) -> + pid(). +opaque1_neg({X, _}) -> X. + +% returns a tuple +-spec opaque2_neg( + erl_anno:anno()) -> + pid(). +opaque2_neg([X, _]) -> X. + +% returns an atom +-spec opaque_3_neg( + sets:set(atom()) +) -> pid(). +opaque_3_neg({ + X, _, _, _, _, _, _, _, _ +}) -> X. + +% returns a 2-tuple +-spec opaque_4_neg( + gb_sets:set(atom()), + {term(), term()} +) -> pid(). +opaque_4_neg(X, X) -> X. + -spec record_as_tuple1_neg (#my_rec{}, tuple()) -> none(). record_as_tuple1_neg(R, R) -> R. diff --git a/test/test_projects/eqwalizer_tests/check/src/scoping.erl b/test_projects/eqwalizer_tests/check/src/scoping.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/scoping.erl rename to test_projects/eqwalizer_tests/check/src/scoping.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/skip.erl b/test_projects/eqwalizer_tests/check/src/skip.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/skip.erl rename to test_projects/eqwalizer_tests/check/src/skip.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/static_maybe.erl b/test_projects/eqwalizer_tests/check/src/static_maybe.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/static_maybe.erl rename to test_projects/eqwalizer_tests/check/src/static_maybe.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl b/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl rename to test_projects/eqwalizer_tests/check/src/strict_complex_types.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/strict_fun.erl b/test_projects/eqwalizer_tests/check/src/strict_fun.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/strict_fun.erl rename to test_projects/eqwalizer_tests/check/src/strict_fun.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/strict_receive.erl b/test_projects/eqwalizer_tests/check/src/strict_receive.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/strict_receive.erl rename to test_projects/eqwalizer_tests/check/src/strict_receive.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl b/test_projects/eqwalizer_tests/check/src/subtype_neg.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl rename to test_projects/eqwalizer_tests/check/src/subtype_neg.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl b/test_projects/eqwalizer_tests/check/src/subtype_pos.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl rename to test_projects/eqwalizer_tests/check/src/subtype_pos.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/t_maps.erl b/test_projects/eqwalizer_tests/check/src/t_maps.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/t_maps.erl rename to test_projects/eqwalizer_tests/check/src/t_maps.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl b/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl rename to test_projects/eqwalizer_tests/check/src/tagged_tuples.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/test.erl b/test_projects/eqwalizer_tests/check/src/test.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/test.erl rename to test_projects/eqwalizer_tests/check/src/test.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/tries.erl b/test_projects/eqwalizer_tests/check/src/tries.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/tries.erl rename to test_projects/eqwalizer_tests/check/src/tries.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/tuple_union.erl b/test_projects/eqwalizer_tests/check/src/tuple_union.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/tuple_union.erl rename to test_projects/eqwalizer_tests/check/src/tuple_union.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/type_aliases.erl b/test_projects/eqwalizer_tests/check/src/type_aliases.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/type_aliases.erl rename to test_projects/eqwalizer_tests/check/src/type_aliases.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/type_asserts.erl b/test_projects/eqwalizer_tests/check/src/type_asserts.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/type_asserts.erl rename to test_projects/eqwalizer_tests/check/src/type_asserts.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/type_predicates.erl b/test_projects/eqwalizer_tests/check/src/type_predicates.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/type_predicates.erl rename to test_projects/eqwalizer_tests/check/src/type_predicates.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/united_fun.erl b/test_projects/eqwalizer_tests/check/src/united_fun.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/united_fun.erl rename to test_projects/eqwalizer_tests/check/src/united_fun.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/unspecced.erl b/test_projects/eqwalizer_tests/check/src/unspecced.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/unspecced.erl rename to test_projects/eqwalizer_tests/check/src/unspecced.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl b/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl rename to test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl b/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl rename to test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/vars1.erl b/test_projects/eqwalizer_tests/check/src/vars1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/vars1.erl rename to test_projects/eqwalizer_tests/check/src/vars1.erl diff --git a/test/test_projects/eqwalizer_tests/check/src/vars2.erl b/test_projects/eqwalizer_tests/check/src/vars2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/src/vars2.erl rename to test_projects/eqwalizer_tests/check/src/vars2.erl diff --git a/test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl b/test_projects/eqwalizer_tests/check/test/check_SUITE.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl rename to test_projects/eqwalizer_tests/check/test/check_SUITE.erl diff --git a/test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl b/test_projects/eqwalizer_tests/debug/include/debug_header.hrl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl rename to test_projects/eqwalizer_tests/debug/include/debug_header.hrl diff --git a/test/test_projects/eqwalizer_tests/debug/src/attributes.erl b/test_projects/eqwalizer_tests/debug/src/attributes.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/attributes.erl rename to test_projects/eqwalizer_tests/debug/src/attributes.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/debug.app.src b/test_projects/eqwalizer_tests/debug/src/debug.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/debug.app.src rename to test_projects/eqwalizer_tests/debug/src/debug.app.src diff --git a/test/test_projects/eqwalizer_tests/debug/src/debug_header.erl b/test_projects/eqwalizer_tests/debug/src/debug_header.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/debug_header.erl rename to test_projects/eqwalizer_tests/debug/src/debug_header.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/expand.erl b/test_projects/eqwalizer_tests/debug/src/expand.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/expand.erl rename to test_projects/eqwalizer_tests/debug/src/expand.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/expr1.erl b/test_projects/eqwalizer_tests/debug/src/expr1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/expr1.erl rename to test_projects/eqwalizer_tests/debug/src/expr1.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/expr2.erl b/test_projects/eqwalizer_tests/debug/src/expr2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/expr2.erl rename to test_projects/eqwalizer_tests/debug/src/expr2.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/records_wip.erl b/test_projects/eqwalizer_tests/debug/src/records_wip.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/records_wip.erl rename to test_projects/eqwalizer_tests/debug/src/records_wip.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/types1.erl b/test_projects/eqwalizer_tests/debug/src/types1.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/types1.erl rename to test_projects/eqwalizer_tests/debug/src/types1.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/types2.erl b/test_projects/eqwalizer_tests/debug/src/types2.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/types2.erl rename to test_projects/eqwalizer_tests/debug/src/types2.erl diff --git a/test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl b/test_projects/eqwalizer_tests/debug/src/wip_maps.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl rename to test_projects/eqwalizer_tests/debug/src/wip_maps.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/basics.erl b/test_projects/eqwalizer_tests/elm_core/src/basics.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/basics.erl rename to test_projects/eqwalizer_tests/elm_core/src/basics.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src b/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src rename to test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/list.erl b/test_projects/eqwalizer_tests/elm_core/src/list.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/list.erl rename to test_projects/eqwalizer_tests/elm_core/src/list.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/map.erl b/test_projects/eqwalizer_tests/elm_core/src/map.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/map.erl rename to test_projects/eqwalizer_tests/elm_core/src/map.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl b/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl rename to test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl b/test_projects/eqwalizer_tests/elm_core/src/maybe.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl rename to test_projects/eqwalizer_tests/elm_core/src/maybe.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/result.erl b/test_projects/eqwalizer_tests/elm_core/src/result.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/result.erl rename to test_projects/eqwalizer_tests/elm_core/src/result.erl diff --git a/test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl b/test_projects/eqwalizer_tests/elm_core/src/tuple.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl rename to test_projects/eqwalizer_tests/elm_core/src/tuple.erl diff --git a/test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl b/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl rename to test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl diff --git a/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src b/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src rename to test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src diff --git a/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl b/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl rename to test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl diff --git a/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl b/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl rename to test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl b/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl rename to test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl diff --git a/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src b/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src rename to test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src diff --git a/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl b/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl rename to test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/readme.md b/test_projects/eqwalizer_tests/eqwater/readme.md similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/readme.md rename to test_projects/eqwalizer_tests/eqwater/readme.md diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl b/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl similarity index 85% rename from test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl rename to test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl index b7a5f8b81b..ff4a3a1d84 100644 --- a/test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl +++ b/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl @@ -30,9 +30,3 @@ test3_neg({{Tag1, _}, {_, _}}) -> -> none(). test4_neg({{_, _}, {Tag2, _}}) -> Tag2. - --record(foo, {f1 :: atom(), f2 :: atom()}). - --spec test5(#foo{} | {ok, a | ok}) -> ok. -test5({ok, a}) -> ok; -test5({ok, V}) -> V. diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src b/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src rename to test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl similarity index 98% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater.erl index 839cc3d8c5..f63202453a 100644 --- a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl +++ b/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl @@ -1300,11 +1300,3 @@ negate_number(N) -> N. (dynamic(), ok | err) -> ok. refine_dynamic(D, A) when is_atom(D) andalso A == err -> ok; refine_dynamic(D, A) when is_atom(D) -> A. - --spec negate_fun(F :: fun((number(), number()) -> term()) | ok) -> ok. -negate_fun(F) when is_function(F, 2) -> ok; -negate_fun(F) -> F. - --spec negate_fun_neg(F :: fun((number(), number()) -> term()) | ok) -> ok. -negate_fun_neg(F) when is_function(F, 2) -> F; -negate_fun_neg(F) -> F. diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl similarity index 89% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl index 68d938f790..78ae852b8a 100644 --- a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl +++ b/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl @@ -59,13 +59,3 @@ 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/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl rename to test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl diff --git a/test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl b/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl rename to test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl diff --git a/test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer b/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer similarity index 100% rename from test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer rename to test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer diff --git a/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src b/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src rename to test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src diff --git a/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl b/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl rename to test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl diff --git a/test/test_projects/eqwalizer_tests/options/src/bad_maps.erl b/test_projects/eqwalizer_tests/options/src/bad_maps.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/bad_maps.erl rename to test_projects/eqwalizer_tests/options/src/bad_maps.erl diff --git a/test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl b/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl rename to test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl diff --git a/test/test_projects/eqwalizer_tests/options/src/options.app.src b/test_projects/eqwalizer_tests/options/src/options.app.src similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/options.app.src rename to test_projects/eqwalizer_tests/options/src/options.app.src diff --git a/test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl b/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl rename to test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl diff --git a/test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl b/test_projects/eqwalizer_tests/options/src/redundant_guards.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl rename to test_projects/eqwalizer_tests/options/src/redundant_guards.erl diff --git a/test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl b/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl similarity index 100% rename from test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl rename to test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl diff --git a/test/test_projects/eqwalizer_tests/rebar.config b/test_projects/eqwalizer_tests/rebar.config similarity index 100% rename from test/test_projects/eqwalizer_tests/rebar.config rename to test_projects/eqwalizer_tests/rebar.config diff --git a/test_projects/in_place_tests/.elp.toml b/test_projects/in_place_tests/.elp.toml new file mode 100644 index 0000000000..b6c251f03c --- /dev/null +++ b/test_projects/in_place_tests/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/in_place_tests/README.md b/test_projects/in_place_tests/README.md similarity index 100% rename from test/test_projects/in_place_tests/README.md rename to test_projects/in_place_tests/README.md diff --git a/test/test_projects/in_place_tests/app_a/extra/app_a.erl b/test_projects/in_place_tests/app_a/extra/app_a.erl similarity index 100% rename from test/test_projects/in_place_tests/app_a/extra/app_a.erl rename to test_projects/in_place_tests/app_a/extra/app_a.erl diff --git a/test/test_projects/in_place_tests/app_a/include/app_a.hrl b/test_projects/in_place_tests/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/in_place_tests/app_a/include/app_a.hrl rename to test_projects/in_place_tests/app_a/include/app_a.hrl diff --git a/test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl b/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl rename to test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl diff --git a/test/test_projects/in_place_tests/app_a/include/diagnostics.hrl b/test_projects/in_place_tests/app_a/include/diagnostics.hrl similarity index 100% rename from test/test_projects/in_place_tests/app_a/include/diagnostics.hrl rename to test_projects/in_place_tests/app_a/include/diagnostics.hrl diff --git a/test/test_projects/hierarchical_config/app_a/src/app_a.app.src b/test_projects/in_place_tests/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/hierarchical_config/app_a/src/app_a.app.src rename to test_projects/in_place_tests/app_a/src/app_a.app.src diff --git a/test/test_projects/in_place_tests/app_a/src/app_a.erl b/test_projects/in_place_tests/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/in_place_tests/app_a/src/app_a.erl rename to test_projects/in_place_tests/app_a/src/app_a.erl diff --git a/test/test_projects/in_place_tests/app_a/src/lints.erl b/test_projects/in_place_tests/app_a/src/lints.erl similarity index 100% rename from test/test_projects/in_place_tests/app_a/src/lints.erl rename to test_projects/in_place_tests/app_a/src/lints.erl diff --git a/test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl b/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl rename to test_projects/in_place_tests/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/in_place_tests/erlang_ls.config b/test_projects/in_place_tests/erlang_ls.config similarity index 100% rename from test/test_projects/in_place_tests/erlang_ls.config rename to test_projects/in_place_tests/erlang_ls.config diff --git a/test/test_projects/in_place_tests/rebar.config b/test_projects/in_place_tests/rebar.config similarity index 100% rename from test/test_projects/in_place_tests/rebar.config rename to test_projects/in_place_tests/rebar.config diff --git a/test_projects/include_lib_dependency_test/.elp.toml b/test_projects/include_lib_dependency_test/.elp.toml new file mode 100644 index 0000000000..65e106a9e2 --- /dev/null +++ b/test_projects/include_lib_dependency_test/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl b/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl similarity index 100% rename from test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl rename to test_projects/include_lib_dependency_test/external_app/include/external_header.hrl diff --git a/test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src b/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src similarity index 100% rename from test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src rename to test_projects/include_lib_dependency_test/external_app/src/external_app.app.src diff --git a/test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl b/test_projects/include_lib_dependency_test/external_app/src/external_app.erl similarity index 100% rename from test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl rename to test_projects/include_lib_dependency_test/external_app/src/external_app.erl diff --git a/test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl b/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl similarity index 100% rename from test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl rename to test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl diff --git a/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src b/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src similarity index 100% rename from test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src rename to test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src diff --git a/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl b/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl similarity index 100% rename from test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl rename to test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl diff --git a/test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src b/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src similarity index 100% rename from test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src rename to test_projects/include_lib_dependency_test/main_app/src/main_app.app.src diff --git a/test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl b/test_projects/include_lib_dependency_test/main_app/src/main_app.erl similarity index 100% rename from test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl rename to test_projects/include_lib_dependency_test/main_app/src/main_app.erl diff --git a/test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl b/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl similarity index 100% rename from test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl rename to test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl diff --git a/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src b/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src similarity index 100% rename from test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src rename to test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src diff --git a/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl b/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl similarity index 100% rename from test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl rename to test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl diff --git a/test/test_projects/include_lib_dependency_test/rebar.config b/test_projects/include_lib_dependency_test/rebar.config similarity index 100% rename from test/test_projects/include_lib_dependency_test/rebar.config rename to test_projects/include_lib_dependency_test/rebar.config diff --git a/test_projects/linter/.elp.toml b/test_projects/linter/.elp.toml new file mode 100644 index 0000000000..a887ab835c --- /dev/null +++ b/test_projects/linter/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/linter/.elp_lint.toml b/test_projects/linter/.elp_lint.toml similarity index 100% rename from test/test_projects/linter/.elp_lint.toml rename to test_projects/linter/.elp_lint.toml diff --git a/test/test_projects/linter/.gitignore b/test_projects/linter/.gitignore similarity index 100% rename from test/test_projects/linter/.gitignore rename to test_projects/linter/.gitignore diff --git a/test/test_projects/linter/app_a/include/app_a.hrl b/test_projects/linter/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/linter/app_a/include/app_a.hrl rename to test_projects/linter/app_a/include/app_a.hrl diff --git a/test/test_projects/in_place_tests/app_a/src/app_a.app.src b/test_projects/linter/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/in_place_tests/app_a/src/app_a.app.src rename to test_projects/linter/app_a/src/app_a.app.src diff --git a/test/test_projects/linter/app_a/src/app_a.erl b/test_projects/linter/app_a/src/app_a.erl similarity index 70% rename from test/test_projects/linter/app_a/src/app_a.erl rename to test_projects/linter/app_a/src/app_a.erl index 86593395dd..167b8e6d6d 100644 --- a/test/test_projects/linter/app_a/src/app_a.erl +++ b/test_projects/linter/app_a/src/app_a.erl @@ -14,9 +14,3 @@ 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/test_projects/linter/app_a/src/app_a_edoc.erl b/test_projects/linter/app_a/src/app_a_edoc.erl similarity index 100% rename from test/test_projects/linter/app_a/src/app_a_edoc.erl rename to test_projects/linter/app_a/src/app_a_edoc.erl diff --git a/test/test_projects/linter/app_a/src/app_a_unused_param.erl b/test_projects/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test/test_projects/linter/app_a/src/app_a_unused_param.erl rename to test_projects/linter/app_a/src/app_a_unused_param.erl diff --git a/test/test_projects/linter/app_a/src/custom_function_matches.erl b/test_projects/linter/app_a/src/custom_function_matches.erl similarity index 100% rename from test/test_projects/linter/app_a/src/custom_function_matches.erl rename to test_projects/linter/app_a/src/custom_function_matches.erl diff --git a/test/test_projects/linter/app_a/src/expression_updates_literal.erl b/test_projects/linter/app_a/src/expression_updates_literal.erl similarity index 100% rename from test/test_projects/linter/app_a/src/expression_updates_literal.erl rename to test_projects/linter/app_a/src/expression_updates_literal.erl diff --git a/test/test_projects/linter/app_a/src/spelling.erl b/test_projects/linter/app_a/src/spelling.erl similarity index 100% rename from test/test_projects/linter/app_a/src/spelling.erl rename to test_projects/linter/app_a/src/spelling.erl diff --git a/test/test_projects/linter/app_a/test/app_a_SUITE.erl b/test_projects/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/linter/app_a/test/app_a_SUITE.erl rename to test_projects/linter/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/linter/app_a/test/app_a_test_helpers.erl b/test_projects/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test/test_projects/linter/app_a/test/app_a_test_helpers.erl rename to test_projects/linter/app_a/test/app_a_test_helpers.erl diff --git a/test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl b/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl similarity index 100% rename from test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl rename to test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl diff --git a/test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl b/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl rename to test_projects/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test/test_projects/hierarchical_config/app_b/src/app_b.app.src b/test_projects/linter/app_b/src/app_b.app.src similarity index 100% rename from test/test_projects/hierarchical_config/app_b/src/app_b.app.src rename to test_projects/linter/app_b/src/app_b.app.src diff --git a/test/test_projects/linter/app_b/src/app_b.erl b/test_projects/linter/app_b/src/app_b.erl similarity index 100% rename from test/test_projects/linter/app_b/src/app_b.erl rename to test_projects/linter/app_b/src/app_b.erl diff --git a/test/test_projects/linter/app_b/src/app_b_unused_param.erl b/test_projects/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test/test_projects/linter/app_b/src/app_b_unused_param.erl rename to test_projects/linter/app_b/src/app_b_unused_param.erl diff --git a/test/test_projects/linter/elp_lint_adhoc.toml b/test_projects/linter/elp_lint_adhoc.toml similarity index 100% rename from test/test_projects/linter/elp_lint_adhoc.toml rename to test_projects/linter/elp_lint_adhoc.toml diff --git a/test/test_projects/linter/elp_lint_custom_function_matches.toml b/test_projects/linter/elp_lint_custom_function_matches.toml similarity index 100% rename from test/test_projects/linter/elp_lint_custom_function_matches.toml rename to test_projects/linter/elp_lint_custom_function_matches.toml diff --git a/test/test_projects/linter/elp_lint_test1.toml b/test_projects/linter/elp_lint_test1.toml similarity index 100% rename from test/test_projects/linter/elp_lint_test1.toml rename to test_projects/linter/elp_lint_test1.toml diff --git a/test/test_projects/linter/elp_lint_test2.toml b/test_projects/linter/elp_lint_test2.toml similarity index 54% rename from test/test_projects/linter/elp_lint_test2.toml rename to test_projects/linter/elp_lint_test2.toml index 0b70173c8b..22878a22a6 100644 --- a/test/test_projects/linter/elp_lint_test2.toml +++ b/test_projects/linter/elp_lint_test2.toml @@ -1,5 +1,2 @@ enabled_lints =['L1268', 'W0011', 'W0012'] disabled_lints = [] - -[linters.compile-warn-missing-spec] -enabled = true diff --git a/test/test_projects/linter/elp_lint_test_ignore.toml b/test_projects/linter/elp_lint_test_ignore.toml similarity index 100% rename from test/test_projects/linter/elp_lint_test_ignore.toml rename to test_projects/linter/elp_lint_test_ignore.toml diff --git a/test/test_projects/linter/elp_lint_warnings_as_errors.toml b/test_projects/linter/elp_lint_warnings_as_errors.toml similarity index 100% rename from test/test_projects/linter/elp_lint_warnings_as_errors.toml rename to test_projects/linter/elp_lint_warnings_as_errors.toml diff --git a/test/test_projects/linter/rebar.config b/test_projects/linter/rebar.config similarity index 100% rename from test/test_projects/linter/rebar.config rename to test_projects/linter/rebar.config diff --git a/test_projects/linter_bad_config/.elp.toml b/test_projects/linter_bad_config/.elp.toml new file mode 100644 index 0000000000..a887ab835c --- /dev/null +++ b/test_projects/linter_bad_config/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/linter_bad_config/.elp_lint.toml b/test_projects/linter_bad_config/.elp_lint.toml similarity index 100% rename from test/test_projects/linter_bad_config/.elp_lint.toml rename to test_projects/linter_bad_config/.elp_lint.toml diff --git a/test/test_projects/linter_bad_config/.gitignore b/test_projects/linter_bad_config/.gitignore similarity index 100% rename from test/test_projects/linter_bad_config/.gitignore rename to test_projects/linter_bad_config/.gitignore diff --git a/test/test_projects/linter_bad_config/app_a/include/app_a.hrl b/test_projects/linter_bad_config/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/linter_bad_config/app_a/include/app_a.hrl rename to test_projects/linter_bad_config/app_a/include/app_a.hrl diff --git a/test/test_projects/linter/app_a/src/app_a.app.src b/test_projects/linter_bad_config/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/linter/app_a/src/app_a.app.src rename to test_projects/linter_bad_config/app_a/src/app_a.app.src diff --git a/test/test_projects/linter_bad_config/app_a/src/app_a.erl b/test_projects/linter_bad_config/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/linter_bad_config/app_a/src/app_a.erl rename to test_projects/linter_bad_config/app_a/src/app_a.erl diff --git a/test_projects/linter_bad_config/linter/.elp.toml b/test_projects/linter_bad_config/linter/.elp.toml new file mode 100644 index 0000000000..a887ab835c --- /dev/null +++ b/test_projects/linter_bad_config/linter/.elp.toml @@ -0,0 +1,5 @@ +[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/test_projects/linter_bad_config/linter/.elp_lint.toml b/test_projects/linter_bad_config/linter/.elp_lint.toml similarity index 100% rename from test/test_projects/linter_bad_config/linter/.elp_lint.toml rename to test_projects/linter_bad_config/linter/.elp_lint.toml diff --git a/test/test_projects/linter_bad_config/linter/.gitignore b/test_projects/linter_bad_config/linter/.gitignore similarity index 100% rename from test/test_projects/linter_bad_config/linter/.gitignore rename to test_projects/linter_bad_config/linter/.gitignore diff --git a/test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl b/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl rename to test_projects/linter_bad_config/linter/app_a/include/app_a.hrl diff --git a/test/test_projects/linter_bad_config/app_a/src/app_a.app.src b/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/linter_bad_config/app_a/src/app_a.app.src rename to test_projects/linter_bad_config/linter/app_a/src/app_a.app.src diff --git a/test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl b/test_projects/linter_bad_config/linter/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl rename to test_projects/linter_bad_config/linter/app_a/src/app_a.erl diff --git a/test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl b/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl rename to test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl diff --git a/test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl b/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl rename to test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl b/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl rename to test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl diff --git a/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl b/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl rename to test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test/test_projects/linter/app_b/src/app_b.app.src b/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src similarity index 100% rename from test/test_projects/linter/app_b/src/app_b.app.src rename to test_projects/linter_bad_config/linter/app_b/src/app_b.app.src diff --git a/test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl b/test_projects/linter_bad_config/linter/app_b/src/app_b.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl rename to test_projects/linter_bad_config/linter/app_b/src/app_b.erl diff --git a/test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl b/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl rename to test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl diff --git a/test/test_projects/linter_bad_config/linter/rebar.config b/test_projects/linter_bad_config/linter/rebar.config similarity index 100% rename from test/test_projects/linter_bad_config/linter/rebar.config rename to test_projects/linter_bad_config/linter/rebar.config diff --git a/test/test_projects/linter_bad_config/rebar.config b/test_projects/linter_bad_config/rebar.config similarity index 100% rename from test/test_projects/linter_bad_config/rebar.config rename to test_projects/linter_bad_config/rebar.config diff --git a/test_projects/parse_error/.elp.toml b/test_projects/parse_error/.elp.toml new file mode 100644 index 0000000000..bf2c1ad580 --- /dev/null +++ b/test_projects/parse_error/.elp.toml @@ -0,0 +1,8 @@ +[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/test_projects/parse_error/.gitignore b/test_projects/parse_error/.gitignore similarity index 100% rename from test/test_projects/parse_error/.gitignore rename to test_projects/parse_error/.gitignore diff --git a/test/test_projects/parse_error/.rebar.root b/test_projects/parse_error/.rebar.root similarity index 100% rename from test/test_projects/parse_error/.rebar.root rename to test_projects/parse_error/.rebar.root diff --git a/test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src b/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src rename to test_projects/parse_error/eqwalizer/src/eqwalizer.app.src diff --git a/test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl b/test_projects/parse_error/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl rename to test_projects/parse_error/eqwalizer/src/eqwalizer.erl diff --git a/test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl b/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl rename to test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src b/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src rename to test_projects/parse_error/parse_error_a/src/parse_error_a.app.src diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl b/test_projects/parse_error/parse_error_a/src/parse_error_a.erl similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl rename to test_projects/parse_error/parse_error_a/src/parse_error_a.erl diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl b/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl rename to test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl b/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl rename to test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl b/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl rename to test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl diff --git a/test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl b/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl similarity index 100% rename from test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl rename to test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl diff --git a/test/test_projects/parse_error/rebar.config b/test_projects/parse_error/rebar.config similarity index 100% rename from test/test_projects/parse_error/rebar.config rename to test_projects/parse_error/rebar.config diff --git a/test_projects/standard/.elp.toml b/test_projects/standard/.elp.toml new file mode 100644 index 0000000000..09ff5a4d5a --- /dev/null +++ b/test_projects/standard/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test_projects/standard/..." ] +source_root = "whatsapp/elp/test_projects/standard" diff --git a/test/test_projects/standard/.gitignore b/test_projects/standard/.gitignore similarity index 100% rename from test/test_projects/standard/.gitignore rename to test_projects/standard/.gitignore diff --git a/test/test_projects/standard/.rebar.root b/test_projects/standard/.rebar.root similarity index 100% rename from test/test_projects/standard/.rebar.root rename to test_projects/standard/.rebar.root diff --git a/test/test_projects/standard/app_a/.eqwalizer b/test_projects/standard/app_a/.eqwalizer similarity index 100% rename from test/test_projects/standard/app_a/.eqwalizer rename to test_projects/standard/app_a/.eqwalizer diff --git a/test/test_projects/standard/app_a/extra/app_a.erl b/test_projects/standard/app_a/extra/app_a.erl similarity index 100% rename from test/test_projects/standard/app_a/extra/app_a.erl rename to test_projects/standard/app_a/extra/app_a.erl diff --git a/test/test_projects/standard/app_a/include/app_a.hrl b/test_projects/standard/app_a/include/app_a.hrl similarity index 100% rename from test/test_projects/standard/app_a/include/app_a.hrl rename to test_projects/standard/app_a/include/app_a.hrl diff --git a/test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src b/test_projects/standard/app_a/src/app_a.app.src similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src rename to test_projects/standard/app_a/src/app_a.app.src diff --git a/test/test_projects/standard/app_a/src/app_a.erl b/test_projects/standard/app_a/src/app_a.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a.erl rename to test_projects/standard/app_a/src/app_a.erl diff --git a/test/test_projects/standard/app_a/src/app_a_errors_generated.erl b/test_projects/standard/app_a/src/app_a_errors_generated.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_errors_generated.erl rename to test_projects/standard/app_a/src/app_a_errors_generated.erl diff --git a/test/test_projects/standard/app_a/src/app_a_fixme.erl b/test_projects/standard/app_a/src/app_a_fixme.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_fixme.erl rename to test_projects/standard/app_a/src/app_a_fixme.erl diff --git a/test/test_projects/standard/app_a/src/app_a_ignored.erl b/test_projects/standard/app_a/src/app_a_ignored.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_ignored.erl rename to test_projects/standard/app_a/src/app_a_ignored.erl diff --git a/test/test_projects/standard/app_a/src/app_a_lists.erl b/test_projects/standard/app_a/src/app_a_lists.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_lists.erl rename to test_projects/standard/app_a/src/app_a_lists.erl diff --git a/test/test_projects/standard/app_a/src/app_a_mod2.erl b/test_projects/standard/app_a/src/app_a_mod2.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_mod2.erl rename to test_projects/standard/app_a/src/app_a_mod2.erl diff --git a/test/test_projects/standard/app_a/src/app_a_no_errors.erl b/test_projects/standard/app_a/src/app_a_no_errors.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_no_errors.erl rename to test_projects/standard/app_a/src/app_a_no_errors.erl diff --git a/test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl b/test_projects/standard/app_a/src/app_a_no_errors_generated.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl rename to test_projects/standard/app_a/src/app_a_no_errors_generated.erl diff --git a/test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl b/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl similarity index 100% rename from test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl rename to test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl diff --git a/test/test_projects/standard/app_a/test/app_a_SUITE.erl b/test_projects/standard/app_a/test/app_a_SUITE.erl similarity index 100% rename from test/test_projects/standard/app_a/test/app_a_SUITE.erl rename to test_projects/standard/app_a/test/app_a_SUITE.erl diff --git a/test/test_projects/standard/app_a/test/app_a_test_helpers.erl b/test_projects/standard/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test/test_projects/standard/app_a/test/app_a_test_helpers.erl rename to test_projects/standard/app_a/test/app_a_test_helpers.erl diff --git a/test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl b/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl b/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl rename to test_projects/standard/app_a/test/app_test_helpers_no_errors.erl diff --git a/test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src b/test_projects/standard/app_b/src/app_b.app.src similarity index 100% rename from test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src rename to test_projects/standard/app_b/src/app_b.app.src diff --git a/test/test_projects/standard/app_b/src/app_b.erl b/test_projects/standard/app_b/src/app_b.erl similarity index 100% rename from test/test_projects/standard/app_b/src/app_b.erl rename to test_projects/standard/app_b/src/app_b.erl diff --git a/test/test_projects/standard/eqwalizer/src/eqwalizer.app.src b/test_projects/standard/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test/test_projects/standard/eqwalizer/src/eqwalizer.app.src rename to test_projects/standard/eqwalizer/src/eqwalizer.app.src diff --git a/test/test_projects/standard/eqwalizer/src/eqwalizer.erl b/test_projects/standard/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test/test_projects/standard/eqwalizer/src/eqwalizer.erl rename to test_projects/standard/eqwalizer/src/eqwalizer.erl diff --git a/test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl b/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl rename to test_projects/standard/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/standard/erlang_ls.config b/test_projects/standard/erlang_ls.config similarity index 100% rename from test/test_projects/standard/erlang_ls.config rename to test_projects/standard/erlang_ls.config diff --git a/test/test_projects/standard/rebar.config b/test_projects/standard/rebar.config similarity index 100% rename from test/test_projects/standard/rebar.config rename to test_projects/standard/rebar.config diff --git a/website/docs/erlang-error-index/w/W0045.md b/website/docs/erlang-error-index/w/W0045.md index c68f83ef4d..13c908d8a0 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: A module with this name exists elsewhere + %% ^^^^^^^^^ warning: Duplicate module name ``` ## Explanation diff --git a/website/docs/erlang-error-index/w/W0048.md b/website/docs/erlang-error-index/w/W0048.md index a6e1631148..42d21cec9c 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 using the -dialyzer attribute. +%% ^^^^^^^^^ 💡 warning: Avoid -dialyzer attribute. ``` ## Explanation diff --git a/website/docs/erlang-error-index/w/W0050.md b/website/docs/erlang-error-index/w/W0050.md index 8c8210dcca..a0d43d7171 100644 --- a/website/docs/erlang-error-index/w/W0050.md +++ b/website/docs/erlang-error-index/w/W0050.md @@ -20,26 +20,40 @@ size(Input) -> ## Explanation -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 [`size/1`](https://www.erlang.org/doc/apps/erts/erlang#size/1) BIF returns +the size for both tuples and binaries. -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). +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). -What to do instead: +When one knows that the value being tested must be a tuple, `tuple_size/1` +should always be preferred. -- For tuples, use `tuple_size/1`. +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 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_: +For the above use case, a better approach would be: ```erlang -4 = byte_size(<<0:25>>). -3 = size(<<0:25>>). +-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). ``` 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 deleted file mode 100644 index 14848f5898..0000000000 --- a/website/docs/erlang-error-index/w/W0056.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -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 deleted file mode 100644 index 990c4648b6..0000000000 --- a/website/docs/erlang-error-index/w/W0057.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -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 deleted file mode 100644 index acad4c2d79..0000000000 --- a/website/docs/erlang-error-index/w/W0058.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -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 deleted file mode 100644 index fd2e990cb3..0000000000 --- a/website/docs/erlang-error-index/w/W0059.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -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 deleted file mode 100644 index 91b972dacf..0000000000 --- a/website/docs/erlang-error-index/w/W0060.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -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 90a9eb5f1e..557027a138 100644 --- a/website/docs/get-started/configure-project/elp-toml.md +++ b/website/docs/get-started/configure-project/elp-toml.md @@ -32,9 +32,6 @@ enabled = false [rebar] profile = "test" - -[otp] -exclude_apps = ["megaco", "eunit"] ``` ## Configuration Sections @@ -111,21 +108,9 @@ 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 | 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"] -``` +| Key | Type | Description | +| ------- | ------- | -------------------------------- | +| enabled | Boolean | Discover the project using Buck2 | :::warning @@ -141,22 +126,3 @@ Configure ELP for [rebar3](https://rebar3.org/)-based projects. | Key | Type | Description | Default | | ------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ------- | | profile | String | The `rebar3` profile to use for project discovery. Only used if the `file` property is specified in the [build_info](#build-info) section. | test | - -### \[otp\] {#otp} - -Configure ELP's interaction with OTP (Open Telecom Platform) applications. - -| Key | Type | Description | Default | -| ------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | -| exclude_apps | Array of Strings | List of OTP application names to exclude from indexing. This can help improve performance by not loading rarely used OTP apps. | [] | - -The `exclude_apps` setting allows you to specify OTP applications that should not be indexed by ELP. This can be useful for: - -- Improving performance by excluding rarely used OTP applications like `megaco` -- Reducing memory usage when working on projects that don't need certain OTP libraries - -Example usage: -```toml -[otp] -exclude_apps = ["diameter", "megaco"] -``` diff --git a/website/docs/get-started/editors/emacs.md b/website/docs/get-started/editors/emacs.md index fab357f4a4..5b19fd09f2 100644 --- a/website/docs/get-started/editors/emacs.md +++ b/website/docs/get-started/editors/emacs.md @@ -4,107 +4,15 @@ 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 [lsp-mode](https://emacs-lsp.github.io/lsp-mode/) LSP client. -## Eglot +## Requirements -Eglot is part of Emacs core since Emacs 29. For earlier versions it can be -installed with the `eglot` package. +### `lsp-mode` -### Configuration +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. -```elisp -(use-package eglot - :ensure t - :hook ((erlang-mode . eglot-ensure)) - - :config - ;; Remove default LSP server - (setopt eglot-server-programs - (assq-delete-all 'erlang-mode eglot-server-programs)) - - ;; Enable ELP - (add-to-list 'eglot-server-programs - '(erlang-mode . ("elp" "server")))) -``` - -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. - -### Configuration +## Configure Emacs Add the following to your emacs `.emacs` file or equivalent. @@ -124,21 +32,13 @@ 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/). - -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. +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/). ## 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 74cc3b9159..84f903cd64 100644 --- a/website/docs/get-started/install.md +++ b/website/docs/get-started/install.md @@ -4,26 +4,9 @@ sidebar_position: 2 # Install ELP -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 +The easiest way to install to ELP is [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 @@ -68,14 +51,18 @@ correctly installed. ## From Source -Clone the ELP repository, including submodules: +### Prerequisites + +To be able to compile ELP from source, you need a copy of the +[eqWAlizer](https://github.com/WhatsApp/eqwalizer) typechecker for Erlang. + +Clone the eqWAlizer repository: ``` -git clone --recurse-submodules https://github.com/WhatsApp/erlang-language-platform.git -cd erlang-language-platform +git clone https://github.com/WhatsApp/eqwalizer.git ``` -Enter the `eqwalizer` submodule and build it. Notice the double `eqwalizer` in +Enter the `eqwalizer` repository and build it. Notice the double `eqwalizer` in the `pushd` command. ``` @@ -84,38 +71,43 @@ sbt assembly popd ``` -Point the `ELP_EQWALIZER_PATH` environment variable to the path of the produced -`eqwalizer.jar` file: +Get the path of the produced `eqwalizer.jar` file: ``` -export ELP_EQWALIZER_PATH=$(find "$(pwd)" -name eqwalizer.jar) +find . -name eqwalizer.jar | readlink -f ``` -Point the `EQWALIZER_DIR` environment variable to the path of the -`eqwalizer_support` directory: +Point the `ELP_EQWALIZER_PATH` environment variable to the path returned above: ``` -export EQWALIZER_DIR=$(find "$(pwd)" -name eqwalizer_support) +export ELP_EQWALIZER_PATH=/path/to/eqwalizer.jar ``` -Now we can compile ELP: - -:::tip - -The commands below will produce a release build, which has the best runtime -performance, at the price of a slower compilation time. If you are developing -ELP, consider using `cargo build --profile release-thin`, as it provides a much -faster development loop while still producing a performant binary. You will find -the corresponding binary in `target/release-thin/elp`. - -::: +Note: The ELP build assumes that you clone the eqwalizer and ELP +repositories from the same starting directory, so they are +"siblings". This is because there is a cross-reference within ELP to +`../eqwalizer/eqwalizer_support`. If you are unable to do this, set ``` +export EQWALIZER_DIR=
/eqwalizer_support +``` + +### Compile ELP + +Clone the ELP repository: + +``` +git clone https://github.com/WhatsApp/erlang-language-platform.git +``` + +Enter the ELP repo and compile it: + +``` +cd erlang-language-platform cargo build --release ``` -The produced executable will be available in: `target/release/elp`, so ensure it -is included in your `PATH`. E.g.: +The produced executable will be available in: `target/release/elp`, so ensure it is included in your `PATH`. E.g.: ``` mkdir -p ~/bin diff --git a/website/package.json b/website/package.json index 49a34973ca..4053a7477f 100644 --- a/website/package.json +++ b/website/package.json @@ -16,12 +16,12 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "3.9.2", - "@docusaurus/preset-classic": "3.9.2", + "@docusaurus/core": "3.6.3", + "@docusaurus/preset-classic": "3.6.3", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", - "docusaurus-plugin-internaldocs-fb": "^1.18.4", "prism-react-renderer": "^2.3.0", + "docusaurus-plugin-internaldocs-fb": "^1.18.4", "react": "^18.0.0", "react-dom": "^18.0.0" }, @@ -62,11 +62,6 @@ "brace-expansion": ">=1.1.12", "webpack-dev-server": ">=5.2.1", "on-headers": ">=1.1.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" + "mermaid": ">=11.10.0" } } diff --git a/website/yarn.lock b/website/yarn.lock index 1ac9f2033a..c07cd453c7 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2,191 +2,274 @@ # yarn lockfile v1 -"@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== +"@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== dependencies: - "@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" + "@algolia/autocomplete-shared" "1.17.7" -"@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== +"@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== dependencies: - "@ai-sdk/provider" "2.0.0" - "@standard-schema/spec" "^1.0.0" - eventsource-parser "^3.0.6" + "@algolia/autocomplete-shared" "1.17.7" -"@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== +"@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== dependencies: - json-schema "^0.4.0" + "@algolia/autocomplete-shared" "1.17.7" -"@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== +"@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== dependencies: - "@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" -"@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== +"@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== dependencies: - "@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/cache-common" "4.24.0" -"@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== +"@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== dependencies: - "@algolia/autocomplete-plugin-algolia-insights" "1.19.2" - "@algolia/autocomplete-shared" "1.19.2" + "@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": - 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== +"@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== dependencies: - "@algolia/autocomplete-shared" "1.19.2" + "@algolia/client-common" "4.24.0" + "@algolia/client-search" "4.24.0" + "@algolia/transporter" "4.24.0" -"@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== +"@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== dependencies: - "@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" + "@algolia/client-search" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" -"@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== +"@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== dependencies: - "@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" + "@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": - 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== +"@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== dependencies: - "@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/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" -"@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.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-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== +"@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== dependencies: - "@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" + "@algolia/requester-browser-xhr" "5.15.0" + "@algolia/requester-fetch" "5.15.0" + "@algolia/requester-node-http" "5.15.0" -"@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== +"@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== dependencies: - "@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" + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.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== + 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/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.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== +"@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== dependencies: - "@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" + "@algolia/requester-browser-xhr" "5.15.0" + "@algolia/requester-fetch" "5.15.0" + "@algolia/requester-node-http" "5.15.0" -"@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/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/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== +"@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== dependencies: - "@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" -"@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== +"@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== dependencies: - "@algolia/client-common" "5.44.0" + "@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/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== +"@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== dependencies: - "@algolia/client-common" "5.44.0" + "@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/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== +"@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== dependencies: - "@algolia/client-common" "5.44.0" + "@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/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== + 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" "@ampproject/remapping@^2.2.0": version "2.3.0" @@ -209,7 +292,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.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": +"@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": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -1163,136 +1246,92 @@ 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.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/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/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/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/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-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-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== +"@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== dependencies: - "@csstools/color-helpers" "^5.1.0" - "@csstools/css-calc" "^2.1.4" + "@csstools/color-helpers" "^5.0.1" + "@csstools/css-calc" "^2.1.0" -"@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": +"@csstools/css-parser-algorithms@^3.0.4": version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" - integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== -"@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/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/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/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-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== +"@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== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@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== +"@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== 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/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/utilities" "^2.0.0" -"@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== +"@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== 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/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/utilities" "^2.0.0" -"@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== +"@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== 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/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + "@csstools/postcss-progressive-custom-properties" "^4.0.0" "@csstools/utilities" "^2.0.0" -"@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== +"@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== 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-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/css-calc" "^2.1.0" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" "@csstools/postcss-font-format-keywords@^4.0.0": version "4.0.0" @@ -1302,67 +1341,67 @@ "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@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== +"@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== dependencies: - "@csstools/css-color-parser" "^3.1.0" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-color-parser" "^3.0.6" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" -"@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== +"@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== 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/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/utilities" "^2.0.0" -"@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== +"@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== 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/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/utilities" "^2.0.0" -"@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== +"@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== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/postcss-progressive-custom-properties" "^4.0.0" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@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-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-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== +"@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== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@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== +"@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== dependencies: - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" - "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + "@csstools/postcss-progressive-custom-properties" "^4.0.0" "@csstools/utilities" "^2.0.0" "@csstools/postcss-logical-float-and-clear@^3.0.0": @@ -1387,32 +1426,32 @@ dependencies: postcss-value-parser "^4.2.0" -"@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== +"@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== dependencies: - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" "@csstools/utilities" "^2.0.0" -"@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== +"@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== dependencies: - "@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/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/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== +"@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== dependencies: - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" - "@csstools/media-query-list-parser" "^4.0.3" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + "@csstools/media-query-list-parser" "^4.0.2" "@csstools/postcss-nested-calc@^4.0.0": version "4.0.0" @@ -1429,42 +1468,42 @@ dependencies: postcss-value-parser "^4.2.0" -"@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== +"@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== 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/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/utilities" "^2.0.0" -"@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== +"@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== dependencies: postcss-value-parser "^4.2.0" -"@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== +"@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== dependencies: - "@csstools/css-calc" "^2.1.4" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-calc" "^2.1.0" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" -"@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== +"@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== 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/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/utilities" "^2.0.0" "@csstools/postcss-scope-pseudo-class@^4.0.1": @@ -1474,50 +1513,50 @@ dependencies: postcss-selector-parser "^7.0.0" -"@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== +"@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== dependencies: - "@csstools/css-calc" "^2.1.4" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-calc" "^2.1.0" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" -"@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== +"@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== dependencies: - "@csstools/css-calc" "^2.1.4" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-calc" "^2.1.0" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" -"@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== +"@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== dependencies: - "@csstools/color-helpers" "^5.1.0" + "@csstools/color-helpers" "^5.0.1" postcss-value-parser "^4.2.0" -"@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== +"@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== dependencies: - "@csstools/css-calc" "^2.1.4" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/css-calc" "^2.1.0" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" "@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.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-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-specificity@^5.0.0": version "5.0.0" @@ -1534,34 +1573,25 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@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/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/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== +"@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== dependencies: - "@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" + "@algolia/autocomplete-core" "1.17.7" + "@algolia/autocomplete-preset-algolia" "1.17.7" + "@docsearch/css" "3.8.0" + algoliasearch "^5.12.0" -"@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== +"@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== dependencies: "@babel/core" "^7.25.9" "@babel/generator" "^7.25.9" @@ -1573,54 +1603,55 @@ "@babel/runtime" "^7.25.9" "@babel/runtime-corejs3" "^7.25.9" "@babel/traverse" "^7.25.9" - "@docusaurus/logger" "3.9.2" - "@docusaurus/utils" "3.9.2" + "@docusaurus/logger" "3.6.3" + "@docusaurus/utils" "3.6.3" babel-plugin-dynamic-import-node "^2.3.3" fs-extra "^11.1.1" tslib "^2.6.0" -"@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== +"@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== dependencies: "@babel/core" "^7.25.9" - "@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" + "@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" babel-loader "^9.2.1" - clean-css "^5.3.3" + clean-css "^5.3.2" copy-webpack-plugin "^11.0.0" - css-loader "^6.11.0" + css-loader "^6.8.1" 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.2" + mini-css-extract-plugin "^2.9.1" null-loader "^4.0.1" - postcss "^8.5.4" - postcss-loader "^7.3.4" - postcss-preset-env "^10.2.1" + postcss "^8.4.26" + postcss-loader "^7.3.3" + postcss-preset-env "^10.1.0" + react-dev-utils "^12.0.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.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== +"@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== dependencies: - "@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" + "@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" boxen "^6.2.1" chalk "^4.1.2" chokidar "^3.5.3" @@ -1628,68 +1659,69 @@ 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-helmet-async "npm:@slorber/react-helmet-async@1.3.0" + react-dev-utils "^12.0.1" + 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" - tinypool "^1.0.2" + shelljs "^0.8.5" tslib "^2.6.0" update-notifier "^6.0.2" webpack "^5.95.0" webpack-bundle-analyzer "^4.10.2" - webpack-dev-server "^5.2.2" + webpack-dev-server "^4.15.2" webpack-merge "^6.0.1" -"@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== +"@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== dependencies: cssnano-preset-advanced "^6.1.2" - postcss "^8.5.4" + postcss "^8.4.38" postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@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== +"@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== dependencies: chalk "^4.1.2" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@docusaurus/logger" "3.9.2" - "@docusaurus/utils" "3.9.2" - "@docusaurus/utils-validation" "3.9.2" + "@docusaurus/logger" "3.6.3" + "@docusaurus/utils" "3.6.3" + "@docusaurus/utils-validation" "3.6.3" "@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 "^2.0.2" + image-size "^1.0.2" mdast-util-mdx "^3.0.0" mdast-util-to-string "^4.0.0" rehype-raw "^7.0.0" @@ -1705,209 +1737,182 @@ vfile "^6.0.1" webpack "^5.88.1" -"@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== +"@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== dependencies: - "@docusaurus/types" "3.9.2" + "@docusaurus/types" "3.6.3" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" "@types/react-router-dom" "*" - react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" + react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@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== +"@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== dependencies: - "@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" + "@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" cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" lodash "^4.17.21" - schema-dts "^1.1.2" + reading-time "^1.5.0" 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.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== +"@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== 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/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" + "@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" "@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.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== +"@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== dependencies: - "@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" + "@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" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@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== +"@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== dependencies: - "@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" + "@docusaurus/core" "3.6.3" + "@docusaurus/types" "3.6.3" + "@docusaurus/utils" "3.6.3" fs-extra "^11.1.1" - react-json-view-lite "^2.3.0" + react-json-view-lite "^1.2.0" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@docusaurus/core" "3.9.2" - "@docusaurus/types" "3.9.2" - "@docusaurus/utils-validation" "3.9.2" + "@docusaurus/core" "3.6.3" + "@docusaurus/types" "3.6.3" + "@docusaurus/utils-validation" "3.6.3" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@docusaurus/core" "3.9.2" - "@docusaurus/types" "3.9.2" - "@docusaurus/utils-validation" "3.9.2" + "@docusaurus/core" "3.6.3" + "@docusaurus/types" "3.6.3" + "@docusaurus/utils-validation" "3.6.3" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@docusaurus/core" "3.9.2" - "@docusaurus/types" "3.9.2" - "@docusaurus/utils-validation" "3.9.2" + "@docusaurus/core" "3.6.3" + "@docusaurus/types" "3.6.3" + "@docusaurus/utils-validation" "3.6.3" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@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" + "@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" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@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/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/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== +"@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== dependencies: - "@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" + "@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" "@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.5.4" + postcss "^8.4.26" prism-react-renderer "^2.3.0" prismjs "^1.29.0" react-router-dom "^5.3.4" @@ -1915,15 +1920,15 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@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== +"@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== dependencies: - "@docusaurus/mdx-loader" "3.9.2" - "@docusaurus/module-type-aliases" "3.9.2" - "@docusaurus/utils" "3.9.2" - "@docusaurus/utils-common" "3.9.2" + "@docusaurus/mdx-loader" "3.6.3" + "@docusaurus/module-type-aliases" "3.6.3" + "@docusaurus/utils" "3.6.3" + "@docusaurus/utils-common" "3.6.3" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -1933,21 +1938,21 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@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== +"@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== dependencies: - "@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" + "@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" clsx "^2.0.0" eta "^2.2.0" fs-extra "^11.1.1" @@ -1955,62 +1960,61 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@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== +"@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== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" -"@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== +"@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== 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 "npm:@slorber/react-helmet-async@1.3.0" + react-helmet-async "^1.3.0" utility-types "^3.10.0" webpack "^5.95.0" webpack-merge "^5.9.0" -"@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== +"@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== dependencies: - "@docusaurus/types" "3.9.2" + "@docusaurus/types" "3.6.3" tslib "^2.6.0" -"@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== +"@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== dependencies: - "@docusaurus/logger" "3.9.2" - "@docusaurus/utils" "3.9.2" - "@docusaurus/utils-common" "3.9.2" + "@docusaurus/logger" "3.6.3" + "@docusaurus/utils" "3.6.3" + "@docusaurus/utils-common" "3.6.3" 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.9.2": - version "3.9.2" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.9.2.tgz#ffab7922631c7e0febcb54e6d499f648bf8a89eb" - integrity sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ== +"@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== dependencies: - "@docusaurus/logger" "3.9.2" - "@docusaurus/types" "3.9.2" - "@docusaurus/utils-common" "3.9.2" + "@docusaurus/logger" "3.6.3" + "@docusaurus/types" "3.6.3" + "@docusaurus/utils-common" "3.6.3" + "@svgr/webpack" "^8.1.0" 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" @@ -2020,9 +2024,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" @@ -2059,18 +2063,6 @@ 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" @@ -2248,11 +2240,6 @@ "@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" @@ -2325,11 +2312,6 @@ 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" @@ -2853,7 +2835,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@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": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2939,6 +2921,11 @@ 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" @@ -3105,11 +3092,6 @@ 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" @@ -3251,15 +3233,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -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: +accepts@~1.3.4, accepts@~1.3.8: 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== @@ -3284,7 +3258,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.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== @@ -3297,16 +3271,6 @@ 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" @@ -3314,7 +3278,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.5.2: +ajv-keywords@^3.4.1, 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== @@ -3326,7 +3290,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.5: +ajv@^6.12.2, 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== @@ -3346,32 +3310,52 @@ ajv@^8.0.0, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -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== +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== dependencies: "@algolia/events" "^4.0.1" -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== +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== dependencies: - "@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" + "@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" ansi-align@^3.0.1: version "3.0.1" @@ -3449,6 +3433,11 @@ 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" @@ -3470,6 +3459,11 @@ 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" @@ -3489,18 +3483,6 @@ 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" @@ -3567,11 +3549,6 @@ 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" @@ -3592,20 +3569,23 @@ 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@^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== +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== dependencies: - 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" + 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" bonjour-service@^1.2.1: version "1.3.0" @@ -3648,7 +3628,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@>=1.1.12, brace-expansion@^1.1.7, brace-expansion@^2.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-4.0.1.tgz#3387e13eaa2992025d05ea47308f77e4a8dedd1e" integrity sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA== @@ -3662,7 +3642,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2: +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: version "4.24.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== @@ -3672,17 +3652,6 @@ browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.23.3, 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" @@ -3708,7 +3677,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== @@ -3739,14 +3708,6 @@ 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" @@ -3757,14 +3718,6 @@ 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" @@ -3798,17 +3751,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, 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== +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== 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.2: +chalk@^4.0.0, chalk@^4.1.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== @@ -3890,7 +3843,7 @@ chevrotain@~11.0.3: "@chevrotain/utils" "11.0.3" lodash-es "4.17.21" -chokidar@^3.5.3, chokidar@^3.6.0: +chokidar@^3.4.2, 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== @@ -3915,7 +3868,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.3, clean-css@~5.3.2: +clean-css@^5.2.2, clean-css@^5.3.2, 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== @@ -4125,12 +4078,14 @@ 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@^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-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-type@^1.0.5: +content-type@~1.0.4, 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== @@ -4140,16 +4095,21 @@ 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.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" - integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== +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@>=0.7.0, cookie@^0.7.1: +cookie@0.7.1, cookie@>=0.7.0: 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" @@ -4198,6 +4158,17 @@ 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" @@ -4208,7 +4179,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.3: +cross-spawn@>=6.0.6, cross-spawn@^7.0.0, 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== @@ -4236,16 +4207,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.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== +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== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" postcss-value-parser "^4.2.0" -css-loader@^6.11.0: +css-loader@^6.8.1: version "6.11.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== @@ -4324,10 +4295,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.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.4.2.tgz#1a367ab1904c97af0bb2c7ae179764deae7b078b" - integrity sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg== +cssdb@^8.2.1: + version "8.2.2" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.2.2.tgz#0a5bcbc47a297e6b0296e6082f60363e17b337d4" + integrity sha512-Z3kpWyvN68aKyeMxOUGmffQeHjvrzDxbre2B2ikr/WqQ4ZMkhHu2nOD6uwSeq3TpuOYU7ckvmJRAUIt6orkYUg== cssesc@^3.0.0: version "3.0.0" @@ -4716,7 +4687,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.9, debug@^2.6.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -4730,13 +4701,6 @@ 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" @@ -4770,7 +4734,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.3.1: +deepmerge@^4.2.2, 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== @@ -4821,6 +4785,20 @@ 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" @@ -4828,7 +4806,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== @@ -4838,16 +4816,29 @@ 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.3: +dequal@^2.0.0: 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" @@ -5022,15 +5013,6 @@ 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" @@ -5046,11 +5028,6 @@ 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" @@ -5081,7 +5058,12 @@ emoticon@^4.0.1: resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-4.1.0.tgz#d5a156868ee173095627a33de3f1e914c3dde79e" integrity sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ== -encodeurl@^2.0.0: +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: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== @@ -5123,11 +5105,6 @@ 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" @@ -5138,13 +5115,6 @@ 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" @@ -5334,7 +5304,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== @@ -5347,7 +5317,7 @@ eval@^0.1.8: "@types/node" "*" require-like ">= 0.1.1" -eventemitter3@^4.0.0, eventemitter3@^4.0.4: +eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -5357,64 +5327,47 @@ 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.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== +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== dependencies: - 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" + 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" exsolve@^1.0.7: version "1.0.7" @@ -5502,6 +5455,11 @@ 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" @@ -5509,17 +5467,18 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -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== +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== dependencies: - 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" + 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" find-cache-dir@^4.0.0: version "4.0.0" @@ -5529,6 +5488,21 @@ 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" @@ -5554,6 +5528,33 @@ 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" @@ -5574,15 +5575,10 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -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== +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-extra@^10.1.0: version "10.1.0" @@ -5602,6 +5598,26 @@ 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" @@ -5643,36 +5659,12 @@ 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-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: +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== @@ -5701,14 +5693,29 @@ 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.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== +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== dependencies: - minimatch "^10.1.1" + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" minipass "^7.1.2" - path-scurry "^2.0.0" + 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" global-dirs@^3.0.0: version "3.0.1" @@ -5717,6 +5724,22 @@ 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" @@ -5727,7 +5750,7 @@ globals@^15.14.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== -globby@^11.1.0: +globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5750,7 +5773,7 @@ globby@^13.1.1, globby@^13.1.4: merge2 "^1.4.1" slash "^4.0.0" -gopd@^1.0.1, gopd@^1.2.0: +gopd@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== @@ -5828,7 +5851,7 @@ has-proto@^1.0.1: dependencies: call-bind "^1.0.7" -has-symbols@^1.0.3, has-symbols@^1.1.0: +has-symbols@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== @@ -6198,16 +6221,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, 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== +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== dependencies: - depd "~2.0.0" - inherits "~2.0.4" - setprototypeof "~1.2.0" - statuses "~2.0.2" - toidentifier "~1.0.1" + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" http-errors@~1.6.2: version "1.6.3" @@ -6253,16 +6276,18 @@ 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" @@ -6270,13 +6295,6 @@ 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" @@ -6292,7 +6310,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@^2.0.2: +image-size@>=1.2.1, image-size@^1.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== @@ -6302,7 +6320,12 @@ immediate@^3.2.3: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== -import-fresh@^3.3.0: +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: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -6330,22 +6353,30 @@ 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.0: +ini@^1.3.4, ini@^1.3.5, 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== @@ -6370,6 +6401,11 @@ 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" @@ -6541,6 +6577,11 @@ 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" @@ -6568,11 +6609,6 @@ 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" @@ -6585,10 +6621,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-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-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-typed-array@^1.1.3: version "1.1.13" @@ -6646,7 +6682,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@2.1.1, jackspeak@^3.1.2: version "2.1.1" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.1.1.tgz#2a42db4cfbb7e55433c28b6f75d8b796af9669cd" integrity sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw== @@ -6708,17 +6744,17 @@ joi@^17.9.2: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - 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== + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -6747,11 +6783,6 @@ 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" @@ -6870,6 +6901,11 @@ 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" @@ -6879,6 +6915,21 @@ 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" @@ -6940,10 +6991,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@^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@^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@^5.1.1: version "5.1.1" @@ -6989,15 +7040,14 @@ marked@^16.0.0: resolved "https://registry.yarnpkg.com/marked/-/marked-16.2.0.tgz#c407a4f7ed3acc1110812525cfd1b0ed8502792c" integrity sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg== -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-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" mdast-util-directive@^3.0.0: version "3.0.0" @@ -7324,10 +7374,24 @@ mdast-util-phrasing@^4.0.0: "@types/mdast" "^4.0.0" unist-util-is "^6.0.0" -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== +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== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -7392,10 +7456,17 @@ 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@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" - integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== +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" memfs@^4.6.0: version "4.17.2" @@ -7407,10 +7478,10 @@ memfs@^4.6.0: tree-dump "^1.0.1" tslib "^2.0.0" -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-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-stream@^2.0.0: version "2.0.0" @@ -7448,6 +7519,11 @@ 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" @@ -8103,7 +8179,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.0.0, micromark-util-sanitize-uri@^1.1.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== @@ -8225,11 +8301,6 @@ 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" @@ -8242,24 +8313,17 @@ 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.34: +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: 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-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== +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== mimic-response@^3.1.0: version "3.1.0" @@ -8271,10 +8335,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.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== +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== dependencies: schema-utils "^4.0.0" tapable "^2.2.1" @@ -8284,26 +8348,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.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: 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@^10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55" - integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ== +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== dependencies: - "@isaacs/brace-expansion" "^5.0.0" + brace-expansion "^2.0.1" 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@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -8338,7 +8402,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== @@ -8360,11 +8424,6 @@ 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" @@ -8375,11 +8434,6 @@ 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" @@ -8422,21 +8476,16 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -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-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-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" @@ -8464,13 +8513,6 @@ 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" @@ -8496,10 +8538,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.3: - version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" - integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== +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-is@^1.1.5: version "1.1.6" @@ -8529,7 +8571,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== @@ -8541,20 +8583,13 @@ 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.4.0: +once@^1.3.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" @@ -8584,10 +8619,19 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -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@^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-limit@^4.0.0: version "4.0.0" @@ -8596,6 +8640,20 @@ 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" @@ -8610,14 +8668,6 @@ 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" @@ -8627,12 +8677,15 @@ p-retry@^6.2.0: is-network-error "^1.0.0" retry "^0.13.1" -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" +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== package-json@^8.1.0: version "8.1.1" @@ -8678,7 +8731,7 @@ parse-entities@^4.0.0: is-decimal "^2.0.0" is-hexadecimal "^2.0.0" -parse-json@^5.2.0: +parse-json@^5.0.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== @@ -8713,7 +8766,7 @@ parse5@^7.0.0: dependencies: entities "^4.5.0" -parseurl@^1.3.3, parseurl@~1.3.2: +parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -8736,17 +8789,32 @@ 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.0.0, path-key@^3.1.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== @@ -8768,13 +8836,18 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -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== +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== dependencies: - lru-cache "^11.0.0" - minipass "^7.1.2" + 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== path-to-regexp@3.3.0: version "3.3.0" @@ -8788,11 +8861,6 @@ 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" @@ -8852,6 +8920,13 @@ 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" @@ -8892,15 +8967,15 @@ postcss-clamp@^4.1.0: dependencies: postcss-value-parser "^4.2.0" -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== +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== 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/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/utilities" "^2.0.0" postcss-color-hex-alpha@^10.0.0: @@ -8937,35 +9012,35 @@ postcss-convert-values@^6.1.0: browserslist "^4.23.0" postcss-value-parser "^4.2.0" -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== +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== dependencies: - "@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" + "@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" -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== +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== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.5" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/cascade-layer-name-parser" "^2.0.4" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -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== +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== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.5" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" + "@csstools/cascade-layer-name-parser" "^2.0.4" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" postcss-selector-parser "^7.0.0" postcss-dir-pseudo-class@^9.0.1: @@ -9002,12 +9077,12 @@ postcss-discard-unused@^6.0.5: dependencies: postcss-selector-parser "^6.0.16" -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== +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== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/postcss-progressive-custom-properties" "^4.0.0" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" @@ -9043,18 +9118,18 @@ postcss-image-set-function@^7.0.0: "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -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== +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== 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/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/utilities" "^2.0.0" -postcss-loader@^7.3.4: +postcss-loader@^7.3.3: version "7.3.4" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== @@ -9063,10 +9138,10 @@ postcss-loader@^7.3.4: jiti "^1.20.0" semver "^7.5.4" -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== +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== dependencies: postcss-value-parser "^4.2.0" @@ -9156,12 +9231,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -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== +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== dependencies: - "@csstools/selector-resolve-nested" "^3.1.0" + "@csstools/selector-resolve-nested" "^3.0.0" "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" @@ -9259,71 +9334,67 @@ postcss-place@^10.0.0: dependencies: postcss-value-parser "^4.2.0" -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== +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== dependencies: - "@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-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-font-format-keywords" "^4.0.0" - "@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-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-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.4" - "@csstools/postcss-media-minmax" "^2.0.9" - "@csstools/postcss-media-queries-aspect-ratio-number-values" "^3.0.5" + "@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-nested-calc" "^4.0.0" "@csstools/postcss-normalize-display-values" "^4.0.0" - "@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-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-scope-pseudo-class" "^4.0.1" - "@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-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-unset-value" "^4.0.0" - autoprefixer "^10.4.21" - browserslist "^4.26.0" + autoprefixer "^10.4.19" + browserslist "^4.23.1" css-blank-pseudo "^7.0.1" - css-has-pseudo "^7.0.3" + css-has-pseudo "^7.0.1" css-prefers-color-scheme "^10.0.0" - cssdb "^8.4.2" + cssdb "^8.2.1" postcss-attribute-case-insensitive "^7.0.1" postcss-clamp "^4.1.0" - postcss-color-functional-notation "^7.0.12" + postcss-color-functional-notation "^7.0.6" postcss-color-hex-alpha "^10.0.0" postcss-color-rebeccapurple "^10.0.0" - postcss-custom-media "^11.0.6" - postcss-custom-properties "^14.0.6" - postcss-custom-selectors "^8.0.5" + postcss-custom-media "^11.0.5" + postcss-custom-properties "^14.0.4" + postcss-custom-selectors "^8.0.4" postcss-dir-pseudo-class "^9.0.1" - postcss-double-position-gradients "^6.0.4" + postcss-double-position-gradients "^6.0.0" 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.12" - postcss-logical "^8.1.0" - postcss-nesting "^13.0.2" + postcss-lab-function "^7.0.6" + postcss-logical "^8.0.0" + postcss-nesting "^13.0.1" postcss-opacity-percentage "^3.0.0" postcss-overflow-shorthand "^6.0.0" postcss-page-break "^3.0.4" @@ -9421,7 +9492,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.33: +postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -9430,15 +9501,6 @@ postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.33: 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" @@ -9504,7 +9566,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== @@ -9524,12 +9586,12 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -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== +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== dependencies: - side-channel "^1.1.0" + side-channel "^1.0.6" quansync@^0.2.11: version "0.2.11" @@ -9558,20 +9620,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@^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== +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== dependencies: - bytes "~3.1.2" - http-errors "~2.0.1" - iconv-lite "~0.7.0" - unpipe "~1.0.0" + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" rc@1.2.8: version "1.2.8" @@ -9583,6 +9645,36 @@ 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" @@ -9591,15 +9683,29 @@ react-dom@^18.0.0: loose-envify "^1.1.0" scheduler "^0.23.2" -react-fast-compare@^3.2.0: +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: 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@npm:@slorber/react-helmet-async@1.3.0": +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: version "1.3.0" - resolved "https://registry.yarnpkg.com/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz#11fbc6094605cf60aa04a28c17e0aab894b4ecff" - integrity sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A== + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== dependencies: "@babel/runtime" "^7.12.5" invariant "^2.2.4" @@ -9612,10 +9718,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@^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-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-lifecycles-compat@^3.0.0: version "3.0.4" @@ -9726,6 +9832,18 @@ 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" @@ -9766,6 +9884,13 @@ 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" @@ -9956,10 +10081,20 @@ remark-parse@^11.0.0: micromark-util-types "^2.0.0" unified "^11.0.0" -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== +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== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -10029,7 +10164,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.14.2: +resolve@^1.1.6, 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== @@ -10055,6 +10190,13 @@ 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" @@ -10070,16 +10212,10 @@ roughjs@^4.6.6: points-on-curve "^0.2.0" points-on-path "^0.2.1" -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" +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== rtlcss@^4.1.0: version "4.3.0" @@ -10125,7 +10261,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.0.0": +"safer-buffer@>= 2.1.2 < 3", "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== @@ -10142,10 +10278,14 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -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@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-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" @@ -10209,27 +10349,29 @@ 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.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4, semver@^7.6.3: +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: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -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== +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== dependencies: - 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" + 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" serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" @@ -10264,15 +10406,15 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -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== +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== dependencies: - encodeurl "^2.0.0" - escape-html "^1.0.3" - parseurl "^1.3.3" - send "^1.2.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" set-function-length@^1.2.2: version "1.2.2" @@ -10291,7 +10433,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== @@ -10320,56 +10462,40 @@ 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.8.1: +shell-quote@^1.7.3, 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== -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== +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: - es-errors "^1.3.0" - object-inspect "^1.13.3" + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.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== +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-bound "^1.0.2" + call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.5" - object-inspect "^1.13.3" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" -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: +signal-exit@^3.0.0, signal-exit@^3.0.2: 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" @@ -10504,16 +10630,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" @@ -10587,11 +10713,6 @@ 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" @@ -10679,14 +10800,6 @@ 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" @@ -10702,6 +10815,11 @@ 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" @@ -10728,6 +10846,11 @@ 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" @@ -10747,11 +10870,6 @@ 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" @@ -10772,11 +10890,6 @@ 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" @@ -10792,7 +10905,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== @@ -10857,14 +10970,13 @@ 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@^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== +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== dependencies: - content-type "^1.0.5" - media-typer "^1.1.0" - mime-types "^3.0.0" + media-typer "0.3.0" + mime-types "~2.1.24" typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -10963,6 +11075,11 @@ 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" @@ -11104,7 +11221,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== @@ -11117,14 +11234,6 @@ 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" @@ -11166,11 +11275,6 @@ 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" @@ -11197,6 +11301,11 @@ 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" @@ -11230,7 +11339,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== @@ -11402,7 +11511,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@^5.2.2: +webpack-dev-server@>=5.2.1, webpack-dev-server@^4.15.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== @@ -11542,6 +11651,13 @@ 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" @@ -11633,6 +11749,11 @@ 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" @@ -11657,16 +11778,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"