Compare commits

..

No commits in common. "main" and "2025-11-04" have entirely different histories.

774 changed files with 14615 additions and 12578 deletions

View file

@ -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

View file

@ -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

View file

@ -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
@ -140,40 +120,24 @@ when and how the diagnostic runs:
### 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`.
ELP uses a declarative test fixture system that allows you to write tests with inline annotations and markers directly in test strings. This system is defined in `/data/sandcastle/boxes/fbsource/fbcode/whatsapp/elp/crates/project_model/src/test_fixture.rs`.
#### Key Features
1. **File Organization**: Use `//- /path/to/file.erl` to define multiple files
in a single test
2. **Metadata Markers**: Specify app names, include paths, OTP apps, etc. using
metadata after the path
1. **File Organization**: Use `//- /path/to/file.erl` to define multiple files in a single test
2. **Metadata Markers**: Specify app names, include paths, OTP apps, etc. using metadata after the path
3. **Annotations**: Mark expected diagnostics or ranges using `%% ^^^` syntax
4. **Cursors and Ranges**: Use `~` markers to indicate positions or ranges in
test code
4. **Cursors and Ranges**: Use `~` markers to indicate positions or ranges in test code
#### Annotation Syntax
Annotations allow you to mark expected diagnostics, types, or other information
directly in test code:
Annotations allow you to mark expected diagnostics, types, or other information directly in test code:
- **Basic annotation**: `%% ^^^ some text` - Points to the range above matching
the caret length
- **Top-of-file marker**: `%% <<< text` (at file start) - Creates annotation at
position 0..0
- **File-wide annotation**: `%% ^^^file text` - Annotation spans the entire file
contents
- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position
instead of first `^`
- **Basic annotation**: `%% ^^^ some text` - Points to the range above matching the caret length
- **Top-of-file marker**: `%% <<< text` (at file start) - Creates annotation at position 0..0
- **File-wide annotation**: `%% ^^^file text` - Annotation spans the entire file contents
- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position instead of first `^`
- **Multiline annotations**: Use continuation lines with `%% | next line`
- Continuation lines are particularly useful for diagnostics with related information:
```erlang
foo() -> syntax error oops.
%% ^^^^^ error: P1711: syntax error before: error
%% | Related info: 0:45-50 function foo/0 undefined
```
#### Example Test Fixture
@ -182,8 +146,9 @@ let fixture = r#"
//- /src/main.erl
-module(main).
foo( -> ok. %%
%% ^ error: W0004: Missing ')'~
foo(X) ->
X + undefined.
%% ^^^^^^^^^ error: type mismatch
"#;
```
@ -195,37 +160,34 @@ foo( -> ok. %%
### Running Tests for Specific Crates
When running tests for a specific crate, you need to specify the crate name, not
the directory name. The mapping is:
When running tests for a specific crate, you need to specify the crate name, not the directory name. The mapping is:
| Crate Name | Directory Name |
| -------------------- | ----------------------- |
| `elp` | `crates/elp` |
| `elp_base_db` | `crates/base_db` |
| `elp_eqwalizer` | `crates/eqwalizer` |
| Crate Name | Directory Name |
|------------|----------------|
| `elp_base_db` | `crates/base_db` |
| `elp_eqwalizer` | `crates/eqwalizer` |
| `elp_erlang_service` | `crates/erlang_service` |
| `elp_ide` | `crates/ide` |
| `elp_ide_assists` | `crates/ide_assists` |
| `elp_ide` | `crates/ide` |
| `elp_ide_assists` | `crates/ide_assists` |
| `elp_ide_completion` | `crates/ide_completion` |
| `elp_ide_db` | `crates/ide_db` |
| `elp_ide_ssr` | `crates/ide_ssr` |
| `elp_log` | `crates/elp_log` |
| `elp_project_model` | `crates/project_model` |
| `elp_syntax` | `crates/syntax` |
| `elp_text_edit` | `crates/text_edit` |
| `elp_types_db` | `crates/types_db` |
| `hir` | `crates/hir` |
| `elp_ide_db` | `crates/ide_db` |
| `elp_ide_ssr` | `crates/ide_ssr` |
| `elp_log` | `crates/elp_log` |
| `elp_project_model` | `crates/project_model` |
| `elp_syntax` | `crates/syntax` |
| `elp_text_edit` | `crates/text_edit` |
| `elp_types_db` | `crates/types_db` |
| `hir` | `crates/hir` |
| `erl_ast` | `crates/erl_ast` |
Example: To run tests for the `elp_ide` crate:
```bash
cargo test -p elp_ide
./meta/cargo.sh test -p elp_ide
```
Or to run tests in a specific directory:
```bash
cargo test --manifest-path crates/ide/Cargo.toml
./meta/cargo.sh test --manifest-path crates/ide/Cargo.toml
```
### Existing tests
@ -315,8 +277,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

12
.vscode/tasks.json vendored
View file

@ -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",

18
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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);

View file

@ -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<String> = env::args().collect();
println!("ARGS: {:?}", args);
}

View file

@ -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<FileId, Vec<(TextRange, Option<String>)>>,
pub annotations: FxHashMap<FileId, Vec<(TextRange, String)>>,
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());
@ -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<DB: SourceDatabaseExt>(&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) {

View file

@ -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<Regex> = {
let regexes: Vec<Vec<Regex>> = 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::<Vec<Regex>>()
};

View file

@ -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

View file

@ -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;

View file

@ -11,7 +11,6 @@
use std::cmp::Ordering;
use std::env;
use std::fs;
use std::io::IsTerminal;
use std::path::PathBuf;
use anyhow::Result;
@ -72,17 +71,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<String>,
}
#[derive(Clone, Debug, Bpaf)]
@ -155,6 +143,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 +161,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 +181,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 +205,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 +274,8 @@ pub struct Lint {
guard(format_guard, "Please use json")
)]
pub format: Option<String>,
/// Optional prefix to prepend to each diagnostic file path. Only used when --format=json is set
pub prefix: Option<String>,
/// Include diagnostics produced by erlc
pub include_erlc_diagnostics: bool,
@ -334,9 +332,6 @@ 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<String>,
@ -391,33 +386,6 @@ pub struct Ssr {
#[bpaf(long("parens"))]
pub paren_strategy: bool,
/// Dump a configuration snippet that can be put in .elp_lint.toml to match the given SSR patterns
pub dump_config: bool,
/// Show source code context for matches
#[bpaf(long("show-source"))]
pub show_source: bool,
/// Print NUM lines of leading context, enables --show-source
#[bpaf(short('B'), long("before-context"), argument("NUM"))]
pub before_context: Option<usize>,
/// Print NUM lines of trailing context, enables --show-source
#[bpaf(short('A'), long("after-context"), argument("NUM"))]
pub after_context: Option<usize>,
/// Print NUM lines of output context, enables --show-source
#[bpaf(short('C'), long("context"), argument("NUM"))]
pub context: Option<usize>,
/// Print SEP on line between matches with context, enables --show-source
#[bpaf(long("group-separator"), argument("SEP"))]
pub group_separator: Option<String>,
/// 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,
@ -474,6 +442,8 @@ pub struct Glean {
pub pretty: bool,
/// Output each fact separately
pub multi: bool,
/// Optional prefix to prepend to each fact
pub prefix: Option<String>,
}
#[derive(Clone, Debug, Bpaf)]
@ -520,16 +490,6 @@ pub struct Args {
/// Use buck2 targets for first stage project loading
pub buck_quick_start: bool,
/// Use color in output; WHEN is 'always', 'never', or 'auto'
#[bpaf(
long("color"),
long("colour"),
argument("WHEN"),
fallback(Some("always".to_string())),
guard(color_guard, "Please use always, never, or auto")
)]
pub color: Option<String>,
#[bpaf(external(command))]
pub command: Command,
}
@ -544,20 +504,6 @@ impl Args {
BuckQueryConfig::BuildGeneratedCode
}
}
/// Determine if color should be used based on the --color argument
pub fn should_use_color(&self) -> bool {
match self.color.as_deref() {
Some("always") => true,
Some("never") => false,
Some("auto") | None => {
// Check NO_COLOR environment variable - if set (regardless of value), disable color
// Also check if stdout is connected to a TTY
env::var("NO_COLOR").is_err() && std::io::stdout().is_terminal()
}
_ => false, // Should be caught by the guard, but handle anyway
}
}
}
pub fn command() -> impl Parser<Command> {
@ -629,12 +575,6 @@ pub fn command() -> impl Parser<Command> {
.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()
@ -696,7 +636,6 @@ pub fn command() -> impl Parser<Command> {
dialyze_all,
lint,
ssr,
search,
parse_all,
parse_elp,
explain,
@ -786,25 +725,6 @@ fn format_guard(format: &Option<String>) -> bool {
}
}
fn severity_completer(_: &Option<String>) -> Vec<(String, Option<String>)> {
vec![
("error".to_string(), None),
("warning".to_string(), None),
("weak_warning".to_string(), None),
("information".to_string(), None),
]
}
fn severity_guard(severity: &Option<String>) -> bool {
match severity {
None => true,
Some(s) if s == "error" || s == "warning" || s == "weak_warning" || s == "information" => {
true
}
_ => false,
}
}
fn macros_completer(_: &Option<String>) -> Vec<(String, Option<String>)> {
vec![
("expand".to_string(), None),
@ -820,14 +740,6 @@ fn macros_guard(format: &Option<String>) -> bool {
}
}
fn color_guard(color: &Option<String>) -> 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<String>) -> bool {
!data.is_empty()
@ -908,11 +820,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<String>) -> Result<MacroStrategy> {

View file

@ -24,6 +24,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;
@ -57,35 +58,6 @@ use crate::args::ParseAllElp;
use crate::reporting;
use crate::reporting::print_memory_usage;
fn parse_severity(severity: &str) -> Option<diagnostics::Severity> {
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<diagnostics::Severity>,
) -> bool {
match min_severity {
None => true,
Some(min) => severity_rank(diag_severity) <= severity_rank(min),
}
}
#[derive(Debug)]
struct ParseResult {
name: String,
@ -160,7 +132,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 +144,15 @@ pub fn parse_all(
let db = loaded.analysis_host.raw_database();
// We need a `Url` for converting to the lsp_types::Diagnostic for
// printing, but do not print it out. So just create a dummy value
let url = lsp_types::Url::parse("file:///unused_url").ok().unwrap();
telemetry::report_elapsed_time("parse-elp operational", start_time);
let memory_end = MemoryUsage::now();
let memory_used = memory_end - memory_start;
let min_severity = args
.severity
.as_ref()
.and_then(|s| parse_severity(s.as_str()));
res.retain(|parse_result| {
parse_result
.diagnostics
.diagnostics_for(parse_result.file_id)
.iter()
.any(|diag| meets_severity_threshold(diag.severity, min_severity))
});
if res.is_empty() {
if args.is_format_normal() {
writeln!(cli, "No errors reported")?;
@ -207,7 +171,6 @@ pub fn parse_all(
for diags in res {
let mut combined: Vec<diagnostics::Diagnostic> =
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,7 +197,7 @@ 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)?;
}
}
}
@ -279,10 +242,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 +289,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 +300,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 +321,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 +330,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 +340,13 @@ fn do_parse_all_seq(
fn do_parse_one(
db: &Analysis,
vfs: &Vfs,
config: &DiagnosticsConfig,
to: &Option<PathBuf>,
file_id: FileId,
name: &str,
) -> Result<Option<ParseResult>> {
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 +364,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:?}")?;
}
}

View file

@ -186,7 +186,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());
@ -266,7 +269,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)
@ -334,7 +339,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);
@ -401,7 +408,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 +482,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 +602,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();

View file

@ -150,15 +150,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 +169,7 @@ pub fn do_parse_one(
relative_path: relative_path.to_owned(),
line_num,
msg: err.msg.to_owned(),
range: range.copied(),
range,
}
})
.collect();

View file

@ -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<String>,
}
impl From<GleanFileId> 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<GleanFileId, String>,
prefix: Option<&String>,
) -> Option<(
FileFact,
FileLinesFact,
@ -937,7 +957,7 @@ impl GleanIndexer {
Option<(Vec<FunctionDeclarationFact>, XRefFact)>,
Option<ModuleFact>,
)> {
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<String> = None; // @oss-only
ModuleFact::new(
@ -1153,12 +1173,16 @@ impl GleanIndexer {
file_id: FileId,
path: &VfsPath,
project_id: ProjectId,
prefix: Option<&String>,
) -> Option<FileFact> {
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<VarDecl> {
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

View file

@ -13,14 +13,13 @@ 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 +52,17 @@ use elp_ide::elp_ide_db::elp_base_db::ProjectId;
use elp_ide::elp_ide_db::elp_base_db::Vfs;
use elp_ide::elp_ide_db::elp_base_db::VfsPath;
use elp_ide::elp_ide_db::source_change::SourceChange;
use elp_ide_db::text_edit::TextSize;
use elp_log::telemetry;
use elp_project_model::AppName;
use elp_project_model::AppType;
use elp_project_model::DiscoverConfig;
use elp_project_model::buck::BuckQueryConfig;
use elp_text_edit::TextSize;
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use hir::FormIdx;
use hir::InFile;
use indicatif::ParallelProgressIterator;
use itertools::Itertools;
use paths::Utf8PathBuf;
use rayon::prelude::ParallelBridge;
@ -132,101 +132,47 @@ pub fn load_project(
)
}
fn do_diagnostics_all(
cli: &mut dyn Cli,
fn do_parse_all(
cli: &dyn Cli,
analysis: &Analysis,
project_id: &ProjectId,
config: &DiagnosticsConfig,
args: &Lint,
loaded: &LoadResult,
module: &Option<String>,
) -> Result<(Vec<(String, FileId, DiagnosticCollection)>, bool, bool)> {
) -> Result<Vec<(String, FileId, DiagnosticCollection)>> {
let module_index = analysis.module_index(*project_id).unwrap();
let module_iter = module_index.iter_own();
let ignored_apps: FxHashSet<Option<Option<AppName>>> = 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 +239,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 +279,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 +288,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 +376,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,225 +388,14 @@ 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<String>,
err_in_diag: &mut bool,
module_count: &mut i32,
result: &(String, FileId, DiagnosticCollection),
) -> Result<bool> {
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<String>,
err_in_diag: &mut bool,
module_count: &mut i32,
result: &(String, FileId, DiagnosticCollection),
) -> Result<bool> {
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<diagnostics::Diagnostic>)],
) -> 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<String>,
err_in_diag: &mut bool,
module_count: &mut i32,
result: &(String, FileId, DiagnosticCollection),
) -> Result<bool> {
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<diagnostics::Diagnostic>)],
) -> 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<DiagnosticsConfig> {
let cfg_from_file = if args.read_config || args.config_file.is_some() {
read_lint_config_file(&args.project, &args.config_file)?
@ -736,82 +421,12 @@ fn get_diagnostics_config(args: &Lint) -> Result<DiagnosticsConfig> {
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 +611,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 +628,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::<Vec<_>>()
};
let changes = changes
.iter()
.filter_map(|d| {
form_from_diff(&self.analysis_host.analysis(), file_id, d)
})
.collect::<Vec<_>>();
for form_id in &changed_forms {
for form_id in &changes {
self.changed_forms.insert(InFile::new(file_id, *form_id));
}
@ -1031,24 +648,24 @@ impl<'a> Lints<'a> {
.flatten()
.collect::<Vec<_>>();
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 +737,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 +807,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 +942,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;
@ -1469,11 +1088,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 +1109,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![""],
);

File diff suppressed because it is too large Load diff

View file

@ -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}")?;
}

View file

@ -157,9 +157,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 +177,7 @@ impl ShellCommand {
rebar,
app: app.into(),
include_generated,
include_tests,
bail_on_error: false,
})));
}
@ -183,9 +185,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 +204,7 @@ impl ShellCommand {
rebar,
format: None,
include_generated,
include_tests,
bail_on_error: false,
stats: false,
list_modules: false,
@ -222,8 +226,10 @@ COMMANDS:
eqwalize <modules> 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 <app> Eqwalize all modules in specified application
--include-tests Also eqwalize test modules from project
--clause-coverage Use experimental clause coverage checker
";

View file

@ -11,12 +11,10 @@
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;
@ -30,9 +28,7 @@ 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;
@ -45,6 +41,7 @@ use elp_project_model::AppType;
use elp_project_model::DiscoverConfig;
use elp_project_model::buck::BuckQueryConfig;
use hir::Semantic;
use indicatif::ParallelProgressIterator;
use paths::Utf8PathBuf;
use rayon::prelude::ParallelBridge;
use rayon::prelude::ParallelIterator;
@ -65,7 +62,6 @@ 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();
@ -89,17 +85,10 @@ pub fn run_ssr_command(
let mut lint_config = LintConfig::default();
for pattern in &args.ssr_specs {
let normalized_pattern = normalize_ssr_pattern(pattern);
let severity = if args.dump_config {
// Set the severity so that squiggles are shown in the VS Code UI
Some(diagnostics::Severity::Information)
} else {
None
};
let ssr_lint = diagnostics::Lint::LintMatchSsr(MatchSsr {
ssr_pattern: normalized_pattern,
message: None,
strategy: Some(strategy),
severity,
});
lint_config.ad_hoc_lints.lints.push(ssr_lint);
}
@ -116,20 +105,15 @@ pub fn run_ssr_command(
.set_experimental(false)
.set_use_cli_severity(false);
if args.dump_config {
let result = toml::to_string::<LintsFromConfig>(&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(());
if diagnostics_config.enabled.all_enabled() && args.is_format_normal() {
writeln!(cli, "Reporting all diagnostics codes")?;
}
// 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);
let r = run_ssr(cli, &mut loaded, &diagnostics_config, args);
telemetry::report_elapsed_time("ssr done", start_time);
@ -149,7 +133,6 @@ pub fn run_ssr(
loaded: &mut LoadResult,
diagnostics_config: &DiagnosticsConfig,
args: &Ssr,
use_color: bool,
) -> Result<()> {
let analysis = loaded.analysis();
let (file_id, name) = match &args.module {
@ -183,23 +166,8 @@ pub fn run_ssr(
},
};
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,
)?;
}
let mut diags = match (file_id, name) {
(None, _) => do_parse_all(cli, &analysis, &loaded.project_id, diagnostics_config, args)?,
(Some(file_id), Some(name)) => {
if let Some(app) = &args.app
&& let Ok(Some(file_app)) = analysis.file_app_name(file_id)
@ -207,171 +175,42 @@ pub fn run_ssr(
{
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)?;
}
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:?}")
}
};
if match_count == 0 {
if diags.is_empty() {
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<diagnostics::Diagnostic>),
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,
)?;
diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
if args.is_format_json() {
for (_name, file_id, diags) in &diags {
for diag in diags {
// We use JSON output for CI, and want to see warnings too.
// So do not filter on errors only
let vfs_path = loaded.vfs.file_path(*file_id);
let analysis = loaded.analysis();
let root_path = &analysis
.project_data(*file_id)
.unwrap_or_else(|_err| panic!("could not find project data"))
.unwrap_or_else(|| panic!("could not find project data"))
.root_dir;
let relative_path = reporting::get_relative_path(root_path, vfs_path);
print_diagnostic_json(diag, &analysis, *file_id, relative_path, false, cli)?;
}
}
} else {
writeln!(cli, "Matches found in {} modules:", diags.len())?;
for (name, file_id, diags) in &diags {
writeln!(cli, " {}: {}", name, diags.len())?;
for diag in diags {
print_diagnostic(diag, &loaded.analysis(), *file_id, false, cli)?;
}
writeln!(cli)?;
}
}
}
@ -394,6 +233,41 @@ fn load_project(
query_config,
)
}
fn do_parse_all(
cli: &dyn Cli,
analysis: &Analysis,
project_id: &ProjectId,
config: &DiagnosticsConfig,
args: &Ssr,
) -> Result<Vec<(String, FileId, Vec<diagnostics::Diagnostic>)>> {
let module_index = analysis.module_index(*project_id).unwrap();
let module_iter = module_index.iter_own();
let pb = cli.progress(module_iter.len() as u64, "Scanning modules");
let app_name = args.app.as_ref().map(|name| AppName(name.to_string()));
Ok(module_iter
.par_bridge()
.progress_with(pb)
.map_with(
analysis.clone(),
|db, (module_name, _file_source, file_id)| {
if !otp_file_to_ignore(db, file_id)
&& db.file_app_type(file_id).ok() != Some(Some(AppType::Dep))
&& (app_name.is_none()
|| db.file_app_name(file_id).ok().as_ref() == Some(&app_name))
{
do_parse_one(db, config, file_id, module_name.as_str(), args).unwrap()
} else {
None
}
},
)
.flatten()
.collect())
}
fn do_parse_one(
db: &Analysis,
config: &DiagnosticsConfig,
@ -401,9 +275,6 @@ fn do_parse_one(
name: &str,
args: &Ssr,
) -> Result<Option<(String, FileId, Vec<diagnostics::Diagnostic>)>> {
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);
}
@ -415,6 +286,7 @@ fn do_parse_one(
config
.lints_from_config
.get_diagnostics(&mut diags, &sema, file_id);
sema.parse(file_id);
diags
})?;
@ -430,17 +302,11 @@ 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)?;
}
writeln!(cli, " {}", diag.print(&line_index, use_cli_severity))?;
Ok(())
}
@ -464,254 +330,3 @@ fn print_diagnostic_json(
)?;
Ok(())
}
/// Print a line with color highlighting
fn print_line_with_color(
line_num: usize,
line_content: &str,
is_match_line: bool,
start: &LineCol,
end: &LineCol,
current_line: u32,
cli: &mut dyn Cli,
) -> Result<(), anyhow::Error> {
// Line number in gray
write!(cli, "\x1b[90m{:4} |\x1b[0m ", line_num)?;
if !is_match_line {
// Non-match line: print normally
writeln!(cli, "{}", line_content)?;
} else {
// Match line: highlight the matched portion
if current_line == start.line && current_line == end.line {
// Single-line match
let start_col = start.col_utf16 as usize;
let end_col = end.col_utf16 as usize;
let before = &line_content[..start_col.min(line_content.len())];
let matched =
&line_content[start_col.min(line_content.len())..end_col.min(line_content.len())];
let after = &line_content[end_col.min(line_content.len())..];
write!(cli, "{}", before)?;
write!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold
writeln!(cli, "{}", after)?;
} else if current_line == start.line {
// First line of multi-line match
let start_col = start.col_utf16 as usize;
let before = &line_content[..start_col.min(line_content.len())];
let matched = &line_content[start_col.min(line_content.len())..];
write!(cli, "{}", before)?;
writeln!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold
} else if current_line == end.line {
// Last line of multi-line match
let end_col = end.col_utf16 as usize;
let matched = &line_content[..end_col.min(line_content.len())];
let after = &line_content[end_col.min(line_content.len())..];
write!(cli, "\x1b[91;1m{}\x1b[0m", matched)?; // Red bold
writeln!(cli, "{}", after)?;
} else {
// Middle line of multi-line match
writeln!(cli, "\x1b[91;1m{}\x1b[0m", line_content)?; // Red bold
}
}
Ok(())
}
/// Calculate context lines from the new grep-style arguments
fn calculate_context_lines(args: &Ssr) -> (usize, usize) {
// -C/--context takes precedence and sets both before and after
if let Some(context) = args.context {
return (context, context);
}
// Otherwise use individual before/after values, defaulting to 0
let before = args.before_context.unwrap_or(0);
let after = args.after_context.unwrap_or(0);
(before, after)
}
/// Determine if a group separator should be shown
fn should_show_group_separator(args: &Ssr, has_context: bool) -> Option<String> {
// 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(())
}

View file

@ -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<usize> {
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<usize> {
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<usize> {
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<u8>);
impl Default for Fake {

View file

@ -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.
@ -180,7 +180,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) {

View file

@ -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<F>(
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<F>(
get_file_info: F,
fn from_related(
line_index: &LineIndex,
url: &Url,
r: &Option<Vec<RelatedInformation>>,
) -> Option<Vec<DiagnosticRelatedInformation>>
where
F: Fn(elp_ide::elp_ide_db::elp_base_db::FileId) -> Option<(LineIndex, Url)>,
{
) -> Option<Vec<DiagnosticRelatedInformation>> {
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()
})

View file

@ -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();
}
@ -193,7 +193,6 @@ mod tests {
ssr_pattern: "ssr: _@A = 10.".to_string(),
message: None,
strategy: None,
severity: None,
}),
],
},

View file

@ -192,7 +192,7 @@ impl ReloadManager {
self.get_query_config()
}
pub fn get_query_config(&self) -> BuckQueryConfig {
fn get_query_config(&self) -> BuckQueryConfig {
if self.buck_quick_start {
match self.buck_generated {
BuckGenerated::NoLoadDone => BuckQueryConfig::BuckTargetsOnly,

View file

@ -1 +0,0 @@
Project Initialisation Failed: invalid or missing buck 2 configuration

View file

@ -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
Diagnostics reported in 4 modules:
app_a: 3
0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced.
5:9-5:31::[WeakWarning] [W0037] Unspecific include.
0:8-0:13::[WeakWarning] [W0046] The module is not documented.
auto_gen_a: 2
0:0-0:0::[Error] [W0012] Please add "-compile(warn_missing_spec_all)." to the module. If exported functions are not all specced, they need to be specced.
0:8-0:18::[WeakWarning] [W0046] The module is not documented.
eqwalizer: 1
1:8-1:17::[WeakWarning] [W0046] The module is not documented.
wa_buck2_module_search: 6
19:0-19:18::[WeakWarning] [W0040] The function is non-trivial, exported, but not documented.
2:8-2:30::[WeakWarning] [W0046] The module is not documented.
54:18-54:31::[WeakWarning] [W0051] Binary string can be written using sigil syntax.
56:18-56:28::[WeakWarning] [W0051] Binary string can be written using sigil syntax.
56:38-56:51::[WeakWarning] [W0051] Binary string can be written using sigil syntax.
60:30-60:40::[WeakWarning] [W0051] Binary string can be written using sigil syntax.

View file

@ -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 ')'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"}

View file

@ -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]).

View file

@ -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

View file

@ -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)

View file

@ -1,2 +0,0 @@
module specified: erlang_diagnostics_errors_gen
No matches found

View file

@ -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

View file

@ -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 <PROJECT> Path to directory with project, or to a JSON file (defaults to `.`)
--as <PROFILE> Rebar3 profile to pickup (default is test)
--format <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

View file

@ -1,4 +1,4 @@
Usage: [--project PROJECT] [--as PROFILE] [--rebar] [--bail-on-error] <APP>
Usage: [--project PROJECT] [--as PROFILE] [--include-tests] [--rebar] [--bail-on-error] <APP>
Available positional items:
<APP> app name
@ -6,6 +6,7 @@ Available positional items:
Available options:
--project <PROJECT> Path to directory with project, or to a JSON file (defaults to `.`)
--as <PROFILE> 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

View file

@ -1,9 +1,10 @@
Usage: [--project PROJECT] [--bail-on-error] <TARGET>
Usage: [--project PROJECT] [--include-tests] [--bail-on-error] <TARGET>
Available positional items:
<TARGET> target, like //erl/chatd/...
Available options:
--project <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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -72,6 +72,17 @@ Because in the expression's type:
Differs from the expected type: pid()
}
------------------------------ Detailed message ------------------------------
{'p', pid() | reference()} is not compatible with {'a', atom()} | {'p', pid()}
because
at tuple index 2:
{'p', pid() | reference()} is not compatible with {'p', pid()}
because
pid() | reference() is not compatible with pid()
because
reference() is not compatible with pid()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/case_predicates.erl:144:10
@ -88,6 +99,14 @@ Because in the expression's type:
However the following candidate: 'restarting'
Differs from the expected type: {'p', pid()} | 'undefined'
------------------------------ Detailed message ------------------------------
'undefined' | 'restarting' is not compatible with {'p', pid()} | 'undefined'
because
'restarting' is not compatible with {'p', pid()} | 'undefined'
because
'restarting' is not compatible with {'p', pid()}
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/case_predicates.erl:174:16

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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' 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

View file

@ -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

View file

@ -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,6 +118,13 @@ Because in the expression's type:
Context expects type: binary()
, atom()}
------------------------------ Detailed message ------------------------------
at tuple index 2:
{'foo', atom(), atom()} is not compatible with {'foo', binary(), atom()}
because
atom() is not compatible with binary()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/error_messages.erl:37:31
@ -102,6 +143,18 @@ Because in the expression's type:
The expected map has no corresponding key for: large_map_key_a.
, ... }
------------------------------ Detailed message ------------------------------
#{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}}
because
at key `foo`:
#{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}}
because
#{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c'} | #{large_map_key_d => 'large_map_val_d'}
because
#{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c'}
key `large_map_key_a` is declared in the former but not in the latter and the latter map has no default association
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/error_messages.erl:41:31
@ -120,4 +173,16 @@ Because in the expression's type:
The expected map has no corresponding key for: large_map_key_a.
, ... }
------------------------------ Detailed message ------------------------------
#{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'}
because
at key `foo`:
#{foo => #{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'}} is not compatible with #{foo => #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'}
because
#{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'} | 'any'
because
#{large_map_key_a => 'large_map_val_a', large_map_key_b => 'large_map_val_b'} is not compatible with #{large_map_key_c => 'large_map_val_c', large_map_key_d => 'large_map_val_d'}
key `large_map_key_a` is declared in the former but not in the latter and the latter map has no default association
8 ERRORS

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,13 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types
┌─ check/src/gradual_misc.erl:46:41
46 │ refine_tuple_neg(T) when is_tuple(T) -> T;
│ ^ T.
│ ^
│ │
│ T.
Expression has type: {'b', 'c'}
Context expected type: 'a' | {none()}
expected union does not contain any tuple type of size 2
4 ERRORS

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
@ -195,6 +207,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 +323,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 +393,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 +424,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 +509,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 +552,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 +752,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
@ -698,6 +775,16 @@ Because in the expression's type:
However the following candidate: 'v2_op'
Differs from the expected type: 'stuff1' | 'v0_op2' | 'stuff2' | 'v0_op1' | 'v1_op2' | ...
------------------------------ Detailed message ------------------------------
v2_op() is not compatible with v1_op()
because
'v2_op' | v1_op() is not compatible with v1_op()
because
'v2_op' | v1_op() is not compatible with 'v1_op1' | 'v1_op2' | stuff() | v0_op()
because
'v2_op' is not compatible with 'v1_op1' | 'v1_op2' | stuff() | v0_op()
error: type_alias_is_non_productive (See https://fb.me/eqwalizer_errors#type_alias_is_non_productive)
┌─ check/src/misc.erl:760:1
@ -747,6 +834,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 +861,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 +907,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 +939,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 +962,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

View file

@ -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

View file

@ -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

View file

@ -16,6 +16,16 @@ Because in the expression's type:
Context expects type: #{...}
The expected map has no corresponding key for: {ok, error}.
------------------------------ Detailed message ------------------------------
sets:set({'ok', 'error'}) is not compatible with sets:set({'ok', 'ok'})
because
#set{} | #{{ok, error} => []} is not compatible with sets:set({'ok', 'ok'})
because
#set{} | #{{ok, error} => []} is not compatible with #set{} | #{{ok, ok} => []}
because
#{{ok, error} => []} is not compatible with #set{} | #{{ok, ok} => []}
error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered)
┌─ check/src/opaque.erl:29:1
@ -37,6 +47,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 +93,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 +117,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

View file

@ -13,6 +13,12 @@ Because in the expression's type:
Here the type is: term()
Context expects type: #{...}
------------------------------ Detailed message ------------------------------
term() is not compatible with logger:metadata()
because
term() is not compatible with #{domain => [atom()], file => file:filename(), gl => pid(), line => number(), mfa => {atom(), atom(), number()}, pid => pid(), report_cb => logger:report_cb(), time => logger:timestamp(), atom() => term()}
error: reveal_type (See https://fb.me/eqwalizer_errors#reveal_type)
┌─ check/src/other.erl:59:27

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
@ -200,6 +228,12 @@ Because in the expression's type:
Context expects type: number() | 'false' | 'true'
No candidate matches in the expected union.
------------------------------ Detailed message ------------------------------
'a' is not compatible with number() | boolean()
because
'a' is not compatible with number()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/records.erl:287:23
@ -216,6 +250,12 @@ Because in the expression's type:
Context expects type: number() | 'false' | 'true'
No candidate matches in the expected union.
------------------------------ Detailed message ------------------------------
'a' is not compatible with number() | boolean()
because
'a' is not compatible with number()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/records.erl:292:5
@ -233,6 +273,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
@ -249,6 +297,12 @@ Because in the expression's type:
Context expects type: number() | 'false' | 'true'
No candidate matches in the expected union.
------------------------------ Detailed message ------------------------------
'a' is not compatible with number() | boolean()
because
'a' is not compatible with number()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/records.erl:314:5
@ -266,6 +320,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 +343,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
@ -315,6 +383,15 @@ Because in the expression's type:
No candidate matches in the expected union.
}
------------------------------ Detailed message ------------------------------
at tuple index 2:
{'int_bool_box', 'a'} is not compatible with {'int_bool_box', number() | boolean()}
because
'a' is not compatible with number() | boolean()
because
'a' is not compatible with number()
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/records.erl:334:5
@ -332,6 +409,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
@ -348,6 +434,12 @@ Because in the expression's type:
However the following candidate: 'false'
Differs from the expected type: 'true'
------------------------------ Detailed message ------------------------------
boolean() | 'true' is not compatible with 'true'
because
'false' is not compatible with 'true'
error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types)
┌─ check/src/records.erl:355:13
@ -371,6 +463,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 +486,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 +510,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 +594,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

View file

@ -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
@ -121,6 +172,16 @@ Because in the expression's type:
Context expects type: #{a := ..., ...}
The type of the expression is missing the following required keys: a.
------------------------------ Detailed message ------------------------------
mChainB() is not compatible with mChainA()
because
'nil' | #{b := mChainB()} is not compatible with mChainA()
because
'nil' | #{b := mChainB()} is not compatible with 'nil' | #{a := mChainA()}
because
#{b := mChainB()} is not compatible with 'nil' | #{a := mChainA()}
error: expected_fun_type (See https://fb.me/eqwalizer_errors#expected_fun_type)
┌─ check/src/recursive_aliases.erl:138:15
@ -149,6 +210,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

View file

@ -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
@ -224,6 +273,13 @@ 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
@ -254,6 +310,13 @@ 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
@ -311,6 +374,13 @@ 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
@ -328,6 +398,15 @@ 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
@ -345,6 +424,13 @@ 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
@ -362,6 +448,15 @@ 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
@ -379,6 +474,13 @@ 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
@ -395,6 +497,12 @@ 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
@ -411,6 +519,12 @@ 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
@ -427,6 +541,12 @@ 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

Some files were not shown because too many files have changed in this diff Show more