From babf61214c206f063911391aa6a4d1667106def4 Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Thu, 11 Dec 2025 07:25:42 -0800 Subject: [PATCH 01/46] Bad occurrence typing with maps:foreach -- repro Summary: Occurrence typing doesn't take place in `maps:foreach` (and several other custom functions). Repro. Reviewed By: michalmuskala Differential Revision: D88949717 fbshipit-source-id: d6225a708f0c632d914c4ca38b1d789a3b077311 --- .../eqwater/eqwater_maps.pretty | 18 +++++++++++++++++- .../eqwater/src/eqwater_maps.erl | 10 ++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty index 037e6b14a6..b5f637ead3 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/eqwater/eqwater_maps.pretty @@ -70,4 +70,20 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: #{a := dynamic(), dynamic() => dynamic()} Context expected type: 'err' -5 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ eqwater/src/eqwater_maps.erl:70:29 + │ +70 │ (_, #{a := V}) -> is_ok(V) + │ ^ + │ │ + │ V. +Expression has type: 'ok' | 'err' +Context expected type: 'ok' + │ + +Because in the expression's type: + Here the type is a union type with some valid candidates: 'ok' + However the following candidate: 'err' + Differs from the expected type: 'ok' + +6 ERRORS diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl b/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl index 78ae852b8a..68d938f790 100644 --- a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl +++ b/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl @@ -59,3 +59,13 @@ map_occ_08_neg(_) -> err. map_occ_09(#{a := undefined}) -> 1; map_occ_09(#{a := Map}) -> Map#{2 => 2}; map_occ_09(_) -> 3. + +-spec is_ok(ok) -> ok. +is_ok(ok) -> ok. + +-spec map_occ_foreach_neg(#{term() => #{a => ok | err}}) -> ok. +map_occ_foreach_neg(M) -> + maps:foreach(fun + (_, #{a := err}) -> ok; + (_, #{a := V}) -> is_ok(V) + end, M). From 0085fba772989aff5049d846d3988667cdd5fbc1 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Fri, 12 Dec 2025 01:38:41 -0800 Subject: [PATCH 02/46] Add `--severity` argument to `elp parse-elp` to limit output Summary: As title. Reviewed By: TD5 Differential Revision: D88963207 fbshipit-source-id: a8cc5ddfedcfa8063537e586ac086c8a0caac465 --- crates/elp/src/bin/args.rs | 30 ++++++++++++++++ crates/elp/src/bin/elp_parse_cli.rs | 36 +++++++++++++++++++ crates/elp/src/bin/main.rs | 18 ++++++++++ .../parse_all_diagnostics_error.stdout | 5 +++ .../src/resources/test/parse_elp_help.stdout | 3 +- 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout diff --git a/crates/elp/src/bin/args.rs b/crates/elp/src/bin/args.rs index 0f057ce1e6..db3bc33df2 100644 --- a/crates/elp/src/bin/args.rs +++ b/crates/elp/src/bin/args.rs @@ -72,6 +72,17 @@ pub struct ParseAllElp { /// Report system memory usage and other statistics #[bpaf(long("report-system-stats"))] pub report_system_stats: bool, + /// Minimum severity level to report. Valid values: error, warning, weak_warning, information + #[bpaf( + argument("SEVERITY"), + complete(severity_completer), + fallback(None), + guard( + severity_guard, + "Please use error, warning, weak_warning, or information" + ) + )] + pub severity: Option, } #[derive(Clone, Debug, Bpaf)] @@ -783,6 +794,25 @@ fn format_guard(format: &Option) -> bool { } } +fn severity_completer(_: &Option) -> Vec<(String, Option)> { + vec![ + ("error".to_string(), None), + ("warning".to_string(), None), + ("weak_warning".to_string(), None), + ("information".to_string(), None), + ] +} + +fn severity_guard(severity: &Option) -> bool { + match severity { + None => true, + Some(s) if s == "error" || s == "warning" || s == "weak_warning" || s == "information" => { + true + } + _ => false, + } +} + fn macros_completer(_: &Option) -> Vec<(String, Option)> { vec![ ("expand".to_string(), None), diff --git a/crates/elp/src/bin/elp_parse_cli.rs b/crates/elp/src/bin/elp_parse_cli.rs index 16d4675d7d..3e57d69b17 100644 --- a/crates/elp/src/bin/elp_parse_cli.rs +++ b/crates/elp/src/bin/elp_parse_cli.rs @@ -57,6 +57,35 @@ use crate::args::ParseAllElp; use crate::reporting; use crate::reporting::print_memory_usage; +fn parse_severity(severity: &str) -> Option { + match severity { + "error" => Some(diagnostics::Severity::Error), + "warning" => Some(diagnostics::Severity::Warning), + "weak_warning" => Some(diagnostics::Severity::WeakWarning), + "information" => Some(diagnostics::Severity::Information), + _ => None, + } +} + +fn severity_rank(severity: diagnostics::Severity) -> u8 { + match severity { + diagnostics::Severity::Error => 1, + diagnostics::Severity::Warning => 2, + diagnostics::Severity::WeakWarning => 3, + diagnostics::Severity::Information => 4, + } +} + +fn meets_severity_threshold( + diag_severity: diagnostics::Severity, + min_severity: Option, +) -> bool { + match min_severity { + None => true, + Some(min) => severity_rank(diag_severity) <= severity_rank(min), + } +} + #[derive(Debug)] struct ParseResult { name: String, @@ -162,6 +191,10 @@ pub fn parse_all( } res.sort_by(|a, b| a.name.cmp(&b.name)); let mut err_in_diag = false; + let min_severity = args + .severity + .as_ref() + .and_then(|s| parse_severity(s.as_str())); for diags in res { let mut combined: Vec = diags.diagnostics.diagnostics_for(diags.file_id); @@ -172,6 +205,9 @@ pub fn parse_all( let line_index = db.file_line_index(diags.file_id); combined.sort_by(|a, b| a.range.start().cmp(&b.range.start())); for diag in combined { + if !meets_severity_threshold(diag.severity, min_severity) { + continue; + } if args.is_format_json() { err_in_diag = true; let vfs_path = loaded.vfs.file_path(diags.file_id); diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 48fc5cc621..e1d26e3e7e 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -671,6 +671,24 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn parse_all_diagnostics_severity(buck: bool) { + simple_snapshot_expect_error( + args_vec![ + "parse-elp", + "--module", + "diagnostics", + "--severity", + "error" + ], + "diagnostics", + expect_file!("../resources/test/diagnostics/parse_all_diagnostics_error.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn parse_elp_file_attribute(buck: bool) { diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout new file mode 100644 index 0000000000..ca3ca245ab --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout @@ -0,0 +1,5 @@ +module specified: diagnostics +Diagnostics reported in 1 modules: + diagnostics: 6 + 3:0-3:35::[Error] [L0000] Issue in included file + 5:30-5:44::[Error] [L1295] type undefined_type/0 undefined diff --git a/crates/elp/src/resources/test/parse_elp_help.stdout b/crates/elp/src/resources/test/parse_elp_help.stdout index 1457aed5c5..75cc38354e 100644 --- a/crates/elp/src/resources/test/parse_elp_help.stdout +++ b/crates/elp/src/resources/test/parse_elp_help.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--module MODULE] [--file ARG] [--to TO] [--no-diags] [--experimental] [--as PROFILE] [--dump-includes] [--rebar] [--include-generated] [--serial] [--use-cli-severity] [[--format FORMAT]] [--report-system-stats] +Usage: [--project PROJECT] [--module MODULE] [--file ARG] [--to TO] [--no-diags] [--experimental] [--as PROFILE] [--dump-includes] [--rebar] [--include-generated] [--serial] [--use-cli-severity] [[--format FORMAT]] [--report-system-stats] [[--severity SEVERITY]] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) @@ -15,4 +15,5 @@ Available options: --use-cli-severity If specified, use the provided CLI severity mapping instead of the default one --format Show diagnostics in JSON format --report-system-stats Report system memory usage and other statistics + --severity Minimum severity level to report. Valid values: error, warning, weak_warning, information -h, --help Prints help information From a16af7e9e29c234045961e3139129a8c03af9282 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Fri, 12 Dec 2025 02:19:15 -0800 Subject: [PATCH 03/46] Update emacs eglot instructions Summary: Add a stanza to set the config so we get semantic highlighting of `dynamic()`, and types on hover. Reviewed By: michalmuskala Differential Revision: D88957646 fbshipit-source-id: 5be4d69fdcf02999b840081baae076b85f9a9260 --- website/docs/get-started/editors/emacs.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/website/docs/get-started/editors/emacs.md b/website/docs/get-started/editors/emacs.md index 072e6902da..fab357f4a4 100644 --- a/website/docs/get-started/editors/emacs.md +++ b/website/docs/get-started/editors/emacs.md @@ -37,9 +37,8 @@ additional configuration options. ### Semantic tokens -Semantic token support has been added to eglot, but is not yet in the -released version. But it is possible to install the updated version -of eglot. +Semantic token support has been added to eglot, but is not yet in the released +version. But it is possible to install the updated version of eglot. To do so, add @@ -52,8 +51,14 @@ to your `init.el`, then run `M-x eglot-upgrade-eglot` Once upgraded, add the following to the `(use-package` entry for `eglot` ```elisp - (setq eglot-semantic-token-modifiers ' - ("bound" "exported_function" "exported_type" "deprecated_function" "type_dynamic")) +(setq-default eglot-workspace-configuration + ;; Run `elp config` to see that options can be used here + ;; Use `eglot-show-workspace-configuration` to see what is sent + '(:elp (:highlightDynamic (:enable t) + :typesOnHover (:enable t) )) + + eglot-semantic-token-modifiers + '("bound" "exported_function" "exported_type" "deprecated_function" "type_dynamic")) ;; Each face name arises as a template from the modifiers as ;; "eglot-semantic-%s-face" @@ -92,7 +97,6 @@ Once upgraded, add the following to the `(use-package` entry for `eglot` (eglot--widening (font-lock-flush))))) ``` - ## lsp-mode Install the `lsp-mode` package, which is a generic Emacs client for LSP servers. From 553c90d63143c82057d4888b40109ec9dca2f329 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 12 Dec 2025 03:02:33 -0800 Subject: [PATCH 04/46] Bump OSS VS Code extension to 0.47.0 Summary: Due to some [most likely transient infra issues](https://github.com/WhatsApp/erlang-language-platform/actions/runs/20137940955), the latest release was only partially published. Bump the extension to trigger a new release. Reviewed By: alanz Differential Revision: D89024759 fbshipit-source-id: fce412fee451eb72246c121706108af3ac34d414 --- editors/code/package-lock.json | 4 ++-- editors/code/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 1adcc191a7..6309d4c81e 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1,12 +1,12 @@ { "name": "erlang-language-platform", - "version": "0.46.0", + "version": "0.47.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "erlang-language-platform", - "version": "0.46.0", + "version": "0.47.0", "hasInstallScript": true, "license": "Apache2", "devDependencies": { diff --git a/editors/code/package.json b/editors/code/package.json index ebf55e7c86..eb7ced3148 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -4,7 +4,7 @@ "description": "Erlang Language Support for VS Code, by WhatsApp.", "author": "Meta Platforms, Inc", "license": "Apache2", - "version": "0.46.0", + "version": "0.47.0", "icon": "images/elp-logo-color.png", "homepage": "https://whatsapp.github.io/erlang-language-platform/", "repository": { From 887d287c9491c7058e48df140e959a0f02b10ec9 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Fri, 12 Dec 2025 03:56:28 -0800 Subject: [PATCH 05/46] tweak `elp parse-elp --severity` output Summary: Apply the severity filter to the results before printing any information about them. This prevents printing a module name that has no errors in it, and gives a correct count in the module diagnostic summary Reviewed By: TD5 Differential Revision: D89043311 fbshipit-source-id: aa0c813314f32ca9c23d753c598974a1eb605390 --- crates/elp/src/bin/elp_parse_cli.rs | 21 ++++++++++++------- .../parse_all_diagnostics_error.stdout | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/elp/src/bin/elp_parse_cli.rs b/crates/elp/src/bin/elp_parse_cli.rs index 3e57d69b17..770ab60456 100644 --- a/crates/elp/src/bin/elp_parse_cli.rs +++ b/crates/elp/src/bin/elp_parse_cli.rs @@ -176,6 +176,19 @@ pub fn parse_all( 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")?; @@ -191,13 +204,10 @@ pub fn parse_all( } res.sort_by(|a, b| a.name.cmp(&b.name)); let mut err_in_diag = false; - let min_severity = args - .severity - .as_ref() - .and_then(|s| parse_severity(s.as_str())); for diags in res { let mut combined: Vec = diags.diagnostics.diagnostics_for(diags.file_id); + combined.retain(|diag| meets_severity_threshold(diag.severity, min_severity)); if args.is_format_normal() { writeln!(cli, " {}: {}", diags.name, combined.len())?; } @@ -205,9 +215,6 @@ pub fn parse_all( let line_index = db.file_line_index(diags.file_id); combined.sort_by(|a, b| a.range.start().cmp(&b.range.start())); for diag in combined { - if !meets_severity_threshold(diag.severity, min_severity) { - continue; - } if args.is_format_json() { err_in_diag = true; let vfs_path = loaded.vfs.file_path(diags.file_id); diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout index ca3ca245ab..3af8bfb086 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_error.stdout @@ -1,5 +1,5 @@ module specified: diagnostics Diagnostics reported in 1 modules: - diagnostics: 6 + diagnostics: 2 3:0-3:35::[Error] [L0000] Issue in included file 5:30-5:44::[Error] [L1295] type undefined_type/0 undefined From eb5e0abbf8a19f764fc93f1293872da87b74ee7f Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 12 Dec 2025 04:22:36 -0800 Subject: [PATCH 06/46] Install Buck2 in OSS CI workflow Summary: In preparation for enabling Buck2 tests on GitHub, we install the Buck2 binary. Reviewed By: michalmuskala Differential Revision: D89038926 fbshipit-source-id: 7e37c7e9ec143391e10eba8597df2b2aa9d55d0f --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf3834b5ad..2fcdf48405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,8 @@ jobs: run: | sudo apt-get update sudo apt-get install -y crossbuild-essential-arm64 + - name: Install Buck2 + uses: dtolnay/install-buck2@latest - id: setup-erlang uses: ./.github/actions/setup-erlang with: From 2bd3d57d021262cc9e00552c2004be5de05e42e0 Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Fri, 12 Dec 2025 06:08:41 -0800 Subject: [PATCH 07/46] Clean-up old error messages Summary: Use new error message generation for callback type errors, add corresponding test Cleanup old error message generation Reviewed By: TD5 Differential Revision: D88941623 fbshipit-source-id: 262c2031c7d19ad2528ef63a5d73d146fd84d408 --- .../check/callbacks3_neg-OTP-26.pretty | 27 ++++++++++++++----- .../check/callbacks3_neg-OTP-27.pretty | 27 ++++++++++++++----- .../check/callbacks3_neg-OTP-28.pretty | 26 +++++++++++++----- .../check/src/callbacks3_neg.erl | 7 ++++- 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty index 63be36e638..6cb2012096 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty @@ -1,14 +1,27 @@ error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 + ┌─ check/src/callbacks3_neg.erl:13:1 │ -12 │ -behavior(gen_server). +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_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. + │ 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'} │ - '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()} +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. + } -1 ERROR +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty index 63be36e638..6cb2012096 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty @@ -1,14 +1,27 @@ error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 + ┌─ check/src/callbacks3_neg.erl:13:1 │ -12 │ -behavior(gen_server). +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_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. + │ 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'} │ - '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()} +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. + } -1 ERROR +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty index a24c7a6a48..849039f298 100644 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty @@ -1,14 +1,26 @@ 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 + ┌─ check/src/callbacks3_neg.erl:13:1 │ -12 │ -behavior(gen_server). +13 │ -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' + +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(), gen_server:action()} | {'stop', term(), term()}, Got: 'wrong_ret'. + │ Incorrect return type for implementation of gen_server:handle_info/2. +Expected: {'noreply', term()} | {'noreply', term(), gen_server:action()} | {'stop', term(), term()} +Got: {'noreply', 'ok', 'wrong_atom'} │ - '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()} +Because in the expression's type: + { 'noreply', 'ok', + Here the type is: 'wrong_atom' + Context expects type: gen_server:action() + } -1 ERROR +2 ERRORS diff --git a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl index 8152487669..f904f0e718 100644 --- a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl +++ b/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl @@ -7,7 +7,8 @@ -export([ init/1, handle_call/3, - handle_cast/2 + handle_cast/2, + handle_info/2 ]). -behavior(gen_server). @@ -19,3 +20,7 @@ handle_call(_, _From, State) -> -spec handle_cast(ok, ok) -> wrong_ret. handle_cast(_, _) -> wrong_ret. + +-spec handle_info(ok, ok) -> {noreply, ok, wrong_atom}. +handle_info(_, _) -> + {noreply, ok, wrong_atom}. From b525eac0eaca72daeee43ba3ed32a432a8a83847 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 12 Dec 2025 06:17:31 -0800 Subject: [PATCH 08/46] Pass range to fixes callback Summary: It is common for the `fixes` callback to act on the original match range. By making the matched range available to the callback, we avoid manually including the range in the linter-specific context. Reviewed By: alanz Differential Revision: D89048282 fbshipit-source-id: 01faa9842f04c34efb14a7d803cf5317794ea321 --- crates/ide/src/diagnostics.rs | 3 ++- .../ide/src/diagnostics/boolean_precedence.rs | 15 +++++++-------- crates/ide/src/diagnostics/edoc.rs | 8 ++------ .../src/diagnostics/macro_precedence_suprise.rs | 14 ++++++-------- .../missing_compile_warn_missing_spec.rs | 6 ++---- .../ide/src/diagnostics/misspelled_attribute.rs | 7 +++---- .../ide/src/diagnostics/undocumented_module.rs | 17 ++++++++++------- crates/ide/src/diagnostics/unused_include.rs | 1 + crates/ide/src/diagnostics/unused_macro.rs | 8 +++++++- 9 files changed, 40 insertions(+), 39 deletions(-) diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index e3aa185b19..9852303132 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -865,6 +865,7 @@ pub(crate) trait GenericLinter: Linter { fn fixes( &self, _context: &Self::Context, + _range: TextRange, _sema: &Semantic, _file_id: FileId, ) -> Option> { @@ -898,7 +899,7 @@ impl GenericDiagnostics for T { if let Some(matches) = self.matches(sema, file_id) { for matched in matches { let message = self.match_description(&matched.context); - let fixes = self.fixes(&matched.context, sema, file_id); + let fixes = self.fixes(&matched.context, matched.range, sema, file_id); let tag = self.tag(&matched.context); let mut d = Diagnostic::new(self.id(), message, matched.range) .with_fixes(fixes) diff --git a/crates/ide/src/diagnostics/boolean_precedence.rs b/crates/ide/src/diagnostics/boolean_precedence.rs index 21e374a4f5..f8f7851f8a 100644 --- a/crates/ide/src/diagnostics/boolean_precedence.rs +++ b/crates/ide/src/diagnostics/boolean_precedence.rs @@ -66,7 +66,6 @@ impl Linter for BooleanPrecedenceLinter { #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Context { - range: TextRange, preceding_ws_range: TextRange, op: Op, lhs_complex: bool, @@ -101,6 +100,7 @@ impl GenericLinter for BooleanPrecedenceLinter { fn fixes( &self, context: &Self::Context, + range: TextRange, _sema: &Semantic, file_id: FileId, ) -> Option> { @@ -109,36 +109,36 @@ impl GenericLinter for BooleanPrecedenceLinter { // Add "replace with preferred operator" fix let assist_message = format!("Replace '{}' with '{}'", context.op, context.op.preferred()); let edit = TextEdit::replace( - context.op.range(context.range, context.preceding_ws_range), + context.op.range(range, context.preceding_ws_range), context.op.preferred().to_string(), ); fixes.push(fix( "replace_boolean_operator", &assist_message, SourceChange::from_text_edit(file_id, edit), - context.range, + range, )); // Add "add parens" fixes if applicable if context.lhs_complex { - fixes.push(parens_fix("LHS", file_id, context)); + fixes.push(parens_fix("LHS", file_id, context, range)); } if context.rhs_complex { - fixes.push(parens_fix("RHS", file_id, context)); + fixes.push(parens_fix("RHS", file_id, context, range)); } Some(fixes) } } -fn parens_fix(side: &str, file_id: FileId, context: &Context) -> Assist { +fn parens_fix(side: &str, file_id: FileId, context: &Context, range: TextRange) -> Assist { let assist_message = format!("Add parens to {side}"); let edit = add_parens_edit(&context.add_parens_range); fix( "replace_boolean_operator_add_parens", &assist_message, SourceChange::from_text_edit(file_id, edit), - context.range, + range, ) } @@ -231,7 +231,6 @@ fn collect_match( matches.push(GenericLinterMatchContext { range, context: Context { - range, preceding_ws_range, op: binop, lhs_complex, diff --git a/crates/ide/src/diagnostics/edoc.rs b/crates/ide/src/diagnostics/edoc.rs index ead44d7331..052c85d57f 100644 --- a/crates/ide/src/diagnostics/edoc.rs +++ b/crates/ide/src/diagnostics/edoc.rs @@ -56,7 +56,6 @@ impl Linter for EdocLinter { pub struct Context { header_ptr: Option>, doc_start: TextSize, - range: TextRange, } impl GenericLinter for EdocLinter { @@ -77,7 +76,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: doc.range, }, }); } @@ -88,7 +86,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: equiv.range, }, }); } @@ -99,7 +96,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: deprecated.range, }, }); } @@ -111,7 +107,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: hidden.range, }, }); } @@ -123,6 +118,7 @@ impl GenericLinter for EdocLinter { fn fixes( &self, context: &Self::Context, + range: TextRange, sema: &Semantic, file_id: FileId, ) -> Option> { @@ -134,7 +130,7 @@ impl GenericLinter for EdocLinter { file_id, header, context.doc_start, - context.range, + range, )]) } } diff --git a/crates/ide/src/diagnostics/macro_precedence_suprise.rs b/crates/ide/src/diagnostics/macro_precedence_suprise.rs index 838ddb3d9c..6cd5fe2429 100644 --- a/crates/ide/src/diagnostics/macro_precedence_suprise.rs +++ b/crates/ide/src/diagnostics/macro_precedence_suprise.rs @@ -35,9 +35,7 @@ use crate::diagnostics::Linter; use crate::fix; #[derive(Debug, Default, Clone, PartialEq)] -pub(crate) struct MacroPrecedenceContext { - range: TextRange, -} +pub(crate) struct MacroPrecedenceContext; pub(crate) struct MacroPrecedenceSupriseLinter; @@ -96,10 +94,9 @@ impl GenericLinter for MacroPrecedenceSupriseLinter { { let range = ast.range(); if range.file_id == file_id { - let context = MacroPrecedenceContext { range: range.range }; res.push(GenericLinterMatchContext { range: range.range, - context, + context: MacroPrecedenceContext, }); } } @@ -113,16 +110,17 @@ impl GenericLinter for MacroPrecedenceSupriseLinter { fn fixes( &self, - context: &Self::Context, + _context: &Self::Context, + range: TextRange, _sema: &Semantic, file_id: FileId, ) -> Option> { - let edit = add_parens_edit(&context.range); + let edit = add_parens_edit(&range); let fix = fix( "macro_precedence_add_parens", "Add parens to macro call", SourceChange::from_text_edit(file_id, edit), - context.range, + range, ); Some(vec![fix]) } diff --git a/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs b/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs index b1f24eb60f..dd0b677dce 100644 --- a/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs +++ b/crates/ide/src/diagnostics/missing_compile_warn_missing_spec.rs @@ -75,7 +75,6 @@ impl Linter for MissingCompileWarnMissingSpec { pub struct Context { found: Found, compile_option_id: Option, - target_range: TextRange, } impl GenericLinter for MissingCompileWarnMissingSpec { @@ -94,7 +93,6 @@ impl GenericLinter for MissingCompileWarnMissingSpec { context: Context { found: Found::No, compile_option_id: None, - target_range: DIAGNOSTIC_WHOLE_FILE_RANGE, }, }); } @@ -149,7 +147,6 @@ impl GenericLinter for MissingCompileWarnMissingSpec { context: Context { found: what.0, compile_option_id: what.1, - target_range: range, }, }); } @@ -160,6 +157,7 @@ impl GenericLinter for MissingCompileWarnMissingSpec { fn fixes( &self, context: &Self::Context, + range: TextRange, sema: &Semantic, file_id: FileId, ) -> Option> { @@ -184,7 +182,7 @@ impl GenericLinter for MissingCompileWarnMissingSpec { "add_warn_missing_spec_all", "Add compile option 'warn_missing_spec_all'", edit, - context.target_range, + range, )]) } } diff --git a/crates/ide/src/diagnostics/misspelled_attribute.rs b/crates/ide/src/diagnostics/misspelled_attribute.rs index f328f57b20..15ad1dd9d8 100644 --- a/crates/ide/src/diagnostics/misspelled_attribute.rs +++ b/crates/ide/src/diagnostics/misspelled_attribute.rs @@ -55,7 +55,6 @@ impl Linter for MisspelledAttributeLinter { #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Context { - range: TextRange, attr_name: String, suggested_rename: String, } @@ -88,7 +87,6 @@ impl GenericLinter for MisspelledAttributeLinter { res.push(GenericLinterMatchContext { range: attr_name_range, context: Context { - range: attr_name_range, attr_name: attr.name.to_string(), suggested_rename: suggested_rename.to_string(), }, @@ -110,16 +108,17 @@ impl GenericLinter for MisspelledAttributeLinter { fn fixes( &self, context: &Self::Context, + range: TextRange, _sema: &Semantic, file_id: FileId, ) -> Option> { - let edit = TextEdit::replace(context.range, context.suggested_rename.clone()); + let edit = TextEdit::replace(range, context.suggested_rename.clone()); let msg = format!("Change to '{}'", context.suggested_rename); Some(vec![fix( "fix_misspelled_attribute", &msg, SourceChange::from_text_edit(file_id, edit), - context.range, + range, )]) } } diff --git a/crates/ide/src/diagnostics/undocumented_module.rs b/crates/ide/src/diagnostics/undocumented_module.rs index eeb4caf7b1..1b2c80cfc9 100644 --- a/crates/ide/src/diagnostics/undocumented_module.rs +++ b/crates/ide/src/diagnostics/undocumented_module.rs @@ -48,9 +48,7 @@ impl Linter for UndocumentedModuleLinter { } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct Context { - module_name_range: TextRange, -} +pub struct Context; impl GenericLinter for UndocumentedModuleLinter { type Context = Context; @@ -71,16 +69,21 @@ impl GenericLinter for UndocumentedModuleLinter { if module_has_no_docs { let module_name = module_attribute.name()?; let module_name_range = module_name.syntax().text_range(); - let context = Context { module_name_range }; res.push(GenericLinterMatchContext { range: module_name_range, - context, + context: Context, }); } Some(res) } - fn fixes(&self, context: &Context, sema: &Semantic, file_id: FileId) -> Option> { + fn fixes( + &self, + _context: &Context, + range: TextRange, + sema: &Semantic, + file_id: FileId, + ) -> Option> { let insert_offset = helpers::moduledoc_insert_offset(sema, file_id)?; let mut builder = SourceChangeBuilder::new(file_id); builder.insert(insert_offset, "-moduledoc false.\n"); @@ -89,7 +92,7 @@ impl GenericLinter for UndocumentedModuleLinter { "add_moduledoc_false", "Add `-moduledoc false.` attribute", source_change, - context.module_name_range, + range, ); Some(vec![fix]) } diff --git a/crates/ide/src/diagnostics/unused_include.rs b/crates/ide/src/diagnostics/unused_include.rs index eed17b3c5c..eb26bcd923 100644 --- a/crates/ide/src/diagnostics/unused_include.rs +++ b/crates/ide/src/diagnostics/unused_include.rs @@ -137,6 +137,7 @@ impl GenericLinter for UnusedIncludeLinter { fn fixes( &self, context: &Self::Context, + _range: TextRange, _sema: &Semantic, file_id: FileId, ) -> Option> { diff --git a/crates/ide/src/diagnostics/unused_macro.rs b/crates/ide/src/diagnostics/unused_macro.rs index 089371f85b..6ea8756b91 100644 --- a/crates/ide/src/diagnostics/unused_macro.rs +++ b/crates/ide/src/diagnostics/unused_macro.rs @@ -88,7 +88,13 @@ impl GenericLinter for UnusedMacroLinter { Some(DiagnosticTag::Unused) } - fn fixes(&self, context: &Context, _sema: &Semantic, file_id: FileId) -> Option> { + fn fixes( + &self, + context: &Context, + _range: TextRange, + _sema: &Semantic, + file_id: FileId, + ) -> Option> { Some(vec![delete_unused_macro( file_id, context.delete_range, From 166770cc92c3db50613b6d2a1ba9acf13e240307 Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Mon, 15 Dec 2025 03:16:34 -0800 Subject: [PATCH 09/46] Remove logic for --include-tests, opt-in tests Summary: Remove all logic related to `--include-tests` in ELP for eqWAlizer. Enable eqWAlizer on tests by default. Reviewed By: michalmuskala Differential Revision: D89049936 fbshipit-source-id: b3702ddcc8cdb9a3ced65ec6855918ea37b2a072 --- crates/elp/src/bin/args.rs | 8 -------- crates/elp/src/bin/eqwalizer_cli.rs | 17 ++++------------- crates/elp/src/bin/glean.rs | 2 +- crates/elp/src/bin/reporting.rs | 11 ++--------- crates/elp/src/bin/shell.rs | 10 ++-------- crates/elp/src/convert.rs | 9 +-------- .../src/resources/test/eqwalize_all_help.stdout | 3 +-- .../elp/src/resources/test/eqwalize_app.stdout | 3 +-- .../resources/test/eqwalize_target_help.stdout | 3 +-- .../eqwalize_all_bail_on_error_failure.pretty | 10 +++++++++- .../standard/eqwalize_all_diagnostics.jsonl | 1 + .../standard/eqwalize_all_diagnostics.pretty | 10 +++++++++- .../standard/eqwalize_all_diagnostics_gen.jsonl | 1 + .../eqwalize_app_diagnostics_gen_rebar.pretty | 10 +++++++++- .../eqwalize_app_diagnostics_rebar.pretty | 10 +++++++++- crates/elp/src/server.rs | 2 +- crates/elp/src/snapshot.rs | 4 ++-- crates/elp/tests/slow-tests/buck_tests.rs | 2 +- crates/ide/src/lib.rs | 8 ++++---- crates/ide_db/src/eqwalizer.rs | 14 +++++--------- crates/ide_db/src/lib.rs | 2 +- 21 files changed, 65 insertions(+), 75 deletions(-) diff --git a/crates/elp/src/bin/args.rs b/crates/elp/src/bin/args.rs index db3bc33df2..2d97f50562 100644 --- a/crates/elp/src/bin/args.rs +++ b/crates/elp/src/bin/args.rs @@ -155,8 +155,6 @@ pub struct EqwalizeAll { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Exit with a non-zero status code if any errors are found pub bail_on_error: bool, /// Print statistics when done @@ -173,8 +171,6 @@ pub struct EqwalizeTarget { /// Also eqwalize opted-in generated modules from application (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Exit with a non-zero status code if any errors are found pub bail_on_error: bool, /// target, like //erl/chatd/... @@ -193,8 +189,6 @@ pub struct EqwalizeApp { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// Run with rebar pub rebar: bool, /// Exit with a non-zero status code if any errors are found @@ -217,8 +211,6 @@ pub struct EqwalizeStats { /// Also eqwalize opted-in generated modules from project (deprecated) #[bpaf(hide)] pub include_generated: bool, - /// Also eqwalize test modules from project - pub include_tests: bool, /// If specified, use the provided CLI severity mapping instead of the default one pub use_cli_severity: bool, } diff --git a/crates/elp/src/bin/eqwalizer_cli.rs b/crates/elp/src/bin/eqwalizer_cli.rs index a91bcdd869..141b2157d0 100644 --- a/crates/elp/src/bin/eqwalizer_cli.rs +++ b/crates/elp/src/bin/eqwalizer_cli.rs @@ -186,10 +186,7 @@ pub fn do_eqwalize_all( .par_bridge() .progress_with(pb.clone()) .map_with(analysis.clone(), |analysis, (name, _source, file_id)| { - if analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() - && !otp_file_to_ignore(analysis, file_id) + if analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { if args.stats { add_stat(name.to_string()); @@ -269,9 +266,7 @@ pub fn do_eqwalize_app( .iter_own() .filter_map(|(_name, _source, file_id)| { if analysis.file_app_name(file_id).ok()? == Some(AppName(args.app.clone())) - && analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() + && analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { Some(file_id) @@ -339,9 +334,7 @@ pub fn eqwalize_target( let vfs_path = VfsPath::from(src.clone()); if let Some((file_id, _)) = loaded.vfs.file_id(&vfs_path) { at_least_one_found = true; - if analysis - .should_eqwalize(file_id, args.include_tests) - .unwrap() + if analysis.should_eqwalize(file_id).unwrap() && !otp_file_to_ignore(analysis, file_id) { file_ids.push(file_id); @@ -408,9 +401,7 @@ pub fn eqwalize_stats( .par_bridge() .progress_with(pb.clone()) .map_with(analysis.clone(), |analysis, (name, _source, file_id)| { - if analysis - .should_eqwalize(file_id, args.include_tests) - .expect("cancelled") + if analysis.should_eqwalize(file_id).expect("cancelled") && !otp_file_to_ignore(analysis, file_id) { analysis diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index 2a45e450f2..bfd4757106 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -1560,7 +1560,7 @@ impl GleanIndexer { vars: FxHashMap<&Location, &String>, ) -> Vec { let mut result = vec![]; - if !db.is_eqwalizer_enabled(file_id, false) { + if !db.is_eqwalizer_enabled(file_id) { return result; } let module_diagnostics = db.eqwalizer_diagnostics_by_project(project_id, vec![file_id]); diff --git a/crates/elp/src/bin/reporting.rs b/crates/elp/src/bin/reporting.rs index fe38287629..7ab10459fe 100644 --- a/crates/elp/src/bin/reporting.rs +++ b/crates/elp/src/bin/reporting.rs @@ -227,9 +227,6 @@ impl Reporter for JsonReporter<'_> { diagnostics: &[EqwalizerDiagnostic], ) -> Result<()> { let line_index = self.analysis.line_index(file_id)?; - // Pass include_Tests = false so that errors for tests files that are not opted-in are tagged as - // arc_types::Severity::Disabled and don't break CI. - let eqwalizer_enabled = self.analysis.is_eqwalizer_enabled(file_id, false).unwrap(); let file_path = &self.loaded.vfs.file_path(file_id); let root_path = &self .analysis @@ -238,12 +235,8 @@ impl Reporter for JsonReporter<'_> { .root_dir; let relative_path = get_relative_path(root_path, file_path); for diagnostic in diagnostics { - let diagnostic = convert::eqwalizer_to_arc_diagnostic( - diagnostic, - &line_index, - relative_path, - eqwalizer_enabled, - ); + let diagnostic = + convert::eqwalizer_to_arc_diagnostic(diagnostic, &line_index, relative_path); let diagnostic = serde_json::to_string(&diagnostic)?; writeln!(self.cli, "{diagnostic}")?; } diff --git a/crates/elp/src/bin/shell.rs b/crates/elp/src/bin/shell.rs index 84ab218710..13ff79ed36 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -157,10 +157,9 @@ impl ShellCommand { } "eqwalize-app" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-app".into(), @@ -177,7 +176,6 @@ impl ShellCommand { rebar, app: app.into(), include_generated, - include_tests, bail_on_error: false, }))); } @@ -185,10 +183,9 @@ impl ShellCommand { } "eqwalize-all" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-all".into(), @@ -204,7 +201,6 @@ impl ShellCommand { rebar, format: None, include_generated, - include_tests, bail_on_error: false, stats: false, list_modules: false, @@ -226,10 +222,8 @@ COMMANDS: eqwalize Eqwalize specified modules --clause-coverage Use experimental clause coverage checker eqwalize-all Eqwalize all modules in the current project - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker eqwalize-app Eqwalize all modules in specified application - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker "; diff --git a/crates/elp/src/convert.rs b/crates/elp/src/convert.rs index 3715353c11..c8db07a5d0 100644 --- a/crates/elp/src/convert.rs +++ b/crates/elp/src/convert.rs @@ -126,18 +126,11 @@ pub fn eqwalizer_to_arc_diagnostic( d: &EqwalizerDiagnostic, line_index: &LineIndex, relative_path: &Path, - eqwalizer_enabled: bool, ) -> arc_types::Diagnostic { let pos = position(line_index, d.range.start()); let line_num = pos.line + 1; let character = Some(pos.character + 1); - let severity = if eqwalizer_enabled { - arc_types::Severity::Error - } else { - // We use Severity::Disabled so that diagnostics are reported in cont lint - // but not in CI. - arc_types::Severity::Disabled - }; + let severity = arc_types::Severity::Error; // formatting: https://fburl.com/max_wiki_link_to_phabricator_rich_text let explanation = match &d.explanation { Some(s) => format!("```\n{s}\n```"), diff --git a/crates/elp/src/resources/test/eqwalize_all_help.stdout b/crates/elp/src/resources/test/eqwalize_all_help.stdout index 43f66a0a3a..67497c4000 100644 --- a/crates/elp/src/resources/test/eqwalize_all_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_all_help.stdout @@ -1,11 +1,10 @@ -Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--include-tests] [--bail-on-error] [--stats] [--list-modules] +Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--bail-on-error] [--stats] [--list-modules] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) --format Show diagnostics in JSON format --rebar Run with rebar - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found --stats Print statistics when done --list-modules When printing statistics, include the list of modules parsed diff --git a/crates/elp/src/resources/test/eqwalize_app.stdout b/crates/elp/src/resources/test/eqwalize_app.stdout index eaf1d3126a..bb128f249a 100644 --- a/crates/elp/src/resources/test/eqwalize_app.stdout +++ b/crates/elp/src/resources/test/eqwalize_app.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--as PROFILE] [--include-tests] [--rebar] [--bail-on-error] +Usage: [--project PROJECT] [--as PROFILE] [--rebar] [--bail-on-error] Available positional items: app name @@ -6,7 +6,6 @@ Available positional items: Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) - --include-tests Also eqwalize test modules from project --rebar Run with rebar --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/eqwalize_target_help.stdout b/crates/elp/src/resources/test/eqwalize_target_help.stdout index eec74e4938..e9a01166dd 100644 --- a/crates/elp/src/resources/test/eqwalize_target_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_target_help.stdout @@ -1,10 +1,9 @@ -Usage: [--project PROJECT] [--include-tests] [--bail-on-error] +Usage: [--project PROJECT] [--bail-on-error] Available positional items: target, like //erl/chatd/... Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty b/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty index bfbe45f508..1d868bbbc6 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_all_bail_on_error_failure.pretty @@ -192,6 +192,14 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_b/src/app_b.erl:16:5 │ @@ -200,4 +208,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -20 ERRORS +21 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl index 8636389098..b311705d68 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.jsonl @@ -17,4 +17,5 @@ {"path":"app_a/src/app_a_mod2.erl","line":22,"char":1,"code":"ELP","severity":"error","name":"eqWAlizer: type_alias_is_non_productive","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nrecursive type invalid/0 is not productive\n```\n\n> [docs on `type_alias_is_non_productive`](https://fb.me/eqwalizer_errors#type_alias_is_non_productive)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":31,"char":9,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_test_helpers.erl","line":6,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'error'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/test/app_a_test_helpers_not_opted_in.erl","line":5,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_b/src/app_b.erl","line":16,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"L","replacement":null,"description":"```lang=error,counterexample\n`L`.\n\nExpression has type: [T]\nContext expected type: T\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty index bfbe45f508..1d868bbbc6 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics.pretty @@ -192,6 +192,14 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) ┌─ app_b/src/app_b.erl:16:5 │ @@ -200,4 +208,4 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: [T] Context expected type: T -20 ERRORS +21 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl index 8636389098..b311705d68 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl +++ b/crates/elp/src/resources/test/standard/eqwalize_all_diagnostics_gen.jsonl @@ -17,4 +17,5 @@ {"path":"app_a/src/app_a_mod2.erl","line":22,"char":1,"code":"ELP","severity":"error","name":"eqWAlizer: type_alias_is_non_productive","original":null,"replacement":null,"description":"```lang=error,counterexample\n\nrecursive type invalid/0 is not productive\n```\n\n> [docs on `type_alias_is_non_productive`](https://fb.me/eqwalizer_errors#type_alias_is_non_productive)","docPath":null} {"path":"app_a/src/app_a_mod2.erl","line":31,"char":9,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'an_atom'","replacement":null,"description":"```lang=error,counterexample\n`'an_atom'`.\n\nExpression has type: 'an_atom'\nContext expected type: number()\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_a/test/app_a_test_helpers.erl","line":6,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'wrong_ret'","replacement":null,"description":"```lang=error,counterexample\n`'wrong_ret'`.\n\nExpression has type: 'wrong_ret'\nContext expected type: 'error'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} +{"path":"app_a/test/app_a_test_helpers_not_opted_in.erl","line":5,"char":11,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"'error'","replacement":null,"description":"```lang=error,counterexample\n`'error'`.\n\nExpression has type: 'error'\nContext expected type: 'ok'\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} {"path":"app_b/src/app_b.erl","line":16,"char":5,"code":"ELP","severity":"error","name":"eqWAlizer: incompatible_types","original":"L","replacement":null,"description":"```lang=error,counterexample\n`L`.\n\nExpression has type: [T]\nContext expected type: T\n```\n\n> [docs on `incompatible_types`](https://fb.me/eqwalizer_errors#incompatible_types)","docPath":null} diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty index 67d78913b6..043aa00af0 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_gen_rebar.pretty @@ -192,4 +192,12 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' -19 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +20 ERRORS diff --git a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty index 67d78913b6..043aa00af0 100644 --- a/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty +++ b/crates/elp/src/resources/test/standard/eqwalize_app_diagnostics_rebar.pretty @@ -192,4 +192,12 @@ error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types Expression has type: 'wrong_ret' Context expected type: 'error' -19 ERRORS +error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) + ┌─ app_a/test/app_a_test_helpers_not_opted_in.erl:5:11 + │ +5 │ fail() -> error. + │ ^^^^^ 'error'. +Expression has type: 'error' +Context expected type: 'ok' + +20 ERRORS diff --git a/crates/elp/src/server.rs b/crates/elp/src/server.rs index 65c75a1bbc..98890640bc 100644 --- a/crates/elp/src/server.rs +++ b/crates/elp/src/server.rs @@ -2073,7 +2073,7 @@ impl Server { }; for (_, _, file_id) in module_index.iter_own() { - match snapshot.analysis.should_eqwalize(file_id, false) { + match snapshot.analysis.should_eqwalize(file_id) { Ok(true) => { files.push(file_id); } diff --git a/crates/elp/src/snapshot.rs b/crates/elp/src/snapshot.rs index e79251f73a..9aa3e7eeb4 100644 --- a/crates/elp/src/snapshot.rs +++ b/crates/elp/src/snapshot.rs @@ -193,7 +193,7 @@ impl Snapshot { ) -> Result<()> { let _ = self.analysis.def_map(file_id)?; if optimize_for_eqwalizer { - let should_eqwalize = self.analysis.should_eqwalize(file_id, false)?; + let should_eqwalize = self.analysis.should_eqwalize(file_id)?; if should_eqwalize { let _ = self.analysis.module_ast(file_id)?; } @@ -242,7 +242,7 @@ impl Snapshot { let file_ids: Vec = module_index .iter_own() .filter_map(|(_, _, file_id)| { - if let Ok(true) = self.analysis.should_eqwalize(file_id, false) { + if let Ok(true) = self.analysis.should_eqwalize(file_id) { Some(file_id) } else { None diff --git a/crates/elp/tests/slow-tests/buck_tests.rs b/crates/elp/tests/slow-tests/buck_tests.rs index e76fdb8bad..76540e2515 100644 --- a/crates/elp/tests/slow-tests/buck_tests.rs +++ b/crates/elp/tests/slow-tests/buck_tests.rs @@ -65,7 +65,7 @@ mod tests { let ast = analysis.module_ast(file_id).unwrap(); assert_eq!(ast.errors, vec![]); let eq_enabled = analysis - .is_eqwalizer_enabled(file_id, false) + .is_eqwalizer_enabled(file_id) .unwrap_or_else(|_| panic!("Failed to check if eqwalizer enabled for {module}")); assert_eq!(eq_enabled, eqwalizer_enabled); let project_data = analysis.project_data(file_id).unwrap(); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index dcaff6de70..009edd4684 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -251,9 +251,9 @@ impl Analysis { }) } - pub fn should_eqwalize(&self, file_id: FileId, include_tests: bool) -> Cancellable { + pub fn should_eqwalize(&self, file_id: FileId) -> Cancellable { let is_in_app = self.file_app_type(file_id).ok() == Some(Some(AppType::App)); - Ok(is_in_app && self.is_eqwalizer_enabled(file_id, include_tests)?) + Ok(is_in_app && self.is_eqwalizer_enabled(file_id)?) } /// Computes the set of eqwalizer diagnostics for the given files, @@ -383,8 +383,8 @@ impl Analysis { /// - the app (the module belongs to) has `.eqwalizer` marker in the roof /// - or the module has `-typing([eqwalizer]).` pragma /// - or the whole project has `enable_all=true` in its `.elp.toml` file - pub fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> Cancellable { - self.with_db(|db| db.is_eqwalizer_enabled(file_id, include_tests)) + pub fn is_eqwalizer_enabled(&self, file_id: FileId) -> Cancellable { + self.with_db(|db| db.is_eqwalizer_enabled(file_id)) } /// ETF for the module's abstract forms diff --git a/crates/ide_db/src/eqwalizer.rs b/crates/ide_db/src/eqwalizer.rs index 4deed79778..fca577bb21 100644 --- a/crates/ide_db/src/eqwalizer.rs +++ b/crates/ide_db/src/eqwalizer.rs @@ -12,7 +12,6 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::FileRange; -use elp_base_db::FileSource; use elp_base_db::ModuleName; use elp_base_db::ProjectId; use elp_base_db::SourceDatabase; @@ -89,7 +88,7 @@ pub trait EqwalizerDatabase: fn types_for_file(&self, file_id: FileId) -> Option>>; fn has_eqwalizer_module_marker(&self, file_id: FileId) -> bool; fn has_eqwalizer_ignore_marker(&self, file_id: FileId) -> bool; - fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> bool; + fn is_eqwalizer_enabled(&self, file_id: FileId) -> bool; } pub fn eqwalizer_diagnostics_by_project( @@ -114,7 +113,7 @@ fn type_at_position( db: &dyn EqwalizerDatabase, range: FileRange, ) -> Option> { - if !db.is_eqwalizer_enabled(range.file_id, false) { + if !db.is_eqwalizer_enabled(range.file_id) { return None; } let project_id = db.file_app_data(range.file_id)?.project_id; @@ -149,7 +148,7 @@ fn type_at_position( } fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option>> { - if !db.is_eqwalizer_enabled(file_id, false) { + if !db.is_eqwalizer_enabled(file_id) { return None; } let project_id = db.file_app_data(file_id)?.project_id; @@ -162,7 +161,7 @@ fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option bool { +fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId) -> bool { if !otp_supported_by_eqwalizer() { return false; } @@ -178,11 +177,8 @@ fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId, include_tes let project = db.project_data(project_id); let eqwalizer_config = &project.eqwalizer_config; let module_index = db.module_index(project_id); - let is_src = module_index.file_source_for_file(file_id) == Some(FileSource::Src); - let is_test_opted_in = db.is_test_suite_or_test_helper(file_id) == Some(true) && include_tests; let global_opt_in = eqwalizer_config.enable_all; - let opt_in = - (global_opt_in && (is_src || is_test_opted_in)) || db.has_eqwalizer_module_marker(file_id); + let opt_in = global_opt_in || db.has_eqwalizer_module_marker(file_id); let ignored_in_config = if let Some(module_name) = module_index.module_for_file(file_id) { eqwalizer_config .ignore_modules_compiled_patterns diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 9b64d09b31..30a67cfa24 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -385,7 +385,7 @@ impl TypedSemantic for RootDatabase { let project_id = app_data.project_id; - let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id, false); + let eqwalizer_enabled = self.is_eqwalizer_enabled(file_id); if !eqwalizer_enabled { return Some(vec![]); } From d2da476882e325c0d431a1623dc4f5e7710e91b9 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Tue, 16 Dec 2025 02:03:43 -0800 Subject: [PATCH 10/46] Add test project for code gen Summary: ELP using buck is able to trigger rules that generate code, and load this generated code as part of the project. This diff brings in a test project with an example of this process, for ensuring our support is sold. Reviewed By: robertoaloi Differential Revision: D88953198 fbshipit-source-id: b92cf86b556f5465b2a528895b4e56852630f82e --- test_projects/codegen_test/.elp.toml | 8 + test_projects/codegen_test/README.md | 139 ++++++++++++++++++ .../app_a/src/codegen_test.app.src | 14 ++ .../codegen_test/app_a/src/example_usage.erl | 51 +++++++ .../app_a/test/codegen_test_SUITE.erl | 77 ++++++++++ .../generated/example_service_client.erl | 37 +++++ .../generated/example_service_types.erl | 55 +++++++ .../generated/example_service_types.hrl | 27 ++++ .../templates/example_service_client.erl | 38 +++++ .../templates/example_service_types.erl | 56 +++++++ .../templates/example_service_types.hrl | 28 ++++ 11 files changed, 530 insertions(+) create mode 100644 test_projects/codegen_test/.elp.toml create mode 100644 test_projects/codegen_test/README.md create mode 100644 test_projects/codegen_test/app_a/src/codegen_test.app.src create mode 100644 test_projects/codegen_test/app_a/src/example_usage.erl create mode 100644 test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl create mode 100644 test_projects/codegen_test/generated/example_service_client.erl create mode 100644 test_projects/codegen_test/generated/example_service_types.erl create mode 100644 test_projects/codegen_test/generated/example_service_types.hrl create mode 100644 test_projects/codegen_test/templates/example_service_client.erl create mode 100644 test_projects/codegen_test/templates/example_service_types.erl create mode 100644 test_projects/codegen_test/templates/example_service_types.hrl diff --git a/test_projects/codegen_test/.elp.toml b/test_projects/codegen_test/.elp.toml new file mode 100644 index 0000000000..f0f166bcf8 --- /dev/null +++ b/test_projects/codegen_test/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = true +included_targets = [ "fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_app" ] +source_root = "whatsapp/elp/test_projects/codegen_test" + +[eqwalizer] +enable_all = true diff --git a/test_projects/codegen_test/README.md b/test_projects/codegen_test/README.md new file mode 100644 index 0000000000..1af599454c --- /dev/null +++ b/test_projects/codegen_test/README.md @@ -0,0 +1,139 @@ +# Code Generation Test Project + +This test project demonstrates Buck2-based code generation for Erlang +applications using `buck_genrule`. + +## Project Structure + +``` +codegen_test/ +├── .elp.toml # ELP configuration +├── BUCK # Buck build configuration with genrule +├── README.md # This file +├── templates/ # Erlang template files (input) +│ ├── example_service_types.erl # Type definitions template +│ ├── example_service_client.erl # Client stubs template +│ └── example_service_types.hrl # Header file template +├── generated/ # Pre-generated code (for reference) +│ ├── example_service_types.erl # Generated type module +│ ├── example_service_client.erl # Generated client stubs +│ └── example_service_types.hrl # Generated header file +└── app_a/ + ├── src/ + │ ├── codegen_test.app.src # Application metadata + │ └── example_usage.erl # Example using generated code + └── test/ + └── codegen_test_SUITE.erl # Test suite +``` + +## How It Works + +### 1. Template Files + +The templates directory contains well-formed Erlang files that serve as input to +the code generation process. These are complete, valid Erlang files that are +copied to the output directory during the build. + +### 2. Code Generation + +The code is generated automatically during the build process using a +`buck_genrule`: + +```python +buck_genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + bash = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", + cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.erl %OUT%\\example_service_types.erl", +) +``` + +The genrule: + +- Takes the template file as input (`srcs`) +- Defines named outputs using `outs` parameter (creates subtargets for each + file) +- Uses `cp` (bash) or `copy` (cmd_exe) to copy template files to `$OUT` +- Outputs files to `$OUT` directory in `buck-out/` + +### 3. Build Integration + +The `BUCK` file references the generated files using subtarget syntax: + +```python +erlang_app( + name = "example_service_generated", + srcs = [ + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + ":example_service_types_hrl[example_service_types.hrl]", + ], +) +``` + +When Buck builds the application: + +1. The genrule runs first, copying the template Erlang files to the output +2. The generated files are placed in + `buck-out/v2/gen/.../example_service_types_erl/` +3. The `erlang_app` consumes these files via subtarget references (`[filename]` + syntax) +4. The files are copied to the application's build directory in `buck-out/` + +### 4. Using Generated Code + +Application code includes the generated header and uses the types: + +```erlang +-module(example_usage). +-include("example_service_types.hrl"). + +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +get_user_by_id(UserId) -> + example_service_client:get_user(UserId). +``` + +## Building and Testing + +```bash +# Build just the code generation step +buck2 build fbcode//whatsapp/elp/test_projects/codegen_test:example_service_types_erl + +# Build the application (automatically runs code generation) +buck2 build fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_app + +# Run tests +buck2 test fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_SUITE + +# View generated files in buck-out +find buck-out -path "*codegen_test*" -name "example_service_*.erl" +``` + +## Test Coverage + +The test suite (`app_a/test/codegen_test_SUITE.erl`) verifies: + +- Generated record types work correctly +- Generated client functions are callable +- Record fields have correct types and defaults +- Integration with application code + +All tests pass: + +``` +Tests finished: Pass 3. Fail 0. Fatal 0. Skip 0. Build failure 0 +``` diff --git a/test_projects/codegen_test/app_a/src/codegen_test.app.src b/test_projects/codegen_test/app_a/src/codegen_test.app.src new file mode 100644 index 0000000000..7a38d49a24 --- /dev/null +++ b/test_projects/codegen_test/app_a/src/codegen_test.app.src @@ -0,0 +1,14 @@ +{application, codegen_test, + [{description, "Test application demonstrating code generation"}, + {vsn, "1.0.0"}, + {registered, []}, + {applications, + [kernel, + stdlib, + example_service_generated + ]}, + {env,[]}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/test_projects/codegen_test/app_a/src/example_usage.erl b/test_projects/codegen_test/app_a/src/example_usage.erl new file mode 100644 index 0000000000..a624c2089b --- /dev/null +++ b/test_projects/codegen_test/app_a/src/example_usage.erl @@ -0,0 +1,51 @@ +-module(example_usage). +-compile([warn_missing_spec_all]). +-moduledoc """ +Example module that demonstrates using generated code. +This module uses the types and client functions generated from +the example_service.schema file. +""". + +%% Include the generated header file +-include_lib("example_service_generated/include/example_service_types.hrl"). + +%% API exports +-export([ + create_sample_user/0, + create_query_request/1, + get_user_by_id/1 +]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-doc """ +Creates a sample user_info record using generated types +""". +-spec create_sample_user() -> #user_info{}. +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +-doc """ +Creates a query_request record +""". +-spec create_query_request(binary()) -> #query_request{}. +create_query_request(QueryId) -> + #query_request{ + query_id = QueryId, + filters = [{age, greater_than, 18}] + }. + +-doc """ +Uses the generated client to get user information +""". +-spec get_user_by_id(binary()) -> {ok, term()} | {error, term()}. +get_user_by_id(UserId) -> + %% This calls the generated client function + example_service_client:get_user(UserId). diff --git a/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl b/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl new file mode 100644 index 0000000000..797c3c58f3 --- /dev/null +++ b/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl @@ -0,0 +1,77 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Test suite for code generation functionality +%%% @end +%%%------------------------------------------------------------------- +-module(codegen_test_SUITE). + +-include_lib("stdlib/include/assert.hrl"). +-include("example_service_types.hrl"). + +%% CT callbacks +-export([all/0, init_per_suite/1, end_per_suite/1]). + +%% Test cases +-export([ + test_generated_types/1, + test_generated_client/1, + test_user_record_creation/1 +]). + +%%%=================================================================== +%%% CT Callbacks +%%%=================================================================== + +all() -> + [ + test_generated_types, + test_generated_client, + test_user_record_creation + ]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +%%%=================================================================== +%%% Test Cases +%%%=================================================================== + +test_generated_types(_Config) -> + %% Test that we can create a user_info record + User = #user_info{ + user_id = <<"test_123">>, + username = <<"testuser">>, + age = 30, + is_active = true + }, + + %% Verify the record fields + ?assertEqual(<<"test_123">>, User#user_info.user_id), + ?assertEqual(<<"testuser">>, User#user_info.username), + ?assertEqual(30, User#user_info.age), + ?assertEqual(true, User#user_info.is_active), + + ok. + +test_generated_client(_Config) -> + %% Test that the generated client module exists and can be called + Result = example_service_client:get_user(<<"user_123">>), + + %% The generated stub returns {ok, generated_response} + ?assertMatch({ok, _}, Result), + + ok. + +test_user_record_creation(_Config) -> + %% Test the example_usage module + User = example_usage:create_sample_user(), + + %% Verify it's a valid user_info record + ?assertMatch(#user_info{}, User), + ?assertEqual(<<"user_123">>, User#user_info.user_id), + ?assertEqual(25, User#user_info.age), + + ok. diff --git a/test_projects/codegen_test/generated/example_service_client.erl b/test_projects/codegen_test/generated/example_service_client.erl new file mode 100644 index 0000000000..6b38c39425 --- /dev/null +++ b/test_projects/codegen_test/generated/example_service_client.erl @@ -0,0 +1,37 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. diff --git a/test_projects/codegen_test/generated/example_service_types.erl b/test_projects/codegen_test/generated/example_service_types.erl new file mode 100644 index 0000000000..3f89adcba9 --- /dev/null +++ b/test_projects/codegen_test/generated/example_service_types.erl @@ -0,0 +1,55 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. diff --git a/test_projects/codegen_test/generated/example_service_types.hrl b/test_projects/codegen_test/generated/example_service_types.hrl new file mode 100644 index 0000000000..4c957a0906 --- /dev/null +++ b/test_projects/codegen_test/generated/example_service_types.hrl @@ -0,0 +1,27 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). diff --git a/test_projects/codegen_test/templates/example_service_client.erl b/test_projects/codegen_test/templates/example_service_client.erl new file mode 100644 index 0000000000..ab30a89f74 --- /dev/null +++ b/test_projects/codegen_test/templates/example_service_client.erl @@ -0,0 +1,38 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. + diff --git a/test_projects/codegen_test/templates/example_service_types.erl b/test_projects/codegen_test/templates/example_service_types.erl new file mode 100644 index 0000000000..833df167c6 --- /dev/null +++ b/test_projects/codegen_test/templates/example_service_types.erl @@ -0,0 +1,56 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. + diff --git a/test_projects/codegen_test/templates/example_service_types.hrl b/test_projects/codegen_test/templates/example_service_types.hrl new file mode 100644 index 0000000000..88a2e7aed2 --- /dev/null +++ b/test_projects/codegen_test/templates/example_service_types.hrl @@ -0,0 +1,28 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). + From ba4505e981affde892245bd946fce7fb1beba01e Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 16 Dec 2025 08:56:01 -0800 Subject: [PATCH 11/46] Do not run application_env linter on test suites Reviewed By: alanz, TheGeorge, michalmuskala Differential Revision: D89287769 fbshipit-source-id: 29618250ee6d75ede850603cb60c5e4d77e3ff06 --- crates/ide/src/diagnostics/application_env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide/src/diagnostics/application_env.rs b/crates/ide/src/diagnostics/application_env.rs index 1e4fe7c4cf..be22f581f5 100644 --- a/crates/ide/src/diagnostics/application_env.rs +++ b/crates/ide/src/diagnostics/application_env.rs @@ -36,7 +36,7 @@ pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { conditions: DiagnosticConditions { experimental: false, include_generated: true, - include_tests: true, + include_tests: false, default_disabled: false, }, checker: &|diags, sema, file_id, _ext| { From 271a52c37cd77c27c51641d61efbae006888ba61 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Tue, 16 Dec 2025 09:08:25 -0800 Subject: [PATCH 12/46] honour `--include-generated` for `elp ssr` Summary: As title Reviewed By: michalmuskala, TD5 Differential Revision: D89292307 fbshipit-source-id: cbde669d57276b2aaa2c7365906e3ed86f15aec5 --- crates/elp/src/bin/main.rs | 30 +++++++++++++++++++ crates/elp/src/bin/ssr_cli.rs | 3 ++ .../diagnostics/ssr_exclude_generated.stdout | 2 ++ .../diagnostics/ssr_include_generated.stdout | 5 ++++ 4 files changed, 40 insertions(+) create mode 100644 crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout create mode 100644 crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index e1d26e3e7e..c9b2db945a 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -2191,6 +2191,36 @@ mod tests { ) } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn ssr_exclude_generated_by_default(buck: bool) { + simple_snapshot( + args_vec!["ssr", "--module", "erlang_diagnostics_errors_gen", "ok"], + "diagnostics", + expect_file!("../resources/test/diagnostics/ssr_exclude_generated.stdout"), + buck, + None, + ); + } + + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn ssr_include_generated_when_requested(buck: bool) { + simple_snapshot( + args_vec![ + "ssr", + "--module", + "erlang_diagnostics_errors_gen", + "--include-generated", + "ok" + ], + "diagnostics", + expect_file!("../resources/test/diagnostics/ssr_include_generated.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] // We cannot use `should_panic` for this test, since the OSS CI runs with the `buck` feature disabled. diff --git a/crates/elp/src/bin/ssr_cli.rs b/crates/elp/src/bin/ssr_cli.rs index f5729034b2..36f26d4554 100644 --- a/crates/elp/src/bin/ssr_cli.rs +++ b/crates/elp/src/bin/ssr_cli.rs @@ -401,6 +401,9 @@ fn do_parse_one( name: &str, args: &Ssr, ) -> Result)>> { + if !args.include_generated && db.is_generated(file_id)? { + return Ok(None); + } if !args.include_tests && db.is_test_suite_or_test_helper(file_id)?.unwrap_or(false) { return Ok(None); } diff --git a/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout new file mode 100644 index 0000000000..1cef26124c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout @@ -0,0 +1,2 @@ +module specified: erlang_diagnostics_errors_gen +No matches found diff --git a/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout new file mode 100644 index 0000000000..2b88b6e10c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout @@ -0,0 +1,5 @@ +module specified: erlang_diagnostics_errors_gen + erlang_diagnostics_errors_gen: 1 + 6:5-6:7::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ok. + +Matches found in 1 modules From 858b8e64ab85f60b39029d8113cefc5e29b5c9d1 Mon Sep 17 00:00:00 2001 From: Facebook Community Bot Date: Tue, 16 Dec 2025 10:54:56 -0800 Subject: [PATCH 13/46] Re-sync with internal repository (#143) The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging. fbshipit-source-id: a4e36831ac3939cb9a4881ca077b5f8c0d421104 --- .cargo/config.toml | 4 ++-- .vscode/tasks.json | 12 +++++----- crates/base_db/src/lib.rs | 4 ++-- crates/elp/src/arc_types.rs | 4 ++-- crates/elp/src/bin/glean.rs | 12 +++++----- crates/elp/src/bin/main.rs | 4 ++-- crates/elp/src/config.rs | 4 ++-- crates/elp/src/lib.rs | 4 ++-- crates/elp/src/server/setup.rs | 4 ++-- crates/hir/src/lib.rs | 2 +- crates/hir/src/name.rs | 2 +- crates/hir/src/sema.rs | 2 +- crates/hir/src/sema/to_def.rs | 6 ++--- crates/ide/src/annotations.rs | 8 +++---- crates/ide/src/diagnostics.rs | 8 +++---- crates/ide/src/diagnostics/application_env.rs | 4 ++-- .../ide/src/diagnostics/atoms_exhaustion.rs | 14 +++++------ .../ide/src/diagnostics/debugging_function.rs | 4 ++-- .../src/diagnostics/deprecated_function.rs | 4 ++-- .../src/diagnostics/unexported_function.rs | 6 ++--- .../ide/src/diagnostics/unspecific_include.rs | 4 ++-- crates/ide/src/doc_links.rs | 8 +++---- crates/ide/src/lib.rs | 2 +- crates/ide_completion/src/lib.rs | 4 ++-- crates/ide_db/src/diagnostic_code.rs | 24 +++++++++---------- crates/ide_db/src/lib.rs | 2 +- crates/project_model/src/buck.rs | 2 +- erlang_service/src/elp_lint.erl | 8 +++---- 28 files changed, 83 insertions(+), 83 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 7e4e7a0f90..eb89fa1e55 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,8 @@ [alias] xtask = "run --package xtask --" -# @fb-only -# @fb-only +# @fb-only: [build] +# @fb-only: target-dir = "../../../buck-out/elp" [profile.release] codegen-units = 1 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 51f0340659..7572a84e98 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ { "label": "ELP: build (debug)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build", "command": "cargo build", // @oss-only "group": { "kind": "build", @@ -19,7 +19,7 @@ { "label": "ELP: build (release)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build --release", "command": "cargo build --release", // @oss-only "group": { "kind": "build", @@ -34,7 +34,7 @@ { "label": "ELP: build (release-thin)", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh build --profile release-thin --bins", "command": "cargo build --profile release-thin --bins", // @oss-only "group": { "kind": "build", @@ -49,7 +49,7 @@ { "label": "ELP: run clippy on workspace", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/clippy.sh --workspace --tests", "command": "cargo clippy --workspace --tests", // @oss-only "group": { "kind": "build", @@ -64,7 +64,7 @@ { "label": "ELP: run clippy on workspace, apply fixes", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/clippy.sh --workspace --tests --fix", "command": "cargo clippy --workspace --tests --fix", // @oss-only "group": { "kind": "build", @@ -79,7 +79,7 @@ { "label": "ELP: run tests on workspace", "type": "shell", - // @fb-only + // @fb-only: "command": "./meta/cargo.sh test --workspace", "command": "cargo test --workspace", // @oss-only "group": { "kind": "build", diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 6b3757ff43..0cd8df74c9 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -32,7 +32,7 @@ mod module_index; // Public API pub mod fixture; -// @fb-only +// @fb-only: mod meta_only; pub mod test_utils; pub use change::Change; pub use elp_project_model::AppType; @@ -476,7 +476,7 @@ static ref IGNORED_SOURCES: Vec = { let regexes: Vec> = vec![ vec![Regex::new(r"^.*_SUITE_data/.+$").unwrap()], //ignore sources goes here - // @fb-only + // @fb-only: meta_only::ignored_sources_regexes() ]; regexes.into_iter().flatten().collect::>() }; diff --git a/crates/elp/src/arc_types.rs b/crates/elp/src/arc_types.rs index c113311661..374dcdba2f 100644 --- a/crates/elp/src/arc_types.rs +++ b/crates/elp/src/arc_types.rs @@ -8,8 +8,8 @@ * above-listed licenses. */ -// @fb-only -// @fb-only +// @fb-only: /// Types as defined in https://www.internalfb.com/intern/wiki/Linting/adding-linters/#flow-type +// @fb-only: /// and https://www.internalfb.com/code/fbsource/[1238f73dac0efd4009443fee6a345a680dc9401b]/whatsapp/server/erl/tools/lint/arcanist.py?lines=17 use std::path::Path; use serde::Serialize; diff --git a/crates/elp/src/bin/glean.rs b/crates/elp/src/bin/glean.rs index bfd4757106..cb420261d2 100644 --- a/crates/elp/src/bin/glean.rs +++ b/crates/elp/src/bin/glean.rs @@ -84,7 +84,7 @@ const REC_ARITY: u32 = 99; const HEADER_ARITY: u32 = 100; const FACTS_FILE: &str = "facts.json"; -// @fb-only +// @fb-only: mod meta_only; #[derive(Serialize, Debug, Eq, Hash, PartialEq, Clone)] struct GleanFileId(u32); @@ -994,7 +994,7 @@ impl GleanIndexer { .filter(|text| !text.is_empty()) }); - // @fb-only + // @fb-only: let exdoc_link = elp_ide::meta_only::exdoc_links::module_exdoc_link(&module, &sema); let exdoc_link: Option = None; // @oss-only ModuleFact::new( @@ -1532,7 +1532,7 @@ impl GleanIndexer { }) => { let def = macro_def.as_ref()?; let mut resolved = Self::resolve_macro_v2(sema, def, source_file, ctx)?; - // @fb-only + // @fb-only: meta_only::resolve_macro_expansion(sema, *expansion, ctx, &mut resolved); Some(resolved) } hir::AnyExpr::Pat(Pat::MacroCall { macro_def, .. }) @@ -1875,9 +1875,9 @@ impl GleanIndexer { let source_file = sema.parse(file_id); let range = Self::find_range(sema, ctx, &source_file, &expr_source)?; - // @fb-only - // @fb-only - // @fb-only + // @fb-only: use elp_ide::meta_only::wam_links; + // @fb-only: let wam_ctx = wam_links::WamEventCtx::new(sema.db.upcast()); + // @fb-only: let wam_url = wam_ctx.build_wam_link(name).map(|link| link.url()); let wam_url = None; // @oss-only Some(XRef { diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index c9b2db945a..ac2fc901fa 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -40,7 +40,7 @@ mod erlang_service_cli; mod explain_cli; mod glean; mod lint_cli; -// @fb-only +// @fb-only: mod meta_only; mod reporting; mod shell; mod ssr_cli; @@ -110,7 +110,7 @@ fn setup_cli_telemetry(args: &Args) { } _ => { // Initialize CLI telemetry, if used - // @fb-only + // @fb-only: meta_only::initialize_telemetry(); } } } diff --git a/crates/elp/src/config.rs b/crates/elp/src/config.rs index 2637eae5a2..681a022261 100644 --- a/crates/elp/src/config.rs +++ b/crates/elp/src/config.rs @@ -30,7 +30,7 @@ use serde::de::DeserializeOwned; use serde_json::json; use crate::from_json; -// @fb-only +// @fb-only: use crate::meta_only; // Defines the server-side configuration of ELP. We generate *parts* // of VS Code's `package.json` config from this. @@ -180,7 +180,7 @@ impl Config { return; } self.data = ConfigData::from_json(json); - // @fb-only + // @fb-only: meta_only::harmonise_gks(self); } pub fn update_gks(&mut self, json: serde_json::Value) { diff --git a/crates/elp/src/lib.rs b/crates/elp/src/lib.rs index 0e9a2e28e6..f462141e39 100644 --- a/crates/elp/src/lib.rs +++ b/crates/elp/src/lib.rs @@ -37,7 +37,7 @@ pub mod line_endings; pub mod lsp_ext; mod mem_docs; pub mod memory_usage; -// @fb-only +// @fb-only: mod meta_only; mod op_queue; mod project_loader; pub mod reload; @@ -108,7 +108,7 @@ pub fn otp_file_to_ignore(db: &Analysis, file_id: FileId) -> bool { "redbug_dtop", ] .iter() - // @fb-only + // @fb-only: .chain(meta_only::FILES_TO_IGNORE.iter()) .map(SmolStr::new) .collect(); } diff --git a/crates/elp/src/server/setup.rs b/crates/elp/src/server/setup.rs index 8dba975e73..4b8615e07c 100644 --- a/crates/elp/src/server/setup.rs +++ b/crates/elp/src/server/setup.rs @@ -33,7 +33,7 @@ use super::FILE_WATCH_LOGGER_NAME; use super::logger::LspLogger; use crate::config::Config; use crate::from_json; -// @fb-only +// @fb-only: use crate::meta_only::get_log_dir; use crate::server::Handle; use crate::server::LOGGER_NAME; use crate::server::Server; @@ -126,7 +126,7 @@ impl ServerSetup { // Set up a logger for tracking down why we are seeing stale // results when branches are switched, as per T218973130 - // @fb-only + // @fb-only: let log_dir = get_log_dir(); let log_dir = format!("{}/elp", std::env::temp_dir().display()); // @oss-only let _ = fs::create_dir_all(&log_dir); let log_file = format!( diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1ec75fd25e..35585ef702 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -155,7 +155,7 @@ pub use name::MacroName; pub use name::Name; pub use name::NameArity; pub use name::known; -// @fb-only +// @fb-only: pub use name::meta_only; pub use sema::AtomDef; pub use sema::CallDef; pub use sema::DefinitionOrReference; diff --git a/crates/hir/src/name.rs b/crates/hir/src/name.rs index 00ce2f89f1..f94f139d38 100644 --- a/crates/hir/src/name.rs +++ b/crates/hir/src/name.rs @@ -10,7 +10,7 @@ //! See [`Name`]. -// @fb-only +// @fb-only: pub mod meta_only; use std::borrow::Cow; use std::collections::HashSet; diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index 78a7db7c3c..7d1b416a63 100644 --- a/crates/hir/src/sema.rs +++ b/crates/hir/src/sema.rs @@ -102,7 +102,7 @@ use crate::resolver::Resolution; use crate::resolver::Resolver; mod find; -// @fb-only +// @fb-only: pub mod meta_only; pub mod to_def; pub struct ModuleIter(Arc); diff --git a/crates/hir/src/sema/to_def.rs b/crates/hir/src/sema/to_def.rs index 5918054fc5..0c8eba227e 100644 --- a/crates/hir/src/sema/to_def.rs +++ b/crates/hir/src/sema/to_def.rs @@ -42,7 +42,7 @@ use crate::macro_exp; use crate::macro_exp::BuiltInMacro; use crate::macro_exp::MacroExpCtx; use crate::resolver::Resolver; -// @fb-only +// @fb-only: use crate::sema::meta_only; pub trait ToDef: Clone { type Def; @@ -567,7 +567,7 @@ pub fn resolve_call_target( let fn_name: Name = sema.db.lookup_atom(body[*name].as_atom()?); let mo = None; // @oss-only - // @fb-only + // @fb-only: meta_only::resolve_handle_call_target(sema, arity, file_id, &module_name, &fn_name); if let Some(r) = mo { r } else { @@ -890,7 +890,7 @@ lazy_static! { static ref DYNAMIC_CALL_PATTERNS: FxHashMap = { let mut patterns = FxHashMap::default(); add_dynamic_call_patterns(&mut patterns); - // @fb-only + // @fb-only: meta_only::add_dynamic_call_patterns(&mut patterns); patterns }; } diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 6cbdd3469c..d0ae18c158 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -17,7 +17,7 @@ use elp_syntax::TextRange; use fxhash::FxHashMap; use fxhash::FxHashSet; -// @fb-only +// @fb-only: use crate::meta_only; use crate::runnables::Runnable; use crate::runnables::runnables; @@ -57,11 +57,11 @@ pub struct Link { } #[rustfmt::skip] -// @fb-only +// @fb-only: pub(crate) fn annotations(db: &RootDatabase, file_id: FileId) -> Vec { pub(crate) fn annotations(_db: &RootDatabase, _file_id: FileId) -> Vec { // @oss-only - // @fb-only + // @fb-only: let mut annotations = Vec::default(); let annotations = Vec::default(); // @oss-only - // @fb-only + // @fb-only: meta_only::annotations(db, file_id, &mut annotations); annotations } diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 9852303132..093ba35986 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -117,7 +117,7 @@ mod macro_precedence_suprise; mod map_find_to_syntax; mod map_insertion_to_syntax; mod meck; -// @fb-only +// @fb-only: mod meta_only; mod missing_compile_warn_missing_spec; mod missing_module; mod missing_separator; @@ -1525,7 +1525,7 @@ pub fn native_diagnostics( config .lints_from_config .get_diagnostics(&mut res, &sema, file_id); - // @fb-only + // @fb-only: meta_only::diagnostics(&mut res, &sema, file_id, file_kind, config); syntax_diagnostics(&sema, &parse, &mut res, file_id); diagnostics_from_descriptors( &mut res, @@ -1715,7 +1715,7 @@ pub(crate) fn linters() -> Vec { ); // Add meta-only linters - // @fb-only + // @fb-only: all_linters.extend(meta_only::linters()); all_linters } @@ -2593,7 +2593,7 @@ pub fn ct_diagnostics( CommonTestInfo::Result { all, groups } => { let testcases = common_test::runnable_names(&sema, file_id, all, groups).ok(); common_test::unreachable_test(&mut res, &sema, file_id, &testcases); - // @fb-only + // @fb-only: meta_only::ct_diagnostics(&mut res, &sema, file_id, testcases); } CommonTestInfo::EvalError(_error) => { // The error currently does not contain anything useful, so we ignore it diff --git a/crates/ide/src/diagnostics/application_env.rs b/crates/ide/src/diagnostics/application_env.rs index be22f581f5..615b7801f9 100644 --- a/crates/ide/src/diagnostics/application_env.rs +++ b/crates/ide/src/diagnostics/application_env.rs @@ -28,7 +28,7 @@ use crate::codemod_helpers::CheckCallCtx; use crate::codemod_helpers::FunctionMatch; use crate::codemod_helpers::MatchCtx; use crate::codemod_helpers::find_call_in_function; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::Severity; @@ -108,7 +108,7 @@ fn check_function(diags: &mut Vec, sema: &Semantic, def: &FunctionDe vec![2, 3], BadEnvCallAction::AppArg(0), ), - // @fb-only + // @fb-only: diagnostics::meta_only::application_env_bad_matches(), ] .into_iter() .flatten() diff --git a/crates/ide/src/diagnostics/atoms_exhaustion.rs b/crates/ide/src/diagnostics/atoms_exhaustion.rs index 4708e35b1f..56a43a50ca 100644 --- a/crates/ide/src/diagnostics/atoms_exhaustion.rs +++ b/crates/ide/src/diagnostics/atoms_exhaustion.rs @@ -13,7 +13,7 @@ use hir::Semantic; use crate::FunctionMatch; use crate::codemod_helpers::CheckCallCtx; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; @@ -35,9 +35,9 @@ impl Linter for AtomsExhaustionLinter { false } #[rustfmt::skip] - // @fb-only + // @fb-only: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only + // @fb-only: diagnostics::meta_only::is_relevant_file(sema.db.upcast(), file_id) true // @oss-only } } @@ -56,16 +56,16 @@ impl FunctionCallLinter for AtomsExhaustionLinter { // FunctionMatch::mfa("erlang", "binary_to_term", 2), ] .into_iter() - // @fb-only + // @fb-only: .chain(diagnostics::meta_only::atoms_exhaustion_matches().into_iter()) .collect::>() ] } fn check_match(&self, context: &CheckCallCtx<'_, ()>) -> Option { #[rustfmt::skip] - // @fb-only - // @fb-only - // @fb-only + // @fb-only: let sema = context.in_clause.sema; + // @fb-only: let is_safe = + // @fb-only: diagnostics::meta_only::atoms_exhaustion_is_safe(sema, context.in_clause, context.parents); let is_safe = false; // @oss-only if !is_safe { match context.args.as_slice() { diff --git a/crates/ide/src/diagnostics/debugging_function.rs b/crates/ide/src/diagnostics/debugging_function.rs index 94c3d11364..1f0099781d 100644 --- a/crates/ide/src/diagnostics/debugging_function.rs +++ b/crates/ide/src/diagnostics/debugging_function.rs @@ -22,7 +22,7 @@ use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; use crate::diagnostics::Severity; -// @fb-only +// @fb-only: use crate::diagnostics::meta_only; use crate::lazy_function_matches; pub(crate) struct NoDebuggingFunctionLinter; @@ -52,7 +52,7 @@ impl FunctionCallLinter for NoDebuggingFunctionLinter { lazy_function_matches![ vec![FunctionMatch::m("redbug")] .into_iter() - // @fb-only + // @fb-only: .chain(meta_only::debugging_function_matches().into_iter()) .collect::>() ] } diff --git a/crates/ide/src/diagnostics/deprecated_function.rs b/crates/ide/src/diagnostics/deprecated_function.rs index 540edfcca3..469c327eb7 100644 --- a/crates/ide/src/diagnostics/deprecated_function.rs +++ b/crates/ide/src/diagnostics/deprecated_function.rs @@ -41,7 +41,7 @@ use super::DiagnosticDescriptor; use super::Severity; use crate::codemod_helpers::FunctionMatch; use crate::codemod_helpers::FunctionMatcher; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::fix; pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { @@ -88,7 +88,7 @@ fn deprecated_function(diagnostics: &mut Vec, sema: &Semantic, file_ lazy_static! { static ref DEPRECATED_FUNCTIONS: Vec<(FunctionMatch, DeprecationDetails)> = { let matches: Vec> = vec![ - // @fb-only + // @fb-only: diagnostics::meta_only::deprecated_function_matches(), ]; matches.into_iter() .flatten() diff --git a/crates/ide/src/diagnostics/unexported_function.rs b/crates/ide/src/diagnostics/unexported_function.rs index 7aae73a9f1..2e1ebf7f54 100644 --- a/crates/ide/src/diagnostics/unexported_function.rs +++ b/crates/ide/src/diagnostics/unexported_function.rs @@ -27,7 +27,7 @@ use crate::codemod_helpers::CheckCallCtx; use crate::codemod_helpers::MatchCtx; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; -// @fb-only +// @fb-only: use crate::diagnostics::meta_only; use crate::fix; use crate::lazy_function_matches; @@ -45,9 +45,9 @@ impl Linter for UnexportedFunctionLinter { } #[rustfmt::skip] fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only + // @fb-only: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { true // @oss-only - // @fb-only + // @fb-only: meta_only::should_check_for_unexported(sema, file_id) } } diff --git a/crates/ide/src/diagnostics/unspecific_include.rs b/crates/ide/src/diagnostics/unspecific_include.rs index f7405c798a..431740c085 100644 --- a/crates/ide/src/diagnostics/unspecific_include.rs +++ b/crates/ide/src/diagnostics/unspecific_include.rs @@ -152,7 +152,7 @@ fn replace_include_path( #[cfg(test)] mod tests { use elp_ide_db::DiagnosticCode; - // @fb-only + // @fb-only: use elp_ide_db::meta_only::MetaOnlyDiagnosticCode; use expect_test::Expect; use expect_test::expect; @@ -173,7 +173,7 @@ mod tests { #[track_caller] fn check_fix(fixture_before: &str, fixture_after: Expect) { let config = DiagnosticsConfig::default() - // @fb-only + // @fb-only: .disable(DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::MalformedInclude)) .disable(DiagnosticCode::UnusedInclude); tests::check_fix_with_config(config, fixture_before, fixture_after) } diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index d13befe8bb..6a85ab3dc3 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -15,9 +15,9 @@ use elp_syntax::AstNode; use hir::InFile; use hir::Semantic; -// @fb-only +// @fb-only: use crate::meta_only::exdoc_links; -// @fb-only +// @fb-only: mod meta_only; mod otp_links; #[derive(Debug, Clone, PartialEq, Eq)] @@ -40,10 +40,10 @@ pub(crate) fn external_docs(db: &RootDatabase, position: &FilePosition) -> Optio if let Some(class) = SymbolClass::classify(&sema, in_file_token.clone()) { class.iter().for_each(|def| { otp_links::links(&mut doc_links, &sema, &def); - // @fb-only + // @fb-only: exdoc_links::links(&mut doc_links, &sema, &def); }); } - // @fb-only + // @fb-only: meta_only::links(&mut doc_links, node, position); Some(doc_links) } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 009edd4684..ad5c1bb680 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -110,7 +110,7 @@ pub mod diagnostics; pub mod diagnostics_collection; pub mod diff; mod highlight_related; -// @fb-only +// @fb-only: pub mod meta_only; pub use annotations::Annotation; pub use annotations::AnnotationKind; diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 71b4ff02c0..f41aecdd67 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs @@ -40,7 +40,7 @@ mod helpers; mod keywords; mod macros; mod maps; -// @fb-only +// @fb-only: mod meta_only; mod modules; mod records; mod spec; @@ -176,7 +176,7 @@ pub fn completions( } CtxKind::Other => { let _ = attributes::add_completions(&mut acc, ctx) - // @fb-only + // @fb-only: || meta_only::add_completions(&mut acc, ctx) || vars::add_completions(&mut acc, ctx) || maps::add_completions(&mut acc, ctx) || records::add_completions(&mut acc, ctx); diff --git a/crates/ide_db/src/diagnostic_code.rs b/crates/ide_db/src/diagnostic_code.rs index 957caecb09..e9d21cadca 100644 --- a/crates/ide_db/src/diagnostic_code.rs +++ b/crates/ide_db/src/diagnostic_code.rs @@ -20,9 +20,9 @@ use serde::de; use strum::IntoEnumIterator; use strum_macros::EnumIter; -// @fb-only +// @fb-only: use crate::meta_only::MetaOnlyDiagnosticCode; -// @fb-only +// @fb-only: pub const BASE_URL: &str = crate::meta_only::BASE_URL; pub const BASE_URL: &str = "https://whatsapp.github.io/erlang-language-platform/docs"; // @oss-only #[derive(Clone, Debug, PartialEq, Eq, Hash, EnumIter)] @@ -100,7 +100,7 @@ pub enum DiagnosticCode { Eqwalizer(String), // Used for ad-hoc diagnostics via lints/codemods AdHoc(String), - // @fb-only + // @fb-only: MetaOnly(MetaOnlyDiagnosticCode), } // These namespaces map the error codes returned by the Erlang Service. @@ -116,7 +116,7 @@ pub enum Namespace { Parser, EDoc, WhatsApp, - // @fb-only + // @fb-only: MetaOnly, } impl fmt::Display for Namespace { @@ -131,7 +131,7 @@ impl fmt::Display for Namespace { Namespace::Parser => "p", Namespace::EDoc => "o", Namespace::WhatsApp => "w", - // @fb-only + // @fb-only: Namespace::MetaOnly => "meta_only", }; write!(f, "{namespace}") } @@ -164,7 +164,7 @@ impl Namespace { pub fn supports_doc_path(&self) -> bool { match self { Namespace::WhatsApp => true, - // @fb-only + // @fb-only: Namespace::MetaOnly => true, _ => false, } } @@ -259,7 +259,7 @@ impl DiagnosticCode { DiagnosticCode::ErlangService(c) => c.to_string(), DiagnosticCode::Eqwalizer(c) => format!("eqwalizer: {c}"), DiagnosticCode::AdHoc(c) => format!("ad-hoc: {c}"), - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(c) => c.as_code(), } } @@ -360,7 +360,7 @@ impl DiagnosticCode { DiagnosticCode::ErlangService(c) => c.to_string(), DiagnosticCode::Eqwalizer(c) => c.to_string(), DiagnosticCode::AdHoc(c) => format!("ad-hoc: {c}"), - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(c) => c.as_label(), } } @@ -371,7 +371,7 @@ impl DiagnosticCode { pub fn maybe_from_string(s: &str) -> Option { DIAGNOSTIC_CODE_LOOKUPS .get(s).cloned() - // @fb-only + // @fb-only: .or_else(|| MetaOnlyDiagnosticCode::from_str(s).ok().map(DiagnosticCode::MetaOnly)) .or_else( || // Look for ErlangService and AdHoc if let Some(code) = Self::is_adhoc(s) { @@ -388,7 +388,7 @@ impl DiagnosticCode { match self { DiagnosticCode::DefaultCodeForEnumIter => None, DiagnosticCode::AdHoc(_) => None, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(_) => Some(Namespace::MetaOnly), DiagnosticCode::ErlangService(code) => Namespace::from_str(code).ok(), _ => Namespace::from_str(&self.as_code()).ok(), } @@ -397,7 +397,7 @@ impl DiagnosticCode { pub fn supports_doc_path(&self) -> bool { match self { DiagnosticCode::DefaultCodeForEnumIter => false, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::DefaultCodeForEnumIter) => false, _ => true, } } @@ -541,7 +541,7 @@ impl DiagnosticCode { DiagnosticCode::ErlangService(_) => false, DiagnosticCode::Eqwalizer(_) => false, DiagnosticCode::AdHoc(_) => false, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(code) => code.allows_fixme_comment(), } } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 30a67cfa24..aecfd86473 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -57,7 +57,7 @@ pub mod docs; pub mod eqwalizer; mod erl_ast; mod line_index; -// @fb-only +// @fb-only: pub mod meta_only; pub mod metadata; mod search; pub mod text_edit; diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 013f3cbc87..3658a6d783 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -1623,7 +1623,7 @@ mod tests { } // TODO: enable when buck is properly set up on github project - // @fb-only + // @fb-only: const BUCK_TESTS_ENABLED: bool = true; const BUCK_TESTS_ENABLED: bool = false; // @oss-only #[track_caller] diff --git a/erlang_service/src/elp_lint.erl b/erlang_service/src/elp_lint.erl index 609e8c0712..31d2c70248 100644 --- a/erlang_service/src/elp_lint.erl +++ b/erlang_service/src/elp_lint.erl @@ -4372,14 +4372,14 @@ is_format_function(io, fwrite) -> true; is_format_function(io, format) -> true; is_format_function(io_lib, fwrite) -> true; is_format_function(io_lib, format) -> true; -% @fb-only -% @fb-only +% @fb-only: is_format_function(wa_log, send_if) -> true; +% @fb-only: is_format_function(wa_string, format) -> true; is_format_function(M, F) when is_atom(M), is_atom(F) -> false. %% check_format_1([Arg]) -> ok | {warn,Level,Format,[Arg]}. -% @fb-only -% @fb-only +% @fb-only[end= ]: format_args(wa_log, send_if, [_Level, _Meta, _Opts, Format, Args]) -> [Format, Args]; +% @fb-only[end= ]: format_args(wa_string, format, [Format, Args, _Options]) -> [Format, Args]; format_args(_M, _F, As) -> As. From b2ea905e7f748f4f1f5ca154d242ffccaadcf82a Mon Sep 17 00:00:00 2001 From: Open Source Bot Date: Tue, 16 Dec 2025 11:27:09 -0800 Subject: [PATCH 14/46] Updating hashes Summary: GitHub commits: https://github.com/facebook/buck2-prelude/commit/020284a6e874592d03667b4320932b3b772ef578 https://github.com/facebook/facebook-for-woocommerce/commit/261ff05851ee8ec2322780a957569d67af460c5c https://github.com/facebook/fb303/commit/acdd3baf0a4f2a5e4a192d238724a84e72ed4e02 https://github.com/facebook/fbthrift/commit/b435fe91fa611b9d111755bcbef00a989c4cfcf6 https://github.com/facebook/folly/commit/a75b46bbbc8c8660a9dc416661a5531a96a85772 https://github.com/facebook/mvfst/commit/427330df232ec9c2e791334a599acef3ef915f3c https://github.com/facebook/proxygen/commit/fd2a5c46a7704e515215fa4ffdd3ab9593f3cc4b https://github.com/facebook/wangle/commit/e95090adb4e12129b6a87f596070dfa10390695e https://github.com/facebookexperimental/edencommon/commit/c28a0922492b3f79997dff67de4f2df371f25bdb https://github.com/facebookexperimental/rust-shed/commit/fab1e58dfd3eb1e09411173b4e3f61e21d85733a https://github.com/facebookincubator/Facebook-Pixel-for-Wordpress/commit/848ddcedb294e05292c321d8393a5d39ef698ea8 https://github.com/facebookincubator/fizz/commit/270d39b2c34abd4916092e0ef1cec4d520f63a28 https://github.com/facebookincubator/llm_orchestrator/commit/65b11fc09069dbdaa9cd77eafc1ac4e4dd44ea1c https://github.com/whatsapp/eqwalizer/commit/0f514eb3893fa7070835c83ecb49fbea31b0426d Reviewed By: pranavcivi fbshipit-source-id: 0c3453ad24a874db08fbc66cc4945f70c64d25d0 --- eqwalizer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eqwalizer b/eqwalizer index a835ce03b0..0f514eb389 160000 --- a/eqwalizer +++ b/eqwalizer @@ -1 +1 @@ -Subproject commit a835ce03b0308a9869af964e35a24466b49cda51 +Subproject commit 0f514eb3893fa7070835c83ecb49fbea31b0426d From d4e14636b4570e9e06f91e29873ba6dcef18bd2a Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 16 Dec 2025 15:30:53 -0800 Subject: [PATCH 15/46] Do not run selected linters on tests by default Differential Revision: D89302965 fbshipit-source-id: 9b5c1d3c6402a6fea9f3fd5a6d93c2e49acba51e --- crates/ide/src/diagnostics/no_error_logger.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ide/src/diagnostics/no_error_logger.rs b/crates/ide/src/diagnostics/no_error_logger.rs index d587d3f3c7..46f3d15e3e 100644 --- a/crates/ide/src/diagnostics/no_error_logger.rs +++ b/crates/ide/src/diagnostics/no_error_logger.rs @@ -29,6 +29,9 @@ impl Linter for NoErrorLoggerLinter { fn severity(&self, _sema: &Semantic, _file_id: FileId) -> Severity { Severity::Error } + fn should_process_test_files(&self) -> bool { + false + } } impl FunctionCallLinter for NoErrorLoggerLinter { From 37b7c5e28e4be59eefa04ec51e81359e3dcbf9b2 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 17 Dec 2025 01:18:32 -0800 Subject: [PATCH 16/46] Introduce top-level test directory Reviewed By: michalmuskala Differential Revision: D89272460 fbshipit-source-id: 91eb93d7df55a1ca64fe2b05e21097daca3b6d33 --- crates/elp/src/bin/main.rs | 32 +++++++++---------- .../test/xref/unavailable_type.stdout | 4 +-- crates/elp/tests/slow-tests/buck_tests.rs | 4 +-- crates/elp/tests/slow-tests/main.rs | 16 +++++----- crates/ide/src/diagnostics.rs | 2 +- crates/project_model/src/buck.rs | 22 ++++++------- .../test_projects}/.gitignore | 0 .../test_projects}/README.md | 0 test/test_projects/buck_bad_config/.elp.toml | 8 +++++ .../buck_bad_config/src/bad_app.erl | 0 test/test_projects/buck_tests/.elp.toml | 9 ++++++ .../test_projects}/buck_tests/TARGETS.v2_ | 0 .../buck_tests/test_elp/TARGETS.v2_ | 10 +++--- .../buck_tests/test_elp/include/test_elp.hrl | 0 .../buck_tests/test_elp/src/test_elp.app.src | 0 .../buck_tests/test_elp/src/test_elp.erl | 0 .../test_elp/test/test_elp_SUITE.erl | 0 .../handle_update_test1.json | 0 .../handle_update_test2.json | 0 .../test_elp_SUITE_data/untracked_header.hrl | 0 .../test_elp_SUITE_data/untracked_module.erl | 0 .../test_elp/test/test_elp_test_utils.erl | 0 .../test_elp/test/test_elp_test_utils.hrl | 0 .../test_elp_direct_dep/TARGETS.v2_ | 4 +-- .../include/test_elp_direct_dep.hrl | 0 .../src/test_elp_direct_dep.erl | 0 .../src/test_elp_direct_dep_private.hrl | 0 .../test_elp_flat_inside_target/TARGETS.v2_ | 0 .../test_elp_flat_inside_target.erl | 0 .../test_elp_flat_inside_target.hrl | 0 .../test_elp_flat_outside_target.erl | 0 .../test_elp_flat_outside_target.hrl | 0 .../test_elp_ignored/test_elp_ignored.erl | 0 .../include/test_elp_no_private_headers.hrl | 0 .../src/test_elp_no_private_headers.erl | 0 .../src/test_elp_no_headers.erl | 0 .../test_elp_transitive_dep/TARGETS.v2_ | 0 .../include/test_elp_transitive_dep.hrl | 0 .../src/test_elp_transitive_dep.erl | 0 .../src/test_elp_transitive_dep_private.hrl | 0 test/test_projects/buck_tests_2/.elp.toml | 12 +++++++ .../auto_gen_a/include/auto_gen_a.hrl | 0 .../auto_gen_a/out/pretend_generated.erl | 0 .../auto_gen/auto_gen_a/src/auto_gen_a.erl | 0 .../check_include/src/top_includer.erl | 0 .../include/include_with_bug.hrl | 0 .../include/top_includer.hrl | 0 .../include/separate_include.hrl | 0 .../generated/out/generated_header.hrl | 0 .../buck_tests_2/util/app_a/include/junk.hrl | 0 .../buck_tests_2/util/app_a/src/app_a.erl | 0 test/test_projects/codegen_test/.elp.toml | 8 +++++ .../test_projects}/codegen_test/README.md | 6 ++-- .../app_a/src/codegen_test.app.src | 0 .../codegen_test/app_a/src/example_usage.erl | 0 .../app_a/test/codegen_test_SUITE.erl | 0 .../generated/example_service_client.erl | 0 .../generated/example_service_types.erl | 0 .../generated/example_service_types.hrl | 0 .../templates/example_service_client.erl | 0 .../templates/example_service_types.erl | 0 .../templates/example_service_types.hrl | 0 .../custom_build_tool/.elp.toml | 0 .../apps/app_a/include/app_a.hrl | 0 .../apps/app_a/src/app_a.erl | 0 .../apps/app_b/src/app_b.erl | 0 test/test_projects/diagnostics/.elp.toml | 8 +++++ .../test_projects}/diagnostics/README.md | 0 .../diagnostics/app_a/extra/app_a.erl | 0 .../diagnostics/app_a/include/app_a.hrl | 0 .../app_a/include/broken_diagnostics.hrl | 0 .../diagnostics/app_a/include/diagnostics.hrl | 0 .../diagnostics/app_a/src/app_a.app.src | 0 .../diagnostics/app_a/src/app_a.erl | 0 .../app_a/src/broken_parse_trans.erl | 0 .../diagnostics/app_a/src/cascading.erl | 0 .../diagnostics/app_a/src/crlf.erl | 0 .../diagnostics/app_a/src/diagnostics.erl | 0 .../diagnostics/app_a/src/diagnostics.escript | 0 .../app_a/src/diagnostics_errors.escript | 0 .../app_a/src/diagnostics_warnings.escript | 0 .../src/erlang_diagnostics_errors_gen.erl | 0 .../diagnostics/app_a/src/file_attribute.erl | 0 .../diagnostics/app_a/src/lint_recursive.erl | 0 .../diagnostics/app_a/src/lints.erl | 0 .../app_a/src/otp27_docstrings.erl | 0 .../diagnostics/app_a/src/otp27_sigils.erl | 0 .../diagnostics/app_a/src/otp_7655.erl | 0 .../app_a/src/parse_error_a_cascade.erl | 0 .../diagnostics/app_a/src/suppressed.erl | 0 .../diagnostics/app_a/src/syntax.erl | 0 .../diagnostics/app_a/test/app_a_SUITE.erl | 0 .../diagnostics/erlang_ls.config | 0 .../test_projects}/diagnostics/rebar.config | 0 .../diagnostics/test_build_info.json | 0 test/test_projects/end_to_end/.elp.toml | 7 ++++ .../assist_examples/src/add_doc.erl | 0 .../src/assist_examples.app.src | 0 .../assist_examples/src/code_completion.erl | 0 .../assist_examples/src/extract_function.erl | 0 .../assist_examples/src/head_mismatch.erl | 0 .../configerator/source/whatsapp/my_caf.erl | 0 .../end_to_end/definitions/README.md | 0 .../definitions/src/definitions.app.src | 0 .../end_to_end/definitions/src/local_def.erl | 0 .../end_to_end/docs/src/docs.app.src | 0 .../end_to_end/docs/src/docs.erl | 0 .../end_to_end/erlang_ls.config | 0 .../end_to_end/fixtures/erlang-stacktrace.txt | 0 .../test_projects}/end_to_end/hover/README.md | 0 .../end_to_end/hover/src/doc_examples.erl | 0 .../end_to_end/hover/src/hover.app.src | 0 .../test_projects}/end_to_end/rebar.config | 0 .../end_to_end/single_errors/README.md | 0 .../single_errors/src/as_you_type.erl | 0 .../src/compiler_diagnostics.erl | 0 .../single_errors/src/single_errors.app.src | 0 .../single_errors/src/spec_mismatch.erl | 0 .../single_errors/src/spec_mismatch.erl.2 | 0 .../single_errors/src/types_on_hover.erl | 0 .../single_errors/src/unused_macro.erl | 0 .../eqwalizer/src/eqwalizer.app.src | 0 .../eqwalizer/src/eqwalizer_specs.erl | 0 .../test_projects/eqwalizer_callers/.elp.toml | 5 +++ .../eqwalizer_callers/.gitignore | 0 .../eqwalizer_callers/app_a/include/app_a.hrl | 0 .../eqwalizer_callers/app_a/src/app_a.app.src | 0 .../eqwalizer_callers/app_a/src/app_a.erl | 0 .../eqwalizer_callers/app_a/src/app_b.erl | 0 .../app_a/test/app_a_SUITE.erl | 0 .../eqwalizer_callers/rebar.config | 0 .../eqwalizer_ignore_modules/.elp.toml | 0 .../apps/app_a/include/app_a.hrl | 0 .../apps/app_a/src/another_module_a.erl | 0 .../apps/app_a/src/app_a.erl | 0 .../apps/app_b/src/app_b.erl | 0 test/test_projects/eqwalizer_tests/.elp.toml | 8 +++++ .../test_projects}/eqwalizer_tests/.gitignore | 0 .../eqwalizer_tests/.rebar.root | 0 .../check/include/my_header.hrl | 0 .../check/src/any_fun_type.erl | 0 .../eqwalizer_tests/check/src/apply_none.erl | 0 .../eqwalizer_tests/check/src/approx.erl | 0 .../eqwalizer_tests/check/src/as_pat.erl | 0 .../check/src/auto_imports.erl | 0 .../eqwalizer_tests/check/src/behave.erl | 0 .../eqwalizer_tests/check/src/binaries.erl | 0 .../eqwalizer_tests/check/src/booleans.erl | 0 .../check/src/callbacks1_pos.erl | 0 .../check/src/callbacks3_neg.erl | 0 .../check/src/callbacks4_neg.erl | 0 .../check/src/callbacks5_neg.erl | 0 .../check/src/callbacks6_neg.erl | 0 .../check/src/callbacks7_overload_pos.erl | 0 .../check/src/case_predicates.erl | 0 .../eqwalizer_tests/check/src/check.app.src | 0 .../check/src/compiler_macro.erl | 0 .../check/src/complex_maps.erl | 0 .../check/src/comprehensions.erl | 0 .../check/src/contravariant.erl | 0 .../eqwalizer_tests/check/src/custom.erl | 0 .../check/src/detached_specs1.erl | 0 .../check/src/detached_specs2.erl | 0 .../eqwalizer_tests/check/src/dyn_calls.erl | 0 .../check/src/dyn_remote_funs.erl | 0 .../check/src/dynamic_callbacks_1.erl | 0 .../check/src/dynamic_callbacks_2.erl | 0 .../check/src/dynamic_calls.erl | 0 .../check/src/dynamic_catch.erl | 0 .../eqwalizer_tests/check/src/dynamic_fun.erl | 0 .../check/src/dynamic_generics.erl | 0 .../check/src/dynamic_local_funs.erl | 0 .../check/src/dynamic_receive.erl | 0 .../check/src/dynamic_refine.erl | 0 .../check/src/dynamic_try_catch.erl | 0 .../eqwalizer_tests/check/src/elab_clause.erl | 0 .../check/src/error_messages.erl | 0 .../check/src/fancy_generics.erl | 0 .../eqwalizer_tests/check/src/format.erl | 0 .../eqwalizer_tests/check/src/fun_stats.erl | 0 .../eqwalizer_tests/check/src/fun_stats2.erl | 0 .../eqwalizer_tests/check/src/funs.erl | 0 .../eqwalizer_tests/check/src/funs2.erl | 0 .../check/src/funs_uncommon.erl | 0 .../check/src/generic_fun_application.erl | 0 .../check/src/generics_with_unions.erl | 0 .../check/src/gradual_bounded.erl | 0 .../check/src/gradual_complex_types.erl | 0 .../check/src/gradual_custom.erl | 0 .../check/src/gradual_lambdas.erl | 0 .../check/src/gradual_maybe.erl | 0 .../check/src/gradual_misc.erl | 0 .../check/src/gradual_overloaded.erl | 0 .../check/src/gradual_regression_01.erl | 0 .../check/src/gradual_untyped.erl | 0 .../check/src/guard_b_connections.erl | 0 .../eqwalizer_tests/check/src/guards.erl | 0 .../check/src/guards_logic.erl | 0 .../check/src/guards_simple.erl | 0 .../eqwalizer_tests/check/src/hints.erl | 0 .../eqwalizer_tests/check/src/index1.erl | 0 .../eqwalizer_tests/check/src/index2.erl | 0 .../eqwalizer_tests/check/src/iolists.erl | 0 .../eqwalizer_tests/check/src/kp_01.erl | 0 .../eqwalizer_tests/check/src/kp_02.erl | 0 .../eqwalizer_tests/check/src/lists_tests.erl | 0 .../eqwalizer_tests/check/src/misc.erl | 0 .../eqwalizer_tests/check/src/misc_lib.erl | 0 .../check/src/my_behaviour.erl | 0 .../check/src/my_gradual_behaviour.erl | 0 .../eqwalizer_tests/check/src/my_header.erl | 0 .../eqwalizer_tests/check/src/neg.erl | 0 .../check/src/nested1/misc_nested1.erl | 0 .../src/nested1/nested2/misc_nested2.erl | 0 .../eqwalizer_tests/check/src/nowarn.erl | 0 .../check/src/number_comparisons.erl | 0 .../eqwalizer_tests/check/src/numbers.erl | 0 .../eqwalizer_tests/check/src/opaque.erl | 0 .../eqwalizer_tests/check/src/other.erl | 0 .../eqwalizer_tests/check/src/otp28.erl | 0 .../eqwalizer_tests/check/src/otp_opaques.erl | 0 .../eqwalizer_tests/check/src/overloaded.erl | 0 .../check/src/overloaded_specs_union.erl | 0 .../check/src/parametricity.erl | 0 .../eqwalizer_tests/check/src/pats.erl | 0 .../eqwalizer_tests/check/src/pinned.erl | 0 .../eqwalizer_tests/check/src/pos.erl | 0 .../eqwalizer_tests/check/src/records.erl | 0 .../check/src/recursive_aliases.erl | 0 .../eqwalizer_tests/check/src/refine.erl | 0 .../eqwalizer_tests/check/src/scoping.erl | 0 .../eqwalizer_tests/check/src/skip.erl | 0 .../check/src/static_maybe.erl | 0 .../check/src/strict_complex_types.erl | 0 .../eqwalizer_tests/check/src/strict_fun.erl | 0 .../check/src/strict_receive.erl | 0 .../eqwalizer_tests/check/src/subtype_neg.erl | 0 .../eqwalizer_tests/check/src/subtype_pos.erl | 0 .../eqwalizer_tests/check/src/t_maps.erl | 0 .../check/src/tagged_tuples.erl | 0 .../eqwalizer_tests/check/src/test.erl | 0 .../eqwalizer_tests/check/src/tries.erl | 0 .../eqwalizer_tests/check/src/tuple_union.erl | 0 .../check/src/type_aliases.erl | 0 .../check/src/type_asserts.erl | 0 .../check/src/type_predicates.erl | 0 .../eqwalizer_tests/check/src/united_fun.erl | 0 .../eqwalizer_tests/check/src/unspecced.erl | 0 .../check/src/use_dynamic_gradual.erl | 0 .../check/src/use_dynamic_strict.erl | 0 .../eqwalizer_tests/check/src/vars1.erl | 0 .../eqwalizer_tests/check/src/vars2.erl | 0 .../check/test/check_SUITE.erl | 0 .../debug/include/debug_header.hrl | 0 .../eqwalizer_tests/debug/src/attributes.erl | 0 .../eqwalizer_tests/debug/src/debug.app.src | 0 .../debug/src/debug_header.erl | 0 .../eqwalizer_tests/debug/src/expand.erl | 0 .../eqwalizer_tests/debug/src/expr1.erl | 0 .../eqwalizer_tests/debug/src/expr2.erl | 0 .../eqwalizer_tests/debug/src/records_wip.erl | 0 .../eqwalizer_tests/debug/src/types1.erl | 0 .../eqwalizer_tests/debug/src/types2.erl | 0 .../eqwalizer_tests/debug/src/wip_maps.erl | 0 .../eqwalizer_tests/elm_core/src/basics.erl | 0 .../elm_core/src/elm_core.app.src | 0 .../eqwalizer_tests/elm_core/src/list.erl | 0 .../eqwalizer_tests/elm_core/src/map.erl | 0 .../eqwalizer_tests/elm_core/src/map_ffi.erl | 0 .../eqwalizer_tests/elm_core/src/maybe.erl | 0 .../eqwalizer_tests/elm_core/src/result.erl | 0 .../eqwalizer_tests/elm_core/src/tuple.erl | 0 .../eqwalizer/include/eqwalizer.hrl | 0 .../eqwalizer/src/eqwalizer.app.src | 0 .../eqwalizer/src/eqwalizer.erl | 0 .../eqwalizer/src/eqwalizer_specs.erl | 0 .../src/eqwalizer_build_info_prv.erl | 0 .../src/eqwalizer_rebar3.app.src | 0 .../eqwalizer_rebar3/src/eqwalizer_rebar3.erl | 0 .../eqwalizer_tests/eqwater/readme.md | 0 .../eqwater/src/deep_tuples.erl | 0 .../eqwater/src/eqwater.app.src | 0 .../eqwalizer_tests/eqwater/src/eqwater.erl | 0 .../eqwater/src/eqwater_aliases.erl | 0 .../eqwater/src/eqwater_generics.erl | 0 .../eqwater/src/eqwater_lists.erl | 0 .../eqwater/src/eqwater_maps.erl | 0 .../eqwater/src/eqwater_records.erl | 0 .../eqwater/src/eqwater_sound.erl | 0 .../eqwater/src/unlimited_refinement.erl | 0 .../fault_tolerance/.eqwalizer | 0 .../src/fault_tolerance.app.src | 0 .../fault_tolerance/src/fault_tolerance.erl | 0 .../eqwalizer_tests/options/src/bad_maps.erl | 0 .../options/src/dynamic_lambdas.erl | 0 .../options/src/options.app.src | 0 .../src/overloaded_specs_dynamic_result.erl | 0 .../options/src/redundant_guards.erl | 0 .../options/src/uncovered_clauses.erl | 0 .../eqwalizer_tests/rebar.config | 0 .../hierarchical_config/.elp.toml | 5 +++ .../hierarchical_config/.elp_lint.toml | 0 .../hierarchical_config/app_a/.elp_lint.toml | 0 .../app_a/src/app_a.app.src | 0 .../hierarchical_config/app_a/src/app_a.erl | 0 .../app_b/src/app_b.app.src | 0 .../hierarchical_config/app_b/src/app_b.erl | 0 .../hierarchical_config/rebar.config | 0 test/test_projects/in_place_tests/.elp.toml | 5 +++ .../test_projects}/in_place_tests/README.md | 0 .../in_place_tests/app_a/extra/app_a.erl | 0 .../in_place_tests/app_a/include/app_a.hrl | 0 .../app_a/include/broken_diagnostics.hrl | 0 .../app_a/include/diagnostics.hrl | 0 .../in_place_tests/app_a/src/app_a.app.src | 0 .../in_place_tests/app_a/src/app_a.erl | 0 .../in_place_tests/app_a/src/lints.erl | 0 .../in_place_tests/app_a/test/app_a_SUITE.erl | 0 .../in_place_tests/erlang_ls.config | 0 .../in_place_tests/rebar.config | 0 .../include_lib_dependency_test/.elp.toml | 5 +++ .../external_app/include/external_header.hrl | 0 .../external_app/src/external_app.app.src | 0 .../external_app/src/external_app.erl | 0 .../extra_app/include/extra_header.hrl | 0 .../extra_app/src/extra_app.app.src | 0 .../extra_app/src/extra_app.erl | 0 .../main_app/src/main_app.app.src | 0 .../main_app/src/main_app.erl | 0 .../normal_dep/include/assert.hrl | 0 .../normal_dep/src/normal_dep.app.src | 0 .../normal_dep/src/normal_dep.erl | 0 .../include_lib_dependency_test/rebar.config | 0 test/test_projects/linter/.elp.toml | 5 +++ .../test_projects}/linter/.elp_lint.toml | 0 .../test_projects}/linter/.gitignore | 0 .../linter/app_a/include/app_a.hrl | 0 .../linter/app_a/src/app_a.app.src | 0 .../test_projects}/linter/app_a/src/app_a.erl | 0 .../linter/app_a/src/app_a_edoc.erl | 0 .../linter/app_a/src/app_a_ssr.erl | 0 .../linter/app_a/src/app_a_unused_param.erl | 0 .../app_a/src/custom_function_matches.erl | 0 .../app_a/src/expression_updates_literal.erl | 0 .../linter/app_a/src/spelling.erl | 0 .../linter/app_a/test/app_a_SUITE.erl | 0 .../linter/app_a/test/app_a_test_helpers.erl | 0 .../test/app_a_test_helpers_not_opted_in.erl | 0 .../test/app_a_unreachable_test_SUITE.erl | 0 .../app_a/test/app_test_helpers_no_errors.erl | 0 .../linter/app_b/src/app_b.app.src | 0 .../test_projects}/linter/app_b/src/app_b.erl | 0 .../linter/app_b/src/app_b_unused_param.erl | 0 .../test_projects}/linter/elp_lint_adhoc.toml | 0 .../elp_lint_custom_function_matches.toml | 0 .../linter/elp_lint_ssr_adhoc.toml | 0 .../linter/elp_lint_ssr_adhoc_parse_fail.toml | 0 .../test_projects}/linter/elp_lint_test1.toml | 0 .../test_projects}/linter/elp_lint_test2.toml | 0 .../linter/elp_lint_test_ignore.toml | 0 .../linter/elp_lint_warnings_as_errors.toml | 0 .../test_projects}/linter/rebar.config | 0 .../test_projects/linter_bad_config/.elp.toml | 5 +++ .../linter_bad_config/.elp_lint.toml | 0 .../linter_bad_config/.gitignore | 0 .../linter_bad_config/app_a/include/app_a.hrl | 0 .../linter_bad_config/app_a/src/app_a.app.src | 0 .../linter_bad_config/app_a/src/app_a.erl | 0 .../linter_bad_config/linter/.elp.toml | 5 +++ .../linter_bad_config/linter/.elp_lint.toml | 0 .../linter_bad_config/linter/.gitignore | 0 .../linter/app_a/include/app_a.hrl | 0 .../linter/app_a/src/app_a.app.src | 0 .../linter/app_a/src/app_a.erl | 0 .../linter/app_a/src/app_a_unused_param.erl | 0 .../linter/app_a/test/app_a_SUITE.erl | 0 .../linter/app_a/test/app_a_test_helpers.erl | 0 .../test/app_a_test_helpers_not_opted_in.erl | 0 .../app_a/test/app_test_helpers_no_errors.erl | 0 .../linter/app_b/src/app_b.app.src | 0 .../linter/app_b/src/app_b.erl | 0 .../linter/app_b/src/app_b_unused_param.erl | 0 .../linter_bad_config/linter/rebar.config | 0 .../linter_bad_config/rebar.config | 0 test/test_projects/parse_error/.elp.toml | 8 +++++ .../test_projects}/parse_error/.gitignore | 0 .../test_projects}/parse_error/.rebar.root | 0 .../eqwalizer/src/eqwalizer.app.src | 0 .../parse_error/eqwalizer/src/eqwalizer.erl | 0 .../eqwalizer/src/eqwalizer_specs.erl | 0 .../parse_error_a/src/parse_error_a.app.src | 0 .../parse_error_a/src/parse_error_a.erl | 0 .../parse_error_a/src/parse_error_a_bad.erl | 0 .../src/parse_error_a_reference_bad.erl | 0 .../src/parse_error_a_syntax_error.erl | 0 .../parse_error_a/src/parse_error_a_worst.erl | 0 .../test_projects}/parse_error/rebar.config | 0 test/test_projects/standard/.elp.toml | 5 +++ .../test_projects}/standard/.gitignore | 0 .../test_projects}/standard/.rebar.root | 0 .../test_projects}/standard/app_a/.eqwalizer | 0 .../standard/app_a/extra/app_a.erl | 0 .../standard/app_a/include/app_a.hrl | 0 .../standard/app_a/src/app_a.app.src | 0 .../standard/app_a/src/app_a.erl | 0 .../app_a/src/app_a_errors_generated.erl | 0 .../standard/app_a/src/app_a_fixme.erl | 0 .../standard/app_a/src/app_a_ignored.erl | 0 .../standard/app_a/src/app_a_lists.erl | 0 .../standard/app_a/src/app_a_mod2.erl | 0 .../standard/app_a/src/app_a_no_errors.erl | 0 .../app_a/src/app_a_no_errors_generated.erl | 0 .../app_a/src/app_a_no_errors_opted_in.erl | 0 .../standard/app_a/test/app_a_SUITE.erl | 0 .../app_a/test/app_a_test_helpers.erl | 0 .../test/app_a_test_helpers_not_opted_in.erl | 0 .../app_a/test/app_test_helpers_no_errors.erl | 0 .../standard/app_b/src/app_b.app.src | 0 .../standard/app_b/src/app_b.erl | 0 .../standard/eqwalizer/src/eqwalizer.app.src | 0 .../standard/eqwalizer/src/eqwalizer.erl | 0 .../eqwalizer/src/eqwalizer_specs.erl | 0 .../test_projects}/standard/erlang_ls.config | 0 .../test_projects}/standard/rebar.config | 0 test/test_projects/xref/.elp.toml | 5 +++ .../xref/app_a/src/unavailable_type.erl | 0 .../test_projects}/xref/app_b/src/app_b.erl | 0 .../test_projects}/xref/app_c/src/app_c.erl | 0 .../xref/elp_lint_unavailable_type.toml | 0 test_projects/buck_bad_config/.elp.toml | 8 ----- test_projects/buck_tests/.elp.toml | 9 ------ test_projects/buck_tests_2/.elp.toml | 12 ------- test_projects/codegen_test/.elp.toml | 8 ----- test_projects/diagnostics/.elp.toml | 8 ----- test_projects/end_to_end/.elp.toml | 7 ---- test_projects/eqwalizer_callers/.elp.toml | 5 --- test_projects/eqwalizer_tests/.elp.toml | 8 ----- test_projects/hierarchical_config/.elp.toml | 5 --- test_projects/in_place_tests/.elp.toml | 5 --- .../include_lib_dependency_test/.elp.toml | 5 --- test_projects/linter/.elp.toml | 5 --- test_projects/linter_bad_config/.elp.toml | 5 --- .../linter_bad_config/linter/.elp.toml | 5 --- test_projects/parse_error/.elp.toml | 8 ----- test_projects/standard/.elp.toml | 5 --- test_projects/xref/.elp.toml | 5 --- 446 files changed, 163 insertions(+), 163 deletions(-) rename {test_projects => test/test_projects}/.gitignore (100%) rename {test_projects => test/test_projects}/README.md (100%) create mode 100644 test/test_projects/buck_bad_config/.elp.toml rename {test_projects => test/test_projects}/buck_bad_config/src/bad_app.erl (100%) create mode 100644 test/test_projects/buck_tests/.elp.toml rename {test_projects => test/test_projects}/buck_tests/TARGETS.v2_ (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/TARGETS.v2_ (76%) rename {test_projects => test/test_projects}/buck_tests/test_elp/include/test_elp.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/src/test_elp.app.src (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/src/test_elp.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_SUITE.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_test_utils.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp/test/test_elp_test_utils.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_direct_dep/TARGETS.v2_ (61%) rename {test_projects => test/test_projects}/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_ignored/test_elp_ignored.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_transitive_dep/TARGETS.v2_ (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl (100%) rename {test_projects => test/test_projects}/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl (100%) create mode 100644 test/test_projects/buck_tests_2/.elp.toml rename {test_projects => test/test_projects}/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl (100%) rename {test_projects => test/test_projects}/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl (100%) rename {test_projects => test/test_projects}/buck_tests_2/check_include/src/top_includer.erl (100%) rename {test_projects => test/test_projects}/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/check_include_separate_1/include/top_includer.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/check_include_separate_2/include/separate_include.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/generated/out/generated_header.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/util/app_a/include/junk.hrl (100%) rename {test_projects => test/test_projects}/buck_tests_2/util/app_a/src/app_a.erl (100%) create mode 100644 test/test_projects/codegen_test/.elp.toml rename {test_projects => test/test_projects}/codegen_test/README.md (93%) rename {test_projects => test/test_projects}/codegen_test/app_a/src/codegen_test.app.src (100%) rename {test_projects => test/test_projects}/codegen_test/app_a/src/example_usage.erl (100%) rename {test_projects => test/test_projects}/codegen_test/app_a/test/codegen_test_SUITE.erl (100%) rename {test_projects => test/test_projects}/codegen_test/generated/example_service_client.erl (100%) rename {test_projects => test/test_projects}/codegen_test/generated/example_service_types.erl (100%) rename {test_projects => test/test_projects}/codegen_test/generated/example_service_types.hrl (100%) rename {test_projects => test/test_projects}/codegen_test/templates/example_service_client.erl (100%) rename {test_projects => test/test_projects}/codegen_test/templates/example_service_types.erl (100%) rename {test_projects => test/test_projects}/codegen_test/templates/example_service_types.hrl (100%) rename {test_projects => test/test_projects}/custom_build_tool/.elp.toml (100%) rename {test_projects => test/test_projects}/custom_build_tool/apps/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/custom_build_tool/apps/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/custom_build_tool/apps/app_b/src/app_b.erl (100%) create mode 100644 test/test_projects/diagnostics/.elp.toml rename {test_projects => test/test_projects}/diagnostics/README.md (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/extra/app_a.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/include/broken_diagnostics.hrl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/include/diagnostics.hrl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/broken_parse_trans.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/cascading.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/crlf.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/diagnostics.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/diagnostics.escript (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/diagnostics_errors.escript (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/diagnostics_warnings.escript (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/file_attribute.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/lint_recursive.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/lints.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/otp27_docstrings.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/otp27_sigils.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/otp_7655.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/parse_error_a_cascade.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/suppressed.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/src/syntax.erl (100%) rename {test_projects => test/test_projects}/diagnostics/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/diagnostics/erlang_ls.config (100%) rename {test_projects => test/test_projects}/diagnostics/rebar.config (100%) rename {test_projects => test/test_projects}/diagnostics/test_build_info.json (100%) create mode 100644 test/test_projects/end_to_end/.elp.toml rename {test_projects => test/test_projects}/end_to_end/assist_examples/src/add_doc.erl (100%) rename {test_projects => test/test_projects}/end_to_end/assist_examples/src/assist_examples.app.src (100%) rename {test_projects => test/test_projects}/end_to_end/assist_examples/src/code_completion.erl (100%) rename {test_projects => test/test_projects}/end_to_end/assist_examples/src/extract_function.erl (100%) rename {test_projects => test/test_projects}/end_to_end/assist_examples/src/head_mismatch.erl (100%) rename {test_projects => test/test_projects}/end_to_end/caf/configerator/source/whatsapp/my_caf.erl (100%) rename {test_projects => test/test_projects}/end_to_end/definitions/README.md (100%) rename {test_projects => test/test_projects}/end_to_end/definitions/src/definitions.app.src (100%) rename {test_projects => test/test_projects}/end_to_end/definitions/src/local_def.erl (100%) rename {test_projects => test/test_projects}/end_to_end/docs/src/docs.app.src (100%) rename {test_projects => test/test_projects}/end_to_end/docs/src/docs.erl (100%) rename {test_projects => test/test_projects}/end_to_end/erlang_ls.config (100%) rename {test_projects => test/test_projects}/end_to_end/fixtures/erlang-stacktrace.txt (100%) rename {test_projects => test/test_projects}/end_to_end/hover/README.md (100%) rename {test_projects => test/test_projects}/end_to_end/hover/src/doc_examples.erl (100%) rename {test_projects => test/test_projects}/end_to_end/hover/src/hover.app.src (100%) rename {test_projects => test/test_projects}/end_to_end/rebar.config (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/README.md (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/as_you_type.erl (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/compiler_diagnostics.erl (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/single_errors.app.src (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/spec_mismatch.erl (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/spec_mismatch.erl.2 (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/types_on_hover.erl (100%) rename {test_projects => test/test_projects}/end_to_end/single_errors/src/unused_macro.erl (100%) rename {test_projects => test/test_projects}/eqwalizer/src/eqwalizer.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer/src/eqwalizer_specs.erl (100%) create mode 100644 test/test_projects/eqwalizer_callers/.elp.toml rename {test_projects => test/test_projects}/eqwalizer_callers/.gitignore (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/app_a/src/app_b.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_callers/rebar.config (100%) rename {test_projects => test/test_projects}/eqwalizer_ignore_modules/.elp.toml (100%) rename {test_projects => test/test_projects}/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl (100%) create mode 100644 test/test_projects/eqwalizer_tests/.elp.toml rename {test_projects => test/test_projects}/eqwalizer_tests/.gitignore (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/.rebar.root (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/include/my_header.hrl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/any_fun_type.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/apply_none.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/approx.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/as_pat.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/auto_imports.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/behave.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/binaries.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/booleans.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks1_pos.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks3_neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks4_neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks5_neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks6_neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/callbacks7_overload_pos.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/case_predicates.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/check.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/compiler_macro.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/complex_maps.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/comprehensions.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/contravariant.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/custom.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/detached_specs1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/detached_specs2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dyn_calls.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dyn_remote_funs.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_callbacks_1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_callbacks_2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_calls.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_catch.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_fun.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_generics.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_local_funs.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_receive.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_refine.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/dynamic_try_catch.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/elab_clause.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/error_messages.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/fancy_generics.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/format.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/fun_stats.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/fun_stats2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/funs.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/funs2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/funs_uncommon.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/generic_fun_application.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/generics_with_unions.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_bounded.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_complex_types.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_custom.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_lambdas.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_maybe.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_misc.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_overloaded.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_regression_01.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/gradual_untyped.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/guard_b_connections.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/guards.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/guards_logic.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/guards_simple.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/hints.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/index1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/index2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/iolists.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/kp_01.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/kp_02.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/lists_tests.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/misc.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/misc_lib.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/my_behaviour.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/my_gradual_behaviour.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/my_header.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/nested1/misc_nested1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/nowarn.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/number_comparisons.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/numbers.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/opaque.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/other.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/otp28.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/otp_opaques.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/overloaded.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/overloaded_specs_union.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/parametricity.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/pats.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/pinned.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/pos.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/records.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/recursive_aliases.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/refine.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/scoping.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/skip.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/static_maybe.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/strict_complex_types.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/strict_fun.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/strict_receive.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/subtype_neg.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/subtype_pos.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/t_maps.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/tagged_tuples.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/test.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/tries.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/tuple_union.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/type_aliases.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/type_asserts.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/type_predicates.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/united_fun.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/unspecced.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/use_dynamic_gradual.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/use_dynamic_strict.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/vars1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/src/vars2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/check/test/check_SUITE.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/include/debug_header.hrl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/attributes.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/debug.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/debug_header.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/expand.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/expr1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/expr2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/records_wip.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/types1.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/types2.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/debug/src/wip_maps.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/basics.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/elm_core.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/list.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/map.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/map_ffi.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/maybe.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/result.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/elm_core/src/tuple.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer/src/eqwalizer.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/readme.md (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/deep_tuples.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_aliases.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_generics.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_lists.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_maps.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_records.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/eqwater_sound.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/eqwater/src/unlimited_refinement.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/fault_tolerance/.eqwalizer (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/bad_maps.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/dynamic_lambdas.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/options.app.src (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/redundant_guards.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/options/src/uncovered_clauses.erl (100%) rename {test_projects => test/test_projects}/eqwalizer_tests/rebar.config (100%) create mode 100644 test/test_projects/hierarchical_config/.elp.toml rename {test_projects => test/test_projects}/hierarchical_config/.elp_lint.toml (100%) rename {test_projects => test/test_projects}/hierarchical_config/app_a/.elp_lint.toml (100%) rename {test_projects => test/test_projects}/hierarchical_config/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/hierarchical_config/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/hierarchical_config/app_b/src/app_b.app.src (100%) rename {test_projects => test/test_projects}/hierarchical_config/app_b/src/app_b.erl (100%) rename {test_projects => test/test_projects}/hierarchical_config/rebar.config (100%) create mode 100644 test/test_projects/in_place_tests/.elp.toml rename {test_projects => test/test_projects}/in_place_tests/README.md (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/extra/app_a.erl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/include/broken_diagnostics.hrl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/include/diagnostics.hrl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/src/lints.erl (100%) rename {test_projects => test/test_projects}/in_place_tests/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/in_place_tests/erlang_ls.config (100%) rename {test_projects => test/test_projects}/in_place_tests/rebar.config (100%) create mode 100644 test/test_projects/include_lib_dependency_test/.elp.toml rename {test_projects => test/test_projects}/include_lib_dependency_test/external_app/include/external_header.hrl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/external_app/src/external_app.app.src (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/external_app/src/external_app.erl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/extra_app/include/extra_header.hrl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/extra_app/src/extra_app.app.src (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/extra_app/src/extra_app.erl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/main_app/src/main_app.app.src (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/main_app/src/main_app.erl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/normal_dep/include/assert.hrl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/normal_dep/src/normal_dep.app.src (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/normal_dep/src/normal_dep.erl (100%) rename {test_projects => test/test_projects}/include_lib_dependency_test/rebar.config (100%) create mode 100644 test/test_projects/linter/.elp.toml rename {test_projects => test/test_projects}/linter/.elp_lint.toml (100%) rename {test_projects => test/test_projects}/linter/.gitignore (100%) rename {test_projects => test/test_projects}/linter/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/linter/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/app_a_edoc.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/app_a_ssr.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/app_a_unused_param.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/custom_function_matches.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/expression_updates_literal.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/src/spelling.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/test/app_a_test_helpers.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/test/app_a_test_helpers_not_opted_in.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/test/app_a_unreachable_test_SUITE.erl (100%) rename {test_projects => test/test_projects}/linter/app_a/test/app_test_helpers_no_errors.erl (100%) rename {test_projects => test/test_projects}/linter/app_b/src/app_b.app.src (100%) rename {test_projects => test/test_projects}/linter/app_b/src/app_b.erl (100%) rename {test_projects => test/test_projects}/linter/app_b/src/app_b_unused_param.erl (100%) rename {test_projects => test/test_projects}/linter/elp_lint_adhoc.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_custom_function_matches.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_ssr_adhoc.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_ssr_adhoc_parse_fail.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_test1.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_test2.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_test_ignore.toml (100%) rename {test_projects => test/test_projects}/linter/elp_lint_warnings_as_errors.toml (100%) rename {test_projects => test/test_projects}/linter/rebar.config (100%) create mode 100644 test/test_projects/linter_bad_config/.elp.toml rename {test_projects => test/test_projects}/linter_bad_config/.elp_lint.toml (100%) rename {test_projects => test/test_projects}/linter_bad_config/.gitignore (100%) rename {test_projects => test/test_projects}/linter_bad_config/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/linter_bad_config/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/linter_bad_config/app_a/src/app_a.erl (100%) create mode 100644 test/test_projects/linter_bad_config/linter/.elp.toml rename {test_projects => test/test_projects}/linter_bad_config/linter/.elp_lint.toml (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/.gitignore (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/src/app_a_unused_param.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_b/src/app_b.app.src (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_b/src/app_b.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/app_b/src/app_b_unused_param.erl (100%) rename {test_projects => test/test_projects}/linter_bad_config/linter/rebar.config (100%) rename {test_projects => test/test_projects}/linter_bad_config/rebar.config (100%) create mode 100644 test/test_projects/parse_error/.elp.toml rename {test_projects => test/test_projects}/parse_error/.gitignore (100%) rename {test_projects => test/test_projects}/parse_error/.rebar.root (100%) rename {test_projects => test/test_projects}/parse_error/eqwalizer/src/eqwalizer.app.src (100%) rename {test_projects => test/test_projects}/parse_error/eqwalizer/src/eqwalizer.erl (100%) rename {test_projects => test/test_projects}/parse_error/eqwalizer/src/eqwalizer_specs.erl (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a.app.src (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a.erl (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a_bad.erl (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl (100%) rename {test_projects => test/test_projects}/parse_error/parse_error_a/src/parse_error_a_worst.erl (100%) rename {test_projects => test/test_projects}/parse_error/rebar.config (100%) create mode 100644 test/test_projects/standard/.elp.toml rename {test_projects => test/test_projects}/standard/.gitignore (100%) rename {test_projects => test/test_projects}/standard/.rebar.root (100%) rename {test_projects => test/test_projects}/standard/app_a/.eqwalizer (100%) rename {test_projects => test/test_projects}/standard/app_a/extra/app_a.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/include/app_a.hrl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a.app.src (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_errors_generated.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_fixme.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_ignored.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_lists.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_mod2.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_no_errors.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_no_errors_generated.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/src/app_a_no_errors_opted_in.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/test/app_a_SUITE.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/test/app_a_test_helpers.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/test/app_a_test_helpers_not_opted_in.erl (100%) rename {test_projects => test/test_projects}/standard/app_a/test/app_test_helpers_no_errors.erl (100%) rename {test_projects => test/test_projects}/standard/app_b/src/app_b.app.src (100%) rename {test_projects => test/test_projects}/standard/app_b/src/app_b.erl (100%) rename {test_projects => test/test_projects}/standard/eqwalizer/src/eqwalizer.app.src (100%) rename {test_projects => test/test_projects}/standard/eqwalizer/src/eqwalizer.erl (100%) rename {test_projects => test/test_projects}/standard/eqwalizer/src/eqwalizer_specs.erl (100%) rename {test_projects => test/test_projects}/standard/erlang_ls.config (100%) rename {test_projects => test/test_projects}/standard/rebar.config (100%) create mode 100644 test/test_projects/xref/.elp.toml rename {test_projects => test/test_projects}/xref/app_a/src/unavailable_type.erl (100%) rename {test_projects => test/test_projects}/xref/app_b/src/app_b.erl (100%) rename {test_projects => test/test_projects}/xref/app_c/src/app_c.erl (100%) rename {test_projects => test/test_projects}/xref/elp_lint_unavailable_type.toml (100%) delete mode 100644 test_projects/buck_bad_config/.elp.toml delete mode 100644 test_projects/buck_tests/.elp.toml delete mode 100644 test_projects/buck_tests_2/.elp.toml delete mode 100644 test_projects/codegen_test/.elp.toml delete mode 100644 test_projects/diagnostics/.elp.toml delete mode 100644 test_projects/end_to_end/.elp.toml delete mode 100644 test_projects/eqwalizer_callers/.elp.toml delete mode 100644 test_projects/eqwalizer_tests/.elp.toml delete mode 100644 test_projects/hierarchical_config/.elp.toml delete mode 100644 test_projects/in_place_tests/.elp.toml delete mode 100644 test_projects/include_lib_dependency_test/.elp.toml delete mode 100644 test_projects/linter/.elp.toml delete mode 100644 test_projects/linter_bad_config/.elp.toml delete mode 100644 test_projects/linter_bad_config/linter/.elp.toml delete mode 100644 test_projects/parse_error/.elp.toml delete mode 100644 test_projects/standard/.elp.toml delete mode 100644 test_projects/xref/.elp.toml diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index ac2fc901fa..38ef3c3880 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -288,7 +288,7 @@ mod tests { let (_stdout, stderr, code) = elp(args_vec![ "parse-all", "--project", - "../../test_projects/standard", + "../../test/test_projects/standard", "--to", tmp.path(), ]); @@ -306,7 +306,7 @@ mod tests { fn parse_all_complete(project: &str) -> Result { // Just check the command returns. - let project_path = format!("../../test_projects/{project}"); + let project_path = format!("../../test/test_projects/{project}"); let tmp = Builder::new().prefix("elp_parse_all_").tempdir().unwrap(); let (_stdout, _stderr, code) = elp(args_vec![ "parse-all", @@ -606,7 +606,7 @@ mod tests { simple_snapshot( args_vec![ "eqwalize-target", - "//whatsapp/elp/test_projects/standard:app_a", + "//whatsapp/elp/test/test_projects/standard:app_a", ], "standard", expect_file!("../resources/test/standard/eqwalize_target_diagnostics.pretty"), @@ -1444,7 +1444,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/does_not_exist.toml" + "../../test/test_projects/linter/does_not_exist.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_invalid_output.stdout"), @@ -1456,7 +1456,7 @@ mod tests { &[], false, Some(expect![[r#" - unable to read "../../test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) + unable to read "../../test/test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) "#]]), ) .expect("bad test"); @@ -1472,7 +1472,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/elp_lint_test1.toml" + "../../test/test_projects/linter/elp_lint_test1.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_output.stdout"), @@ -1498,7 +1498,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/elp_lint_adhoc.toml", + "../../test/test_projects/linter/elp_lint_adhoc.toml", "--module", "app_b", "--apply-fix", @@ -1529,7 +1529,7 @@ mod tests { "--diagnostic-ignore", "W0011", "--config-file", - "../../test_projects/linter/elp_lint_test_ignore.toml" + "../../test/test_projects/linter/elp_lint_test_ignore.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_ignore.stdout"), @@ -1573,7 +1573,7 @@ mod tests { &[], false, Some(expect![[r#" - failed to read "../../test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 + failed to read "../../test/test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 "#]]), ) .expect("bad test"); @@ -1625,7 +1625,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_test2.toml" + "../../test/test_projects/linter/elp_lint_test2.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_explicit_enable_output.stdout"), @@ -1943,7 +1943,7 @@ mod tests { "lint", "--no-stream" "--config-file", - "../../test_projects/linter/elp_lint_warnings_as_errors.toml" + "../../test/test_projects/linter/elp_lint_warnings_as_errors.toml" ], "linter", expect_file!("../resources/test/linter/warnings_as_errors.stdout"), @@ -1958,7 +1958,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_custom_function_matches.toml", + "../../test/test_projects/linter/elp_lint_custom_function_matches.toml", "--module", "custom_function_matches" ], @@ -1975,7 +1975,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/xref/elp_lint_unavailable_type.toml", + "../../test/test_projects/xref/elp_lint_unavailable_type.toml", "--module", "unavailable_type" ], @@ -1992,7 +1992,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_ssr_adhoc.toml", + "../../test/test_projects/linter/elp_lint_ssr_adhoc.toml", ], "linter", expect_file!("../resources/test/linter/ssr_ad_hoc.stdout"), @@ -2007,7 +2007,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", + "../../test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", ], "linter", expect_file!("../resources/test/linter/ssr_ad_hoc_parse_fail.stdout"), @@ -3099,7 +3099,7 @@ mod tests { } fn project_path(project: &str) -> String { - format!("../../test_projects/{project}") + format!("../../test/test_projects/{project}") } fn strip_ansi_codes(s: &str) -> String { diff --git a/crates/elp/src/resources/test/xref/unavailable_type.stdout b/crates/elp/src/resources/test/xref/unavailable_type.stdout index 7064c359ff..b6338343c0 100644 --- a/crates/elp/src/resources/test/xref/unavailable_type.stdout +++ b/crates/elp/src/resources/test/xref/unavailable_type.stdout @@ -1,5 +1,5 @@ Reporting all diagnostics codes module specified: unavailable_type Diagnostics reported: -app_a/src/unavailable_type.erl:10:43-10:58::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test_projects/xref:app_a'). -app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test_projects/xref:app_a'). +app_a/src/unavailable_type.erl:10:43-10:58::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test/test_projects/xref:app_a'). +app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test/test_projects/xref:app_a'). diff --git a/crates/elp/tests/slow-tests/buck_tests.rs b/crates/elp/tests/slow-tests/buck_tests.rs index 76540e2515..91f2b234d9 100644 --- a/crates/elp/tests/slow-tests/buck_tests.rs +++ b/crates/elp/tests/slow-tests/buck_tests.rs @@ -31,7 +31,7 @@ mod tests { #[test] #[ignore] fn test_success_case() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let cli = Fake::default(); @@ -76,7 +76,7 @@ mod tests { #[test] #[ignore] fn test_load_buck_targets() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let (elp_config, buck_config) = diff --git a/crates/elp/tests/slow-tests/main.rs b/crates/elp/tests/slow-tests/main.rs index 60167593fd..e256ce2c62 100644 --- a/crates/elp/tests/slow-tests/main.rs +++ b/crates/elp/tests/slow-tests/main.rs @@ -36,7 +36,7 @@ use crate::support::diagnostic_project; fn test_run_mock_lsp() { if cfg!(feature = "buck") { let workspace_root = AbsPathBuf::assert( - Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/end_to_end"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/end_to_end"), ); // Sanity check @@ -70,7 +70,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -99,7 +99,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -128,7 +128,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -157,7 +157,7 @@ fn test_run_mock_lsp() { } ], "textDocument": { - "uri": "file:///[..]/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", + "uri": "file:///[..]/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl", "version": 0 } } @@ -175,7 +175,7 @@ fn test_run_mock_lsp() { fn test_e2e_eqwalizer_module() { if cfg!(feature = "buck") { let workspace_root = AbsPathBuf::assert( - Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/standard"), + Utf8Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/standard"), ); // Sanity check @@ -321,7 +321,7 @@ fn test_e2e_eqwalizer_module() { "source": "eqWAlizer" } ], - "uri": "file:///[..]/test_projects/standard/app_a/src/app_a.erl", + "uri": "file:///[..]/test/test_projects/standard/app_a/src/app_a.erl", "version": 0 }"#]], ); @@ -334,7 +334,7 @@ fn test_e2e_eqwalizer_module() { // #[test] // fn test_e2e_eqwalizer_header() { // let workspace_root = -// AbsPathBuf::assert(Path::new(env!("CARGO_WORKSPACE_DIR")).join("test_projects/standard")); +// AbsPathBuf::assert(Path::new(env!("CARGO_WORKSPACE_DIR")).join("test/test_projects/standard")); // // Sanity check // assert!(std::fs::metadata(&workspace_root).is_ok()); diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 093ba35986..733347bfcb 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -3564,7 +3564,7 @@ main(X) -> #[test] fn group_related_diagnostics_elp_only() { // Demonstrate that ELP does not pick up a syntax error in the - // spec, same code as in test_projects/diagnostics/app_a/src/syntax.erl + // spec, same code as in test/test_projects/diagnostics/app_a/src/syntax.erl check_diagnostics( r#" -module(main). diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 3658a6d783..bb3ec38b5f 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -1655,7 +1655,7 @@ mod tests { .arg("--") .args(generated_args) .arg("--included_targets") - .arg("fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/...") + .arg("fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/...") .output() .unwrap(); if !output.status.success() { @@ -1679,15 +1679,15 @@ mod tests { false, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1697,7 +1697,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, @@ -1842,21 +1842,21 @@ mod tests { fn build_info_buck_bxl_generated_query() { if BUCK_TESTS_ENABLED { // Note that there is now a value for `srcs` in the - // "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" + // "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target check_buck_bxl_query( true, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1866,12 +1866,12 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" ], "includes": [], "labels": [ diff --git a/test_projects/.gitignore b/test/test_projects/.gitignore similarity index 100% rename from test_projects/.gitignore rename to test/test_projects/.gitignore diff --git a/test_projects/README.md b/test/test_projects/README.md similarity index 100% rename from test_projects/README.md rename to test/test_projects/README.md diff --git a/test/test_projects/buck_bad_config/.elp.toml b/test/test_projects/buck_bad_config/.elp.toml new file mode 100644 index 0000000000..a4d80e289f --- /dev/null +++ b/test/test_projects/buck_bad_config/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_bad_config/..." ] +source_root = "whatsapp/elp/test/test_projects/buck_bad_config" + +[eqwalizer] +enable_all = false diff --git a/test_projects/buck_bad_config/src/bad_app.erl b/test/test_projects/buck_bad_config/src/bad_app.erl similarity index 100% rename from test_projects/buck_bad_config/src/bad_app.erl rename to test/test_projects/buck_bad_config/src/bad_app.erl diff --git a/test/test_projects/buck_tests/.elp.toml b/test/test_projects/buck_tests/.elp.toml new file mode 100644 index 0000000000..0fbba8f0d6 --- /dev/null +++ b/test/test_projects/buck_tests/.elp.toml @@ -0,0 +1,9 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests/..." ] +excluded_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests:test_elp_ignored" ] +source_root = "whatsapp/elp/test/test_projects/buck_tests" + +[eqwalizer] +enable_all = false diff --git a/test_projects/buck_tests/TARGETS.v2_ b/test/test_projects/buck_tests/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/TARGETS.v2_ rename to test/test_projects/buck_tests/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ similarity index 76% rename from test_projects/buck_tests/test_elp/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp/TARGETS.v2_ index 83089813da..528f2dbc2a 100644 --- a/test_projects/buck_tests/test_elp/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ @@ -7,11 +7,11 @@ erlang_application( ]), app_src = "src/test_elp.app.src", applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_direct_dep:test_elp_direct_dep", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_private_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_public_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_flat_outside_target", - "//whatsapp/elp/test_projects/buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", + "//whatsapp/elp/test/test_projects/buck_tests/test_elp_direct_dep:test_elp_direct_dep", + "//whatsapp/elp/test/test_projects/buck_tests:test_elp_no_private_headers", + "//whatsapp/elp/test/test_projects/buck_tests:test_elp_no_public_headers", + "//whatsapp/elp/test/test_projects/buck_tests:test_elp_flat_outside_target", + "//whatsapp/elp/test/test_projects/buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp/include/test_elp.hrl b/test/test_projects/buck_tests/test_elp/include/test_elp.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/include/test_elp.hrl rename to test/test_projects/buck_tests/test_elp/include/test_elp.hrl diff --git a/test_projects/buck_tests/test_elp/src/test_elp.app.src b/test/test_projects/buck_tests/test_elp/src/test_elp.app.src similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.app.src rename to test/test_projects/buck_tests/test_elp/src/test_elp.app.src diff --git a/test_projects/buck_tests/test_elp/src/test_elp.erl b/test/test_projects/buck_tests/test_elp/src/test_elp.erl similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.erl rename to test/test_projects/buck_tests/test_elp/src/test_elp.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ similarity index 61% rename from test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ index d0fea8149b..dd46a58842 100644 --- a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ @@ -5,10 +5,10 @@ erlang_application( "src/*.hrl", ]), applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", + "//whatsapp/elp/test/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", ], extra_includes = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp:test_elp", + "//whatsapp/elp/test/test_projects/buck_tests/test_elp:test_elp", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl diff --git a/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl b/test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl similarity index 100% rename from test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl rename to test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl b/test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl rename to test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl b/test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl rename to test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl diff --git a/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl b/test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl rename to test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl diff --git a/test/test_projects/buck_tests_2/.elp.toml b/test/test_projects/buck_tests_2/.elp.toml new file mode 100644 index 0000000000..102f44c3f5 --- /dev/null +++ b/test/test_projects/buck_tests_2/.elp.toml @@ -0,0 +1,12 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/util/app_a/...", + "fbcode//whatsapp/elp/test/test_projects/buck_tests_2:check_include" + ] +excluded_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests_2:test_elp_ignored" ] +source_root = "whatsapp/elp/test/test_projects/buck_tests_2" + +[eqwalizer] +enable_all = false diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl diff --git a/test_projects/buck_tests_2/check_include/src/top_includer.erl b/test/test_projects/buck_tests_2/check_include/src/top_includer.erl similarity index 100% rename from test_projects/buck_tests_2/check_include/src/top_includer.erl rename to test/test_projects/buck_tests_2/check_include/src/top_includer.erl diff --git a/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl b/test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl similarity index 100% rename from test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl rename to test/test_projects/buck_tests_2/check_include_separate_1/include/include_with_bug.hrl diff --git a/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl b/test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl similarity index 100% rename from test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl rename to test/test_projects/buck_tests_2/check_include_separate_1/include/top_includer.hrl diff --git a/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl b/test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl similarity index 100% rename from test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl rename to test/test_projects/buck_tests_2/check_include_separate_2/include/separate_include.hrl diff --git a/test_projects/buck_tests_2/generated/out/generated_header.hrl b/test/test_projects/buck_tests_2/generated/out/generated_header.hrl similarity index 100% rename from test_projects/buck_tests_2/generated/out/generated_header.hrl rename to test/test_projects/buck_tests_2/generated/out/generated_header.hrl diff --git a/test_projects/buck_tests_2/util/app_a/include/junk.hrl b/test/test_projects/buck_tests_2/util/app_a/include/junk.hrl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/include/junk.hrl rename to test/test_projects/buck_tests_2/util/app_a/include/junk.hrl diff --git a/test_projects/buck_tests_2/util/app_a/src/app_a.erl b/test/test_projects/buck_tests_2/util/app_a/src/app_a.erl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/src/app_a.erl rename to test/test_projects/buck_tests_2/util/app_a/src/app_a.erl diff --git a/test/test_projects/codegen_test/.elp.toml b/test/test_projects/codegen_test/.elp.toml new file mode 100644 index 0000000000..52ec390ccb --- /dev/null +++ b/test/test_projects/codegen_test/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = true +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_app" ] +source_root = "whatsapp/elp/test/test_projects/codegen_test" + +[eqwalizer] +enable_all = true diff --git a/test_projects/codegen_test/README.md b/test/test_projects/codegen_test/README.md similarity index 93% rename from test_projects/codegen_test/README.md rename to test/test_projects/codegen_test/README.md index 1af599454c..061fb1914c 100644 --- a/test_projects/codegen_test/README.md +++ b/test/test_projects/codegen_test/README.md @@ -111,13 +111,13 @@ get_user_by_id(UserId) -> ```bash # Build just the code generation step -buck2 build fbcode//whatsapp/elp/test_projects/codegen_test:example_service_types_erl +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:example_service_types_erl # Build the application (automatically runs code generation) -buck2 build fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_app +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_app # Run tests -buck2 test fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_SUITE +buck2 test fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_SUITE # View generated files in buck-out find buck-out -path "*codegen_test*" -name "example_service_*.erl" diff --git a/test_projects/codegen_test/app_a/src/codegen_test.app.src b/test/test_projects/codegen_test/app_a/src/codegen_test.app.src similarity index 100% rename from test_projects/codegen_test/app_a/src/codegen_test.app.src rename to test/test_projects/codegen_test/app_a/src/codegen_test.app.src diff --git a/test_projects/codegen_test/app_a/src/example_usage.erl b/test/test_projects/codegen_test/app_a/src/example_usage.erl similarity index 100% rename from test_projects/codegen_test/app_a/src/example_usage.erl rename to test/test_projects/codegen_test/app_a/src/example_usage.erl diff --git a/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl similarity index 100% rename from test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl rename to test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl diff --git a/test_projects/codegen_test/generated/example_service_client.erl b/test/test_projects/codegen_test/generated/example_service_client.erl similarity index 100% rename from test_projects/codegen_test/generated/example_service_client.erl rename to test/test_projects/codegen_test/generated/example_service_client.erl diff --git a/test_projects/codegen_test/generated/example_service_types.erl b/test/test_projects/codegen_test/generated/example_service_types.erl similarity index 100% rename from test_projects/codegen_test/generated/example_service_types.erl rename to test/test_projects/codegen_test/generated/example_service_types.erl diff --git a/test_projects/codegen_test/generated/example_service_types.hrl b/test/test_projects/codegen_test/generated/example_service_types.hrl similarity index 100% rename from test_projects/codegen_test/generated/example_service_types.hrl rename to test/test_projects/codegen_test/generated/example_service_types.hrl diff --git a/test_projects/codegen_test/templates/example_service_client.erl b/test/test_projects/codegen_test/templates/example_service_client.erl similarity index 100% rename from test_projects/codegen_test/templates/example_service_client.erl rename to test/test_projects/codegen_test/templates/example_service_client.erl diff --git a/test_projects/codegen_test/templates/example_service_types.erl b/test/test_projects/codegen_test/templates/example_service_types.erl similarity index 100% rename from test_projects/codegen_test/templates/example_service_types.erl rename to test/test_projects/codegen_test/templates/example_service_types.erl diff --git a/test_projects/codegen_test/templates/example_service_types.hrl b/test/test_projects/codegen_test/templates/example_service_types.hrl similarity index 100% rename from test_projects/codegen_test/templates/example_service_types.hrl rename to test/test_projects/codegen_test/templates/example_service_types.hrl diff --git a/test_projects/custom_build_tool/.elp.toml b/test/test_projects/custom_build_tool/.elp.toml similarity index 100% rename from test_projects/custom_build_tool/.elp.toml rename to test/test_projects/custom_build_tool/.elp.toml diff --git a/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl b/test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/include/app_a.hrl rename to test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl diff --git a/test_projects/custom_build_tool/apps/app_a/src/app_a.erl b/test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/src/app_a.erl rename to test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl diff --git a/test_projects/custom_build_tool/apps/app_b/src/app_b.erl b/test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_b/src/app_b.erl rename to test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl diff --git a/test/test_projects/diagnostics/.elp.toml b/test/test_projects/diagnostics/.elp.toml new file mode 100644 index 0000000000..44c9516105 --- /dev/null +++ b/test/test_projects/diagnostics/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/diagnostics/..." ] +source_root = "whatsapp/elp/test/test_projects/diagnostics" + +[eqwalizer] +enable_all = false diff --git a/test_projects/diagnostics/README.md b/test/test_projects/diagnostics/README.md similarity index 100% rename from test_projects/diagnostics/README.md rename to test/test_projects/diagnostics/README.md diff --git a/test_projects/diagnostics/app_a/extra/app_a.erl b/test/test_projects/diagnostics/app_a/extra/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/extra/app_a.erl rename to test/test_projects/diagnostics/app_a/extra/app_a.erl diff --git a/test_projects/diagnostics/app_a/include/app_a.hrl b/test/test_projects/diagnostics/app_a/include/app_a.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/app_a.hrl rename to test/test_projects/diagnostics/app_a/include/app_a.hrl diff --git a/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/broken_diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/include/diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/src/app_a.app.src b/test/test_projects/diagnostics/app_a/src/app_a.app.src similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.app.src rename to test/test_projects/diagnostics/app_a/src/app_a.app.src diff --git a/test_projects/diagnostics/app_a/src/app_a.erl b/test/test_projects/diagnostics/app_a/src/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.erl rename to test/test_projects/diagnostics/app_a/src/app_a.erl diff --git a/test_projects/diagnostics/app_a/src/broken_parse_trans.erl b/test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/broken_parse_trans.erl rename to test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl diff --git a/test_projects/diagnostics/app_a/src/cascading.erl b/test/test_projects/diagnostics/app_a/src/cascading.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/cascading.erl rename to test/test_projects/diagnostics/app_a/src/cascading.erl diff --git a/test_projects/diagnostics/app_a/src/crlf.erl b/test/test_projects/diagnostics/app_a/src/crlf.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/crlf.erl rename to test/test_projects/diagnostics/app_a/src/crlf.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.erl b/test/test_projects/diagnostics/app_a/src/diagnostics.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.erl rename to test/test_projects/diagnostics/app_a/src/diagnostics.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.escript b/test/test_projects/diagnostics/app_a/src/diagnostics.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_errors.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_errors.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_warnings.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript diff --git a/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl b/test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl rename to test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl diff --git a/test_projects/diagnostics/app_a/src/file_attribute.erl b/test/test_projects/diagnostics/app_a/src/file_attribute.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/file_attribute.erl rename to test/test_projects/diagnostics/app_a/src/file_attribute.erl diff --git a/test_projects/diagnostics/app_a/src/lint_recursive.erl b/test/test_projects/diagnostics/app_a/src/lint_recursive.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lint_recursive.erl rename to test/test_projects/diagnostics/app_a/src/lint_recursive.erl diff --git a/test_projects/diagnostics/app_a/src/lints.erl b/test/test_projects/diagnostics/app_a/src/lints.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lints.erl rename to test/test_projects/diagnostics/app_a/src/lints.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_docstrings.erl b/test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_docstrings.erl rename to test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_sigils.erl b/test/test_projects/diagnostics/app_a/src/otp27_sigils.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_sigils.erl rename to test/test_projects/diagnostics/app_a/src/otp27_sigils.erl diff --git a/test_projects/diagnostics/app_a/src/otp_7655.erl b/test/test_projects/diagnostics/app_a/src/otp_7655.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp_7655.erl rename to test/test_projects/diagnostics/app_a/src/otp_7655.erl diff --git a/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl b/test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl rename to test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl diff --git a/test_projects/diagnostics/app_a/src/suppressed.erl b/test/test_projects/diagnostics/app_a/src/suppressed.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/suppressed.erl rename to test/test_projects/diagnostics/app_a/src/suppressed.erl diff --git a/test_projects/diagnostics/app_a/src/syntax.erl b/test/test_projects/diagnostics/app_a/src/syntax.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/syntax.erl rename to test/test_projects/diagnostics/app_a/src/syntax.erl diff --git a/test_projects/diagnostics/app_a/test/app_a_SUITE.erl b/test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/diagnostics/app_a/test/app_a_SUITE.erl rename to test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl diff --git a/test_projects/diagnostics/erlang_ls.config b/test/test_projects/diagnostics/erlang_ls.config similarity index 100% rename from test_projects/diagnostics/erlang_ls.config rename to test/test_projects/diagnostics/erlang_ls.config diff --git a/test_projects/diagnostics/rebar.config b/test/test_projects/diagnostics/rebar.config similarity index 100% rename from test_projects/diagnostics/rebar.config rename to test/test_projects/diagnostics/rebar.config diff --git a/test_projects/diagnostics/test_build_info.json b/test/test_projects/diagnostics/test_build_info.json similarity index 100% rename from test_projects/diagnostics/test_build_info.json rename to test/test_projects/diagnostics/test_build_info.json diff --git a/test/test_projects/end_to_end/.elp.toml b/test/test_projects/end_to_end/.elp.toml new file mode 100644 index 0000000000..4d685a40c7 --- /dev/null +++ b/test/test_projects/end_to_end/.elp.toml @@ -0,0 +1,7 @@ +[buck] +enabled = true +build_deps = false +included_targets = ["fbcode//whatsapp/elp/test/test_projects/end_to_end/..."] + +[eqwalizer] +enable_all = false diff --git a/test_projects/end_to_end/assist_examples/src/add_doc.erl b/test/test_projects/end_to_end/assist_examples/src/add_doc.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/add_doc.erl rename to test/test_projects/end_to_end/assist_examples/src/add_doc.erl diff --git a/test_projects/end_to_end/assist_examples/src/assist_examples.app.src b/test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src similarity index 100% rename from test_projects/end_to_end/assist_examples/src/assist_examples.app.src rename to test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src diff --git a/test_projects/end_to_end/assist_examples/src/code_completion.erl b/test/test_projects/end_to_end/assist_examples/src/code_completion.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/code_completion.erl rename to test/test_projects/end_to_end/assist_examples/src/code_completion.erl diff --git a/test_projects/end_to_end/assist_examples/src/extract_function.erl b/test/test_projects/end_to_end/assist_examples/src/extract_function.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/extract_function.erl rename to test/test_projects/end_to_end/assist_examples/src/extract_function.erl diff --git a/test_projects/end_to_end/assist_examples/src/head_mismatch.erl b/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/head_mismatch.erl rename to test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl diff --git a/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl b/test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl similarity index 100% rename from test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl rename to test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl diff --git a/test_projects/end_to_end/definitions/README.md b/test/test_projects/end_to_end/definitions/README.md similarity index 100% rename from test_projects/end_to_end/definitions/README.md rename to test/test_projects/end_to_end/definitions/README.md diff --git a/test_projects/end_to_end/definitions/src/definitions.app.src b/test/test_projects/end_to_end/definitions/src/definitions.app.src similarity index 100% rename from test_projects/end_to_end/definitions/src/definitions.app.src rename to test/test_projects/end_to_end/definitions/src/definitions.app.src diff --git a/test_projects/end_to_end/definitions/src/local_def.erl b/test/test_projects/end_to_end/definitions/src/local_def.erl similarity index 100% rename from test_projects/end_to_end/definitions/src/local_def.erl rename to test/test_projects/end_to_end/definitions/src/local_def.erl diff --git a/test_projects/end_to_end/docs/src/docs.app.src b/test/test_projects/end_to_end/docs/src/docs.app.src similarity index 100% rename from test_projects/end_to_end/docs/src/docs.app.src rename to test/test_projects/end_to_end/docs/src/docs.app.src diff --git a/test_projects/end_to_end/docs/src/docs.erl b/test/test_projects/end_to_end/docs/src/docs.erl similarity index 100% rename from test_projects/end_to_end/docs/src/docs.erl rename to test/test_projects/end_to_end/docs/src/docs.erl diff --git a/test_projects/end_to_end/erlang_ls.config b/test/test_projects/end_to_end/erlang_ls.config similarity index 100% rename from test_projects/end_to_end/erlang_ls.config rename to test/test_projects/end_to_end/erlang_ls.config diff --git a/test_projects/end_to_end/fixtures/erlang-stacktrace.txt b/test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt similarity index 100% rename from test_projects/end_to_end/fixtures/erlang-stacktrace.txt rename to test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt diff --git a/test_projects/end_to_end/hover/README.md b/test/test_projects/end_to_end/hover/README.md similarity index 100% rename from test_projects/end_to_end/hover/README.md rename to test/test_projects/end_to_end/hover/README.md diff --git a/test_projects/end_to_end/hover/src/doc_examples.erl b/test/test_projects/end_to_end/hover/src/doc_examples.erl similarity index 100% rename from test_projects/end_to_end/hover/src/doc_examples.erl rename to test/test_projects/end_to_end/hover/src/doc_examples.erl diff --git a/test_projects/end_to_end/hover/src/hover.app.src b/test/test_projects/end_to_end/hover/src/hover.app.src similarity index 100% rename from test_projects/end_to_end/hover/src/hover.app.src rename to test/test_projects/end_to_end/hover/src/hover.app.src diff --git a/test_projects/end_to_end/rebar.config b/test/test_projects/end_to_end/rebar.config similarity index 100% rename from test_projects/end_to_end/rebar.config rename to test/test_projects/end_to_end/rebar.config diff --git a/test_projects/end_to_end/single_errors/README.md b/test/test_projects/end_to_end/single_errors/README.md similarity index 100% rename from test_projects/end_to_end/single_errors/README.md rename to test/test_projects/end_to_end/single_errors/README.md diff --git a/test_projects/end_to_end/single_errors/src/as_you_type.erl b/test/test_projects/end_to_end/single_errors/src/as_you_type.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/as_you_type.erl rename to test/test_projects/end_to_end/single_errors/src/as_you_type.erl diff --git a/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl b/test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl rename to test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl diff --git a/test_projects/end_to_end/single_errors/src/single_errors.app.src b/test/test_projects/end_to_end/single_errors/src/single_errors.app.src similarity index 100% rename from test_projects/end_to_end/single_errors/src/single_errors.app.src rename to test/test_projects/end_to_end/single_errors/src/single_errors.app.src diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 diff --git a/test_projects/end_to_end/single_errors/src/types_on_hover.erl b/test/test_projects/end_to_end/single_errors/src/types_on_hover.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/types_on_hover.erl rename to test/test_projects/end_to_end/single_errors/src/types_on_hover.erl diff --git a/test_projects/end_to_end/single_errors/src/unused_macro.erl b/test/test_projects/end_to_end/single_errors/src/unused_macro.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/unused_macro.erl rename to test/test_projects/end_to_end/single_errors/src/unused_macro.erl diff --git a/test_projects/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/eqwalizer_callers/.elp.toml b/test/test_projects/eqwalizer_callers/.elp.toml new file mode 100644 index 0000000000..b0c2b55a5b --- /dev/null +++ b/test/test_projects/eqwalizer_callers/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/eqwalizer_callers/..." ] +source_root = "whatsapp/elp/test/test_projects/eqwalizer_callers" diff --git a/test_projects/eqwalizer_callers/.gitignore b/test/test_projects/eqwalizer_callers/.gitignore similarity index 100% rename from test_projects/eqwalizer_callers/.gitignore rename to test/test_projects/eqwalizer_callers/.gitignore diff --git a/test_projects/eqwalizer_callers/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.app.src b/test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.app.src rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_b.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_b.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_b.erl diff --git a/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl b/test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl rename to test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl diff --git a/test_projects/eqwalizer_callers/rebar.config b/test/test_projects/eqwalizer_callers/rebar.config similarity index 100% rename from test_projects/eqwalizer_callers/rebar.config rename to test/test_projects/eqwalizer_callers/rebar.config diff --git a/test_projects/eqwalizer_ignore_modules/.elp.toml b/test/test_projects/eqwalizer_ignore_modules/.elp.toml similarity index 100% rename from test_projects/eqwalizer_ignore_modules/.elp.toml rename to test/test_projects/eqwalizer_ignore_modules/.elp.toml diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl diff --git a/test/test_projects/eqwalizer_tests/.elp.toml b/test/test_projects/eqwalizer_tests/.elp.toml new file mode 100644 index 0000000000..9586666619 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/eqwalizer_tests/..." ] +source_root = "whatsapp/elp/test/test_projects/eqwalizer_tests" + +[eqwalizer] +enable_all = true diff --git a/test_projects/eqwalizer_tests/.gitignore b/test/test_projects/eqwalizer_tests/.gitignore similarity index 100% rename from test_projects/eqwalizer_tests/.gitignore rename to test/test_projects/eqwalizer_tests/.gitignore diff --git a/test_projects/eqwalizer_tests/.rebar.root b/test/test_projects/eqwalizer_tests/.rebar.root similarity index 100% rename from test_projects/eqwalizer_tests/.rebar.root rename to test/test_projects/eqwalizer_tests/.rebar.root diff --git a/test_projects/eqwalizer_tests/check/include/my_header.hrl b/test/test_projects/eqwalizer_tests/check/include/my_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/check/include/my_header.hrl rename to test/test_projects/eqwalizer_tests/check/include/my_header.hrl diff --git a/test_projects/eqwalizer_tests/check/src/any_fun_type.erl b/test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/any_fun_type.erl rename to test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl diff --git a/test_projects/eqwalizer_tests/check/src/apply_none.erl b/test/test_projects/eqwalizer_tests/check/src/apply_none.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/apply_none.erl rename to test/test_projects/eqwalizer_tests/check/src/apply_none.erl diff --git a/test_projects/eqwalizer_tests/check/src/approx.erl b/test/test_projects/eqwalizer_tests/check/src/approx.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/approx.erl rename to test/test_projects/eqwalizer_tests/check/src/approx.erl diff --git a/test_projects/eqwalizer_tests/check/src/as_pat.erl b/test/test_projects/eqwalizer_tests/check/src/as_pat.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/as_pat.erl rename to test/test_projects/eqwalizer_tests/check/src/as_pat.erl diff --git a/test_projects/eqwalizer_tests/check/src/auto_imports.erl b/test/test_projects/eqwalizer_tests/check/src/auto_imports.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/auto_imports.erl rename to test/test_projects/eqwalizer_tests/check/src/auto_imports.erl diff --git a/test_projects/eqwalizer_tests/check/src/behave.erl b/test/test_projects/eqwalizer_tests/check/src/behave.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/behave.erl rename to test/test_projects/eqwalizer_tests/check/src/behave.erl diff --git a/test_projects/eqwalizer_tests/check/src/binaries.erl b/test/test_projects/eqwalizer_tests/check/src/binaries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/binaries.erl rename to test/test_projects/eqwalizer_tests/check/src/binaries.erl diff --git a/test_projects/eqwalizer_tests/check/src/booleans.erl b/test/test_projects/eqwalizer_tests/check/src/booleans.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/booleans.erl rename to test/test_projects/eqwalizer_tests/check/src/booleans.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/case_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/case_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/case_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/case_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/check.app.src b/test/test_projects/eqwalizer_tests/check/src/check.app.src similarity index 100% rename from test_projects/eqwalizer_tests/check/src/check.app.src rename to test/test_projects/eqwalizer_tests/check/src/check.app.src diff --git a/test_projects/eqwalizer_tests/check/src/compiler_macro.erl b/test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/compiler_macro.erl rename to test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl diff --git a/test_projects/eqwalizer_tests/check/src/complex_maps.erl b/test/test_projects/eqwalizer_tests/check/src/complex_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/complex_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/complex_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/comprehensions.erl b/test/test_projects/eqwalizer_tests/check/src/comprehensions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/comprehensions.erl rename to test/test_projects/eqwalizer_tests/check/src/comprehensions.erl diff --git a/test_projects/eqwalizer_tests/check/src/contravariant.erl b/test/test_projects/eqwalizer_tests/check/src/contravariant.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/contravariant.erl rename to test/test_projects/eqwalizer_tests/check/src/contravariant.erl diff --git a/test_projects/eqwalizer_tests/check/src/custom.erl b/test/test_projects/eqwalizer_tests/check/src/custom.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/custom.erl rename to test/test_projects/eqwalizer_tests/check/src/custom.erl diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs1.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs1.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs2.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs2.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_refine.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/elab_clause.erl b/test/test_projects/eqwalizer_tests/check/src/elab_clause.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/elab_clause.erl rename to test/test_projects/eqwalizer_tests/check/src/elab_clause.erl diff --git a/test_projects/eqwalizer_tests/check/src/error_messages.erl b/test/test_projects/eqwalizer_tests/check/src/error_messages.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/error_messages.erl rename to test/test_projects/eqwalizer_tests/check/src/error_messages.erl diff --git a/test_projects/eqwalizer_tests/check/src/fancy_generics.erl b/test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fancy_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/format.erl b/test/test_projects/eqwalizer_tests/check/src/format.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/format.erl rename to test/test_projects/eqwalizer_tests/check/src/format.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats2.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats2.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs.erl b/test/test_projects/eqwalizer_tests/check/src/funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs.erl rename to test/test_projects/eqwalizer_tests/check/src/funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs2.erl b/test/test_projects/eqwalizer_tests/check/src/funs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs2.erl rename to test/test_projects/eqwalizer_tests/check/src/funs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl b/test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs_uncommon.erl rename to test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl diff --git a/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl b/test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generic_fun_application.erl rename to test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl diff --git a/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl b/test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generics_with_unions.erl rename to test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_bounded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_custom.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_custom.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_misc.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_misc.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_untyped.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl diff --git a/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl b/test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guard_b_connections.erl rename to test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards.erl b/test/test_projects/eqwalizer_tests/check/src/guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards.erl rename to test/test_projects/eqwalizer_tests/check/src/guards.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_logic.erl b/test/test_projects/eqwalizer_tests/check/src/guards_logic.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_logic.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_logic.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_simple.erl b/test/test_projects/eqwalizer_tests/check/src/guards_simple.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_simple.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_simple.erl diff --git a/test_projects/eqwalizer_tests/check/src/hints.erl b/test/test_projects/eqwalizer_tests/check/src/hints.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/hints.erl rename to test/test_projects/eqwalizer_tests/check/src/hints.erl diff --git a/test_projects/eqwalizer_tests/check/src/index1.erl b/test/test_projects/eqwalizer_tests/check/src/index1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index1.erl rename to test/test_projects/eqwalizer_tests/check/src/index1.erl diff --git a/test_projects/eqwalizer_tests/check/src/index2.erl b/test/test_projects/eqwalizer_tests/check/src/index2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index2.erl rename to test/test_projects/eqwalizer_tests/check/src/index2.erl diff --git a/test_projects/eqwalizer_tests/check/src/iolists.erl b/test/test_projects/eqwalizer_tests/check/src/iolists.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/iolists.erl rename to test/test_projects/eqwalizer_tests/check/src/iolists.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_01.erl b/test/test_projects/eqwalizer_tests/check/src/kp_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_01.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_02.erl b/test/test_projects/eqwalizer_tests/check/src/kp_02.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_02.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_02.erl diff --git a/test_projects/eqwalizer_tests/check/src/lists_tests.erl b/test/test_projects/eqwalizer_tests/check/src/lists_tests.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/lists_tests.erl rename to test/test_projects/eqwalizer_tests/check/src/lists_tests.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc.erl b/test/test_projects/eqwalizer_tests/check/src/misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc.erl rename to test/test_projects/eqwalizer_tests/check/src/misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc_lib.erl b/test/test_projects/eqwalizer_tests/check/src/misc_lib.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc_lib.erl rename to test/test_projects/eqwalizer_tests/check/src/misc_lib.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_header.erl b/test/test_projects/eqwalizer_tests/check/src/my_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_header.erl rename to test/test_projects/eqwalizer_tests/check/src/my_header.erl diff --git a/test_projects/eqwalizer_tests/check/src/neg.erl b/test/test_projects/eqwalizer_tests/check/src/neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/neg.erl rename to test/test_projects/eqwalizer_tests/check/src/neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl diff --git a/test_projects/eqwalizer_tests/check/src/nowarn.erl b/test/test_projects/eqwalizer_tests/check/src/nowarn.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nowarn.erl rename to test/test_projects/eqwalizer_tests/check/src/nowarn.erl diff --git a/test_projects/eqwalizer_tests/check/src/number_comparisons.erl b/test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/number_comparisons.erl rename to test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl diff --git a/test_projects/eqwalizer_tests/check/src/numbers.erl b/test/test_projects/eqwalizer_tests/check/src/numbers.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/numbers.erl rename to test/test_projects/eqwalizer_tests/check/src/numbers.erl diff --git a/test_projects/eqwalizer_tests/check/src/opaque.erl b/test/test_projects/eqwalizer_tests/check/src/opaque.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/opaque.erl rename to test/test_projects/eqwalizer_tests/check/src/opaque.erl diff --git a/test_projects/eqwalizer_tests/check/src/other.erl b/test/test_projects/eqwalizer_tests/check/src/other.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/other.erl rename to test/test_projects/eqwalizer_tests/check/src/other.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp28.erl b/test/test_projects/eqwalizer_tests/check/src/otp28.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp28.erl rename to test/test_projects/eqwalizer_tests/check/src/otp28.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp_opaques.erl b/test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp_opaques.erl rename to test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/parametricity.erl b/test/test_projects/eqwalizer_tests/check/src/parametricity.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/parametricity.erl rename to test/test_projects/eqwalizer_tests/check/src/parametricity.erl diff --git a/test_projects/eqwalizer_tests/check/src/pats.erl b/test/test_projects/eqwalizer_tests/check/src/pats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pats.erl rename to test/test_projects/eqwalizer_tests/check/src/pats.erl diff --git a/test_projects/eqwalizer_tests/check/src/pinned.erl b/test/test_projects/eqwalizer_tests/check/src/pinned.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pinned.erl rename to test/test_projects/eqwalizer_tests/check/src/pinned.erl diff --git a/test_projects/eqwalizer_tests/check/src/pos.erl b/test/test_projects/eqwalizer_tests/check/src/pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pos.erl rename to test/test_projects/eqwalizer_tests/check/src/pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/records.erl b/test/test_projects/eqwalizer_tests/check/src/records.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/records.erl rename to test/test_projects/eqwalizer_tests/check/src/records.erl diff --git a/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/recursive_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/refine.erl b/test/test_projects/eqwalizer_tests/check/src/refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/refine.erl rename to test/test_projects/eqwalizer_tests/check/src/refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/scoping.erl b/test/test_projects/eqwalizer_tests/check/src/scoping.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/scoping.erl rename to test/test_projects/eqwalizer_tests/check/src/scoping.erl diff --git a/test_projects/eqwalizer_tests/check/src/skip.erl b/test/test_projects/eqwalizer_tests/check/src/skip.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/skip.erl rename to test/test_projects/eqwalizer_tests/check/src/skip.erl diff --git a/test_projects/eqwalizer_tests/check/src/static_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/static_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/static_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/static_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_fun.erl b/test/test_projects/eqwalizer_tests/check/src/strict_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_receive.erl b/test/test_projects/eqwalizer_tests/check/src/strict_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_neg.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_pos.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/t_maps.erl b/test/test_projects/eqwalizer_tests/check/src/t_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/t_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/t_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl b/test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tagged_tuples.erl rename to test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl diff --git a/test_projects/eqwalizer_tests/check/src/test.erl b/test/test_projects/eqwalizer_tests/check/src/test.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/test.erl rename to test/test_projects/eqwalizer_tests/check/src/test.erl diff --git a/test_projects/eqwalizer_tests/check/src/tries.erl b/test/test_projects/eqwalizer_tests/check/src/tries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tries.erl rename to test/test_projects/eqwalizer_tests/check/src/tries.erl diff --git a/test_projects/eqwalizer_tests/check/src/tuple_union.erl b/test/test_projects/eqwalizer_tests/check/src/tuple_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tuple_union.erl rename to test/test_projects/eqwalizer_tests/check/src/tuple_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/type_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/type_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_asserts.erl b/test/test_projects/eqwalizer_tests/check/src/type_asserts.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_asserts.erl rename to test/test_projects/eqwalizer_tests/check/src/type_asserts.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/type_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/type_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/united_fun.erl b/test/test_projects/eqwalizer_tests/check/src/united_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/united_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/united_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/unspecced.erl b/test/test_projects/eqwalizer_tests/check/src/unspecced.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/unspecced.erl rename to test/test_projects/eqwalizer_tests/check/src/unspecced.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars1.erl b/test/test_projects/eqwalizer_tests/check/src/vars1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars1.erl rename to test/test_projects/eqwalizer_tests/check/src/vars1.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars2.erl b/test/test_projects/eqwalizer_tests/check/src/vars2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars2.erl rename to test/test_projects/eqwalizer_tests/check/src/vars2.erl diff --git a/test_projects/eqwalizer_tests/check/test/check_SUITE.erl b/test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/test/check_SUITE.erl rename to test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl diff --git a/test_projects/eqwalizer_tests/debug/include/debug_header.hrl b/test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/debug/include/debug_header.hrl rename to test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl diff --git a/test_projects/eqwalizer_tests/debug/src/attributes.erl b/test/test_projects/eqwalizer_tests/debug/src/attributes.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/attributes.erl rename to test/test_projects/eqwalizer_tests/debug/src/attributes.erl diff --git a/test_projects/eqwalizer_tests/debug/src/debug.app.src b/test/test_projects/eqwalizer_tests/debug/src/debug.app.src similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug.app.src rename to test/test_projects/eqwalizer_tests/debug/src/debug.app.src diff --git a/test_projects/eqwalizer_tests/debug/src/debug_header.erl b/test/test_projects/eqwalizer_tests/debug/src/debug_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug_header.erl rename to test/test_projects/eqwalizer_tests/debug/src/debug_header.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expand.erl b/test/test_projects/eqwalizer_tests/debug/src/expand.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expand.erl rename to test/test_projects/eqwalizer_tests/debug/src/expand.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr1.erl b/test/test_projects/eqwalizer_tests/debug/src/expr1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr1.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr2.erl b/test/test_projects/eqwalizer_tests/debug/src/expr2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr2.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/records_wip.erl b/test/test_projects/eqwalizer_tests/debug/src/records_wip.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/records_wip.erl rename to test/test_projects/eqwalizer_tests/debug/src/records_wip.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types1.erl b/test/test_projects/eqwalizer_tests/debug/src/types1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types1.erl rename to test/test_projects/eqwalizer_tests/debug/src/types1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types2.erl b/test/test_projects/eqwalizer_tests/debug/src/types2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types2.erl rename to test/test_projects/eqwalizer_tests/debug/src/types2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/wip_maps.erl b/test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/wip_maps.erl rename to test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/basics.erl b/test/test_projects/eqwalizer_tests/elm_core/src/basics.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/basics.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/basics.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src b/test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src rename to test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src diff --git a/test_projects/eqwalizer_tests/elm_core/src/list.erl b/test/test_projects/eqwalizer_tests/elm_core/src/list.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/list.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/list.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/maybe.erl b/test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/maybe.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/result.erl b/test/test_projects/eqwalizer_tests/elm_core/src/result.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/result.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/result.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/tuple.erl b/test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/tuple.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl b/test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl rename to test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl diff --git a/test_projects/eqwalizer_tests/eqwater/readme.md b/test/test_projects/eqwalizer_tests/eqwater/readme.md similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/readme.md rename to test/test_projects/eqwalizer_tests/eqwater/readme.md diff --git a/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl b/test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_records.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_sound.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl b/test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/unlimited_refinement.erl diff --git a/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer b/test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer rename to test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl diff --git a/test_projects/eqwalizer_tests/options/src/bad_maps.erl b/test/test_projects/eqwalizer_tests/options/src/bad_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/bad_maps.erl rename to test/test_projects/eqwalizer_tests/options/src/bad_maps.erl diff --git a/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl b/test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl rename to test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl diff --git a/test_projects/eqwalizer_tests/options/src/options.app.src b/test/test_projects/eqwalizer_tests/options/src/options.app.src similarity index 100% rename from test_projects/eqwalizer_tests/options/src/options.app.src rename to test/test_projects/eqwalizer_tests/options/src/options.app.src diff --git a/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl b/test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl rename to test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl diff --git a/test_projects/eqwalizer_tests/options/src/redundant_guards.erl b/test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/redundant_guards.erl rename to test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl diff --git a/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl b/test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl rename to test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl diff --git a/test_projects/eqwalizer_tests/rebar.config b/test/test_projects/eqwalizer_tests/rebar.config similarity index 100% rename from test_projects/eqwalizer_tests/rebar.config rename to test/test_projects/eqwalizer_tests/rebar.config diff --git a/test/test_projects/hierarchical_config/.elp.toml b/test/test_projects/hierarchical_config/.elp.toml new file mode 100644 index 0000000000..a650134c9f --- /dev/null +++ b/test/test_projects/hierarchical_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/hierarchical_config/..." ] +source_root = "whatsapp/elp/test/test_projects/hierarchical_config" diff --git a/test_projects/hierarchical_config/.elp_lint.toml b/test/test_projects/hierarchical_config/.elp_lint.toml similarity index 100% rename from test_projects/hierarchical_config/.elp_lint.toml rename to test/test_projects/hierarchical_config/.elp_lint.toml diff --git a/test_projects/hierarchical_config/app_a/.elp_lint.toml b/test/test_projects/hierarchical_config/app_a/.elp_lint.toml similarity index 100% rename from test_projects/hierarchical_config/app_a/.elp_lint.toml rename to test/test_projects/hierarchical_config/app_a/.elp_lint.toml diff --git a/test_projects/hierarchical_config/app_a/src/app_a.app.src b/test/test_projects/hierarchical_config/app_a/src/app_a.app.src similarity index 100% rename from test_projects/hierarchical_config/app_a/src/app_a.app.src rename to test/test_projects/hierarchical_config/app_a/src/app_a.app.src diff --git a/test_projects/hierarchical_config/app_a/src/app_a.erl b/test/test_projects/hierarchical_config/app_a/src/app_a.erl similarity index 100% rename from test_projects/hierarchical_config/app_a/src/app_a.erl rename to test/test_projects/hierarchical_config/app_a/src/app_a.erl diff --git a/test_projects/hierarchical_config/app_b/src/app_b.app.src b/test/test_projects/hierarchical_config/app_b/src/app_b.app.src similarity index 100% rename from test_projects/hierarchical_config/app_b/src/app_b.app.src rename to test/test_projects/hierarchical_config/app_b/src/app_b.app.src diff --git a/test_projects/hierarchical_config/app_b/src/app_b.erl b/test/test_projects/hierarchical_config/app_b/src/app_b.erl similarity index 100% rename from test_projects/hierarchical_config/app_b/src/app_b.erl rename to test/test_projects/hierarchical_config/app_b/src/app_b.erl diff --git a/test_projects/hierarchical_config/rebar.config b/test/test_projects/hierarchical_config/rebar.config similarity index 100% rename from test_projects/hierarchical_config/rebar.config rename to test/test_projects/hierarchical_config/rebar.config diff --git a/test/test_projects/in_place_tests/.elp.toml b/test/test_projects/in_place_tests/.elp.toml new file mode 100644 index 0000000000..64140d2852 --- /dev/null +++ b/test/test_projects/in_place_tests/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/in_place_tests/..." ] +source_root = "whatsapp/elp/test/test_projects/in_place_tests" diff --git a/test_projects/in_place_tests/README.md b/test/test_projects/in_place_tests/README.md similarity index 100% rename from test_projects/in_place_tests/README.md rename to test/test_projects/in_place_tests/README.md diff --git a/test_projects/in_place_tests/app_a/extra/app_a.erl b/test/test_projects/in_place_tests/app_a/extra/app_a.erl similarity index 100% rename from test_projects/in_place_tests/app_a/extra/app_a.erl rename to test/test_projects/in_place_tests/app_a/extra/app_a.erl diff --git a/test_projects/in_place_tests/app_a/include/app_a.hrl b/test/test_projects/in_place_tests/app_a/include/app_a.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/app_a.hrl rename to test/test_projects/in_place_tests/app_a/include/app_a.hrl diff --git a/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/in_place_tests/app_a/include/diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/diagnostics.hrl diff --git a/test_projects/in_place_tests/app_a/src/app_a.app.src b/test/test_projects/in_place_tests/app_a/src/app_a.app.src similarity index 100% rename from test_projects/in_place_tests/app_a/src/app_a.app.src rename to test/test_projects/in_place_tests/app_a/src/app_a.app.src diff --git a/test_projects/in_place_tests/app_a/src/app_a.erl b/test/test_projects/in_place_tests/app_a/src/app_a.erl similarity index 100% rename from test_projects/in_place_tests/app_a/src/app_a.erl rename to test/test_projects/in_place_tests/app_a/src/app_a.erl diff --git a/test_projects/in_place_tests/app_a/src/lints.erl b/test/test_projects/in_place_tests/app_a/src/lints.erl similarity index 100% rename from test_projects/in_place_tests/app_a/src/lints.erl rename to test/test_projects/in_place_tests/app_a/src/lints.erl diff --git a/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl b/test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/in_place_tests/app_a/test/app_a_SUITE.erl rename to test/test_projects/in_place_tests/app_a/test/app_a_SUITE.erl diff --git a/test_projects/in_place_tests/erlang_ls.config b/test/test_projects/in_place_tests/erlang_ls.config similarity index 100% rename from test_projects/in_place_tests/erlang_ls.config rename to test/test_projects/in_place_tests/erlang_ls.config diff --git a/test_projects/in_place_tests/rebar.config b/test/test_projects/in_place_tests/rebar.config similarity index 100% rename from test_projects/in_place_tests/rebar.config rename to test/test_projects/in_place_tests/rebar.config diff --git a/test/test_projects/include_lib_dependency_test/.elp.toml b/test/test_projects/include_lib_dependency_test/.elp.toml new file mode 100644 index 0000000000..02b5b7d244 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/include_lib_dependency_test/..." ] +source_root = "whatsapp/elp/test/test_projects/include_lib_dependency_test" diff --git a/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl b/test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/include/external_header.hrl rename to test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.app.src rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.erl b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.erl rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl diff --git a/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl b/test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl rename to test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.app.src rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.erl b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.erl rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl diff --git a/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl b/test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl rename to test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl diff --git a/test_projects/include_lib_dependency_test/rebar.config b/test/test_projects/include_lib_dependency_test/rebar.config similarity index 100% rename from test_projects/include_lib_dependency_test/rebar.config rename to test/test_projects/include_lib_dependency_test/rebar.config diff --git a/test/test_projects/linter/.elp.toml b/test/test_projects/linter/.elp.toml new file mode 100644 index 0000000000..bc8b86088c --- /dev/null +++ b/test/test_projects/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] +source_root = "whatsapp/elp/test/test_projects/linter" diff --git a/test_projects/linter/.elp_lint.toml b/test/test_projects/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter/.elp_lint.toml rename to test/test_projects/linter/.elp_lint.toml diff --git a/test_projects/linter/.gitignore b/test/test_projects/linter/.gitignore similarity index 100% rename from test_projects/linter/.gitignore rename to test/test_projects/linter/.gitignore diff --git a/test_projects/linter/app_a/include/app_a.hrl b/test/test_projects/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter/app_a/include/app_a.hrl rename to test/test_projects/linter/app_a/include/app_a.hrl diff --git a/test_projects/linter/app_a/src/app_a.app.src b/test/test_projects/linter/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter/app_a/src/app_a.app.src rename to test/test_projects/linter/app_a/src/app_a.app.src diff --git a/test_projects/linter/app_a/src/app_a.erl b/test/test_projects/linter/app_a/src/app_a.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a.erl rename to test/test_projects/linter/app_a/src/app_a.erl diff --git a/test_projects/linter/app_a/src/app_a_edoc.erl b/test/test_projects/linter/app_a/src/app_a_edoc.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_edoc.erl rename to test/test_projects/linter/app_a/src/app_a_edoc.erl diff --git a/test_projects/linter/app_a/src/app_a_ssr.erl b/test/test_projects/linter/app_a/src/app_a_ssr.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_ssr.erl rename to test/test_projects/linter/app_a/src/app_a_ssr.erl diff --git a/test_projects/linter/app_a/src/app_a_unused_param.erl b/test/test_projects/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test_projects/linter/app_a/src/app_a_unused_param.erl rename to test/test_projects/linter/app_a/src/app_a_unused_param.erl diff --git a/test_projects/linter/app_a/src/custom_function_matches.erl b/test/test_projects/linter/app_a/src/custom_function_matches.erl similarity index 100% rename from test_projects/linter/app_a/src/custom_function_matches.erl rename to test/test_projects/linter/app_a/src/custom_function_matches.erl diff --git a/test_projects/linter/app_a/src/expression_updates_literal.erl b/test/test_projects/linter/app_a/src/expression_updates_literal.erl similarity index 100% rename from test_projects/linter/app_a/src/expression_updates_literal.erl rename to test/test_projects/linter/app_a/src/expression_updates_literal.erl diff --git a/test_projects/linter/app_a/src/spelling.erl b/test/test_projects/linter/app_a/src/spelling.erl similarity index 100% rename from test_projects/linter/app_a/src/spelling.erl rename to test/test_projects/linter/app_a/src/spelling.erl diff --git a/test_projects/linter/app_a/test/app_a_SUITE.erl b/test/test_projects/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_SUITE.erl rename to test/test_projects/linter/app_a/test/app_a_SUITE.erl diff --git a/test_projects/linter/app_a/test/app_a_test_helpers.erl b/test/test_projects/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_test_helpers.erl rename to test/test_projects/linter/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl b/test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl similarity index 100% rename from test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl rename to test/test_projects/linter/app_a/test/app_a_unreachable_test_SUITE.erl diff --git a/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/linter/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test_projects/linter/app_b/src/app_b.app.src b/test/test_projects/linter/app_b/src/app_b.app.src similarity index 100% rename from test_projects/linter/app_b/src/app_b.app.src rename to test/test_projects/linter/app_b/src/app_b.app.src diff --git a/test_projects/linter/app_b/src/app_b.erl b/test/test_projects/linter/app_b/src/app_b.erl similarity index 100% rename from test_projects/linter/app_b/src/app_b.erl rename to test/test_projects/linter/app_b/src/app_b.erl diff --git a/test_projects/linter/app_b/src/app_b_unused_param.erl b/test/test_projects/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test_projects/linter/app_b/src/app_b_unused_param.erl rename to test/test_projects/linter/app_b/src/app_b_unused_param.erl diff --git a/test_projects/linter/elp_lint_adhoc.toml b/test/test_projects/linter/elp_lint_adhoc.toml similarity index 100% rename from test_projects/linter/elp_lint_adhoc.toml rename to test/test_projects/linter/elp_lint_adhoc.toml diff --git a/test_projects/linter/elp_lint_custom_function_matches.toml b/test/test_projects/linter/elp_lint_custom_function_matches.toml similarity index 100% rename from test_projects/linter/elp_lint_custom_function_matches.toml rename to test/test_projects/linter/elp_lint_custom_function_matches.toml diff --git a/test_projects/linter/elp_lint_ssr_adhoc.toml b/test/test_projects/linter/elp_lint_ssr_adhoc.toml similarity index 100% rename from test_projects/linter/elp_lint_ssr_adhoc.toml rename to test/test_projects/linter/elp_lint_ssr_adhoc.toml diff --git a/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml b/test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml similarity index 100% rename from test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml rename to test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml diff --git a/test_projects/linter/elp_lint_test1.toml b/test/test_projects/linter/elp_lint_test1.toml similarity index 100% rename from test_projects/linter/elp_lint_test1.toml rename to test/test_projects/linter/elp_lint_test1.toml diff --git a/test_projects/linter/elp_lint_test2.toml b/test/test_projects/linter/elp_lint_test2.toml similarity index 100% rename from test_projects/linter/elp_lint_test2.toml rename to test/test_projects/linter/elp_lint_test2.toml diff --git a/test_projects/linter/elp_lint_test_ignore.toml b/test/test_projects/linter/elp_lint_test_ignore.toml similarity index 100% rename from test_projects/linter/elp_lint_test_ignore.toml rename to test/test_projects/linter/elp_lint_test_ignore.toml diff --git a/test_projects/linter/elp_lint_warnings_as_errors.toml b/test/test_projects/linter/elp_lint_warnings_as_errors.toml similarity index 100% rename from test_projects/linter/elp_lint_warnings_as_errors.toml rename to test/test_projects/linter/elp_lint_warnings_as_errors.toml diff --git a/test_projects/linter/rebar.config b/test/test_projects/linter/rebar.config similarity index 100% rename from test_projects/linter/rebar.config rename to test/test_projects/linter/rebar.config diff --git a/test/test_projects/linter_bad_config/.elp.toml b/test/test_projects/linter_bad_config/.elp.toml new file mode 100644 index 0000000000..bc8b86088c --- /dev/null +++ b/test/test_projects/linter_bad_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] +source_root = "whatsapp/elp/test/test_projects/linter" diff --git a/test_projects/linter_bad_config/.elp_lint.toml b/test/test_projects/linter_bad_config/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/.elp_lint.toml rename to test/test_projects/linter_bad_config/.elp_lint.toml diff --git a/test_projects/linter_bad_config/.gitignore b/test/test_projects/linter_bad_config/.gitignore similarity index 100% rename from test_projects/linter_bad_config/.gitignore rename to test/test_projects/linter_bad_config/.gitignore diff --git a/test_projects/linter_bad_config/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/app_a/include/app_a.hrl diff --git a/test_projects/linter_bad_config/app_a/src/app_a.app.src b/test/test_projects/linter_bad_config/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter_bad_config/app_a/src/app_a.app.src rename to test/test_projects/linter_bad_config/app_a/src/app_a.app.src diff --git a/test_projects/linter_bad_config/app_a/src/app_a.erl b/test/test_projects/linter_bad_config/app_a/src/app_a.erl similarity index 100% rename from test_projects/linter_bad_config/app_a/src/app_a.erl rename to test/test_projects/linter_bad_config/app_a/src/app_a.erl diff --git a/test/test_projects/linter_bad_config/linter/.elp.toml b/test/test_projects/linter_bad_config/linter/.elp.toml new file mode 100644 index 0000000000..bc8b86088c --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] +source_root = "whatsapp/elp/test/test_projects/linter" diff --git a/test_projects/linter_bad_config/linter/.elp_lint.toml b/test/test_projects/linter_bad_config/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/linter/.elp_lint.toml rename to test/test_projects/linter_bad_config/linter/.elp_lint.toml diff --git a/test_projects/linter_bad_config/linter/.gitignore b/test/test_projects/linter_bad_config/linter/.gitignore similarity index 100% rename from test_projects/linter_bad_config/linter/.gitignore rename to test/test_projects/linter_bad_config/linter/.gitignore diff --git a/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src b/test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a.app.src rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a.app.src diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a.erl b/test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a.erl rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a.erl diff --git a/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl b/test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl rename to test/test_projects/linter_bad_config/linter/app_a/src/app_a_unused_param.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_SUITE.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/linter_bad_config/linter/app_a/test/app_test_helpers_no_errors.erl diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src b/test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b.app.src rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b.app.src diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b.erl b/test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b.erl rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b.erl diff --git a/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl b/test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl similarity index 100% rename from test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl rename to test/test_projects/linter_bad_config/linter/app_b/src/app_b_unused_param.erl diff --git a/test_projects/linter_bad_config/linter/rebar.config b/test/test_projects/linter_bad_config/linter/rebar.config similarity index 100% rename from test_projects/linter_bad_config/linter/rebar.config rename to test/test_projects/linter_bad_config/linter/rebar.config diff --git a/test_projects/linter_bad_config/rebar.config b/test/test_projects/linter_bad_config/rebar.config similarity index 100% rename from test_projects/linter_bad_config/rebar.config rename to test/test_projects/linter_bad_config/rebar.config diff --git a/test/test_projects/parse_error/.elp.toml b/test/test_projects/parse_error/.elp.toml new file mode 100644 index 0000000000..a38e24f657 --- /dev/null +++ b/test/test_projects/parse_error/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/parse_error/..." ] +source_root = "whatsapp/elp/test/test_projects/parse_error" + +[eqwalizer] +enable_all = false diff --git a/test_projects/parse_error/.gitignore b/test/test_projects/parse_error/.gitignore similarity index 100% rename from test_projects/parse_error/.gitignore rename to test/test_projects/parse_error/.gitignore diff --git a/test_projects/parse_error/.rebar.root b/test/test_projects/parse_error/.rebar.root similarity index 100% rename from test_projects/parse_error/.rebar.root rename to test/test_projects/parse_error/.rebar.root diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.app.src rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl diff --git a/test_projects/parse_error/rebar.config b/test/test_projects/parse_error/rebar.config similarity index 100% rename from test_projects/parse_error/rebar.config rename to test/test_projects/parse_error/rebar.config diff --git a/test/test_projects/standard/.elp.toml b/test/test_projects/standard/.elp.toml new file mode 100644 index 0000000000..29b2e051a4 --- /dev/null +++ b/test/test_projects/standard/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/standard/..." ] +source_root = "whatsapp/elp/test/test_projects/standard" diff --git a/test_projects/standard/.gitignore b/test/test_projects/standard/.gitignore similarity index 100% rename from test_projects/standard/.gitignore rename to test/test_projects/standard/.gitignore diff --git a/test_projects/standard/.rebar.root b/test/test_projects/standard/.rebar.root similarity index 100% rename from test_projects/standard/.rebar.root rename to test/test_projects/standard/.rebar.root diff --git a/test_projects/standard/app_a/.eqwalizer b/test/test_projects/standard/app_a/.eqwalizer similarity index 100% rename from test_projects/standard/app_a/.eqwalizer rename to test/test_projects/standard/app_a/.eqwalizer diff --git a/test_projects/standard/app_a/extra/app_a.erl b/test/test_projects/standard/app_a/extra/app_a.erl similarity index 100% rename from test_projects/standard/app_a/extra/app_a.erl rename to test/test_projects/standard/app_a/extra/app_a.erl diff --git a/test_projects/standard/app_a/include/app_a.hrl b/test/test_projects/standard/app_a/include/app_a.hrl similarity index 100% rename from test_projects/standard/app_a/include/app_a.hrl rename to test/test_projects/standard/app_a/include/app_a.hrl diff --git a/test_projects/standard/app_a/src/app_a.app.src b/test/test_projects/standard/app_a/src/app_a.app.src similarity index 100% rename from test_projects/standard/app_a/src/app_a.app.src rename to test/test_projects/standard/app_a/src/app_a.app.src diff --git a/test_projects/standard/app_a/src/app_a.erl b/test/test_projects/standard/app_a/src/app_a.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a.erl rename to test/test_projects/standard/app_a/src/app_a.erl diff --git a/test_projects/standard/app_a/src/app_a_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_fixme.erl b/test/test_projects/standard/app_a/src/app_a_fixme.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_fixme.erl rename to test/test_projects/standard/app_a/src/app_a_fixme.erl diff --git a/test_projects/standard/app_a/src/app_a_ignored.erl b/test/test_projects/standard/app_a/src/app_a_ignored.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_ignored.erl rename to test/test_projects/standard/app_a/src/app_a_ignored.erl diff --git a/test_projects/standard/app_a/src/app_a_lists.erl b/test/test_projects/standard/app_a/src/app_a_lists.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_lists.erl rename to test/test_projects/standard/app_a/src/app_a_lists.erl diff --git a/test_projects/standard/app_a/src/app_a_mod2.erl b/test/test_projects/standard/app_a/src/app_a_mod2.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_mod2.erl rename to test/test_projects/standard/app_a/src/app_a_mod2.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors.erl b/test/test_projects/standard/app_a/src/app_a_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_a_SUITE.erl b/test/test_projects/standard/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_SUITE.erl rename to test/test_projects/standard/app_a/test/app_a_SUITE.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl diff --git a/test_projects/standard/app_b/src/app_b.app.src b/test/test_projects/standard/app_b/src/app_b.app.src similarity index 100% rename from test_projects/standard/app_b/src/app_b.app.src rename to test/test_projects/standard/app_b/src/app_b.app.src diff --git a/test_projects/standard/app_b/src/app_b.erl b/test/test_projects/standard/app_b/src/app_b.erl similarity index 100% rename from test_projects/standard/app_b/src/app_b.erl rename to test/test_projects/standard/app_b/src/app_b.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.app.src b/test/test_projects/standard/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/standard/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/standard/erlang_ls.config b/test/test_projects/standard/erlang_ls.config similarity index 100% rename from test_projects/standard/erlang_ls.config rename to test/test_projects/standard/erlang_ls.config diff --git a/test_projects/standard/rebar.config b/test/test_projects/standard/rebar.config similarity index 100% rename from test_projects/standard/rebar.config rename to test/test_projects/standard/rebar.config diff --git a/test/test_projects/xref/.elp.toml b/test/test_projects/xref/.elp.toml new file mode 100644 index 0000000000..0b684fba14 --- /dev/null +++ b/test/test_projects/xref/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "fbcode//whatsapp/elp/test/test_projects/xref/..." ] +source_root = "whatsapp/elp/test/test_projects/xref" diff --git a/test_projects/xref/app_a/src/unavailable_type.erl b/test/test_projects/xref/app_a/src/unavailable_type.erl similarity index 100% rename from test_projects/xref/app_a/src/unavailable_type.erl rename to test/test_projects/xref/app_a/src/unavailable_type.erl diff --git a/test_projects/xref/app_b/src/app_b.erl b/test/test_projects/xref/app_b/src/app_b.erl similarity index 100% rename from test_projects/xref/app_b/src/app_b.erl rename to test/test_projects/xref/app_b/src/app_b.erl diff --git a/test_projects/xref/app_c/src/app_c.erl b/test/test_projects/xref/app_c/src/app_c.erl similarity index 100% rename from test_projects/xref/app_c/src/app_c.erl rename to test/test_projects/xref/app_c/src/app_c.erl diff --git a/test_projects/xref/elp_lint_unavailable_type.toml b/test/test_projects/xref/elp_lint_unavailable_type.toml similarity index 100% rename from test_projects/xref/elp_lint_unavailable_type.toml rename to test/test_projects/xref/elp_lint_unavailable_type.toml diff --git a/test_projects/buck_bad_config/.elp.toml b/test_projects/buck_bad_config/.elp.toml deleted file mode 100644 index a891a3dfc5..0000000000 --- a/test_projects/buck_bad_config/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/buck_bad_config/..." ] -source_root = "whatsapp/elp/test_projects/buck_bad_config" - -[eqwalizer] -enable_all = false diff --git a/test_projects/buck_tests/.elp.toml b/test_projects/buck_tests/.elp.toml deleted file mode 100644 index acb0799975..0000000000 --- a/test_projects/buck_tests/.elp.toml +++ /dev/null @@ -1,9 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests/..." ] -excluded_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests:test_elp_ignored" ] -source_root = "whatsapp/elp/test_projects/buck_tests" - -[eqwalizer] -enable_all = false diff --git a/test_projects/buck_tests_2/.elp.toml b/test_projects/buck_tests_2/.elp.toml deleted file mode 100644 index 35f02a72f3..0000000000 --- a/test_projects/buck_tests_2/.elp.toml +++ /dev/null @@ -1,12 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ - "fbcode//whatsapp/elp/test_projects/buck_tests_2/util/app_a/...", - "fbcode//whatsapp/elp/test_projects/buck_tests_2:check_include" - ] -excluded_targets = [ "fbcode//whatsapp/elp/test_projects/buck_tests_2:test_elp_ignored" ] -source_root = "whatsapp/elp/test_projects/buck_tests_2" - -[eqwalizer] -enable_all = false diff --git a/test_projects/codegen_test/.elp.toml b/test_projects/codegen_test/.elp.toml deleted file mode 100644 index f0f166bcf8..0000000000 --- a/test_projects/codegen_test/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = true -included_targets = [ "fbcode//whatsapp/elp/test_projects/codegen_test:codegen_test_app" ] -source_root = "whatsapp/elp/test_projects/codegen_test" - -[eqwalizer] -enable_all = true diff --git a/test_projects/diagnostics/.elp.toml b/test_projects/diagnostics/.elp.toml deleted file mode 100644 index e5ee70c992..0000000000 --- a/test_projects/diagnostics/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/diagnostics/..." ] -source_root = "whatsapp/elp/test_projects/diagnostics" - -[eqwalizer] -enable_all = false diff --git a/test_projects/end_to_end/.elp.toml b/test_projects/end_to_end/.elp.toml deleted file mode 100644 index f015956e45..0000000000 --- a/test_projects/end_to_end/.elp.toml +++ /dev/null @@ -1,7 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = ["fbcode//whatsapp/elp/test_projects/end_to_end/..."] - -[eqwalizer] -enable_all = false diff --git a/test_projects/eqwalizer_callers/.elp.toml b/test_projects/eqwalizer_callers/.elp.toml deleted file mode 100644 index f4b7f6f6ac..0000000000 --- a/test_projects/eqwalizer_callers/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_callers/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_callers" diff --git a/test_projects/eqwalizer_tests/.elp.toml b/test_projects/eqwalizer_tests/.elp.toml deleted file mode 100644 index ed5c60502f..0000000000 --- a/test_projects/eqwalizer_tests/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_tests/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_tests" - -[eqwalizer] -enable_all = true diff --git a/test_projects/hierarchical_config/.elp.toml b/test_projects/hierarchical_config/.elp.toml deleted file mode 100644 index 1033c181ee..0000000000 --- a/test_projects/hierarchical_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/hierarchical_config/..." ] -source_root = "whatsapp/elp/test_projects/hierarchical_config" diff --git a/test_projects/in_place_tests/.elp.toml b/test_projects/in_place_tests/.elp.toml deleted file mode 100644 index b6c251f03c..0000000000 --- a/test_projects/in_place_tests/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/in_place_tests/..." ] -source_root = "whatsapp/elp/test_projects/in_place_tests" diff --git a/test_projects/include_lib_dependency_test/.elp.toml b/test_projects/include_lib_dependency_test/.elp.toml deleted file mode 100644 index 65e106a9e2..0000000000 --- a/test_projects/include_lib_dependency_test/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/include_lib_dependency_test/..." ] -source_root = "whatsapp/elp/test_projects/include_lib_dependency_test" diff --git a/test_projects/linter/.elp.toml b/test_projects/linter/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/linter_bad_config/.elp.toml b/test_projects/linter_bad_config/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter_bad_config/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/linter_bad_config/linter/.elp.toml b/test_projects/linter_bad_config/linter/.elp.toml deleted file mode 100644 index a887ab835c..0000000000 --- a/test_projects/linter_bad_config/linter/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/linter/..." ] -source_root = "whatsapp/elp/test_projects/linter" diff --git a/test_projects/parse_error/.elp.toml b/test_projects/parse_error/.elp.toml deleted file mode 100644 index bf2c1ad580..0000000000 --- a/test_projects/parse_error/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/parse_error/..." ] -source_root = "whatsapp/elp/test_projects/parse_error" - -[eqwalizer] -enable_all = false diff --git a/test_projects/standard/.elp.toml b/test_projects/standard/.elp.toml deleted file mode 100644 index 09ff5a4d5a..0000000000 --- a/test_projects/standard/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/standard/..." ] -source_root = "whatsapp/elp/test_projects/standard" diff --git a/test_projects/xref/.elp.toml b/test_projects/xref/.elp.toml deleted file mode 100644 index afda912287..0000000000 --- a/test_projects/xref/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/xref/..." ] -source_root = "whatsapp/elp/test_projects/xref" From 271065da03c111d0725de341b387df7183731b02 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 01:32:35 -0800 Subject: [PATCH 17/46] BE: for a fold, track the macro definition for ctx.in_macro Summary: When folding with visible macros, we keep track of a macro stack, so we can decide how to process diagnostics within the body of a macro expansion. To date we have simply stored the `HirIdx` of the macro call, so it can be retrieved and examined by anything that needs it. But the thing we need when processing a macro is in fact what definition was expanded. This diff adds it to the information stored for easy access. It will be used in a subsequent diff. Reviewed By: TD5 Differential Revision: D89269840 fbshipit-source-id: 7ed6212b658de91cdd3e20701edb51161966e8fa --- crates/hir/src/fold.rs | 45 +++++++++++-------- crates/ide/src/codemod_helpers.rs | 4 +- .../src/diagnostics/deprecated_function.rs | 4 +- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/hir/src/fold.rs b/crates/hir/src/fold.rs index 56ea3f56ae..59b12be6fd 100644 --- a/crates/hir/src/fold.rs +++ b/crates/hir/src/fold.rs @@ -339,7 +339,7 @@ pub enum ParentId { #[derive(Debug)] pub struct AnyCallBackCtx<'a> { - pub in_macro: Option, + pub in_macro: Option<(HirIdx, Option>)>, pub parents: &'a Vec, pub item_id: AnyExprId, pub item: AnyExpr, @@ -426,7 +426,7 @@ pub struct FoldCtx<'a, T> { body_origin: BodyOrigin, body: &'a FoldBody<'a>, strategy: Strategy, - macro_stack: Vec, + macro_stack: Vec<(HirIdx, Option>)>, parents: Vec, callback: AnyCallBack<'a, T>, } @@ -594,7 +594,7 @@ impl<'a, T> FoldCtx<'a, T> { .do_fold_pat(pat_id, initial) } - fn in_macro(&self) -> Option { + fn in_macro(&self) -> Option<(HirIdx, Option>)> { self.macro_stack.first().copied() } @@ -752,16 +752,19 @@ impl<'a, T> FoldCtx<'a, T> { crate::Expr::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::Expr(expr_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Expr(expr_id), + }, + *macro_def, + )); let e = self.do_fold_expr(*expansion, acc); self.macro_stack.pop(); e @@ -950,16 +953,19 @@ impl<'a, T> FoldCtx<'a, T> { crate::Pat::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::Pat(pat_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::Pat(pat_id), + }, + *macro_def, + )); let e = self.do_fold_pat(*expansion, acc); self.macro_stack.pop(); e @@ -1165,16 +1171,19 @@ impl<'a, T> FoldCtx<'a, T> { TypeExpr::MacroCall { expansion, args, - macro_def: _, + macro_def, macro_name: _, } => { if self.strategy.macros == MacroStrategy::DoNotExpand { self.do_fold_exprs(args, acc) } else { - self.macro_stack.push(HirIdx { - body_origin: self.body_origin, - idx: AnyExprId::TypeExpr(type_expr_id), - }); + self.macro_stack.push(( + HirIdx { + body_origin: self.body_origin, + idx: AnyExprId::TypeExpr(type_expr_id), + }, + *macro_def, + )); let e = self.do_fold_type_expr(*expansion, acc); self.macro_stack.pop(); e diff --git a/crates/ide/src/codemod_helpers.rs b/crates/ide/src/codemod_helpers.rs index e06ceafd61..2ecd21e82c 100644 --- a/crates/ide/src/codemod_helpers.rs +++ b/crates/ide/src/codemod_helpers.rs @@ -573,8 +573,8 @@ pub(crate) fn find_call_in_function( }; if let Some(extra) = check_call(context) { // Got one. - let call_expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let call_expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; diff --git a/crates/ide/src/diagnostics/deprecated_function.rs b/crates/ide/src/diagnostics/deprecated_function.rs index 469c327eb7..b353d824d2 100644 --- a/crates/ide/src/diagnostics/deprecated_function.rs +++ b/crates/ide/src/diagnostics/deprecated_function.rs @@ -134,8 +134,8 @@ fn check_function( ); let details = match_result.map(|(_match, details)| details.clone()); if target_def.deprecated || match_result.is_some() { - let expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; From 4a83d0eddece5c60bcbf035087784c9319514693 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 01:32:35 -0800 Subject: [PATCH 18/46] BE: extract `bound_vars_by_function` as a method for `sema` Summary: The `mutable_variable` diagnostic processed bound variables organised by function. We intend doing the same for more bound variable diagnostics, so as a precursor extract this a method we can call on `Semantic` Reviewed By: TD5 Differential Revision: D89270590 fbshipit-source-id: 95a520d45e900c0a5c97fd8163cbaedbc15ca840 --- crates/hir/src/sema.rs | 22 +++++++++++++++++++ .../ide/src/diagnostics/mutable_variable.rs | 20 +---------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index 7d1b416a63..68577d2a2e 100644 --- a/crates/hir/src/sema.rs +++ b/crates/hir/src/sema.rs @@ -1006,6 +1006,28 @@ impl Semantic<'_> { // Folds end // ----------------------------------------------------------------- + pub fn bound_vars_by_function( + &self, + file_id: FileId, + ) -> FxHashMap> { + let bound_vars = self.bound_vars_in_pattern_diagnostic(file_id); + let mut bound_vars_by_function: FxHashMap> = + FxHashMap::default(); + bound_vars.iter().for_each(|(function_id, pat_id, _var)| { + bound_vars_by_function + .entry(function_id.value) + .and_modify(|vars| { + vars.insert(*pat_id); + }) + .or_insert_with(|| { + let mut vars = FxHashSet::default(); + vars.insert(*pat_id); + vars + }); + }); + bound_vars_by_function + } + pub fn bound_vars_in_pattern_diagnostic( &self, file_id: FileId, diff --git a/crates/ide/src/diagnostics/mutable_variable.rs b/crates/ide/src/diagnostics/mutable_variable.rs index 52fe32eccf..6878c90d57 100644 --- a/crates/ide/src/diagnostics/mutable_variable.rs +++ b/crates/ide/src/diagnostics/mutable_variable.rs @@ -27,12 +27,8 @@ // use elp_ide_db::elp_base_db::FileId; -use fxhash::FxHashMap; -use fxhash::FxHashSet; use hir::AnyExpr; use hir::Expr; -use hir::FunctionClauseId; -use hir::PatId; use hir::Semantic; use hir::Strategy; use hir::fold::MacroStrategy; @@ -60,21 +56,7 @@ fn mutable_variable_bug( sema: &Semantic, file_id: FileId, ) -> Option<()> { - let mut bound_vars_by_function: FxHashMap> = - FxHashMap::default(); - let bound_vars = sema.bound_vars_in_pattern_diagnostic(file_id); - bound_vars.iter().for_each(|(function_id, pat_id, _var)| { - bound_vars_by_function - .entry(function_id.value) - .and_modify(|vars| { - vars.insert(pat_id); - }) - .or_insert_with(|| { - let mut vars = FxHashSet::default(); - vars.insert(pat_id); - vars - }); - }); + let bound_vars_by_function = sema.bound_vars_by_function(file_id); sema.def_map(file_id) .get_function_clauses() .for_each(|(_, def)| { From 8ff602f1c3efb9324be46b767e44a36e67bfc880 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 19/46] 1/n rename module, basics Summary: This diff stack introduces rename for a module. As this is a complex operation, it will be done in steps. The first one is to put the basics in place - check the new name is valid - Improve rename test fixture to cope with added and renamed files Reviewed By: robertoaloi Differential Revision: D87860388 fbshipit-source-id: 1a240a5e0e5e61d5c128bca0423744f001b7b68d --- crates/elp/src/snapshot.rs | 10 +++ crates/elp/src/to_proto.rs | 108 ++++++++++++++++++++++++++++---- crates/ide/src/rename.rs | 120 +++++++++++++++++++++++++++++++++++- crates/ide_db/src/rename.rs | 57 ++++++++++++++++- 4 files changed, 282 insertions(+), 13 deletions(-) diff --git a/crates/elp/src/snapshot.rs b/crates/elp/src/snapshot.rs index 9aa3e7eeb4..69ea6b97e0 100644 --- a/crates/elp/src/snapshot.rs +++ b/crates/elp/src/snapshot.rs @@ -36,9 +36,11 @@ use parking_lot::Mutex; use parking_lot::RwLock; use serde::Deserialize; use serde::Serialize; +use vfs::AnchoredPathBuf; use crate::config::Config; use crate::convert; +use crate::convert::url_from_abs_path; use crate::line_endings::LineEndings; use crate::mem_docs::MemDocs; use crate::server::EqwalizerTypes; @@ -186,6 +188,14 @@ impl Snapshot { self.line_ending_map.read()[&id] } + pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Option { + let mut base = self.vfs.read().file_path(path.anchor).clone(); + base.pop(); + let path = base.join(&path.path)?; + let path = path.as_path()?; + Some(url_from_abs_path(path)) + } + pub fn update_cache_for_file( &self, file_id: FileId, diff --git a/crates/elp/src/to_proto.rs b/crates/elp/src/to_proto.rs index 70d316d5c6..8ce887d742 100644 --- a/crates/elp/src/to_proto.rs +++ b/crates/elp/src/to_proto.rs @@ -10,6 +10,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. +use std::mem; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -47,6 +48,7 @@ use elp_ide::elp_ide_db::elp_base_db::FileId; use elp_ide::elp_ide_db::elp_base_db::FilePosition; use elp_ide::elp_ide_db::elp_base_db::FileRange; use elp_ide::elp_ide_db::rename::RenameError; +use elp_ide::elp_ide_db::source_change::FileSystemEdit; use elp_ide::elp_ide_db::source_change::SourceChange; use elp_ide_db::text_edit::Indel; use elp_ide_db::text_edit::TextEdit; @@ -142,23 +144,107 @@ pub(crate) fn text_document_edit( }) } +pub(crate) fn text_document_ops( + snap: &Snapshot, + file_system_edit: FileSystemEdit, +) -> Cancellable> { + let mut ops = Vec::new(); + match file_system_edit { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + if let Some(uri) = snap.anchored_path(&dst) { + let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile { + uri: uri.clone(), + options: None, + annotation_id: None, + }); + ops.push(lsp_types::DocumentChangeOperation::Op(create_file)); + if !initial_contents.is_empty() { + let text_document = + lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; + let text_edit = lsp_types::TextEdit { + range: lsp_types::Range::default(), + new_text: initial_contents, + }; + let edit_file = lsp_types::TextDocumentEdit { + text_document, + edits: vec![lsp_types::OneOf::Left(text_edit)], + }; + ops.push(lsp_types::DocumentChangeOperation::Edit(edit_file)); + } + } else { + log::warn!("create file failed: {:?}", dst); + } + } + FileSystemEdit::MoveFile { src, dst } => { + if let Some(new_uri) = snap.anchored_path(&dst) { + let old_uri = snap.file_id_to_url(src); + let rename_file = lsp_types::RenameFile { + old_uri, + new_uri, + options: None, + annotation_id: None, + }; + ops.push(lsp_types::DocumentChangeOperation::Op( + lsp_types::ResourceOp::Rename(rename_file), + )) + } else { + log::warn!("rename file failed: {:?} -> {:?}", src, dst); + } + } + } + Ok(ops) +} + pub(crate) fn workspace_edit( snap: &Snapshot, - source_change: SourceChange, + mut source_change: SourceChange, ) -> Result { - let mut edits: Vec<_> = vec![]; - for (file_id, edit) in source_change.source_file_edits { - // let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?; - let edit = text_document_edit(snap, file_id, edit)?; - edits.push(lsp_types::TextDocumentEdit { - text_document: edit.text_document, - edits: edit.edits.into_iter().collect(), - }); + let mut document_changes: Vec = Vec::new(); + + // This is copying RA's order of operations, first file creates, + // then edits, then file moves. + + // This allows us to apply edits to the file once it has + // moved. Except we have no FileId at that point + for op in &mut source_change.file_system_edits { + if let FileSystemEdit::CreateFile { + dst, + initial_contents, + } = op + { + // replace with a placeholder to avoid cloning the edit + let op = FileSystemEdit::CreateFile { + dst: dst.clone(), + initial_contents: mem::take(initial_contents), + }; + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } } - let document_changes = lsp_types::DocumentChanges::Edits(edits); + + for (file_id, edit) in source_change.source_file_edits { + let edit = text_document_edit(snap, file_id, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } + + for op in source_change.file_system_edits { + if !matches!(op, FileSystemEdit::CreateFile { .. }) { + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } + } + let workspace_edit = lsp_types::WorkspaceEdit { changes: None, - document_changes: Some(document_changes), + document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), change_annotations: None, }; Ok(workspace_edit) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 0cedbe1d90..8164f0d2db 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -195,8 +195,13 @@ pub fn rename_var( #[cfg(test)] pub(crate) mod tests { use elp_ide_db::RootDatabase; + use elp_ide_db::elp_base_db::AnchoredPathBuf; + use elp_ide_db::elp_base_db::FileId; + use elp_ide_db::elp_base_db::VfsPath; use elp_ide_db::elp_base_db::assert_eq_text; + use elp_ide_db::elp_base_db::fixture::ChangeFixture; use elp_ide_db::elp_base_db::fixture::WithFixture as _; + use elp_ide_db::source_change::FileSystemEdit; use elp_ide_db::text_edit::TextEdit; use elp_project_model::test_fixture::trim_indent; use elp_syntax::AstNode; @@ -207,12 +212,16 @@ pub(crate) mod tests { use hir::Semantic; use super::rename_var; + use crate::AnalysisHost; use crate::fixture; #[track_caller] pub(crate) fn check_rename(new_name: &str, fixture_before: &str, fixture_after_str: &str) { let fixture_after_str = &trim_indent(fixture_after_str); - let analysis_after = fixture::multi_file(fixture_after_str); + + let (db_after, fixture_after) = RootDatabase::with_fixture(fixture_after_str); + let host_after = AnalysisHost { db: db_after }; + let analysis_after = host_after.analysis(); let (analysis, position, _) = fixture::position(fixture_before); let rename_result = analysis @@ -232,6 +241,37 @@ pub(crate) mod tests { let expected = analysis_after.file_text(file_id).unwrap().to_string(); assert_eq_text!(&*expected, &*result); } + for op in source_change.file_system_edits { + match op { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &dst.path + ) + }); + let actual = analysis_after.file_text(*new_file.1).unwrap().to_string(); + let expected = initial_contents; + assert_eq_text!(&*expected, &*actual); + } + FileSystemEdit::MoveFile { src, dst } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file renamed to '{}'", + &dst.path + ) + }); + let actual = analysis_after.file_text(*new_file.1).unwrap().to_string(); + let expected = analysis.file_text(src).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); + } + } + } } Err(err) => { if fixture_after_str.starts_with("error:") { @@ -247,6 +287,16 @@ pub(crate) mod tests { }; } + fn find_new_file_id<'a>( + fixture_after: &'a ChangeFixture, + dst: &'a AnchoredPathBuf, + ) -> Option<(&'a VfsPath, &'a FileId)> { + fixture_after + .files_by_path + .iter() + .find(|(name, _)| name.as_path().unwrap().to_string().ends_with(&dst.path)) + } + #[test] fn test_rename_var_1() { check_rename("Y", r#"main() -> I~ = 1."#, r#"main() -> Y = 1."#); @@ -1135,6 +1185,74 @@ pub(crate) mod tests { ); } + // --------------------------------- + // Renaming modules + + #[test] + fn rename_module_fails_name_exists() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + + #[test] + fn rename_module_fails_bad_name_1() { + check_rename( + "Main", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: Invalid new module name: 'Main'"#, + ); + } + + #[test] + fn rename_module_simple() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + "#, + r#" + //- /app_a/src/main_2.erl + -module(main). + "#, + ); + } + + #[test] + fn rename_module_with_usage() { + check_rename( + "main_2", + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + // --------------------------------- #[track_caller] diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 889569ca4e..7e44d0bf34 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -15,8 +15,10 @@ use std::fmt; use std::iter::once; +use elp_base_db::AnchoredPathBuf; use elp_base_db::FileId; use elp_base_db::FileRange; +use elp_base_db::ModuleName; use elp_syntax::AstNode; use elp_syntax::ast; use elp_syntax::ast::in_erlang_module; @@ -26,6 +28,7 @@ use hir::Semantic; use crate::SymbolDefinition; use crate::helpers::get_call; use crate::search::NameLike; +use crate::source_change::FileSystemEdit; use crate::source_change::SourceChange; use crate::text_edit::TextEdit; @@ -106,6 +109,18 @@ pub fn is_valid_type_name(new_name: &String) -> bool { false } +// Delegate checking module name validity to the parser +pub fn is_valid_module_name(new_name: &String) -> bool { + let parse = ast::SourceFile::parse_text(format!("-module({}).", new_name).as_str()); + match parse.tree().forms().next() { + Some(ast::Form::ModuleAttribute(ma)) => match ma.name() { + Some(ast::Name::Atom(atom)) => atom.syntax().text().to_string() == *new_name, + _ => false, + }, + _ => false, + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SafetyChecks { Yes, @@ -122,7 +137,10 @@ impl SymbolDefinition { ) -> RenameResult { match self.clone() { SymbolDefinition::Module(_) => { - rename_error!("Cannot rename module") + if safety_check == SafetyChecks::Yes && !is_valid_module_name(new_name) { + rename_error!("Invalid new module name: '{}'", new_name); + } + self.rename_module(sema, new_name, safety_check) } SymbolDefinition::Function(fun) => { if safety_check == SafetyChecks::Yes && !is_valid_function_name(new_name) { @@ -382,6 +400,43 @@ impl SymbolDefinition { } } } + + fn rename_module( + &self, + sema: &Semantic, + new_name: &str, + safety_check: SafetyChecks, + ) -> RenameResult { + let file_id = self.file().file_id; + if let Some(project_id) = sema.db.file_project_id(file_id) { + let module_index = sema.db.module_index(project_id); + if safety_check == SafetyChecks::Yes { + let new_name_module = ModuleName::new(new_name); + if module_index + .all_modules() + .iter() + .any(|name| name == &new_name_module) + { + rename_error!("module '{}' already exists", new_name); + } + } + + // RA based version + let mut source_change = SourceChange::default(); + let anchor = file_id; + + let path = format!("{new_name}.erl"); + let dst = AnchoredPathBuf { anchor, path }; + source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst }); + + Ok(source_change) + } else { + rename_error!( + "Could not find project for '{:?}'", + self.file().name(sema.db.upcast()) + ) + } + } } fn source_edit_from_usages( From a0e076cb1762bff8cdbf5e69b158b3d77be9d58d Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 20/46] 2/n: rename module: simple rename works Summary: First case, just rename the module Reviewed By: robertoaloi Differential Revision: D87860412 fbshipit-source-id: 0427f09bd2176778e0280b0f45f9331fa279f778 --- crates/ide/src/rename.rs | 6 +++--- crates/ide_db/src/rename.rs | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 8164f0d2db..bebd439216 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -288,10 +288,10 @@ pub(crate) mod tests { } fn find_new_file_id<'a>( - fixture_after: &'a ChangeFixture, + fixture: &'a ChangeFixture, dst: &'a AnchoredPathBuf, ) -> Option<(&'a VfsPath, &'a FileId)> { - fixture_after + fixture .files_by_path .iter() .find(|(name, _)| name.as_path().unwrap().to_string().ends_with(&dst.path)) @@ -1226,7 +1226,7 @@ pub(crate) mod tests { "#, r#" //- /app_a/src/main_2.erl - -module(main). + -module(main_2). "#, ); } diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 7e44d0bf34..141055565f 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -421,13 +421,33 @@ impl SymbolDefinition { } } + let mut renamed_module_edits: Vec = Vec::new(); + + let form_list = sema.form_list(file_id); + if let Some(module_attribute) = form_list.module_attribute() { + let ast = module_attribute.form_id.get_ast(sema.db, file_id); + if let Some(name) = ast.name() { + let range = name.syntax().text_range(); + let mut builder = TextEdit::builder(); + builder.replace(range, new_name.to_string()); + renamed_module_edits.push(builder.finish()); + } + } + // RA based version let mut source_change = SourceChange::default(); let anchor = file_id; let path = format!("{new_name}.erl"); let dst = AnchoredPathBuf { anchor, path }; - source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst }); + let mut initial_contents = sema.db.file_text(anchor).to_string(); + for edit in renamed_module_edits { + edit.apply(&mut initial_contents); + } + source_change.push_file_system_edit(FileSystemEdit::CreateFile { + dst, + initial_contents, + }); Ok(source_change) } else { From 56e66e2cd561519a580650a118d65362266eef18 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 21/46] 3/n: rename module: renaming function references Summary: Rename references to functions exported from a module when the module is renamed. This requires fleshing out the infrastructure to deal with deleting files too. Reviewed By: robertoaloi Differential Revision: D87860436 fbshipit-source-id: b4d04eb0cfc6448a407fa8c3202ef3e4e088b7e8 --- crates/ide/src/rename.rs | 34 ++++++++++++++++---- crates/ide_db/src/rename.rs | 64 ++++++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index bebd439216..cb7c66292c 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -194,6 +194,7 @@ pub fn rename_var( #[cfg(test)] pub(crate) mod tests { + use elp_ide_db::RootDatabase; use elp_ide_db::elp_base_db::AnchoredPathBuf; use elp_ide_db::elp_base_db::FileId; @@ -207,6 +208,7 @@ pub(crate) mod tests { use elp_syntax::AstNode; use elp_syntax::algo; use elp_syntax::ast; + use fxhash::FxHashSet; use hir::AnyExprId; use hir::InFile; use hir::Semantic; @@ -219,19 +221,26 @@ pub(crate) mod tests { pub(crate) fn check_rename(new_name: &str, fixture_before: &str, fixture_after_str: &str) { let fixture_after_str = &trim_indent(fixture_after_str); + let (db_before, fixture) = RootDatabase::with_fixture(fixture_before); + let host_before = AnalysisHost { db: db_before }; + let analysis = host_before.analysis(); + let position = fixture.position(); + let (db_after, fixture_after) = RootDatabase::with_fixture(fixture_after_str); let host_after = AnalysisHost { db: db_after }; let analysis_after = host_after.analysis(); - let (analysis, position, _) = fixture::position(fixture_before); let rename_result = analysis .rename(position, new_name) .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}")); match rename_result { Ok(source_change) => { + let mut file_ids: FxHashSet = FxHashSet::default(); for edit in source_change.source_file_edits { let mut text_edit_builder = TextEdit::builder(); let file_id = edit.0; + // New and old file_id are the same + file_ids.insert(file_id); for indel in edit.1.into_iter() { text_edit_builder.replace(indel.delete, indel.insert); } @@ -242,6 +251,8 @@ pub(crate) mod tests { assert_eq_text!(&*expected, &*result); } for op in source_change.file_system_edits { + let expected; + let new_file_id; match op { FileSystemEdit::CreateFile { dst, @@ -254,9 +265,8 @@ pub(crate) mod tests { &dst.path ) }); - let actual = analysis_after.file_text(*new_file.1).unwrap().to_string(); - let expected = initial_contents; - assert_eq_text!(&*expected, &*actual); + new_file_id = *new_file.1; + expected = initial_contents; } FileSystemEdit::MoveFile { src, dst } => { let new_file = @@ -266,11 +276,21 @@ pub(crate) mod tests { &dst.path ) }); - let actual = analysis_after.file_text(*new_file.1).unwrap().to_string(); - let expected = analysis.file_text(src).unwrap().to_string(); - assert_eq_text!(&*expected, &*actual); + new_file_id = *new_file.1; + expected = analysis.file_text(src).unwrap().to_string(); } } + file_ids.insert(new_file_id); + let actual = analysis_after.file_text(new_file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); + } + // Check the balance of the expectations in the new fixture. + for file_id in &fixture_after.files { + if !file_ids.contains(file_id) { + let actual = analysis_after.file_text(*file_id).unwrap().to_string(); + let expected = analysis.file_text(*file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); + } } } Err(err) => { diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 141055565f..2ad0cd8ff6 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -31,6 +31,7 @@ use crate::search::NameLike; use crate::source_change::FileSystemEdit; use crate::source_change::SourceChange; use crate::text_edit::TextEdit; +use crate::text_edit::TextEditBuilder; pub type RenameResult = Result; @@ -421,8 +422,25 @@ impl SymbolDefinition { } } - let mut renamed_module_edits: Vec = Vec::new(); + let mut source_change = SourceChange::default(); + // Step 1, rename all references + let def_map = sema.def_map(file_id); + // process anything that could be used. functions, types, ?? + def_map.get_functions().for_each(|(_name, f)| { + if f.exported { + let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); + usages.iter().for_each(|(usage_file_id, refs)| { + if usage_file_id != file_id + && let Some(edit) = rename_call_module_in_refs(refs, new_name) + { + source_change.insert_source_edit(usage_file_id, edit); + }; + }); + } + }); + // Make changes in the module being renamed + let mut renamed_module_edit: TextEdit = TextEdit::default(); let form_list = sema.form_list(file_id); if let Some(module_attribute) = form_list.module_attribute() { let ast = module_attribute.form_id.get_ast(sema.db, file_id); @@ -430,20 +448,29 @@ impl SymbolDefinition { let range = name.syntax().text_range(); let mut builder = TextEdit::builder(); builder.replace(range, new_name.to_string()); - renamed_module_edits.push(builder.finish()); + renamed_module_edit + .union(builder.finish()) + .expect("Could not combine TextEdits"); } + def_map.get_functions().for_each(|(_name, f)| { + let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); + usages.iter().for_each(|(usage_file_id, refs)| { + if usage_file_id == file_id + && let Some(edit) = rename_call_module_in_refs(refs, new_name) + { + renamed_module_edit + .union(edit) + .expect("Could not combine TextEdits"); + } + }); + }); } - // RA based version - let mut source_change = SourceChange::default(); let anchor = file_id; - let path = format!("{new_name}.erl"); let dst = AnchoredPathBuf { anchor, path }; let mut initial_contents = sema.db.file_text(anchor).to_string(); - for edit in renamed_module_edits { - edit.apply(&mut initial_contents); - } + renamed_module_edit.apply(&mut initial_contents); source_change.push_file_system_edit(FileSystemEdit::CreateFile { dst, initial_contents, @@ -459,6 +486,27 @@ impl SymbolDefinition { } } +fn rename_call_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { + let mut builder = TextEdit::builder(); + for usage in refs { + let _ = rename_call_module_in_ref(usage, &mut builder, new_name); + } + Some(builder.finish()) +} + +fn rename_call_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let call = get_call(usage.syntax())?; + let _: () = if let Some(ast::Expr::Remote(remote)) = call.expr() { + let module = remote.module()?.module()?; + builder.replace(module.syntax().text_range(), new_name.to_string()); + }; + Some(()) +} + fn source_edit_from_usages( source_change: &mut SourceChange, usages: Vec<(FileId, &[NameLike])>, From bef3dd15f26cbe6ff3d1c07a3598c37e9faff112 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 22/46] 4/n: rename module: exported type references Summary: When renaming a module, rename all references to types within it Reviewed By: robertoaloi Differential Revision: D87860496 fbshipit-source-id: 2ac6bc52cecaef6802fcf9f181b638f81c556d67 --- crates/ide/src/rename.rs | 47 +++++++++++++++++++++--- crates/ide_db/src/rename.rs | 71 ++++++++++++++++++++++++++++--------- 2 files changed, 97 insertions(+), 21 deletions(-) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index cb7c66292c..a7d963d77f 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -288,7 +288,11 @@ pub(crate) mod tests { for file_id in &fixture_after.files { if !file_ids.contains(file_id) { let actual = analysis_after.file_text(*file_id).unwrap().to_string(); - let expected = analysis.file_text(*file_id).unwrap().to_string(); + let expected = if fixture.files.contains(file_id) { + analysis.file_text(*file_id).unwrap().to_string() + } else { + format!("File {:?} not present in original fixture", file_id) + }; assert_eq_text!(&*expected, &*actual); } } @@ -1261,18 +1265,51 @@ pub(crate) mod tests { -export([foo/0]). foo() -> ok. //- /app_a/src/main.erl - -module(ma~in). - -export([foo/0]). - foo() -> ok. + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + //- /app_a/src/other.erl -module(other). -export([bar/0]). bar() -> main:foo(). - "#, + "#, r#"error: module 'main_2' already exists"#, ); } + #[test] + fn rename_module_with_usage_type() { + // TODO: check for compile errors in the fixture + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + "#, + ); + } + // --------------------------------- #[track_caller] diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 2ad0cd8ff6..ab6829be3d 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -429,13 +429,13 @@ impl SymbolDefinition { def_map.get_functions().for_each(|(_name, f)| { if f.exported { let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); - usages.iter().for_each(|(usage_file_id, refs)| { - if usage_file_id != file_id - && let Some(edit) = rename_call_module_in_refs(refs, new_name) - { - source_change.insert_source_edit(usage_file_id, edit); - }; - }); + rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); + } + }); + def_map.get_types().iter().for_each(|(_name, t)| { + if t.exported { + let usages = SymbolDefinition::Type(t.clone()).usages(sema).all(); + rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); } }); @@ -454,15 +454,21 @@ impl SymbolDefinition { } def_map.get_functions().for_each(|(_name, f)| { let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); - usages.iter().for_each(|(usage_file_id, refs)| { - if usage_file_id == file_id - && let Some(edit) = rename_call_module_in_refs(refs, new_name) - { - renamed_module_edit - .union(edit) - .expect("Could not combine TextEdits"); - } - }); + rename_own_module_call_refs( + usages, + file_id, + new_name, + &mut renamed_module_edit, + ); + }); + def_map.get_types().iter().for_each(|(_name, t)| { + let usages = SymbolDefinition::Type(t.clone()).usages(sema).all(); + rename_own_module_call_refs( + usages, + file_id, + new_name, + &mut renamed_module_edit, + ); }); } @@ -486,6 +492,38 @@ impl SymbolDefinition { } } +fn rename_remote_module_call_refs( + usages: crate::UsageSearchResult, + file_id: FileId, + new_name: &str, + source_change: &mut SourceChange, +) { + usages.iter().for_each(|(usage_file_id, refs)| { + if usage_file_id != file_id + && let Some(edit) = rename_call_module_in_refs(refs, new_name) + { + source_change.insert_source_edit(usage_file_id, edit); + }; + }); +} + +fn rename_own_module_call_refs( + usages: crate::UsageSearchResult, + file_id: FileId, + new_name: &str, + renamed_module_edit: &mut TextEdit, +) { + usages.iter().for_each(|(usage_file_id, refs)| { + if usage_file_id == file_id + && let Some(edit) = rename_call_module_in_refs(refs, new_name) + { + renamed_module_edit + .union(edit) + .expect("Could not combine TextEdits"); + } + }); +} + fn rename_call_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { let mut builder = TextEdit::builder(); for usage in refs { @@ -500,6 +538,7 @@ fn rename_call_module_in_ref( new_name: &str, ) -> Option<()> { let call = get_call(usage.syntax())?; + // Note: `ast` uses the same syntax for a function call and a type let _: () = if let Some(ast::Expr::Remote(remote)) = call.expr() { let module = remote.module()?.module()?; builder.replace(module.syntax().text_range(), new_name.to_string()); From 8997372e1a02e0b292b6121a5d7ce630df5db149 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 23/46] 5/n: rename module: move original file Summary: The final step in renaming a module. Move the old file, and apply the needed edits to it. This diff also simplifies the mechanics of the rename, to be more general and in line with other renames, by iterating over usages of the module, rather than ad-hoc checks for things that may be needed. It is still ad-hoc, in the sense that it only looks for call-like things at the moment (fully qualified calls and types). Other usages will come in later diffs Reviewed By: robertoaloi Differential Revision: D87861050 fbshipit-source-id: d393638b310a257f51151fc3caf1b1445c856700 --- crates/elp/src/to_proto.rs | 39 ++++++++++------- crates/ide/src/rename.rs | 70 ++++++++++++++++++++++++++++-- crates/ide_db/src/helpers.rs | 13 +++--- crates/ide_db/src/rename.rs | 40 ++++++----------- crates/ide_db/src/source_change.rs | 58 ++++++++++++++++++++++++- 5 files changed, 169 insertions(+), 51 deletions(-) diff --git a/crates/elp/src/to_proto.rs b/crates/elp/src/to_proto.rs index 8ce887d742..3e5b1fd1e1 100644 --- a/crates/elp/src/to_proto.rs +++ b/crates/elp/src/to_proto.rs @@ -123,9 +123,9 @@ pub(crate) fn optional_versioned_text_document_identifier( pub(crate) fn text_document_edit( snap: &Snapshot, file_id: FileId, + text_document: lsp_types::OptionalVersionedTextDocumentIdentifier, edit: TextEdit, ) -> Result { - let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.analysis.line_index(file_id)?; let line_endings = snap.line_endings(file_id); let edits: Vec> = edit @@ -133,11 +133,6 @@ pub(crate) fn text_document_edit( .map(|it| lsp_types::OneOf::Left(text_edit(&line_index, line_endings, it))) .collect(); - // if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { - // for edit in &mut edits { - // edit.annotation_id = Some(outside_workspace_annotation_id()) - // } - // } Ok(lsp_types::TextDocumentEdit { text_document, edits, @@ -225,8 +220,16 @@ pub(crate) fn workspace_edit( } } + for op in source_change.file_system_edits { + if !matches!(op, FileSystemEdit::CreateFile { .. }) { + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } + } + for (file_id, edit) in source_change.source_file_edits { - let edit = text_document_edit(snap, file_id, edit)?; + let text_document = optional_versioned_text_document_identifier(snap, file_id); + let edit = text_document_edit(snap, file_id, text_document, edit)?; document_changes.push(lsp_types::DocumentChangeOperation::Edit( lsp_types::TextDocumentEdit { text_document: edit.text_document, @@ -235,10 +238,20 @@ pub(crate) fn workspace_edit( )); } - for op in source_change.file_system_edits { - if !matches!(op, FileSystemEdit::CreateFile { .. }) { - let ops = text_document_ops(snap, op)?; - document_changes.extend_from_slice(&ops); + // Edits on renamed files. The LineIndex from the original can be used. + for (file_ref, edit) in source_change.new_file_edits { + if let Some(uri) = snap.anchored_path(&file_ref.clone().into()) { + let version = snap.url_file_version(&uri); + let text_document = lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version }; + let edit = text_document_edit(snap, file_ref.anchor, text_document, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } else { + log::warn!("new file edit failed: {:?}", file_ref); } } @@ -268,10 +281,6 @@ pub(crate) fn code_action( ) -> Result { let mut res = lsp_types::CodeAction { title: assist.label.to_string(), - // group: assist - // .group - // .filter(|_| snap.config.code_action_group()) - // .map(|gr| gr.0), kind: Some(code_action_kind(assist.id.1)), edit: None, is_preferred: None, diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index a7d963d77f..884556585c 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -267,8 +267,10 @@ pub(crate) mod tests { }); new_file_id = *new_file.1; expected = initial_contents; + let actual = analysis_after.file_text(new_file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); } - FileSystemEdit::MoveFile { src, dst } => { + FileSystemEdit::MoveFile { src: _, dst } => { let new_file = find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { panic!( @@ -277,12 +279,40 @@ pub(crate) mod tests { ) }); new_file_id = *new_file.1; - expected = analysis.file_text(src).unwrap().to_string(); + // We simply record the new file id for checking in `fixture_after``. + // The expected value will be updated by the new_file_edits below, + // and the result asserted there } } file_ids.insert(new_file_id); - let actual = analysis_after.file_text(new_file_id).unwrap().to_string(); - assert_eq_text!(&*expected, &*actual); + } + for (dst, op) in source_change.new_file_edits { + // When renaming a module, we move the original file, then apply fixup edits + // to the new file + let anchored_dst = AnchoredPathBuf { + anchor: dst.anchor, + path: dst.path, + }; + let new_file = + find_new_file_id(&fixture_after, &anchored_dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &anchored_dst.path + ) + }); + + let mut text_edit_builder = TextEdit::builder(); + let file_id = *new_file.1; + // New and old file_id are the same + file_ids.insert(file_id); + for indel in op.iter() { + text_edit_builder.replace(indel.delete, indel.insert.to_string()); + } + let mut result = analysis.file_text(file_id).unwrap().to_string(); + let edit = text_edit_builder.finish(); + edit.apply(&mut result); + let expected = analysis_after.file_text(file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*result); } // Check the balance of the expectations in the new fixture. for file_id in &fixture_after.files { @@ -1310,6 +1340,38 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_record() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + // --------------------------------- #[track_caller] diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index c92be86f5f..0b26a23b6a 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -43,12 +43,15 @@ pub fn pick_best_token( tokens.max_by_key(move |t| f(t.kind())) } +/// Given a syntax node, check it it is immediately enclosed in a call, +/// which can represent a function call or a type. +/// For a remote call, the node can be the module or the function name. +/// In the former case, there is an extra level of nesting, so we need +/// to check up to 3 steps up pub fn get_call(syntax: &SyntaxNode) -> Option { - if let Some(call) = ast::Call::cast(syntax.parent()?) { - Some(call) - } else { - ast::Call::cast(syntax.parent()?.parent()?) - } + ast::Call::cast(syntax.parent()?) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?)) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?.parent()?)) } /// Find the first position at the top of the file to add a new diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index ab6829be3d..40273d6577 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -141,7 +141,7 @@ impl SymbolDefinition { if safety_check == SafetyChecks::Yes && !is_valid_module_name(new_name) { rename_error!("Invalid new module name: '{}'", new_name); } - self.rename_module(sema, new_name, safety_check) + self.rename_reference(sema, new_name, parens_needed_in_context, safety_check) } SymbolDefinition::Function(fun) => { if safety_check == SafetyChecks::Yes && !is_valid_function_name(new_name) { @@ -394,6 +394,7 @@ impl SymbolDefinition { ); Ok(source_change) } + SymbolDefinition::Module(_module) => self.rename_module(sema, new_name, safety_check), // Note: This is basically an internal error, this function is called from // SymbolDefinition::rename which already weeds them out _ => { @@ -424,22 +425,10 @@ impl SymbolDefinition { let mut source_change = SourceChange::default(); // Step 1, rename all references - let def_map = sema.def_map(file_id); - // process anything that could be used. functions, types, ?? - def_map.get_functions().for_each(|(_name, f)| { - if f.exported { - let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); - rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); - } - }); - def_map.get_types().iter().for_each(|(_name, t)| { - if t.exported { - let usages = SymbolDefinition::Type(t.clone()).usages(sema).all(); - rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); - } - }); + let usages = self.clone().usages(sema).all(); + rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); - // Make changes in the module being renamed + // Step 2: Make changes in the module being renamed let mut renamed_module_edit: TextEdit = TextEdit::default(); let form_list = sema.form_list(file_id); if let Some(module_attribute) = form_list.module_attribute() { @@ -452,6 +441,7 @@ impl SymbolDefinition { .union(builder.finish()) .expect("Could not combine TextEdits"); } + let def_map = sema.def_map(file_id); def_map.get_functions().for_each(|(_name, f)| { let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); rename_own_module_call_refs( @@ -475,13 +465,8 @@ impl SymbolDefinition { let anchor = file_id; let path = format!("{new_name}.erl"); let dst = AnchoredPathBuf { anchor, path }; - let mut initial_contents = sema.db.file_text(anchor).to_string(); - renamed_module_edit.apply(&mut initial_contents); - source_change.push_file_system_edit(FileSystemEdit::CreateFile { - dst, - initial_contents, - }); - + source_change.insert_new_source_edit(dst.clone().into(), renamed_module_edit); + source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst }); Ok(source_change) } else { rename_error!( @@ -500,7 +485,7 @@ fn rename_remote_module_call_refs( ) { usages.iter().for_each(|(usage_file_id, refs)| { if usage_file_id != file_id - && let Some(edit) = rename_call_module_in_refs(refs, new_name) + && let Some(edit) = rename_module_in_refs(refs, new_name) { source_change.insert_source_edit(usage_file_id, edit); }; @@ -515,7 +500,7 @@ fn rename_own_module_call_refs( ) { usages.iter().for_each(|(usage_file_id, refs)| { if usage_file_id == file_id - && let Some(edit) = rename_call_module_in_refs(refs, new_name) + && let Some(edit) = rename_module_in_refs(refs, new_name) { renamed_module_edit .union(edit) @@ -524,9 +509,12 @@ fn rename_own_module_call_refs( }); } -fn rename_call_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { +fn rename_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { let mut builder = TextEdit::builder(); for usage in refs { + // Note: we cannot blindly replace all occurrences of an + // atom that happens to be a module name + // We will flesh out other usages as we need them let _ = rename_call_module_in_ref(usage, &mut builder, new_name); } Some(builder.finish()) diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index e4afbd8742..60b62279fe 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs @@ -30,9 +30,36 @@ use crate::text_edit::TextEdit; use crate::text_edit::TextEditBuilder; use crate::tree_diff::diff; +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)] +pub struct HashableAnchoredPathBuf { + /// File that this path is relative to. + pub anchor: FileId, + /// Path relative to `anchor`'s containing directory. + pub path: String, +} + +impl From for HashableAnchoredPathBuf { + fn from(value: AnchoredPathBuf) -> Self { + HashableAnchoredPathBuf { + anchor: value.anchor, + path: value.path, + } + } +} + +impl From for AnchoredPathBuf { + fn from(value: HashableAnchoredPathBuf) -> Self { + AnchoredPathBuf { + anchor: value.anchor, + path: value.path, + } + } +} + #[derive(Default, Debug, Clone)] pub struct SourceChange { pub source_file_edits: FxHashMap, + pub new_file_edits: FxHashMap, pub file_system_edits: Vec, pub is_snippet: bool, } @@ -46,6 +73,7 @@ impl SourceChange { ) -> Self { SourceChange { source_file_edits, + new_file_edits: FxHashMap::default(), file_system_edits, is_snippet: false, } @@ -74,6 +102,22 @@ impl SourceChange { } } + /// Inserts a [`TextEdit`] for the given [`AnchoredPathBuf`]. This properly handles merging existing + /// edits for a file if some already exist. + pub fn insert_new_source_edit(&mut self, file_id: HashableAnchoredPathBuf, edit: TextEdit) { + match self.new_file_edits.entry(file_id) { + Entry::Occupied(mut entry) => { + never!( + entry.get_mut().union(edit).is_err(), + "overlapping edits for same file" + ); + } + Entry::Vacant(entry) => { + entry.insert(edit); + } + } + } + pub fn push_file_system_edit(&mut self, edit: FileSystemEdit) { self.file_system_edits.push(edit); } @@ -85,12 +129,15 @@ impl SourceChange { pub fn merge(mut self, other: SourceChange) -> SourceChange { self.extend(other.source_file_edits); self.extend(other.file_system_edits); + self.extend(other.new_file_edits); self.is_snippet |= other.is_snippet; self } pub fn is_empty(&self) -> bool { - self.source_file_edits.is_empty() && self.file_system_edits.is_empty() + self.source_file_edits.is_empty() + && self.file_system_edits.is_empty() + && self.new_file_edits.is_empty() } pub fn text_range(&self, file_id: FileId) -> Option { @@ -116,10 +163,18 @@ impl Extend for SourceChange { } } +impl Extend<(HashableAnchoredPathBuf, TextEdit)> for SourceChange { + fn extend>(&mut self, iter: T) { + iter.into_iter() + .for_each(|(file_id, edit)| self.insert_new_source_edit(file_id, edit)); + } +} + impl From> for SourceChange { fn from(source_file_edits: FxHashMap) -> SourceChange { SourceChange { source_file_edits, + new_file_edits: FxHashMap::default(), file_system_edits: Vec::new(), is_snippet: false, } @@ -265,6 +320,7 @@ impl From for SourceChange { fn from(edit: FileSystemEdit) -> SourceChange { SourceChange { source_file_edits: Default::default(), + new_file_edits: Default::default(), file_system_edits: vec![edit], is_snippet: false, } From 9015d80659bdcea35dd3ac5b7148fccfaaf644ef Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 24/46] 6/n: rename module: external fun reference Summary: A module name can occur in an external fun. Rename it too. Reviewed By: robertoaloi Differential Revision: D88849514 fbshipit-source-id: e715628a6fbfe419cd56761fe62b43d3b68ecde4 --- crates/ide/src/rename.rs | 33 +++++++++++++++++++++++++++++++++ crates/ide_db/src/helpers.rs | 8 ++++++++ crates/ide_db/src/rename.rs | 13 +++++++++++++ 3 files changed, 54 insertions(+) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 884556585c..992573c770 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1372,6 +1372,39 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_fun() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:foo/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:foo/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + // --------------------------------- #[track_caller] diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 0b26a23b6a..038b41f0be 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -54,6 +54,14 @@ pub fn get_call(syntax: &SyntaxNode) -> Option { .or_else(|| ast::Call::cast(syntax.parent()?.parent()?.parent()?)) } +pub fn get_external_fun(syntax: &SyntaxNode) -> Option { + if let Some(external_fun) = ast::ExternalFun::cast(syntax.parent()?) { + Some(external_fun) + } else { + ast::ExternalFun::cast(syntax.parent()?.parent()?) + } +} + /// Find the first position at the top of the file to add a new /// form. It will be just after the module attribute, if there is one. pub fn top_insert_position(form_list: &FormList, source: &SourceFile) -> TextSize { diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 40273d6577..31c9b551b6 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -27,6 +27,7 @@ use hir::Semantic; use crate::SymbolDefinition; use crate::helpers::get_call; +use crate::helpers::get_external_fun; use crate::search::NameLike; use crate::source_change::FileSystemEdit; use crate::source_change::SourceChange; @@ -516,6 +517,7 @@ fn rename_module_in_refs(refs: &[NameLike], new_name: &str) -> Option // atom that happens to be a module name // We will flesh out other usages as we need them let _ = rename_call_module_in_ref(usage, &mut builder, new_name); + let _ = rename_external_fun_module_in_ref(usage, &mut builder, new_name); } Some(builder.finish()) } @@ -534,6 +536,17 @@ fn rename_call_module_in_ref( Some(()) } +fn rename_external_fun_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let external_fun = get_external_fun(usage.syntax())?; + let module = external_fun.module()?; + builder.replace(module.name()?.syntax().text_range(), new_name.to_string()); + Some(()) +} + fn source_edit_from_usages( source_change: &mut SourceChange, usages: Vec<(FileId, &[NameLike])>, From 83d5b4dfd0902bacf67b3ecdb0edc0205e1d2e16 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 25/46] 7/n: rename module: check module name in a call Summary: When identifying a call from a reference, we start from the ast token for the module name, and ascend the ast until we find a call. But up to now we have not checked that the module we started from is the module part of the call MFA. This diff adds that check. Reviewed By: robertoaloi Differential Revision: D88934411 fbshipit-source-id: 81a57520ec834210fcea8fd6920653cc45f9890c --- crates/ide/src/rename.rs | 69 +++++++++++++++++++++++++++++++++++++ crates/ide_db/src/rename.rs | 13 ++++--- crates/ide_db/src/search.rs | 2 +- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 992573c770..01d26b0f87 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1372,6 +1372,42 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_fun_arg() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> + meck:new(main, [passthrough]), + ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> + meck:new(main, [passthrough]), + ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + #[test] fn rename_module_with_usage_fun() { check_rename( @@ -1405,6 +1441,39 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_fun_as_module() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:main/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:main/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + // --------------------------------- #[track_caller] diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 31c9b551b6..a22e2b0acf 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -529,10 +529,15 @@ fn rename_call_module_in_ref( ) -> Option<()> { let call = get_call(usage.syntax())?; // Note: `ast` uses the same syntax for a function call and a type - let _: () = if let Some(ast::Expr::Remote(remote)) = call.expr() { - let module = remote.module()?.module()?; - builder.replace(module.syntax().text_range(), new_name.to_string()); - }; + if let Some(ast::Expr::Remote(remote)) = call.expr() { + let module = remote.module()?; + if let Some(ast::ExprMax::Atom(atom)) = module.module() + && let NameLike::Name(ast::Name::Atom(name_atom)) = usage + && atom.syntax() == name_atom.syntax() + { + builder.replace(atom.syntax().text_range(), new_name.to_string()); + } + } Some(()) } diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 20b4f050b4..1e66598359 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -325,7 +325,7 @@ impl<'a> FindUsages<'a> { /// Represents possible ast reference points - /// a string for header, or ast::Name for everything else -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NameLike { Name(ast::Name), String(ast::String), From cc0a9c26d50e7038008e0ad587dcf6e0144297ef Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 26/46] 8/n: rename module: well known function arguments Summary: Certain functions are known to take a module name as an argument. We already have a list of those that correspond to referencing a function as an MFA, in `to_def.rs`. We extend this with some other examples, and check for renaming them too as part of the module rename. The list is currently incommplete, and can be filled out based on usage. A future diff may look at ways of generating it from `-spec` information. Reviewed By: robertoaloi Differential Revision: D88943301 fbshipit-source-id: 05c67a108b1dc5b64f4353872427361fd039171f --- crates/hir/src/sema/to_def.rs | 177 +++++++++++++++++++++++++++++++++- crates/ide/src/rename.rs | 8 +- crates/ide_db/src/rename.rs | 72 ++++++++++++-- 3 files changed, 247 insertions(+), 10 deletions(-) diff --git a/crates/hir/src/sema/to_def.rs b/crates/hir/src/sema/to_def.rs index 0c8eba227e..01d17278a2 100644 --- a/crates/hir/src/sema/to_def.rs +++ b/crates/hir/src/sema/to_def.rs @@ -885,7 +885,155 @@ fn add_dynamic_call_patterns(patterns: &mut FxHashMap Self { + Self { + index, + arg_type: ModuleArgType::Atom, + } + } + + /// Creates a pattern where the argument is a list of module atoms. + pub const fn list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::List, + } + } + + /// Creates a pattern where the argument can be either a single atom or a list. + pub const fn atom_or_list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::AtomOrList, + } + } + + /// Returns true if this pattern accepts a single atom. + pub const fn accepts_atom(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::Atom | ModuleArgType::AtomOrList + ) + } + + /// Returns true if this pattern accepts a list of atoms. + pub const fn accepts_list(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::List | ModuleArgType::AtomOrList + ) + } +} + +fn add_module_argument_patterns(patterns: &mut FxHashMap) { + // Each entry follows the format: + // (module, function, arity) -> ModuleArgPattern + // + // Where: + // module: Module name (Some("meck"), Some("application"), etc.) + // function: Function name as string literal (e.g., "new", "get_env") + // arity: Number of arguments this function pattern expects + // ModuleArgPattern: Contains the argument index and the expected type + // + // All indexes are 0-based. + + // meck - mocking library + // meck:new/2 accepts either a single module atom or a list of modules + patterns.insert((Some("meck"), "called", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "called", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "capture", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "capture", 6), ModuleArgPattern::atom(1)); + patterns.insert( + (Some("meck"), "delete", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "delete", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expects", 2), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "history", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "history", 2), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "loop", 4), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 1), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 2), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "num_calls", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "num_calls", 4), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("meck"), "reset", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "sequence", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "unload", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "validate", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "wait", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "wait", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "wait", 6), ModuleArgPattern::atom(1)); + + // code module - module loading and management + // These functions from the Erlang stdlib take module() as their argument + patterns.insert((Some("code"), "load_file", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "ensure_loaded", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "delete", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "soft_purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_loaded", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "get_object_code", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "module_md5", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_sticky", 1), ModuleArgPattern::atom(0)); +} + +// Lazy static initialization for the patterns maps lazy_static! { static ref DYNAMIC_CALL_PATTERNS: FxHashMap = { let mut patterns = FxHashMap::default(); @@ -893,12 +1041,39 @@ lazy_static! { // @fb-only: meta_only::add_dynamic_call_patterns(&mut patterns); patterns }; + static ref MODULE_ARGUMENT_PATTERNS: FxHashMap = { + let mut patterns = FxHashMap::default(); + add_module_argument_patterns(&mut patterns); + // @fb-only: meta_only::add_module_argument_patterns(&mut patterns); + patterns + }; + /// Combined patterns for module argument positions. + /// Merges dynamic call patterns (that have module_arg_index) with simple module argument patterns. + /// Used by rename operations where we only care about the module argument position. + static ref COMBINED_MODULE_ARG_PATTERNS: FxHashMap = { + let mut patterns: FxHashMap = FxHashMap::default(); + // Add module_arg_index from dynamic call patterns (where present) + for (key, pattern) in DYNAMIC_CALL_PATTERNS.iter() { + if let Some(module_idx) = pattern.module_arg_index { + patterns.insert(*key, ModuleArgPattern::atom(module_idx)); + } + } + // Add from simple module argument patterns + for (key, module_arg_pattern) in MODULE_ARGUMENT_PATTERNS.iter() { + patterns.insert(*key, *module_arg_pattern); + } + patterns + }; } fn get_dynamic_call_patterns() -> &'static FxHashMap { &DYNAMIC_CALL_PATTERNS } +pub fn get_module_arg_patterns() -> &'static FxHashMap { + &COMBINED_MODULE_ARG_PATTERNS +} + fn look_for_dynamic_call( sema: &Semantic, file_id: FileId, diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 01d26b0f87..92e5125063 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1387,6 +1387,9 @@ pub(crate) mod tests { -spec bar() -> main:foo(). bar() -> meck:new(main, [passthrough]), + meck:new([other, main] , [passthrough]), + meck:unload(main), + apply(main, foo, []), ok. -record(main, {field :: main:foo()}). "#, @@ -1401,7 +1404,10 @@ pub(crate) mod tests { -export([bar/0]). -spec bar() -> main_3:foo(). bar() -> - meck:new(main, [passthrough]), + meck:new(main_3, [passthrough]), + meck:new([other, main_3] , [passthrough]), + meck:unload(main_3), + apply(main_3, foo, []), ok. -record(main, {field :: main_3:foo()}). "#, diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index a22e2b0acf..e9ec7d96f1 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -528,16 +528,72 @@ fn rename_call_module_in_ref( new_name: &str, ) -> Option<()> { let call = get_call(usage.syntax())?; - // Note: `ast` uses the same syntax for a function call and a type - if let Some(ast::Expr::Remote(remote)) = call.expr() { - let module = remote.module()?; - if let Some(ast::ExprMax::Atom(atom)) = module.module() - && let NameLike::Name(ast::Name::Atom(name_atom)) = usage - && atom.syntax() == name_atom.syntax() - { - builder.replace(atom.syntax().text_range(), new_name.to_string()); + // We can only rename an atom usage + let usage_atom = match usage { + NameLike::Name(ast::Name::Atom(atom)) => atom, + _ => return Some(()), + }; + + // First check if this is the module part of a remote call (e.g., module:function()) + if let Some(ast::Expr::Remote(remote)) = call.expr() + && let Some(module) = remote.module() + && let Some(ast::ExprMax::Atom(mod_atom)) = module.module() + && mod_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + return Some(()); + } + + // Check if this is a known function call that takes a module as an argument + // Extract function name and optional module name based on call type + let (module_name, function_name) = match call.expr()? { + ast::Expr::Remote(remote) => { + let module = remote.module()?; + let mod_atom = match module.module()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + let fun_atom = match remote.fun()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + (Some(mod_atom.text()?), fun_atom.text()?) + } + ast::Expr::ExprMax(ast::ExprMax::Atom(fun_atom)) => (None, fun_atom.text()?), + _ => return Some(()), + }; + + let args = call.args()?; + let args_vec: Vec<_> = args.args().collect(); + let arity = args_vec.len(); + let pattern_key = (module_name.as_deref(), function_name.as_str(), arity); + + // Use combined patterns that merge dynamic call patterns and module argument patterns + let combined_patterns = hir::sema::to_def::get_module_arg_patterns(); + if let Some(pattern) = combined_patterns.get(&pattern_key) + && let Some(arg) = args_vec.get(pattern.index) + { + match arg { + ast::Expr::ExprMax(ast::ExprMax::Atom(arg_atom)) + if pattern.accepts_atom() && arg_atom.syntax() == usage_atom.syntax() => + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + } + ast::Expr::ExprMax(ast::ExprMax::List(list)) if pattern.accepts_list() => { + // Handle list of modules (e.g., meck:new([mod1, mod2], Options)) + for expr in list.exprs() { + if let ast::Expr::ExprMax(ast::ExprMax::Atom(list_atom)) = expr + && list_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + break; + } + } + } + _ => {} } } + Some(()) } From 91a0d968dbcef5f6b05426f50d518946d450564f Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 27/46] 9/n: rename module: remote calls in original module Summary: Simplify the rename module logic to use the standard approach, find usages, rename each of them. And in the process ensure that remote calls in the module being renamed are also renamed. Reviewed By: robertoaloi Differential Revision: D88947976 fbshipit-source-id: c703be8392b7ac634ced2c45d8553d33882d460b --- crates/ide/src/rename.rs | 35 ++++++++++++++++++++- crates/ide_db/src/rename.rs | 61 +++++++++++-------------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 92e5125063..4a7b566bcf 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1286,7 +1286,7 @@ pub(crate) mod tests { } #[test] - fn rename_module_with_usage() { + fn rename_module_fails_dup_name() { check_rename( "main_2", r#" @@ -1310,6 +1310,39 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_internal() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + //------------------ + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + bar() -> main_2:foo(). + baz() -> main_2:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main_2:foo(). + "#, + ); + } #[test] fn rename_module_with_usage_type() { // TODO: check for compile errors in the fixture diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index e9ec7d96f1..bd5a49f8cf 100644 --- a/crates/ide_db/src/rename.rs +++ b/crates/ide_db/src/rename.rs @@ -427,10 +427,16 @@ impl SymbolDefinition { let mut source_change = SourceChange::default(); // Step 1, rename all references let usages = self.clone().usages(sema).all(); - rename_remote_module_call_refs(usages, file_id, new_name, &mut source_change); - - // Step 2: Make changes in the module being renamed let mut renamed_module_edit: TextEdit = TextEdit::default(); + rename_remote_module_call_refs( + usages, + file_id, + new_name, + &mut source_change, + &mut renamed_module_edit, + ); + + // Step 2: Rename the module attribute in the module being renamed let form_list = sema.form_list(file_id); if let Some(module_attribute) = form_list.module_attribute() { let ast = module_attribute.form_id.get_ast(sema.db, file_id); @@ -442,25 +448,6 @@ impl SymbolDefinition { .union(builder.finish()) .expect("Could not combine TextEdits"); } - let def_map = sema.def_map(file_id); - def_map.get_functions().for_each(|(_name, f)| { - let usages = SymbolDefinition::Function(f.clone()).usages(sema).all(); - rename_own_module_call_refs( - usages, - file_id, - new_name, - &mut renamed_module_edit, - ); - }); - def_map.get_types().iter().for_each(|(_name, t)| { - let usages = SymbolDefinition::Type(t.clone()).usages(sema).all(); - rename_own_module_call_refs( - usages, - file_id, - new_name, - &mut renamed_module_edit, - ); - }); } let anchor = file_id; @@ -483,30 +470,18 @@ fn rename_remote_module_call_refs( file_id: FileId, new_name: &str, source_change: &mut SourceChange, -) { - usages.iter().for_each(|(usage_file_id, refs)| { - if usage_file_id != file_id - && let Some(edit) = rename_module_in_refs(refs, new_name) - { - source_change.insert_source_edit(usage_file_id, edit); - }; - }); -} - -fn rename_own_module_call_refs( - usages: crate::UsageSearchResult, - file_id: FileId, - new_name: &str, renamed_module_edit: &mut TextEdit, ) { usages.iter().for_each(|(usage_file_id, refs)| { - if usage_file_id == file_id - && let Some(edit) = rename_module_in_refs(refs, new_name) - { - renamed_module_edit - .union(edit) - .expect("Could not combine TextEdits"); - } + if let Some(edit) = rename_module_in_refs(refs, new_name) { + if usage_file_id == file_id { + renamed_module_edit + .union(edit) + .expect("Could not combine TextEdits"); + } else { + source_change.insert_source_edit(usage_file_id, edit); + } + }; }); } From 4a051d1af88ebb2ae80d31f85fba061a10c7e098 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:00:04 -0800 Subject: [PATCH 28/46] 10/n: rename module: in defines RHS Summary: Add a test demonstrating that the module rename happens in `.hrl` files too Reviewed By: TheGeorge Differential Revision: D89053579 fbshipit-source-id: f999484c08a5c33c76986156c10a2f6a7d34fc52 --- crates/ide/src/rename.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 4a7b566bcf..ee5bfb7219 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1513,6 +1513,52 @@ pub(crate) mod tests { ); } + #[test] + fn rename_module_with_usage_define() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main:foo(U), + ?FOO(U), + ok. + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main_3:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main_3:foo(U), + ?FOO(U), + ok. + "#, + ); + } + // --------------------------------- #[track_caller] From 7057624c7339b554d099a2e4f38bcc72b3d33a0e Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 06:36:56 -0800 Subject: [PATCH 29/46] `elp lint`: print diagnostics in the case we are not streaming and not applying a fix Summary: Fix two bugs for `--no-stream` for `elp lint` 1. args.skip_stream_print() now skips if either the flag is set, or `--apply-fixes` is set 2. When we are not streaming, and also not applyiing fixes, print the diagnostics Reviewed By: robertoaloi Differential Revision: D89369072 fbshipit-source-id: 2279aca861fb09c5b33adb0000e3a58821026064 --- crates/elp/src/bin/args.rs | 2 +- crates/elp/src/bin/lint_cli.rs | 81 ++++++++--- crates/elp/src/bin/main.rs | 12 ++ .../test/diagnostics/lint_no_stream.stdout | 135 ++++++++++++++++++ .../parse_elp_lint_ignore_apps_b.stdout | 2 +- 5 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout diff --git a/crates/elp/src/bin/args.rs b/crates/elp/src/bin/args.rs index 2d97f50562..c9790e9314 100644 --- a/crates/elp/src/bin/args.rs +++ b/crates/elp/src/bin/args.rs @@ -911,7 +911,7 @@ impl Lint { /// 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 + self.apply_fix || self.no_stream } } diff --git a/crates/elp/src/bin/lint_cli.rs b/crates/elp/src/bin/lint_cli.rs index f7ac4ff114..241a3d4481 100644 --- a/crates/elp/src/bin/lint_cli.rs +++ b/crates/elp/src/bin/lint_cli.rs @@ -295,7 +295,7 @@ pub fn do_codemod( let res; let streamed_err_in_diag; let mut any_diagnostics_printed = false; - let initial_diags = { + let mut initial_diags = { // We put this in its own block so that analysis is // freed before we apply lints. To apply lints // recursively, we need to update the underlying @@ -394,30 +394,54 @@ pub fn do_codemod( let mut err_in_diag = streamed_err_in_diag; // At this point, the analysis variable from above is dropped - // Print "No diagnostics reported" if no diagnostics were found after filtering - if !any_diagnostics_printed { - if args.is_format_normal() { - writeln!(cli, "No diagnostics reported")?; + // 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; } - } else { - if args.apply_fix && diagnostics_config.enabled.all_enabled() { + } + + // Handle apply_fix case separately since it needs to filter diagnostics anyway + if args.apply_fix { + if diagnostics_config.enabled.all_enabled() { bail!( "We cannot apply fixes if all diagnostics enabled. Perhaps provide --diagnostic-filter" ); } - if args.apply_fix && !diagnostics_config.enabled.all_enabled() { - let mut initial_diags = { - let analysis = loaded.analysis(); - filter_diagnostics( - &analysis, - &args.module, - Some(&diagnostics_config.enabled), - &initial_diags, - &FxHashSet::default(), - )? - }; + + 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() { - initial_diags.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); + 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() { @@ -428,7 +452,7 @@ pub fn do_codemod( &mut err_in_diag, module_count, has_diagnostics, - &initial_diags, + &filtered_diags, )?; } else { { @@ -442,7 +466,7 @@ pub fn do_codemod( &mut err_in_diag, module_count, has_diagnostics, - &initial_diags, + &filtered_diags, )?; // Analysis is dropped here } @@ -456,7 +480,7 @@ pub fn do_codemod( &mut loaded.vfs, args, &mut changed_files, - initial_diags, + filtered_diags, ); // We handle the fix application result here, so // the overall status of whether error-severity @@ -468,8 +492,19 @@ pub fn do_codemod( writeln!(cli, "Apply fix failed: {err:#}").ok(); } }; + + if err_in_diag { + bail!("Errors found") + } } - if err_in_diag { + } else { + // Non-apply-fix case: rely on any_diagnostics_printed which is set + // correctly based on filtered diagnostics during streaming/batch printing + if !any_diagnostics_printed { + if args.is_format_normal() { + writeln!(cli, "No diagnostics reported")?; + } + } else if err_in_diag { bail!("Errors found") } } diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 38ef3c3880..7a649f0c4a 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -1591,6 +1591,18 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn lint_no_stream_produces_output(buck: bool) { + simple_snapshot_expect_error( + args_vec!["lint", "--no-stream"], + "diagnostics", + expect_file!("../resources/test/diagnostics/lint_no_stream.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_diagnostics_filter_all_enabled_json(buck: bool) { diff --git a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout new file mode 100644 index 0000000000..b1c2f07150 --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout @@ -0,0 +1,135 @@ +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: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_sigils.erl:11:6-11:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:12:5-12:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:13:5-13:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:14:5-14:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:15:5-15:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:17:6-17:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:18:5-18:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:19:5-19:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:20:5-20:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:21:5-21:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:23:6-23:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:24:5-24:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:25:5-25:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:26:5-26:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:27:5-27:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:29:6-29:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:30:5-30:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:31:5-31:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:32:5-32:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:33:5-33:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:35:6-35:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:36:5-36:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:37:5-37:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:38:5-38:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:39:5-39:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:41:6-41:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:42:5-42:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:43:5-43:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:44:5-44:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:45:5-45:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:47:6-47:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:48:5-48:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:49:5-49:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:50:5-50:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:51:5-51:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:56:5-56:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:57:5-57:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:59:6-59:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:60:5-60:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:61:5-61:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:62:5-62:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:63:5-63:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:65:6-65:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:66:5-66:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:67:5-67:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:68:5-68:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:69:5-69:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:90:5-94:11::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:95:5-99:12::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:102:5-102:24::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +app_a/src/otp27_sigils.erl:128:9-128:24::[Warning] [W0002] Unused macro (THIS_IS_THE_END) +app_a/src/otp27_sigils.erl:112:4-112:5::[Error] [P1711] syntax error before: X + 4:15-4:18: function g/0 undefined + 74:7-74:8: spec for undefined function g/0 +app_a/src/otp27_sigils.erl:71:5-71:6::[Warning] [L1268] variable 'X' is unused +app_a/src/otp_7655.erl:5:1-5:28::[Error] [L1201] no module definition +app_a/src/parse_error_a_cascade.erl:10:20-11:1::[Error] [W0004] Missing 'atom' + 6:6-6:11: function bar/0 undefined +app_a/src/suppressed.erl:8:5-8:9::[Warning] [L1268] variable 'Life' is unused +app_a/src/syntax.erl:5:46-5:47::[Error] [P1711] syntax error before: ')' +app_a/src/syntax.erl:11:9-11:10::[Error] [W0004] Missing ')' diff --git a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout index 3d59c453e3..5a42009118 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_lint_ignore_apps_b.stdout @@ -1,3 +1,3 @@ Diagnostics reported: -app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused app_a/src/app_a.erl:9:6-9:7::[Warning] [W0010] this variable is unused +app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [W0010] this variable is unused From 79c33480c54371f105433f0b5374cb9f6f6e143c Mon Sep 17 00:00:00 2001 From: Victor Lanvin Date: Wed, 17 Dec 2025 08:54:32 -0800 Subject: [PATCH 30/46] Support for OTP version specific tests Summary: Remove the tag `OTPVersionDependent` which created one expected result per OTP version per snapshot. Instead, use `OTPXXOnly` (where `XX` is a version number), that skips checks for OTP versions that are not `XX`. In practice, it's used as `OTP27Only` for now. Reviewed By: michalmuskala, TD5 Differential Revision: D89378687 fbshipit-source-id: 0421622d825ac4e8f393d32529c01eb17577b5e5 --- crates/elp/src/bin/main.rs | 47 +- .../check/callbacks3_neg-OTP-27.pretty | 27 - .../check/callbacks3_neg-OTP-28.pretty | 26 - ...eg-OTP-26.pretty => callbacks3_neg.pretty} | 0 .../check/custom-OTP-27.pretty | 2540 ----------------- .../check/custom-OTP-28.pretty | 2540 ----------------- .../{custom-OTP-26.pretty => custom.pretty} | 0 .../check/src/callbacks3_neg.erl | 2 +- .../eqwalizer_tests/check/src/custom.erl | 2 +- 9 files changed, 26 insertions(+), 5158 deletions(-) delete mode 100644 crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty delete mode 100644 crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty rename crates/elp/src/resources/test/eqwalizer_tests/check/{callbacks3_neg-OTP-26.pretty => callbacks3_neg.pretty} (100%) delete mode 100644 crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty delete mode 100644 crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty rename crates/elp/src/resources/test/eqwalizer_tests/check/{custom-OTP-26.pretty => custom.pretty} (100%) diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 7a649f0c4a..e7ed9051a9 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -443,33 +443,34 @@ mod tests { }) .unwrap(); + let exp_path = expect_file!(format!( + "../resources/test/{}/{}/{}.pretty", + project, + app, + module.as_str(), + )); + let (stdout, _) = cli.to_strings(); + let otp_version = OTP_VERSION.as_ref().expect("MISSING OTP VERSION"); let otp_version_regex = - regex::bytes::Regex::new(&format!("{}OTPVersionDependent", "@")) - .unwrap(); + regex::bytes::Regex::new(&format!("{}OTP([0-9]+)Only", "@")).unwrap(); let contents = analysis.file_text(file_id).unwrap(); - let otp_version_dependent = otp_version_regex - .is_match(&contents.as_bytes()[0..(2001.min(contents.len()))]); - let exp_path = { - if otp_version_dependent { - expect_file!(format!( - "../resources/test/{}/{}/{}-OTP-{}.pretty", - project, - app, - module.as_str(), - otp_version, - )) - } else { - expect_file!(format!( - "../resources/test/{}/{}/{}.pretty", - project, - app, - module.as_str(), - )) + let otp_version_capture = otp_version_regex + .captures(&contents.as_bytes()[0..(2001.min(contents.len()))]); + if let Some((_, [otp_version_only])) = + otp_version_capture.map(|cap| cap.extract()) + { + if otp_version_only == otp_version.as_bytes() { + assert_normalised_file( + exp_path, + &stdout, + project_path.into(), + false, + ); } - }; - let (stdout, _) = cli.to_strings(); - assert_normalised_file(exp_path, &stdout, project_path.into(), false); + } else { + assert_normalised_file(exp_path, &stdout, project_path.into(), false); + } } } EqwalizerDiagnostics::NoAst { module } => { diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty deleted file mode 100644 index 6cb2012096..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty +++ /dev/null @@ -1,27 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:13:1 - │ -13 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ Incorrect return type for implementation of gen_server:handle_cast/2. -Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} -Got: 'wrong_ret' - -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:13:1 - │ -13 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_info/2. -Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} -Got: {'noreply', 'ok', 'wrong_atom'} - │ - -Because in the expression's type: - { 'noreply', 'ok', - Here the type is: 'wrong_atom' - Context expects type: 'infinity' | number() | 'hibernate' | {'continue', term()} - No candidate matches in the expected union. - } - -2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty deleted file mode 100644 index 849039f298..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty +++ /dev/null @@ -1,26 +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(), gen_server:action()} | {'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(), gen_server:action()} | {'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: gen_server:action() - } - -2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty deleted file mode 100644 index efc8c47aa0..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-27.pretty +++ /dev/null @@ -1,2540 +0,0 @@ -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:26:5 - │ -26 │ element(4, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(4, Tup). -Expression has type: #{dynamic() => dynamic()} -Context expected type: atom() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:30:5 - │ -30 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 3 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:40:5 - │ -40 │ element(2, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(2, Tup). -Expression has type: number() | string() | atom() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:44:16 - │ -44 │ element(2, Tup). - │ ^^^ - │ │ - │ Tup. -Expression has type: {atom(), string()} | [dynamic()] -Context expected type: tuple() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {atom(), string()} - However the following candidate: [dynamic()] - Differs from the expected type: tuple() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:48:5 - │ -48 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 2 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:54:5 - │ -54 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(N, Tup). -Expression has type: atom() | number() | string() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:58:5 - │ -58 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Tup). -Expression has type: atom() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:68:5 - │ -68 │ element(1, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(1, Tup). -Expression has type: dynamic() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: dynamic() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:86:5 - │ -86 │ element(N, Rec). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Rec). -Expression has type: 'foo' | 'ok' | 'error' | number() | string() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'error' | 'foo' | 'ok' - However the following candidate: string() - Differs from the expected type: atom() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:91:1 - │ -91 │ ╭ element_2_none_1(Tup) -> -92 │ │ element(42, Tup). - │ ╰────────────────────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:95:1 - │ -95 │ ╭ element_2_none_2(N, Tup) -> -96 │ │ element(N, Tup). - │ ╰───────────────────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:108:5 - │ -108 │ maps:get(K, M). - │ ^^^^^^^^^^^^^^ maps:get(K, M). -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:128:5 - │ -128 │ Res. - │ ^^^ Res. -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:154:12 - │ -154 │ get(a, M). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:160:23 - │ -160 │ Res = maps:get(a, M), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:167:17 - │ -167 │ maps:get(a, M, false). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:173:23 - │ -173 │ Res = maps:get(a, M, false), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:193:5 - │ -193 │ maps:get(K, M, 0). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(K, M, 0). -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:200:5 - │ -200 │ Res. - │ ^^^ - │ │ - │ Res. -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:206:5 - │ -206 │ ╭ ╭ { -207 │ │ │ maps:get(a, M, undefined), -208 │ │ │ maps:get(n, M, undefined) -209 │ │ │ }. - │ ╰─│─────^ {maps:get('a', M, 'undefined'), maps:get('n', M, 'undefined')}. -Expression has type: {atom(), 'undefined' | number()} -Context expected type: {atom(), number()} - │ ╰─────' - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: 'undefined' - Differs from the expected type: number() - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:221:27 - │ -221 │ map_get_2_17_neg(V, M) -> maps:get(V, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M). -Expression has type: 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:233:27 - │ -233 │ map_get_3_19_neg(V, M) -> maps:get(V, M, undefined). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M, 'undefined'). -Expression has type: 'undefined' | 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' | 'undefined' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'undefined' | 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:309:5 - │ -309 │ Res. - │ ^^^ Res. -Expression has type: {'value', #{}} | 'false' -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:363:23 - │ -363 │ fun (_, _) -> self() end, - │ ^^^^^^ erlang:self(). -Expression has type: pid() -Context expected type: boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:382:17 - │ -382 │ maps:filter(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> boolean()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:389:17 - │ -389 │ maps:filter(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((T) -> boolean()), [T]) -> [T]) with 1 type parameter -Context expected type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun()/2 with 1 type parameter - Context expects type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:413:37 - │ -413 │ maps:filter(fun erlang:'=:='/2, X). - │ ^ - │ │ - │ X. -Expression has type: #{K => V} | 'a' -Context expected type: #{term() => term()} | maps:iterator() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: #{K => V} - However the following candidate: 'a' - Differs from the expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:425:20 - │ -425 │ maps:filter(F, non_kv), - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:454:5 - │ -454 │ ╭ ╭ maps:map( -455 │ │ │ fun (_, _) -> self() end, -456 │ │ │ M -457 │ │ │ ). - │ ╰─│─────^ maps:map(fun, M). -Expression has type: #{number() => pid()} -Context expected type: #{number() => boolean()} - │ ╰─────' - -Because in the expression's type: - #{ number() => - Here the type is: pid() - Context expects type: boolean() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:474:14 - │ -474 │ maps:map(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> term()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:481:14 - │ -481 │ maps:map(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters -Context expected type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters - Context expects type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:497:5 - │ -497 │ maps:map(F, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:map(F, M). -Expression has type: #{a := boolean(), b := boolean()} -Context expected type: #{a => 'a', b => 'b'} - │ - -Because in the expression's type: - #{ a => - Here the type is: boolean() - Context expects type: 'a' - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:503:17 - │ -503 │ maps:map(F, non_kv), - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:538:9 - │ -538 │ fun (K, V) -> [K, V] end, [], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ fun. -fun with arity 2 used as fun with 3 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:544:5 - │ -544 │ ╭ ╭ maps:fold( -545 │ │ │ fun (_, _, Acc) -> [Acc] end, -546 │ │ │ [], -547 │ │ │ M -548 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [[[]]] -Context expected type: [number() | 'a' | 'b'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: [[]] - Context expects type: number() | 'a' | 'b' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:545:28 - │ -545 │ fun (_, _, Acc) -> [Acc] end, - │ ^^^^^ - │ │ - │ [Acc]. -Expression has type: [[[[]]]] -Context expected type: [[[]]] - │ - -Because in the expression's type: - [ - [ - [ - Here the type is: [] - Context expects type: none() - ] - ] - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:552:5 - │ -552 │ ╭ maps:fold( -553 │ │ fun (_, _, Acc) -> Acc end, -554 │ │ [], -555 │ │ non_kv -556 │ │ ). - │ ╰─────^ maps:fold(fun, [], 'non_kv'). -Expression has type: [] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:555:9 - │ -555 │ non_kv - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:560:1 - │ -560 │ ╭ maps_fold_3_6(None) -> -561 │ │ maps:fold( -562 │ │ None, -563 │ │ #{}, -564 │ │ #{1 => 1} -565 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:568:1 - │ -568 │ ╭ maps_fold_3_7(None) -> -569 │ │ maps:fold( -570 │ │ None, -571 │ │ None, -572 │ │ None -573 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:578:5 - │ -578 │ ╭ ╭ maps:fold( -579 │ │ │ fun (_K, A, _Acc) -> A end, -580 │ │ │ [], -581 │ │ │ M -582 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [] | atom() -Context expected type: atom() - │ ╰─────' - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: [] - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', []) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun((number(), 'a', []) -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> [] | dynamic()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:636:5 - │ -636 │ ╭ ╭ maps:fold( -637 │ │ │ fun -638 │ │ │ (_K, {i, I}, Acc) -> -639 │ │ │ [I | Acc]; - · │ │ -646 │ │ │ M -647 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [number() | binary() | atom()] -Context expected type: [binary()] | [number()] | [atom()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: number() - Differs from the expected type: binary() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:671:41 - │ -671 │ maps_to_list_7_neg(Num) -> maps:to_list(Num). - │ ^^^ Num. -Expression has type: number() -Context expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:675:25 - │ -675 │ maps_merge_1(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:679:25 - │ -679 │ maps_merge_2(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number() | boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:683:25 - │ -683 │ maps_merge_3(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a := string(), b := boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:697:44 - │ -697 │ maps_merge_7_neg(M1, M2) -> maps:merge(M1, M2). - │ ^^ M2. -Expression has type: number() -Context expected type: #{a => binary()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:701:25 - │ -701 │ maps_merge_8(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'a' | 'b' => atom() | number()} -Context expected type: #{a := atom(), b := number()} | #{a := atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:721:9 - │ -721 │ fun erlang:binary_to_list/1, - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:binary_to_list/1. -Expression has type: fun((binary()) -> [number()]) -Context expected type: fun((number()) -> boolean() | {'true', term()}) - │ - -Because in the expression's type: - fun((binary()) -> - Here the type is: [number()] - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:739:20 - │ -739 │ (3) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:749:9 - │ -749 │ not_a_list - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:791:17 - │ -791 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: {'true', 'a'} | 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {'true', 'a'} - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | {'true', term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:827:9 - │ -827 │ ╭ ╭ fun (a) -> [a]; -828 │ │ │ (b) -> true; -829 │ │ │ (c) -> wrong_ret end, - │ ╰─│────────────────────────────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰────────────────────────────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'true' - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:829:20 - │ -829 │ (c) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:9 - │ -837 │ ╭ ╭ fun (1) -> {true, a}; -838 │ │ │ (2) -> true end, - │ ╰─│───────────────────────────^ fun. -Expression has type: fun((dynamic()) -> {'true', 'a'} | 'true') -Context expected type: fun((Item) -> boolean() | [Item]) - │ ╰───────────────────────────' - -Because in the expression's type: - fun((dynamic()) -> - Here the type is a union type with some valid candidates: 'true' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | [Item] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:20 - │ -837 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | [dynamic()] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(Item) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[Item], [Item]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(dynamic()) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[dynamic()], [dynamic()]} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:846:9 - │ -846 │ ╭ fun (wrong, arity) -> -847 │ │ [a] -848 │ │ end, - │ ╰───────────^ fun. -fun with arity 2 used as fun with 1 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:9 - │ -857 │ ╭ ╭ fun (1) -> {true, a}; -858 │ │ │ (X) -> case X of -859 │ │ │ true -> -860 │ │ │ [a]; - · │ │ -863 │ │ │ end -864 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'false' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:20 - │ -857 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:873:9 - │ -873 │ ╭ ╭ fun (a) -> [a]; -874 │ │ │ (X) -> -875 │ │ │ Res = case X of -876 │ │ │ true -> - · │ │ -881 │ │ │ Res -882 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:881:17 - │ -881 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: ['a'] | 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun((dynamic()) -> boolean() | [dynamic()]) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:925:1 - │ -925 │ ╭ queue_filter_13_neg(Q) -> -926 │ │ queue:filter( -927 │ │ fun atom_to_list/1, -928 │ │ Q -929 │ │ ), -930 │ │ ok. - │ ╰──────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:955:5 - │ -955 │ ╭ lists:keystore( -956 │ │ a, 1, -957 │ │ [{foo, b}, {c, d}], -958 │ │ non_tuple -959 │ │ ). - │ ╰─────^ lists:keystore('a', 1, [{'foo', 'b'}, {'c', 'd'}], 'non_tuple'). -Expression has type: [{'foo', 'b'} | {'c', 'd'} | dynamic()] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:958:9 - │ -958 │ non_tuple - │ ^^^^^^^^^ 'non_tuple'. -Expression has type: 'non_tuple' -Context expected type: tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:966:5 - │ -966 │ ╭ lists:keystore( -967 │ │ a, 1, -968 │ │ [non_tuple], -969 │ │ {replacement} -970 │ │ ). - │ ╰─────^ lists:keystore('a', 1, ['non_tuple'], {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:968:9 - │ -968 │ [non_tuple], - │ ^^^^^^^^^^^ - │ │ - │ ['non_tuple']. -Expression has type: ['non_tuple'] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: 'non_tuple' - Context expects type: tuple() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:975:5 - │ -975 │ ╭ lists:keystore( -976 │ │ a, 1, -977 │ │ non_list, -978 │ │ {replacement} -979 │ │ ). - │ ╰─────^ lists:keystore('a', 1, 'non_list', {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:977:9 - │ -977 │ non_list, - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [tuple()] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:983:1 - │ -983 │ ╭ keystore_7(None) -> -984 │ │ lists:keystore( -985 │ │ a, 1, -986 │ │ None, -987 │ │ {replacement} -988 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:991:1 - │ -991 │ ╭ keystore_8(None) -> -992 │ │ lists:keystore( -993 │ │ a, 1, -994 │ │ None, -995 │ │ None -996 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1021:22 - │ -1021 │ lists:keytake(a, non_num, []), - │ ^^^^^^^ 'non_num'. -Expression has type: 'non_num' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1038:15 - │ -1038 │ lists:sum([a, 1]). - │ ^^^^^^ - │ │ - │ ['a', 1]. -Expression has type: ['a' | number()] -Context expected type: [number()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: 'a' - Differs from the expected type: number() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1042:15 - │ -1042 │ lists:sum(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1100:5 - │ -1100 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1106:5 - │ -1106 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' | 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1111:5 - │ -1111 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1117:5 - │ -1117 │ proplists:get_value(k, L, 3). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 3). -Expression has type: term() -Context expected type: pid() | number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1123:5 - │ -1123 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v1' | 'v2' | 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1129:5 - │ -1129 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1134:28 - │ -1134 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1139:5 - │ -1139 │ proplists:get_value(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', []). -Expression has type: term() -Context expected type: 'default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1144:1 - │ -1144 │ proplists:get_value(k, [], my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', [], 'my_default'). -Expression has type: term() -Context expected type: 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1149:28 - │ -1149 │ proplists:get_value(k, b, my_default). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1154:5 - │ -1154 │ proplists:get_bool(b, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_bool('b', L). -Expression has type: boolean() -Context expected type: 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1160:5 - │ -1160 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1166:5 - │ -1166 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default' | 'v'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' | 'v' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1171:5 - │ -1171 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid()] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1176:5 - │ -1176 │ proplists:get_all_values(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', []). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:5 - │ -1181 │ proplists:get_all_values(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', 'b'). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:33 - │ -1181 │ proplists:get_all_values(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1186:27 - │ -1186 │ proplists:get_bool(b, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1191:5 - │ -1191 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'c' - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1197:5 - │ -1197 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['a' | 'b' | 'c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' | 'b' | 'c' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1202:24 - │ -1202 │ proplists:get_keys(a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1207:5 - │ -1207 │ ╭ ╭ proplists:get_keys( -1208 │ │ │ [{a, b, c}] -1209 │ │ │ ). - │ ╰─│─────^ proplists:get_keys([{'a', 'b', 'c'}]). -Expression has type: [term()] -Context expected type: ['a'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1215:5 - │ -1215 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: 'a' | pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:5 - │ -1220 │ proplists:get_value(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', 'b'). -Expression has type: term() -Context expected type: 'a' | pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:28 - │ -1220 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1225:1 - │ -1225 │ proplists:lookup(self(), [a, {b, true}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup(erlang:self(), ['a', {'b', 'true'}]). -Expression has type: 'none' | tuple() -Context expected type: {'b', 'true'} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: tuple() - However the following candidate: 'none' - Differs from the expected type: {'b', 'true'} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1229:25 - │ -1229 │ proplists:lookup(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1239:5 - │ -1239 │ proplists:lookup(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup('k', []). -Expression has type: 'none' | tuple() -Context expected type: 'none' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'none' - However the following candidate: tuple() - Differs from the expected type: 'none' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1252:29 - │ -1252 │ proplists:lookup_all(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1265:5 - │ -1265 │ ╭ ╭ proplists:lookup_all( -1266 │ │ │ self(), -1267 │ │ │ [] -1268 │ │ │ ). - │ ╰─│─────^ proplists:lookup_all(erlang:self(), []). -Expression has type: [tuple()] -Context expected type: [] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: tuple() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1291:34 - │ -1291 │ proplists:is_defined(self(), b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [A] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1311:5 - │ -1311 │ proplists:split(L, Ks). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:split(L, Ks). -Expression has type: {[[term()]], [term()]} -Context expected type: {[plist('a', 'b')], plist('a', 'b')} - │ - -Because in the expression's type: - { - [ - [ - Here the type is: term() - Context expects type: 'a' | {'a', 'b'} - No candidate matches in the expected union. - ] - ] - , [term()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1315:21 - │ -1315 │ proplists:split(b, []). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1319:25 - │ -1319 │ proplists:split([], b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1324:22 - │ -1324 │ proplists:to_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [atom() | {term(), term()} | term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{K => V} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1363:5 - │ -1363 │ proplists:get_value(a, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1368:5 - │ -1368 │ proplists:get_value(X, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value(X, ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1373:5 - │ -1373 │ proplists:get_value(a, [a], b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a'], 'b'). -Expression has type: term() -Context expected type: 'true' | 'b' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1416:5 - │ -1416 │ file:consult(some_file). - │ ^^^^^^^^^^^^^^^^^^^^^^^ file:consult('some_file'). -Expression has type: {'ok', [dynamic()]} | {'error', {number(), atom(), term()} | 'terminated' | 'badarg' | file:posix() | 'system_limit'} -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1428:5 - │ -1428 │ lists:keysort(2, [{a, c}, {b, d}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:keysort(2, [{'a', 'c'}, {'b', 'd'}]). -Expression has type: [{'a', 'c'} | {'b', 'd'}] -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1432:22 - │ -1432 │ lists:keysort(1, [3]). - │ ^^^ - │ │ - │ [3]. -Expression has type: [number()] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: number() - Context expects type: tuple() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1448:5 - │ -1448 │ ╭ ╭ lists:filtermap( -1449 │ │ │ fun(X) when X div 2 =:= 0 -> -1450 │ │ │ {true, integer_to_list(X)}; -1451 │ │ │ (X) -> - · │ │ -1454 │ │ │ [1, 2, 3, 4] -1455 │ │ │ ). - │ ╰─│─────^ lists:filtermap(fun, [1, 2, 3, 4]). -Expression has type: [string() | number()] -Context expected type: [number()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: string() - Differs from the expected type: number() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1482:5 - │ -1482 │ erlang:min(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:min(X, Y). -Expression has type: number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1497:5 - │ -1497 │ erlang:max(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:max(X, Y). -Expression has type: atom() | number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1523:9 - │ -1523 │ abs(Atom). - │ ^^^^ Atom. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1536:31 - │ -1536 │ seq3_4_wip_neg() -> lists:seq(a, 2, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1539:30 - │ -1539 │ seq3_5_neg() -> lists:seq(1, a, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1542:33 - │ -1542 │ seq3_6_neg() -> lists:seq(1, 2, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1570:31 - │ -1570 │ seq2_4_wip_neg() -> lists:seq(a, 2). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1573:30 - │ -1573 │ seq2_5_neg() -> lists:seq(1, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1622:5 - │ -1622 │ ╭ timer:tc( -1623 │ │ fun() -> -1624 │ │ err -1625 │ │ end -1626 │ │ ). - │ ╰─────^ timer:tc(fun). -Expression has type: {number(), 'err'} -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1635:5 - │ -1635 │ ets:lookup(tab, Any). - │ ^^^^^^^^^^^^^^^^^^^^ ets:lookup('tab', Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:5 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ets:lookup(string_lit, Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:16 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1663:18 - │ -1663 │ ets:tab2list("not atom"). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1702:23 - │ -1702 │ lists:flatten([], 1). - │ ^ 1. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1711:19 - │ -1711 │ lists:flatten(3). - │ ^ 3. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1724:8 - │ -1724 │ -> lists:flatten(X). - │ ^^^^^^^^^^^^^^^^ - │ │ - │ lists:flatten(X). -Expression has type: [{A, B} | {B, A}] -Context expected type: [{A, B}] - │ - -Because in the expression's type: - [ - { - Here the type is: B - Context expects type: A - , A} - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1757:5 - │ -1757 │ ╭ ╭ maps:without( -1758 │ │ │ [a, c, DOrE], -1759 │ │ │ #{ -1760 │ │ │ a => ka, - · │ │ -1764 │ │ │ } -1765 │ │ │ ). - │ ╰─│─────^ maps:without(['a', 'c', DOrE], #{..}). -Expression has type: #{a => 'ka', b => atom(), c => pid(), d => 'kd'} -Context expected type: #{b => atom()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1769:18 - │ -1769 │ maps:without(non_list, #{}). - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1773:22 - │ -1773 │ maps:without([], non_map). - │ ^^^^^^^ 'non_map'. -Expression has type: 'non_map' -Context expected type: #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:1808:1 - │ -1808 │ ╭ maps_without_12_neg(None) -> -1809 │ │ maps:without( -1810 │ │ [a, b], -1811 │ │ None -1812 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1818:5 - │ -1818 │ ╭ ╭ maps:without( -1819 │ │ │ Keys, -1820 │ │ │ #{a => self(), b => self()} -1821 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1826:5 - │ -1826 │ ╭ ╭ maps:without( -1827 │ │ │ [a | improper_tail], -1828 │ │ │ #{a => self(), b => self()} -1829 │ │ │ ). - │ ╰─│─────^ maps:without(['a' | 'improper_tail'], #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1827:12 - │ -1827 │ [a | improper_tail], - │ ^^^^^^^^^^^^^^^^ 'improper_tail'. -Expression has type: 'improper_tail' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1837:5 - │ -1837 │ ╭ ╭ maps:without( -1838 │ │ │ Keys, -1839 │ │ │ #{a => ka, b => self()} -1840 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => 'ka', b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1854:5 - │ -1854 │ maps:without([a, b], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:without(['a', 'b'], M). -Expression has type: #{c := 'cv', d := 'dv'} | #{c := 'cv', e => 'ev'} -Context expected type: #{c := atom()} - │ - -Because in the expression's type: - Here the type is: #{d := ...} - Context expects type: #{...} - The expected map has no corresponding key for: d. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1916:23 - │ -1916 │ custom_overloaded(X). - │ ^ X. -Expression has type: term() -Context expected type: atom() | binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1939:5 - │ -1939 │ {A, N}. - │ ^^^^^^ - │ │ - │ {A, N}. -Expression has type: {atom(), number() | pid()} -Context expected type: {atom(), number()} - │ - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: pid() - Differs from the expected type: number() - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2041:5 - │ -2041 │ filename:join(["server", "erl"]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2046:5 - │ -2046 │ filename:join(["server", <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, <<..>>]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2051:5 - │ -2051 │ filename:join([<<>>, ""]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2066:5 - │ -2066 │ filename:join([<<>>, <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, <<..>>]). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2081:19 - │ -2081 │ filename:join([<<>>, self()]). - │ ^^^^^^^^^^^^^^ - │ │ - │ [<<..>>, erlang:self()]. -Expression has type: [binary() | pid()] -Context expected type: [file:name_all()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: pid() - Differs from the expected type: string() | atom() | file:deep_list() | binary() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2086:5 - │ -2086 │ filename:join("server", "erl"). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2091:5 - │ -2091 │ filename:join("server", <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, <<..>>). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2096:5 - │ -2096 │ filename:join(<<>>, ""). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(<<..>>, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2111:5 - │ -2111 │ filename:join(atom, <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join('atom', <<..>>). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2121:25 - │ -2121 │ filename:join(<<>>, self()). - │ ^^^^^^ - │ │ - │ erlang:self(). -Expression has type: pid() -Context expected type: file:name_all() - │ - -Because in the expression's type: - Here the type is: pid() - Context expects type: string() | atom() | file:deep_list() | binary() - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2150:5 - │ -2150 │ ╭ ╭ queue:filter( -2151 │ │ │ fun my_filter1/1, -2152 │ │ │ Q -2153 │ │ │ ). - │ ╰─│─────^ queue:filter(my_filter1/1, Q). -Expression has type: queue:queue(atom() | number()) -Context expected type: queue:queue(number()) - │ ╰─────' - -Because in the expression's type: - { - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: atom() - Differs from the expected type: number() - ] - , [atom() | number()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2191:5 - │ -2191 │ M3. - │ ^^ - │ │ - │ M3. -Expression has type: #{count := number(), module := 'foo'} | #{module := 'foo'} -Context expected type: state1() - │ - -Because in the expression's type: - The type is a union type with some valid candidates: #{count := number(), module := 'foo'} - However, the following candidate doesn't match: - Here the type is: #{...} - Context expects type: #{count := ..., ...} - The type of the expression is missing the following required keys: count. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2223:13 - │ -2223 │ Atom + Sum - │ ^^^^ Atom. -Expression has type: atom() -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2279:5 - │ -2279 │ maps:remove(A, M). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:remove(A, M). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2320:5 - │ -2320 │ ╭ ╭ maps:filtermap( -2321 │ │ │ fun -2322 │ │ │ (a, V) -> true; -2323 │ │ │ (b, V) -> {true, atom_to_binary(V)}; - · │ │ -2326 │ │ │ M -2327 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{a => atom() | binary(), b => atom() | binary(), c => atom() | binary()} -Context expected type: #{a := atom(), b := binary()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ..., b => ..., ...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2342:5 - │ -2342 │ ╭ ╭ maps:filtermap( -2343 │ │ │ fun (_, V) -> -2344 │ │ │ {true, atom_to_binary(V)} -2345 │ │ │ end, -2346 │ │ │ M -2347 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2354:23 - │ -2354 │ fun (_, _) -> err end, - │ ^^^ - │ │ - │ 'err'. -Expression has type: 'err' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'err' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2362:5 - │ -2362 │ ╭ ╭ maps:filtermap( -2363 │ │ │ fun (_, V) -> {true, atom_to_binary(V)} end, -2364 │ │ │ M -2365 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2363:45 - │ -2363 │ fun (_, V) -> {true, atom_to_binary(V)} end, - │ ^ V. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2377:5 - │ -2377 │ re:replace(Subj, "+", "-", [{return, binary}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'binary'}]). -Expression has type: binary() -Context expected type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2381:5 - │ -2381 │ re:replace(Subj, "+", "-", [{return, list}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'list'}]). -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2385:22 - │ -2385 │ Res = re:replace(Subj, "+", "-", [{return, list}]), - │ ^^^^ - │ │ - │ Subj. -Expression has type: atom() -Context expected type: iodata() | unicode:charlist() - │ - -Because in the expression's type: - Here the type is: atom() - Context expects type: iolist() | binary() - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2386:5 - │ -2386 │ Res. - │ ^^^ Res. -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2390:38 - │ -2390 │ Res = re:replace(Subj, "+", "-", [{return, something}]), - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ [{'return', 'something'}]. -Expression has type: [{'return', 'something'}] -Context expected type: [{'newline', 'cr' | 'lf' | 'any' | 'crlf' | 'anycrlf'} | 'notempty_atstart' | 'noteol' | 'bsr_unicode' | 'notbol' | 'global' | {'match_limit_recursion', number()} | 'bsr_anycrlf' | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - │ - -Because in the expression's type: - [ - { 'return', - Here the type is: 'something' - Context expects type: 'iodata' | 'list' | 'binary' - No candidate matches in the expected union. - } - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2506:5 - │ -2506 │ lists:partition(fun is_number/1, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[number()], [atom()]} -Context expected type: {[atom()], [number()]} - │ - -Because in the expression's type: - { - [ - Here the type is: number() - Context expects type: atom() - ] - , [atom()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2518:5 - │ -2518 │ lists:partition(fun({_Term, V}) -> is_number(V) end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{term(), number()}], [{term(), atom()}]} -Context expected type: {[{term(), atom()}], [{term(), number()}]} - │ - -Because in the expression's type: - { - [ - { term(), - Here the type is: number() - Context expects type: atom() - } - ] - , [{term(), atom()}]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2536:5 - │ -2536 │ lists:partition(fun({ok, _}) -> true; (_) -> false end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} -Context expected type: {[{'ok', atom()}], [{'error', term()}]} - │ - -Because in the expression's type: - { [{'ok', atom()}], - [ - { - Here the type is: 'ok' - Context expects type: 'error' - , atom()} - ] - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2576:33 - │ -2576 │ maps_intersect_2_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2586:33 - │ -2586 │ maps_intersect_4_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a => 'true'} - │ - -Because in the expression's type: - #{ a => - Here the type is: number() - Context expects type: 'true' - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2596:33 - │ -2596 │ maps_intersect_6_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2709:40 - │ -2709 │ (foo, A) -> binary_to_atom(A); - │ ^ A. -Expression has type: atom() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2710:40 - │ -2710 │ (bar, B) -> atom_to_binary(B); - │ ^ B. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2711:50 - │ -2711 │ ({foo, bar}, I) -> binary_to_integer(I); - │ ^ I. -Expression has type: number() -Context expected type: binary() - -202 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty deleted file mode 100644 index 5220990b8f..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-28.pretty +++ /dev/null @@ -1,2540 +0,0 @@ -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:26:5 - │ -26 │ element(4, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(4, Tup). -Expression has type: #{dynamic() => dynamic()} -Context expected type: atom() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:30:5 - │ -30 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 3 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:40:5 - │ -40 │ element(2, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(2, Tup). -Expression has type: number() | string() | atom() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:44:16 - │ -44 │ element(2, Tup). - │ ^^^ - │ │ - │ Tup. -Expression has type: {atom(), string()} | [dynamic()] -Context expected type: tuple() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {atom(), string()} - However the following candidate: [dynamic()] - Differs from the expected type: tuple() - -error: index_out_of_bounds (See https://fb.me/eqwalizer_errors#index_out_of_bounds) - ┌─ check/src/custom.erl:48:5 - │ -48 │ element(42, Tup). - │ ^^^^^^^^^^^^^^^^ 42. -Tried to access element 42 of a tuple with 2 elements - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:54:5 - │ -54 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ erlang:element(N, Tup). -Expression has type: atom() | number() | string() -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:58:5 - │ -58 │ element(N, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Tup). -Expression has type: atom() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:68:5 - │ -68 │ element(1, Tup). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(1, Tup). -Expression has type: dynamic() | number() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: dynamic() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:86:5 - │ -86 │ element(N, Rec). - │ ^^^^^^^^^^^^^^^ - │ │ - │ erlang:element(N, Rec). -Expression has type: 'foo' | 'ok' | 'error' | number() | string() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'error' | 'foo' | 'ok' - However the following candidate: string() - Differs from the expected type: atom() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:91:1 - │ -91 │ ╭ element_2_none_1(Tup) -> -92 │ │ element(42, Tup). - │ ╰────────────────────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:95:1 - │ -95 │ ╭ element_2_none_2(N, Tup) -> -96 │ │ element(N, Tup). - │ ╰───────────────────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:108:5 - │ -108 │ maps:get(K, M). - │ ^^^^^^^^^^^^^^ maps:get(K, M). -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:128:5 - │ -128 │ Res. - │ ^^^ Res. -Expression has type: number() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:154:12 - │ -154 │ get(a, M). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:160:23 - │ -160 │ Res = maps:get(a, M), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:167:17 - │ -167 │ maps:get(a, M, false). - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:173:23 - │ -173 │ Res = maps:get(a, M, false), - │ ^ M. -Expression has type: term() -Context expected type: #{term() => term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:193:5 - │ -193 │ maps:get(K, M, 0). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(K, M, 0). -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:200:5 - │ -200 │ Res. - │ ^^^ - │ │ - │ Res. -Expression has type: number() | atom() -Context expected type: atom() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: number() - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:206:5 - │ -206 │ ╭ ╭ { -207 │ │ │ maps:get(a, M, undefined), -208 │ │ │ maps:get(n, M, undefined) -209 │ │ │ }. - │ ╰─│─────^ {maps:get('a', M, 'undefined'), maps:get('n', M, 'undefined')}. -Expression has type: {atom(), 'undefined' | number()} -Context expected type: {atom(), number()} - │ ╰─────' - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: 'undefined' - Differs from the expected type: number() - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:221:27 - │ -221 │ map_get_2_17_neg(V, M) -> maps:get(V, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M). -Expression has type: 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:233:27 - │ -233 │ map_get_3_19_neg(V, M) -> maps:get(V, M, undefined). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:get(V, M, 'undefined'). -Expression has type: 'undefined' | 'a_v' | 'c_v' -Context expected type: 'a_v' | 'b_v' | 'undefined' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'undefined' | 'a_v' - However the following candidate: 'c_v' - Differs from the expected type: 'a_v' | 'b_v' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:309:5 - │ -309 │ Res. - │ ^^^ Res. -Expression has type: {'value', #{}} | 'false' -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:363:23 - │ -363 │ fun (_, _) -> self() end, - │ ^^^^^^ erlang:self(). -Expression has type: pid() -Context expected type: boolean() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:382:17 - │ -382 │ maps:filter(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> boolean()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:389:17 - │ -389 │ maps:filter(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((T) -> boolean()), [T]) -> [T]) with 1 type parameter -Context expected type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun()/2 with 1 type parameter - Context expects type: fun((number(), 'a' | 'b') -> boolean()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:413:37 - │ -413 │ maps:filter(fun erlang:'=:='/2, X). - │ ^ - │ │ - │ X. -Expression has type: #{K => V} | 'a' -Context expected type: #{term() => term()} | maps:iterator() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: #{K => V} - However the following candidate: 'a' - Differs from the expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:425:20 - │ -425 │ maps:filter(F, non_kv), - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:454:5 - │ -454 │ ╭ ╭ maps:map( -455 │ │ │ fun (_, _) -> self() end, -456 │ │ │ M -457 │ │ │ ). - │ ╰─│─────^ maps:map(fun, M). -Expression has type: #{number() => pid()} -Context expected type: #{number() => boolean()} - │ ╰─────' - -Because in the expression's type: - #{ number() => - Here the type is: pid() - Context expects type: boolean() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:474:14 - │ -474 │ maps:map(F, M). - │ ^ F. -Expression has type: fun(() -> pid()) -Context expected type: fun((number(), 'a' | 'b') -> term()) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:481:14 - │ -481 │ maps:map(F, M). - │ ^ - │ │ - │ F. -Expression has type: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters -Context expected type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((fun((A) -> B), [A]) -> [B]) with 2 type parameters - Context expects type: fun((number(), 'a' | 'b') -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:497:5 - │ -497 │ maps:map(F, M). - │ ^^^^^^^^^^^^^^ - │ │ - │ maps:map(F, M). -Expression has type: #{a := boolean(), b := boolean()} -Context expected type: #{a => 'a', b => 'b'} - │ - -Because in the expression's type: - #{ a => - Here the type is: boolean() - Context expects type: 'a' - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:503:17 - │ -503 │ maps:map(F, non_kv), - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:538:9 - │ -538 │ fun (K, V) -> [K, V] end, [], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ fun. -fun with arity 2 used as fun with 3 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:544:5 - │ -544 │ ╭ ╭ maps:fold( -545 │ │ │ fun (_, _, Acc) -> [Acc] end, -546 │ │ │ [], -547 │ │ │ M -548 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [[[]]] -Context expected type: [number() | 'a' | 'b'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: [[]] - Context expects type: number() | 'a' | 'b' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:545:28 - │ -545 │ fun (_, _, Acc) -> [Acc] end, - │ ^^^^^ - │ │ - │ [Acc]. -Expression has type: [[[[]]]] -Context expected type: [[[]]] - │ - -Because in the expression's type: - [ - [ - [ - Here the type is: [] - Context expects type: none() - ] - ] - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:552:5 - │ -552 │ ╭ maps:fold( -553 │ │ fun (_, _, Acc) -> Acc end, -554 │ │ [], -555 │ │ non_kv -556 │ │ ). - │ ╰─────^ maps:fold(fun, [], 'non_kv'). -Expression has type: [] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:555:9 - │ -555 │ non_kv - │ ^^^^^^ 'non_kv'. -Expression has type: 'non_kv' -Context expected type: #{term() => term()} | maps:iterator() - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:560:1 - │ -560 │ ╭ maps_fold_3_6(None) -> -561 │ │ maps:fold( -562 │ │ None, -563 │ │ #{}, -564 │ │ #{1 => 1} -565 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:568:1 - │ -568 │ ╭ maps_fold_3_7(None) -> -569 │ │ maps:fold( -570 │ │ None, -571 │ │ None, -572 │ │ None -573 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:578:5 - │ -578 │ ╭ ╭ maps:fold( -579 │ │ │ fun (_K, A, _Acc) -> A end, -580 │ │ │ [], -581 │ │ │ M -582 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [] | atom() -Context expected type: atom() - │ ╰─────' - -Because in the expression's type: - Here the type is a union type with some valid candidates: atom() - However the following candidate: [] - Differs from the expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', []) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun((number(), 'a', []) -> term()) with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> term()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:601:9 - │ -601 │ fun folder_bad/3, - │ ^^^^^^^^^^^^^^^^ - │ │ - │ folder_bad/3. -Expression has type: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter -Context expected type: fun((number(), 'a', [] | dynamic()) -> [] | dynamic()) with 0 type parameters - │ - -Because in the expression's type: - Here the type is: fun((term(), term(), Acc) -> [Acc]) with 1 type parameter - Context expects type: fun()/3 with 0 type parameters - The number of type parameters doesn't match. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:636:5 - │ -636 │ ╭ ╭ maps:fold( -637 │ │ │ fun -638 │ │ │ (_K, {i, I}, Acc) -> -639 │ │ │ [I | Acc]; - · │ │ -646 │ │ │ M -647 │ │ │ ). - │ ╰─│─────^ maps:fold(fun, [], M). -Expression has type: [number() | binary() | atom()] -Context expected type: [binary()] | [number()] | [atom()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: number() - Differs from the expected type: binary() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:671:41 - │ -671 │ maps_to_list_7_neg(Num) -> maps:to_list(Num). - │ ^^^ Num. -Expression has type: number() -Context expected type: #{term() => term()} | maps:iterator() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:675:25 - │ -675 │ maps_merge_1(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:679:25 - │ -679 │ maps_merge_2(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a => string(), b => number() | boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{'b' | 'a' | 'c' => number() | string() | atom()} - Context expects type: #{...} (no default association) - The expected map has no default association while the type of the expression has one. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:683:25 - │ -683 │ maps_merge_3(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'b' | 'a' | 'c' => number() | string() | atom()} -Context expected type: #{a := string(), b := boolean(), c => atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:697:44 - │ -697 │ maps_merge_7_neg(M1, M2) -> maps:merge(M1, M2). - │ ^^ M2. -Expression has type: number() -Context expected type: #{a => binary()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:701:25 - │ -701 │ maps_merge_8(M1, M2) -> maps:merge(M1, M2). - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:merge(M1, M2). -Expression has type: #{'a' | 'b' => atom() | number()} -Context expected type: #{a := atom(), b := number()} | #{a := atom()} - │ - -Because in the expression's type: - Here the type is: #{...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:721:9 - │ -721 │ fun erlang:binary_to_list/1, - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:binary_to_list/1. -Expression has type: fun((binary()) -> [number()]) -Context expected type: fun((number()) -> boolean() | {'true', term()}) - │ - -Because in the expression's type: - fun((binary()) -> - Here the type is: [number()] - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:739:20 - │ -739 │ (3) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:749:9 - │ -749 │ not_a_list - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:791:17 - │ -791 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: {'true', 'a'} | 'wrong_ret' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: {'true', 'a'} - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | {'true', term()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:827:9 - │ -827 │ ╭ ╭ fun (a) -> [a]; -828 │ │ │ (b) -> true; -829 │ │ │ (c) -> wrong_ret end, - │ ╰─│────────────────────────────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'true' | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰────────────────────────────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'true' - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:829:20 - │ -829 │ (c) -> wrong_ret end, - │ ^^^^^^^^^ - │ │ - │ 'wrong_ret'. -Expression has type: 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: 'wrong_ret' - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:9 - │ -837 │ ╭ ╭ fun (1) -> {true, a}; -838 │ │ │ (2) -> true end, - │ ╰─│───────────────────────────^ fun. -Expression has type: fun((dynamic()) -> {'true', 'a'} | 'true') -Context expected type: fun((Item) -> boolean() | [Item]) - │ ╰───────────────────────────' - -Because in the expression's type: - fun((dynamic()) -> - Here the type is a union type with some valid candidates: 'true' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | [Item] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:837:20 - │ -837 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | [dynamic()] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(Item) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[Item], [Item]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:839:9 - │ -839 │ not_a_queue - │ ^^^^^^^^^^^ - │ │ - │ 'not_a_queue'. -Expression has type: 'not_a_queue' -Context expected type: queue:queue(dynamic()) - │ - -Because in the expression's type: - Here the type is: 'not_a_queue' - Context expects type: {[dynamic()], [dynamic()]} - -error: fun_arity_mismatch (See https://fb.me/eqwalizer_errors#fun_arity_mismatch) - ┌─ check/src/custom.erl:846:9 - │ -846 │ ╭ fun (wrong, arity) -> -847 │ │ [a] -848 │ │ end, - │ ╰───────────^ fun. -fun with arity 2 used as fun with 1 arguments - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:9 - │ -857 │ ╭ ╭ fun (1) -> {true, a}; -858 │ │ │ (X) -> case X of -859 │ │ │ true -> -860 │ │ │ [a]; - · │ │ -863 │ │ │ end -864 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'false' | {'true', 'a'}) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] | 'false' - However the following candidate: {'true', 'a'} - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:857:20 - │ -857 │ fun (1) -> {true, a}; - │ ^^^^^^^^^ - │ │ - │ {'true', 'a'}. -Expression has type: {'true', 'a'} -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is: {'true', 'a'} - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:873:9 - │ -873 │ ╭ ╭ fun (a) -> [a]; -874 │ │ │ (X) -> -875 │ │ │ Res = case X of -876 │ │ │ true -> - · │ │ -881 │ │ │ Res -882 │ │ │ end, - │ ╰─│───────────^ fun. -Expression has type: fun(('a' | 'b') -> ['a'] | 'wrong_ret') -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ ╰───────────' - -Because in the expression's type: - fun(('a' | 'b') -> - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:881:17 - │ -881 │ Res - │ ^^^ - │ │ - │ Res. -Expression has type: ['a'] | 'wrong_ret' -Context expected type: boolean() | ['a' | 'b'] - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: ['a'] - However the following candidate: 'wrong_ret' - Differs from the expected type: 'false' | 'true' | ['a' | 'b'] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun((dynamic()) -> boolean() | [dynamic()]) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | [dynamic()] - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:891:9 - │ -891 │ fun list_to_atom/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:list_to_atom/1. -Expression has type: fun((string()) -> atom()) -Context expected type: fun(('a' | 'b') -> boolean() | ['a' | 'b']) - │ - -Because in the expression's type: - fun((string()) -> - Here the type is: atom() - Context expects type: 'false' | 'true' | ['a' | 'b'] - No candidate matches in the expected union. - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:900:9 - │ -900 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:909:9 - │ -909 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:919:9 - │ -919 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:925:1 - │ -925 │ ╭ queue_filter_13_neg(Q) -> -926 │ │ queue:filter( -927 │ │ fun atom_to_list/1, -928 │ │ Q -929 │ │ ), -930 │ │ ok. - │ ╰──────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((atom()) -> boolean() | [atom()]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [atom()] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:927:9 - │ -927 │ fun atom_to_list/1, - │ ^^^^^^^^^^^^^^^^^^ - │ │ - │ erlang:atom_to_list/1. -Expression has type: fun((atom()) -> string()) -Context expected type: fun((dynamic(atom())) -> boolean() | [dynamic(atom())]) - │ - -Because in the expression's type: - fun((atom()) -> - Here the type is: string() - Context expects type: boolean() | [dynamic(atom())] - ) - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:955:5 - │ -955 │ ╭ lists:keystore( -956 │ │ a, 1, -957 │ │ [{foo, b}, {c, d}], -958 │ │ non_tuple -959 │ │ ). - │ ╰─────^ lists:keystore('a', 1, [{'foo', 'b'}, {'c', 'd'}], 'non_tuple'). -Expression has type: [{'foo', 'b'} | {'c', 'd'} | dynamic()] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:958:9 - │ -958 │ non_tuple - │ ^^^^^^^^^ 'non_tuple'. -Expression has type: 'non_tuple' -Context expected type: tuple() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:966:5 - │ -966 │ ╭ lists:keystore( -967 │ │ a, 1, -968 │ │ [non_tuple], -969 │ │ {replacement} -970 │ │ ). - │ ╰─────^ lists:keystore('a', 1, ['non_tuple'], {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:968:9 - │ -968 │ [non_tuple], - │ ^^^^^^^^^^^ - │ │ - │ ['non_tuple']. -Expression has type: ['non_tuple'] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: 'non_tuple' - Context expects type: tuple() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:975:5 - │ -975 │ ╭ lists:keystore( -976 │ │ a, 1, -977 │ │ non_list, -978 │ │ {replacement} -979 │ │ ). - │ ╰─────^ lists:keystore('a', 1, 'non_list', {'replacement'}). -Expression has type: [dynamic() | {'replacement'}] -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:977:9 - │ -977 │ non_list, - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [tuple()] - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:983:1 - │ -983 │ ╭ keystore_7(None) -> -984 │ │ lists:keystore( -985 │ │ a, 1, -986 │ │ None, -987 │ │ {replacement} -988 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:991:1 - │ -991 │ ╭ keystore_8(None) -> -992 │ │ lists:keystore( -993 │ │ a, 1, -994 │ │ None, -995 │ │ None -996 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1011:25 - │ -1011 │ lists:keytake(a, 1, non_tup), - │ ^^^^^^^ 'non_tup'. -Expression has type: 'non_tup' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [Tuple] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1016:25 - │ -1016 │ lists:keytake(a, 1, non_list), - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1021:22 - │ -1021 │ lists:keytake(a, non_num, []), - │ ^^^^^^^ 'non_num'. -Expression has type: 'non_num' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1038:15 - │ -1038 │ lists:sum([a, 1]). - │ ^^^^^^ - │ │ - │ ['a', 1]. -Expression has type: ['a' | number()] -Context expected type: [number()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: 'a' - Differs from the expected type: number() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1042:15 - │ -1042 │ lists:sum(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [number()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1062:15 - │ -1062 │ lists:max(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [T] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1082:15 - │ -1082 │ lists:min(not_a_list). - │ ^^^^^^^^^^ 'not_a_list'. -Expression has type: 'not_a_list' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1100:5 - │ -1100 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1106:5 - │ -1106 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() | 'undefined' | 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1111:5 - │ -1111 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1117:5 - │ -1117 │ proplists:get_value(k, L, 3). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 3). -Expression has type: term() -Context expected type: pid() | number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1123:5 - │ -1123 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v1' | 'v2' | 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1129:5 - │ -1129 │ proplists:get_value(k, L, my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L, 'my_default'). -Expression has type: term() -Context expected type: 'v' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1134:28 - │ -1134 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1139:5 - │ -1139 │ proplists:get_value(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', []). -Expression has type: term() -Context expected type: 'default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1144:1 - │ -1144 │ proplists:get_value(k, [], my_default). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', [], 'my_default'). -Expression has type: term() -Context expected type: 'my_default' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1149:28 - │ -1149 │ proplists:get_value(k, b, my_default). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1154:5 - │ -1154 │ proplists:get_bool(b, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_bool('b', L). -Expression has type: boolean() -Context expected type: 'true' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1160:5 - │ -1160 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1166:5 - │ -1166 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid() | 'default' | 'v'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() | 'default' | 'v' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1171:5 - │ -1171 │ proplists:get_all_values(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', L). -Expression has type: [term()] -Context expected type: [pid()] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: pid() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1176:5 - │ -1176 │ proplists:get_all_values(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', []). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:5 - │ -1181 │ proplists:get_all_values(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_all_values('k', 'b'). -Expression has type: [term()] -Context expected type: [] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1181:33 - │ -1181 │ proplists:get_all_values(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1186:27 - │ -1186 │ proplists:get_bool(b, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1191:5 - │ -1191 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'c' - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1197:5 - │ -1197 │ proplists:get_keys(L). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:get_keys(L). -Expression has type: [term()] -Context expected type: ['a' | 'b' | 'c'] - │ - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' | 'b' | 'c' - No candidate matches in the expected union. - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1202:24 - │ -1202 │ proplists:get_keys(a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1207:5 - │ -1207 │ ╭ ╭ proplists:get_keys( -1208 │ │ │ [{a, b, c}] -1209 │ │ │ ). - │ ╰─│─────^ proplists:get_keys([{'a', 'b', 'c'}]). -Expression has type: [term()] -Context expected type: ['a'] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: term() - Context expects type: 'a' - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1215:5 - │ -1215 │ proplists:get_value(k, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', L). -Expression has type: term() -Context expected type: 'a' | pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:5 - │ -1220 │ proplists:get_value(k, b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('k', 'b'). -Expression has type: term() -Context expected type: 'a' | pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1220:28 - │ -1220 │ proplists:get_value(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1225:1 - │ -1225 │ proplists:lookup(self(), [a, {b, true}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup(erlang:self(), ['a', {'b', 'true'}]). -Expression has type: 'none' | tuple() -Context expected type: {'b', 'true'} - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: tuple() - However the following candidate: 'none' - Differs from the expected type: {'b', 'true'} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1229:25 - │ -1229 │ proplists:lookup(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1239:5 - │ -1239 │ proplists:lookup(k, []). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:lookup('k', []). -Expression has type: 'none' | tuple() -Context expected type: 'none' - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: 'none' - However the following candidate: tuple() - Differs from the expected type: 'none' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1252:29 - │ -1252 │ proplists:lookup_all(a, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1265:5 - │ -1265 │ ╭ ╭ proplists:lookup_all( -1266 │ │ │ self(), -1267 │ │ │ [] -1268 │ │ │ ). - │ ╰─│─────^ proplists:lookup_all(erlang:self(), []). -Expression has type: [tuple()] -Context expected type: [] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is: tuple() - Context expects type: none() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1291:34 - │ -1291 │ proplists:is_defined(self(), b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [A] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1295:25 - │ -1295 │ proplists:delete(k, b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [dynamic()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1311:5 - │ -1311 │ proplists:split(L, Ks). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ proplists:split(L, Ks). -Expression has type: {[[term()]], [term()]} -Context expected type: {[plist('a', 'b')], plist('a', 'b')} - │ - -Because in the expression's type: - { - [ - [ - Here the type is: term() - Context expects type: 'a' | {'a', 'b'} - No candidate matches in the expected union. - ] - ] - , [term()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1315:21 - │ -1315 │ proplists:split(b, []). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1319:25 - │ -1319 │ proplists:split([], b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1324:22 - │ -1324 │ proplists:to_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: [atom() | {term(), term()} | term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{K => V} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1358:24 - │ -1358 │ proplists:from_map(b). - │ ^ 'b'. -Expression has type: 'b' -Context expected type: #{dynamic() => dynamic()} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1363:5 - │ -1363 │ proplists:get_value(a, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1368:5 - │ -1368 │ proplists:get_value(X, [a]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value(X, ['a']). -Expression has type: term() -Context expected type: 'true' | 'undefined' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1373:5 - │ -1373 │ proplists:get_value(a, [a], b). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ proplists:get_value('a', ['a'], 'b'). -Expression has type: term() -Context expected type: 'true' | 'b' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1416:5 - │ -1416 │ file:consult(some_file). - │ ^^^^^^^^^^^^^^^^^^^^^^^ file:consult('some_file'). -Expression has type: {'ok', [dynamic()]} | {'error', {number(), atom(), term()} | 'terminated' | 'badarg' | file:posix() | 'system_limit'} -Context expected type: 'nok' - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1428:5 - │ -1428 │ lists:keysort(2, [{a, c}, {b, d}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lists:keysort(2, [{'a', 'c'}, {'b', 'd'}]). -Expression has type: [{'a', 'c'} | {'b', 'd'}] -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1432:22 - │ -1432 │ lists:keysort(1, [3]). - │ ^^^ - │ │ - │ [3]. -Expression has type: [number()] -Context expected type: [tuple()] - │ - -Because in the expression's type: - [ - Here the type is: number() - Context expects type: tuple() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1448:5 - │ -1448 │ ╭ ╭ lists:filtermap( -1449 │ │ │ fun(X) when X div 2 =:= 0 -> -1450 │ │ │ {true, integer_to_list(X)}; -1451 │ │ │ (X) -> - · │ │ -1454 │ │ │ [1, 2, 3, 4] -1455 │ │ │ ). - │ ╰─│─────^ lists:filtermap(fun, [1, 2, 3, 4]). -Expression has type: [string() | number()] -Context expected type: [number()] - │ ╰─────' - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: string() - Differs from the expected type: number() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1482:5 - │ -1482 │ erlang:min(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:min(X, Y). -Expression has type: number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1497:5 - │ -1497 │ erlang:max(X, Y). - │ ^^^^^^^^^^^^^^^^ erlang:max(X, Y). -Expression has type: atom() | number() -Context expected type: none() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1523:9 - │ -1523 │ abs(Atom). - │ ^^^^ Atom. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1536:31 - │ -1536 │ seq3_4_wip_neg() -> lists:seq(a, 2, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1539:30 - │ -1539 │ seq3_5_neg() -> lists:seq(1, a, 1). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1542:33 - │ -1542 │ seq3_6_neg() -> lists:seq(1, 2, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1570:31 - │ -1570 │ seq2_4_wip_neg() -> lists:seq(a, 2). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1573:30 - │ -1573 │ seq2_5_neg() -> lists:seq(1, a). - │ ^ 'a'. -Expression has type: 'a' -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1622:5 - │ -1622 │ ╭ timer:tc( -1623 │ │ fun() -> -1624 │ │ err -1625 │ │ end -1626 │ │ ). - │ ╰─────^ timer:tc(fun). -Expression has type: {number(), 'err'} -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1635:5 - │ -1635 │ ets:lookup(tab, Any). - │ ^^^^^^^^^^^^^^^^^^^^ ets:lookup('tab', Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:5 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ets:lookup(string_lit, Any). -Expression has type: [dynamic()] -Context expected type: pid() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1639:16 - │ -1639 │ ets:lookup("not atom", Any). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1663:18 - │ -1663 │ ets:tab2list("not atom"). - │ ^^^^^^^^^^ string_lit. -Expression has type: string() -Context expected type: ets:tab() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1702:23 - │ -1702 │ lists:flatten([], 1). - │ ^ 1. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1711:19 - │ -1711 │ lists:flatten(3). - │ ^ 3. -Expression has type: number() -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1724:8 - │ -1724 │ -> lists:flatten(X). - │ ^^^^^^^^^^^^^^^^ - │ │ - │ lists:flatten(X). -Expression has type: [{A, B} | {B, A}] -Context expected type: [{A, B}] - │ - -Because in the expression's type: - [ - { - Here the type is: B - Context expects type: A - , A} - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1757:5 - │ -1757 │ ╭ ╭ maps:without( -1758 │ │ │ [a, c, DOrE], -1759 │ │ │ #{ -1760 │ │ │ a => ka, - · │ │ -1764 │ │ │ } -1765 │ │ │ ). - │ ╰─│─────^ maps:without(['a', 'c', DOrE], #{..}). -Expression has type: #{a => 'ka', b => atom(), c => pid(), d => 'kd'} -Context expected type: #{b => atom()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ...} - Context expects type: #{...} - The expected map has no corresponding key for: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1769:18 - │ -1769 │ maps:without(non_list, #{}). - │ ^^^^^^^^ 'non_list'. -Expression has type: 'non_list' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1773:22 - │ -1773 │ maps:without([], non_map). - │ ^^^^^^^ 'non_map'. -Expression has type: 'non_map' -Context expected type: #{term() => term()} - -error: clause_not_covered (See https://fb.me/eqwalizer_errors#clause_not_covered) - ┌─ check/src/custom.erl:1808:1 - │ -1808 │ ╭ maps_without_12_neg(None) -> -1809 │ │ maps:without( -1810 │ │ [a, b], -1811 │ │ None -1812 │ │ ). - │ ╰─────^ Clause is not covered by spec - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1818:5 - │ -1818 │ ╭ ╭ maps:without( -1819 │ │ │ Keys, -1820 │ │ │ #{a => self(), b => self()} -1821 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1826:5 - │ -1826 │ ╭ ╭ maps:without( -1827 │ │ │ [a | improper_tail], -1828 │ │ │ #{a => self(), b => self()} -1829 │ │ │ ). - │ ╰─│─────^ maps:without(['a' | 'improper_tail'], #{..}). -Expression has type: #{a => pid(), b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1827:12 - │ -1827 │ [a | improper_tail], - │ ^^^^^^^^^^^^^^^^ 'improper_tail'. -Expression has type: 'improper_tail' -Context expected type: [term()] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1837:5 - │ -1837 │ ╭ ╭ maps:without( -1838 │ │ │ Keys, -1839 │ │ │ #{a => ka, b => self()} -1840 │ │ │ ). - │ ╰─│─────^ maps:without(Keys, #{..}). -Expression has type: #{a => 'ka', b => pid()} -Context expected type: #{b := pid()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{b => ..., ...} - Context expects type: #{b := ..., ...} - The type of the expression is missing the following required keys: b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1854:5 - │ -1854 │ maps:without([a, b], M). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:without(['a', 'b'], M). -Expression has type: #{c := 'cv', d := 'dv'} | #{c := 'cv', e => 'ev'} -Context expected type: #{c := atom()} - │ - -Because in the expression's type: - Here the type is: #{d := ...} - Context expects type: #{...} - The expected map has no corresponding key for: d. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1916:23 - │ -1916 │ custom_overloaded(X). - │ ^ X. -Expression has type: term() -Context expected type: atom() | binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:1939:5 - │ -1939 │ {A, N}. - │ ^^^^^^ - │ │ - │ {A, N}. -Expression has type: {atom(), number() | pid()} -Context expected type: {atom(), number()} - │ - -Because in the expression's type: - { atom(), - Here the type is a union type with some valid candidates: number() - However the following candidate: pid() - Differs from the expected type: number() - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2041:5 - │ -2041 │ filename:join(["server", "erl"]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2046:5 - │ -2046 │ filename:join(["server", <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([string_lit, <<..>>]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2051:5 - │ -2051 │ filename:join([<<>>, ""]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, string_lit]). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2066:5 - │ -2066 │ filename:join([<<>>, <<>>]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join([<<..>>, <<..>>]). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2081:19 - │ -2081 │ filename:join([<<>>, self()]). - │ ^^^^^^^^^^^^^^ - │ │ - │ [<<..>>, erlang:self()]. -Expression has type: [binary() | pid()] -Context expected type: [file:name_all()] - │ - -Because in the expression's type: - [ - Here the type is a union type with some valid candidates: binary() - However the following candidate: pid() - Differs from the expected type: string() | atom() | file:deep_list() | binary() - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2086:5 - │ -2086 │ filename:join("server", "erl"). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2091:5 - │ -2091 │ filename:join("server", <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(string_lit, <<..>>). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2096:5 - │ -2096 │ filename:join(<<>>, ""). - │ ^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join(<<..>>, string_lit). -Expression has type: file:filename_all() -Context expected type: file:filename() - │ - -Because in the expression's type: - Here the type is: string() | binary() - Context expects type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2111:5 - │ -2111 │ filename:join(atom, <<>>). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ filename:join('atom', <<..>>). -Expression has type: file:filename_all() -Context expected type: binary() - │ - -Because in the expression's type: - Here the type is a union type with some valid candidates: binary() - However the following candidate: string() - Differs from the expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2121:25 - │ -2121 │ filename:join(<<>>, self()). - │ ^^^^^^ - │ │ - │ erlang:self(). -Expression has type: pid() -Context expected type: file:name_all() - │ - -Because in the expression's type: - Here the type is: pid() - Context expects type: string() | atom() | file:deep_list() | binary() - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2150:5 - │ -2150 │ ╭ ╭ queue:filter( -2151 │ │ │ fun my_filter1/1, -2152 │ │ │ Q -2153 │ │ │ ). - │ ╰─│─────^ queue:filter(my_filter1/1, Q). -Expression has type: queue:queue(atom() | number()) -Context expected type: queue:queue(number()) - │ ╰─────' - -Because in the expression's type: - { - [ - Here the type is a union type with some valid candidates: number() - However the following candidate: atom() - Differs from the expected type: number() - ] - , [atom() | number()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2191:5 - │ -2191 │ M3. - │ ^^ - │ │ - │ M3. -Expression has type: #{count := number(), module := 'foo'} | #{module := 'foo'} -Context expected type: state1() - │ - -Because in the expression's type: - The type is a union type with some valid candidates: #{count := number(), module := 'foo'} - However, the following candidate doesn't match: - Here the type is: #{...} - Context expects type: #{count := ..., ...} - The type of the expression is missing the following required keys: count. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2223:13 - │ -2223 │ Atom + Sum - │ ^^^^ Atom. -Expression has type: atom() -Context expected type: number() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2279:5 - │ -2279 │ maps:remove(A, M). - │ ^^^^^^^^^^^^^^^^^ - │ │ - │ maps:remove(A, M). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2320:5 - │ -2320 │ ╭ ╭ maps:filtermap( -2321 │ │ │ fun -2322 │ │ │ (a, V) -> true; -2323 │ │ │ (b, V) -> {true, atom_to_binary(V)}; - · │ │ -2326 │ │ │ M -2327 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{a => atom() | binary(), b => atom() | binary(), c => atom() | binary()} -Context expected type: #{a := atom(), b := binary()} - │ ╰─────' - -Because in the expression's type: - Here the type is: #{a => ..., b => ..., ...} - Context expects type: #{a := ..., b := ..., ...} - The type of the expression is missing the following required keys: a, b. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2342:5 - │ -2342 │ ╭ ╭ maps:filtermap( -2343 │ │ │ fun (_, V) -> -2344 │ │ │ {true, atom_to_binary(V)} -2345 │ │ │ end, -2346 │ │ │ M -2347 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2354:23 - │ -2354 │ fun (_, _) -> err end, - │ ^^^ - │ │ - │ 'err'. -Expression has type: 'err' -Context expected type: boolean() | {'true', term()} - │ - -Because in the expression's type: - Here the type is: 'err' - Context expects type: 'false' | 'true' | {'true', term()} - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2362:5 - │ -2362 │ ╭ ╭ maps:filtermap( -2363 │ │ │ fun (_, V) -> {true, atom_to_binary(V)} end, -2364 │ │ │ M -2365 │ │ │ ). - │ ╰─│─────^ maps:filtermap(fun, M). -Expression has type: #{atom() => binary()} -Context expected type: #{atom() => atom()} - │ ╰─────' - -Because in the expression's type: - #{ atom() => - Here the type is: binary() - Context expects type: atom() - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2363:45 - │ -2363 │ fun (_, V) -> {true, atom_to_binary(V)} end, - │ ^ V. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2377:5 - │ -2377 │ re:replace(Subj, "+", "-", [{return, binary}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'binary'}]). -Expression has type: binary() -Context expected type: string() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2381:5 - │ -2381 │ re:replace(Subj, "+", "-", [{return, list}]). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re:replace(Subj, string_lit, string_lit, [{'return', 'list'}]). -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2385:22 - │ -2385 │ Res = re:replace(Subj, "+", "-", [{return, list}]), - │ ^^^^ - │ │ - │ Subj. -Expression has type: atom() -Context expected type: iodata() | unicode:charlist() - │ - -Because in the expression's type: - Here the type is: atom() - Context expects type: iolist() | binary() - No candidate matches in the expected union. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2386:5 - │ -2386 │ Res. - │ ^^^ Res. -Expression has type: string() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2390:38 - │ -2390 │ Res = re:replace(Subj, "+", "-", [{return, something}]), - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ [{'return', 'something'}]. -Expression has type: [{'return', 'something'}] -Context expected type: ['notempty_atstart' | 'noteol' | 'notbol' | 'global' | {'match_limit_recursion', number()} | re:compile_option() | {'match_limit', number()} | {'offset', number()} | 'notempty' | {'return', 'iodata' | 'list' | 'binary'} | 'anchored'] - │ - -Because in the expression's type: - [ - { 'return', - Here the type is: 'something' - Context expects type: 'iodata' | 'list' | 'binary' - No candidate matches in the expected union. - } - ] - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2506:5 - │ -2506 │ lists:partition(fun is_number/1, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[number()], [atom()]} -Context expected type: {[atom()], [number()]} - │ - -Because in the expression's type: - { - [ - Here the type is: number() - Context expects type: atom() - ] - , [atom()]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2518:5 - │ -2518 │ lists:partition(fun({_Term, V}) -> is_number(V) end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{term(), number()}], [{term(), atom()}]} -Context expected type: {[{term(), atom()}], [{term(), number()}]} - │ - -Because in the expression's type: - { - [ - { term(), - Here the type is: number() - Context expects type: atom() - } - ] - , [{term(), atom()}]} - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2536:5 - │ -2536 │ lists:partition(fun({ok, _}) -> true; (_) -> false end, L). - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ lists:partition(fun, L). -Expression has type: {[{'ok', atom()}], [{'ok', atom()} | {'error', term()}]} -Context expected type: {[{'ok', atom()}], [{'error', term()}]} - │ - -Because in the expression's type: - { [{'ok', atom()}], - [ - { - Here the type is: 'ok' - Context expects type: 'error' - , atom()} - ] - } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2576:33 - │ -2576 │ maps_intersect_2_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2586:33 - │ -2586 │ maps_intersect_4_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a => 'true'} - │ - -Because in the expression's type: - #{ a => - Here the type is: number() - Context expects type: 'true' - , ... } - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2596:33 - │ -2596 │ maps_intersect_6_neg(M1, M2) -> maps:intersect(M1, M2). - │ ^^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ maps:intersect(M1, M2). -Expression has type: #{a => number()} -Context expected type: #{a := number()} - │ - -Because in the expression's type: - Here the type is: #{a => ..., ...} - Context expects type: #{a := ..., ...} - The type of the expression is missing the following required keys: a. - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2709:40 - │ -2709 │ (foo, A) -> binary_to_atom(A); - │ ^ A. -Expression has type: atom() -Context expected type: binary() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2710:40 - │ -2710 │ (bar, B) -> atom_to_binary(B); - │ ^ B. -Expression has type: binary() -Context expected type: atom() - -error: incompatible_types (See https://fb.me/eqwalizer_errors#incompatible_types) - ┌─ check/src/custom.erl:2711:50 - │ -2711 │ ({foo, bar}, I) -> binary_to_integer(I); - │ ^ I. -Expression has type: number() -Context expected type: binary() - -202 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty similarity index 100% rename from crates/elp/src/resources/test/eqwalizer_tests/check/custom-OTP-26.pretty rename to crates/elp/src/resources/test/eqwalizer_tests/check/custom.pretty diff --git a/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl index f904f0e718..854feec923 100644 --- a/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl +++ b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl @@ -3,7 +3,7 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(callbacks3_neg). % @OTPVersionDependent +-module(callbacks3_neg). % @OTP27Only -export([ init/1, handle_call/3, diff --git a/test/test_projects/eqwalizer_tests/check/src/custom.erl b/test/test_projects/eqwalizer_tests/check/src/custom.erl index 911bfa3a0a..b6910b0fc7 100644 --- a/test/test_projects/eqwalizer_tests/check/src/custom.erl +++ b/test/test_projects/eqwalizer_tests/check/src/custom.erl @@ -3,7 +3,7 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(custom). % @OTPVersionDependent +-module(custom). % @OTP27Only -import(maps, [get/2, get/3]). -compile([export_all, nowarn_export_all]). From fa0504304371ad27f99045b6d7e022a5dccc3511 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Wed, 17 Dec 2025 09:53:03 -0800 Subject: [PATCH 31/46] Allow project discovery for OTP (#141) Summary: Some clients, such as emacs eglot, do their own project discovery when a new file is opened, even from the target of a go to definition. They then launch a fresh ELP server if they determine a different project root. In the ELP server setup we preconfigure the OTP project root as known, but with a project manifest of None. This means go to def into an OTP file ends up with an uninitialized project in the new server. This diff makes it so that if the project root is known, but has no project manifest, we do discovery on that root. Pull Request resolved: https://github.com/whatsapp/erlang-language-platform/pull/141 Reviewed By: robertoaloi Differential Revision: D89169465 Pulled By: alanz fbshipit-source-id: db180d67df98fd1f1fd61a53a93f9adfa9e93ebd --- crates/elp/src/project_loader.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/elp/src/project_loader.rs b/crates/elp/src/project_loader.rs index 103353e5db..0f13139201 100644 --- a/crates/elp/src/project_loader.rs +++ b/crates/elp/src/project_loader.rs @@ -100,8 +100,11 @@ impl ProjectLoader { ) -> Option<(ElpConfig, Result, ProjectManifest)> { let mut path_it = path; loop { - if self.project_roots.contains_key(path_it) { - return None; + if let Some(value) = self.project_roots.get(path_it) { + return match value { + None => Some(self.load_manifest(path_it)), + Some(_) => None, + }; } match path_it.parent() { Some(parent) => path_it = parent, From 54dd1089ed841440dd9709c2fc76b971e85719f4 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 18 Dec 2025 03:37:42 -0800 Subject: [PATCH 32/46] Do not run the old_edoc_syntax linter against test files by default Summary: Documentation is generally not produced for test files, so do not run the `old_edoc_syntax` linter for tests. If not, edoc references in tests should be converted to plain comments, not to `-moduledoc` or `-doc` attributes. This behaviour can always be overwritten by setting the `severity` or the `include_tests` properties of the linter via config. Reviewed By: alanz Differential Revision: D89374552 fbshipit-source-id: 8a4299fcbd16a82f62efbc14f47fef46cd08fbc0 --- crates/ide/src/diagnostics/edoc.rs | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/crates/ide/src/diagnostics/edoc.rs b/crates/ide/src/diagnostics/edoc.rs index 052c85d57f..622a20fe13 100644 --- a/crates/ide/src/diagnostics/edoc.rs +++ b/crates/ide/src/diagnostics/edoc.rs @@ -31,7 +31,6 @@ use super::DiagnosticCode; use super::GenericLinter; use super::GenericLinterMatchContext; use super::Linter; -use super::Severity; pub(crate) struct EdocLinter; @@ -44,11 +43,8 @@ impl Linter for EdocLinter { "EDoc style comments are deprecated. Please use Markdown instead." } - fn severity(&self, sema: &Semantic, file_id: FileId) -> Severity { - match sema.db.is_test_suite_or_test_helper(file_id) { - Some(true) => Severity::WeakWarning, - _ => Severity::Warning, - } + fn should_process_test_files(&self) -> bool { + false } } @@ -298,22 +294,6 @@ mod tests { ) } - #[test] - fn test_function_doc_in_test_file() { - check_diagnostics( - r#" - //- /test/main_SUITE.erl extra:test - -module(main_SUITE). - %% @doc This is the main function documentation. - %% ^^^^ 💡 weak: W0038: EDoc style comments are deprecated. Please use Markdown instead. - main() -> - dep(). - - dep() -> ok. - "#, - ) - } - #[test] fn test_function_doc_different_arities() { check_diagnostics( From 439ebade1bd580b67402572c20a81524746e3bd0 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 18 Dec 2025 03:37:42 -0800 Subject: [PATCH 33/46] Rename edoc linter into old_edoc_syntax Summary: The old name was too generic, and could be mis-interepreted as an EDoc linter (a linter that verifies EDoc syntax). By aligning the name of the linter with the actual diagnostic code, we make the intention more clear. No functional change. Reviewed By: alanz Differential Revision: D89374941 fbshipit-source-id: bf8518ed9602bdceca2fcbf3ddb00990f56af529 --- crates/ide/src/diagnostics.rs | 4 ++-- .../ide/src/diagnostics/{edoc.rs => old_edoc_syntax.rs} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename crates/ide/src/diagnostics/{edoc.rs => old_edoc_syntax.rs} (99%) diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 733347bfcb..5341d77da7 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -102,7 +102,6 @@ mod debugging_function; mod dependent_header; mod deprecated_function; mod duplicate_module; -mod edoc; mod effect_free_statement; mod equality_check_with_unnecessary_operator; mod eqwalizer_assists; @@ -131,6 +130,7 @@ mod no_garbage_collect; mod no_nowarn_suppressions; mod no_size; mod nonstandard_integer_formatting; +mod old_edoc_syntax; mod record_tuple_match; mod redundant_assignment; mod replace_call; @@ -1682,7 +1682,7 @@ const GENERIC_LINTERS: &[&dyn GenericDiagnostics] = &[ &duplicate_module::LINTER, &no_nowarn_suppressions::LINTER, ¯o_precedence_suprise::LINTER, - &edoc::LINTER, + &old_edoc_syntax::LINTER, &missing_module::LINTER, &unused_include::LINTER, &misspelled_attribute::LINTER, diff --git a/crates/ide/src/diagnostics/edoc.rs b/crates/ide/src/diagnostics/old_edoc_syntax.rs similarity index 99% rename from crates/ide/src/diagnostics/edoc.rs rename to crates/ide/src/diagnostics/old_edoc_syntax.rs index 622a20fe13..e8524b6e6e 100644 --- a/crates/ide/src/diagnostics/edoc.rs +++ b/crates/ide/src/diagnostics/old_edoc_syntax.rs @@ -32,9 +32,9 @@ use super::GenericLinter; use super::GenericLinterMatchContext; use super::Linter; -pub(crate) struct EdocLinter; +pub(crate) struct OldEdocSyntaxLinter; -impl Linter for EdocLinter { +impl Linter for OldEdocSyntaxLinter { fn id(&self) -> DiagnosticCode { DiagnosticCode::OldEdocSyntax } @@ -54,7 +54,7 @@ pub struct Context { doc_start: TextSize, } -impl GenericLinter for EdocLinter { +impl GenericLinter for OldEdocSyntaxLinter { type Context = Context; fn matches( @@ -131,7 +131,7 @@ impl GenericLinter for EdocLinter { } } -pub static LINTER: EdocLinter = EdocLinter; +pub static LINTER: OldEdocSyntaxLinter = OldEdocSyntaxLinter; fn old_edoc_syntax_fix( sema: &Semantic, From 3804bd17062700a0fb575796083fbc0d2a140fc5 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 18 Dec 2025 03:37:42 -0800 Subject: [PATCH 34/46] Enable undefined_function linter in CI (prod only) Summary: The linter is enabled by default in ELP, and warnings appear in the IDE. But the same linter is not explicitly added to the CI configuration, so warnings are not reported on diffs or in Contlint. This diff enables the linter, aligning the IDE and the CI experience. It also makes it possible to track violations via ContLint. We exclude known false positives via config. Ideally, we would like to report undefined functions in all files, but there are too many false positives in test files to do so. This is often due to mocked modules and test suite cleverness. We can revisit this decision in the future. See T249044930 for details. We disable the linter for tests both in the default value (for the future) and the config (so we don't need to wait for a ELP release to land this change). Reviewed By: alanz Differential Revision: D89393641 fbshipit-source-id: bfb9ac760ba79566301087033af95bb805fdeaa0 --- crates/ide/src/diagnostics/undefined_function.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/ide/src/diagnostics/undefined_function.rs b/crates/ide/src/diagnostics/undefined_function.rs index 189edf06f5..52fa2cfa3c 100644 --- a/crates/ide/src/diagnostics/undefined_function.rs +++ b/crates/ide/src/diagnostics/undefined_function.rs @@ -43,6 +43,13 @@ impl Linter for UndefinedFunctionLinter { fn should_process_generated_files(&self) -> bool { true } + // Ideally, we would like to report undefined functions in all files, but + // there are too many false positives in test files to do so. + // This is often due to mocked modules and test suite cleverness. + // We can revisit this decision in the future. See T249044930. + fn should_process_test_files(&self) -> bool { + false + } } impl FunctionCallLinter for UndefinedFunctionLinter { From d7b1c561ad05e3ddc8b00b2b9c6969ee7e6d2b69 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Thu, 18 Dec 2025 05:36:31 -0800 Subject: [PATCH 35/46] Fix GH CI for OTP 26 Summary: As title. Reviewed By: robertoaloi Differential Revision: D89461312 fbshipit-source-id: 3c0c75da6ccd00735d3f444b123b0bab222d0980 --- crates/elp/src/bin/main.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index e7ed9051a9..3391fe2c63 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -1595,13 +1595,15 @@ mod tests { #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_stream_produces_output(buck: bool) { - simple_snapshot_expect_error( - args_vec!["lint", "--no-stream"], - "diagnostics", - expect_file!("../resources/test/diagnostics/lint_no_stream.stdout"), - buck, - None, - ); + if otp::supports_eep66_sigils() { + simple_snapshot_expect_error( + args_vec!["lint", "--no-stream"], + "diagnostics", + expect_file!("../resources/test/diagnostics/lint_no_stream.stdout"), + buck, + None, + ); + } } #[test_case(false ; "rebar")] From ce0125dec900b669378cda2670af5c3b7d7761e8 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Thu, 18 Dec 2025 07:05:33 -0800 Subject: [PATCH 36/46] 1/n: bound var in match: detect problem Summary: In non-test Erlang code, the following is almost always intended as assignment of a value to the LHS variable ``` Var = expr() ``` It is actually a match, and may fail if `Var` is already bound, and the new value differs from the old. This diff introduces a diagnostic to warn of this case. Reviewed By: robertoaloi Differential Revision: D89374198 fbshipit-source-id: 19dcee50ad6e71d0a606a0eb6cf548b7194d61e6 --- .../test/diagnostics/lint_no_stream.stdout | 3 + .../diagnostics/parse_all_diagnostics1.stdout | 3 +- .../parse_all_diagnostics_json.stdout | 1 + .../diagnostics/parse_otp27_docstrings.jsonl | 4 +- ...e_elp_no_lint_specified_json_output.stdout | 2 + .../parse_elp_no_lint_specified_output.stdout | 2 + .../test/linter/warnings_as_errors.stdout | 2 + crates/hir/src/lib.rs | 4 + crates/ide/src/diagnostics.rs | 2 + crates/ide/src/diagnostics/bound_variable.rs | 178 ++++++++++++++++++ crates/ide/src/tests.rs | 1 + crates/ide_db/src/diagnostic_code.rs | 4 + website/docs/erlang-error-index/w/W0060.md | 48 +++++ 13 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 crates/ide/src/diagnostics/bound_variable.rs create mode 100644 website/docs/erlang-error-index/w/W0060.md diff --git a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout index b1c2f07150..eef1c92c6d 100644 --- a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout +++ b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout @@ -45,6 +45,7 @@ app_a/src/cascading.erl:9:5-9:6::[Error] [W0004] Missing ')' 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 @@ -60,6 +61,8 @@ app_a/src/lints.erl:5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs ' 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 diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout index 7fe610f781..4b0a155055 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics1.stdout @@ -1,9 +1,10 @@ module specified: diagnostics Diagnostics reported in 1 modules: - diagnostics: 6 + diagnostics: 7 2:9-2:26::[Hint] [W0037] Unspecific include. 3:0-3:35::[Error] [L0000] Issue in included file 3:9-3:33::[Hint] [W0037] Unspecific include. 5:30-5:44::[Error] [L1295] type undefined_type/0 undefined 6:0-6:4::[Warning] [L1230] function main/1 is unused 9:0-9:3::[Warning] [L1230] function foo/0 is unused + 11:7-11:11::[Warning] [W0060] Match on a bound variable diff --git a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout index 0c3e5ae9a3..ede82c7c5f 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout +++ b/crates/elp/src/resources/test/diagnostics/parse_all_diagnostics_json.stdout @@ -4,3 +4,4 @@ {"path":"app_a/src/diagnostics.erl","line":6,"char":31,"code":"ELP","severity":"error","name":"L1295 (L1295)","original":null,"replacement":null,"description":"type undefined_type/0 undefined\n\nFor more information see: /erlang-error-index/l/L1295","docPath":null} {"path":"app_a/src/diagnostics.erl","line":7,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function main/1 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} {"path":"app_a/src/diagnostics.erl","line":10,"char":1,"code":"ELP","severity":"warning","name":"L1230 (L1230)","original":null,"replacement":null,"description":"function foo/0 is unused\n\nFor more information see: /erlang-error-index/l/L1230","docPath":null} +{"path":"app_a/src/diagnostics.erl","line":12,"char":8,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} diff --git a/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl b/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl index 5e286c673f..c34c9037db 100644 --- a/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl +++ b/crates/elp/src/resources/test/diagnostics/parse_otp27_docstrings.jsonl @@ -1,4 +1,6 @@ module specified: otp27_docstrings Diagnostics reported in 1 modules: - otp27_docstrings: 1 + otp27_docstrings: 3 + 23:4-23:5::[Warning] [W0060] Match on a bound variable + 29:4-29:5::[Warning] [W0060] Match on a bound variable 33:8-33:23::[Warning] [W0002] Unused macro (THIS_IS_THE_END) diff --git a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout index 6e07ea2bba..7b6a1e705c 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_json_output.stdout @@ -5,6 +5,8 @@ {"path":"app_a/src/app_a.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"W0011 (application_get_env)","original":null,"replacement":null,"description":"module `app_a` belongs to app `app_a`, but reads env for `misc`\n\nFor more information see: /erlang-error-index/w/W0011","docPath":"website/docs/erlang-error-index/w/W0011.md"} {"path":"app_a/src/app_a.erl","line":8,"char":7,"code":"ELP","severity":"warning","name":"W0018 (unexpected_semi_or_dot)","original":null,"replacement":null,"description":"Unexpected ';'\n\nFor more information see: /erlang-error-index/w/W0018","docPath":"website/docs/erlang-error-index/w/W0018.md"} {"path":"app_a/src/app_a.erl","line":9,"char":1,"code":"ELP","severity":"error","name":"P1700 (head_mismatch)","original":null,"replacement":null,"description":"head mismatch 'fooX' vs 'food'\n\nFor more information see: /erlang-error-index/p/P1700","docPath":null} +{"path":"app_a/src/app_a_ssr.erl","line":7,"char":6,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} +{"path":"app_a/src/app_a_ssr.erl","line":8,"char":6,"code":"ELP","severity":"warning","name":"W0060 (bound_var_in_lhs)","original":null,"replacement":null,"description":"Match on a bound variable\n\nFor more information see: /erlang-error-index/w/W0060","docPath":"website/docs/erlang-error-index/w/W0060.md"} {"path":"app_a/src/app_a_unused_param.erl","line":5,"char":5,"code":"ELP","severity":"warning","name":"L1268 (L1268)","original":null,"replacement":null,"description":"variable 'X' is unused\n\nFor more information see: /erlang-error-index/l/L1268","docPath":null} {"path":"app_a/src/custom_function_matches.erl","line":13,"char":5,"code":"ELP","severity":"warning","name":"W0017 (undefined_function)","original":null,"replacement":null,"description":"Function 'excluded:function/0' is undefined.\n\nFor more information see: /erlang-error-index/w/W0017","docPath":"website/docs/erlang-error-index/w/W0017.md"} {"path":"app_a/src/custom_function_matches.erl","line":14,"char":5,"code":"ELP","severity":"warning","name":"W0017 (undefined_function)","original":null,"replacement":null,"description":"Function 'not_excluded:function/0' is undefined.\n\nFor more information see: /erlang-error-index/w/W0017","docPath":"website/docs/erlang-error-index/w/W0017.md"} diff --git a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout index 9f7a5405b4..5bcd503e10 100644 --- a/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout +++ b/crates/elp/src/resources/test/linter/parse_elp_no_lint_specified_output.stdout @@ -8,6 +8,8 @@ app_a/src/app_a.erl:20:1-20:4::[Warning] [L1230] function bat/2 is unused app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` app_a/src/app_a.erl:8:7-8:8::[Warning] [W0018] Unexpected ';' app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' +app_a/src/app_a_ssr.erl:7:6-7:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_ssr.erl:8:6-8:7::[Warning] [W0060] Match on a bound variable app_a/src/app_a_unused_param.erl:5:5-5:6::[Warning] [L1268] variable 'X' is unused app_a/src/custom_function_matches.erl:13:5-13:22::[Warning] [W0017] Function 'excluded:function/0' is undefined. app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. diff --git a/crates/elp/src/resources/test/linter/warnings_as_errors.stdout b/crates/elp/src/resources/test/linter/warnings_as_errors.stdout index 00d8506230..01007e94c4 100644 --- a/crates/elp/src/resources/test/linter/warnings_as_errors.stdout +++ b/crates/elp/src/resources/test/linter/warnings_as_errors.stdout @@ -8,6 +8,8 @@ app_a/src/app_a.erl:20:1-20:4::[Error] [L1230] function bat/2 is unused app_a/src/app_a.erl:5:5-5:35::[Warning] [W0011] module `app_a` belongs to app `app_a`, but reads env for `misc` app_a/src/app_a.erl:8:7-8:8::[Warning] [W0018] Unexpected ';' app_a/src/app_a.erl:9:1-9:5::[Error] [P1700] head mismatch 'fooX' vs 'food' +app_a/src/app_a_ssr.erl:7:6-7:7::[Warning] [W0060] Match on a bound variable +app_a/src/app_a_ssr.erl:8:6-8:7::[Warning] [W0060] Match on a bound variable app_a/src/app_a_unused_param.erl:5:5-5:6::[Error] [L1268] variable 'X' is unused app_a/src/custom_function_matches.erl:13:5-13:22::[Warning] [W0017] Function 'excluded:function/0' is undefined. app_a/src/custom_function_matches.erl:14:5-14:26::[Warning] [W0017] Function 'not_excluded:function/0' is undefined. diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 35585ef702..751a2f4907 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -232,6 +232,10 @@ impl HirIdx { } } + pub fn file_id(&self) -> FileId { + self.body_origin.file_id() + } + /// This function is used to print a representation of the HIR AST /// corresponding to the given `HirIdx`. It is used for debugging /// and testing. diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 5341d77da7..7b23da4461 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -96,6 +96,7 @@ mod application_env; mod atoms_exhaustion; mod binary_string_to_sigil; mod boolean_precedence; +mod bound_variable; mod could_be_a_string_literal; mod cross_node_eval; mod debugging_function; @@ -1687,6 +1688,7 @@ const GENERIC_LINTERS: &[&dyn GenericDiagnostics] = &[ &unused_include::LINTER, &misspelled_attribute::LINTER, &boolean_precedence::LINTER, + &bound_variable::LINTER, ]; /// Unified registry for all types of linters diff --git a/crates/ide/src/diagnostics/bound_variable.rs b/crates/ide/src/diagnostics/bound_variable.rs new file mode 100644 index 0000000000..92f0edadfa --- /dev/null +++ b/crates/ide/src/diagnostics/bound_variable.rs @@ -0,0 +1,178 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +// Diagnostic: bound_variable +// +// Return a warning if the LHS of a match already contains a bound variable. +// + +use elp_ide_db::elp_base_db::FileId; +use hir::AnyExpr; +use hir::Expr; +use hir::Semantic; +use hir::Strategy; +use hir::fold::MacroStrategy; +use hir::fold::ParenStrategy; + +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; + +pub(crate) struct BoundVariableLinter; + +impl Linter for BoundVariableLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::BoundVarInLhs + } + + fn description(&self) -> &'static str { + "Match on a bound variable" + } +} + +impl GenericLinter for BoundVariableLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let bound_vars_by_function = sema.bound_vars_by_function(file_id); + let mut res = Vec::new(); + sema.def_map(file_id) + .get_function_clauses() + .for_each(|(_, def)| { + if def.file.file_id == file_id + && let Some(bound_vars) = bound_vars_by_function.get(&def.function_clause_id) + { + let in_clause = def.in_clause(sema, def); + in_clause.fold_clause( + Strategy { + macros: MacroStrategy::ExpandButIncludeMacroCall, + parens: ParenStrategy::InvisibleParens, + }, + (), + &mut |acc, ctx| { + if let AnyExpr::Expr(Expr::Match { lhs, rhs: _ }) = ctx.item + && bound_vars.contains(&lhs) + && let Some(range) = in_clause.range_for_pat(lhs) + && range.file_id == def.file.file_id + && ctx.in_macro.is_none() + { + res.push(GenericLinterMatchContext { + range: range.range, + context: (), + }); + }; + acc + }, + ); + } + }); + + Some(res) + } +} + +pub static LINTER: BoundVariableLinter = BoundVariableLinter; + +#[cfg(test)] +mod test { + use elp_ide_db::DiagnosticCode; + use expect_test::Expect; + + use crate::diagnostics::DiagnosticsConfig; + use crate::tests::check_diagnostics_with_config; + use crate::tests::check_fix_with_config; + + #[track_caller] + pub(crate) fn check_diagnostics(fixture: &str) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_diagnostics_with_config(config, fixture) + } + + #[track_caller] + pub(crate) fn check_fix(fixture_before: &str, fixture_after: Expect) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_fix_with_config(config, fixture_before, fixture_after) + } + #[test] + fn bound_variable() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + AA = bar(). + %% ^^ 💡 warning: W0060: Match on a bound variable + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_case() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo(Val) -> + case Val of + undefined -> ok; + Val when is_list(Val) -> ok + end. + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_macro() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + -include("inc.hrl"). + + foo(Val) -> + ?A_MACRO(Val). + //- /src/inc.hrl + -define(A_MACRO(X), X=X). + "#, + ) + } + + #[test] + fn bound_variable_ignore_fix() { + check_fix( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + A~A = bar(). + "#, + expect_test::expect![[r#" + -module(bound). + + foo() -> + AA = bar(), + % elp:ignore W0060 (bound_var_in_lhs) + AA = bar(). + "#]], + ) + } +} diff --git a/crates/ide/src/tests.rs b/crates/ide/src/tests.rs index f1a9065e01..55e9494e48 100644 --- a/crates/ide/src/tests.rs +++ b/crates/ide/src/tests.rs @@ -378,6 +378,7 @@ pub(crate) fn check_diagnostics(fixture: &str) { .disable(DiagnosticCode::UnspecificInclude) .disable(DiagnosticCode::BinaryStringToSigil) .disable(DiagnosticCode::HirUnresolvedMacro) + .disable(DiagnosticCode::BoundVarInLhs) .disable(DiagnosticCode::HirUnresolvedInclude); check_diagnostics_with_config(config, fixture) } diff --git a/crates/ide_db/src/diagnostic_code.rs b/crates/ide_db/src/diagnostic_code.rs index e9d21cadca..ec1e1d76cf 100644 --- a/crates/ide_db/src/diagnostic_code.rs +++ b/crates/ide_db/src/diagnostic_code.rs @@ -93,6 +93,7 @@ pub enum DiagnosticCode { ListsReverseAppend, HirUnresolvedMacro, HirUnresolvedInclude, + BoundVarInLhs, // Wrapper for erlang service diagnostic codes ErlangService(String), @@ -256,6 +257,7 @@ impl DiagnosticCode { DiagnosticCode::HirUnresolvedMacro => "W0057".to_string(), DiagnosticCode::HirUnresolvedInclude => "W0058".to_string(), DiagnosticCode::UnavailableType => "W0059".to_string(), + DiagnosticCode::BoundVarInLhs => "W0060".to_string(), DiagnosticCode::ErlangService(c) => c.to_string(), DiagnosticCode::Eqwalizer(c) => format!("eqwalizer: {c}"), DiagnosticCode::AdHoc(c) => format!("ad-hoc: {c}"), @@ -271,6 +273,7 @@ impl DiagnosticCode { DiagnosticCode::HeadMismatch => "head_mismatch".to_string(), DiagnosticCode::SyntaxError => "syntax_error".to_string(), DiagnosticCode::BoundVarInPattern => "bound_var_in_pattern".to_string(), + DiagnosticCode::BoundVarInLhs => "bound_var_in_lhs".to_string(), DiagnosticCode::ModuleMismatch => "module_mismatch".to_string(), DiagnosticCode::UnusedMacro => "unused_macro".to_string(), DiagnosticCode::UnusedRecordField => "unused_record_field".to_string(), @@ -486,6 +489,7 @@ impl DiagnosticCode { DiagnosticCode::ModuleMismatch => false, DiagnosticCode::UnusedInclude => false, DiagnosticCode::BoundVarInPattern => false, + DiagnosticCode::BoundVarInLhs => false, DiagnosticCode::UnusedMacro => false, DiagnosticCode::UnusedRecordField => false, DiagnosticCode::MutableVarBug => false, diff --git a/website/docs/erlang-error-index/w/W0060.md b/website/docs/erlang-error-index/w/W0060.md new file mode 100644 index 0000000000..91b972dacf --- /dev/null +++ b/website/docs/erlang-error-index/w/W0060.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 60 +--- + +# W0060 - Bound Variable in LHS + +## Error + +```erlang +handle_request(Message) -> + Message = next_action(). +%% ^^^^^^^ 💡 warning: W0060: Match on a bound variable +``` + +## Explanation + +This diagnostic flags cases where a variable that is already bound appears on the left-hand side (LHS) of a match expression. This can be problematic if the binding is not intentional and can lead to subtle bugs. + +Consider the following code snippet: + +```erlang showLineNumbers +foo() -> + AA = foo(), + AA = bar(). +``` + +The pattern on line `3` will only match if and only if the result of the call to `bar/0` is the same as the call to `foo/0`. This behaviour could be intentional or not. If not, it can easily lead to bugs. + +An alternative, more explicit, way to express that behaviour - when intentional - could be: + +```erlang showLineNumbers +foo() -> + AA = foo(), + BB = bar(), + AA = BB. +``` + +Or using an assertion: + +```erlang showLineNumbers +foo() -> + AA = foo(), + ?assertEqual(AA, bar()). +``` + +## Semantic highlighting + +Note that we also have semantic highlighting of the more general case, where a bound variable appears in any pattern. From b12cf72c7e2e4e04e92ad8aba957ee5ca86fe10d Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 18 Dec 2025 14:47:07 -0800 Subject: [PATCH 37/46] Standalone Buck2 setup for test_projects Summary: We want to enable Buck2 tests on GitHub. This requires a bit of groundwork. Reviewed By: michalmuskala Differential Revision: D88959148 fbshipit-source-id: e5f88f561e061635aae05c4e3bc22382771a8908 --- crates/elp/src/bin/main.rs | 119 +++++++++--------- .../test/xref/unavailable_type.stdout | 4 +- crates/project_model/src/buck.rs | 81 +++++++----- test/test_projects/.buckconfig | 24 ++++ test/test_projects/.buckroot | 0 test/test_projects/buck_bad_config/.elp.toml | 4 +- test/test_projects/buck_tests/.elp.toml | 6 +- .../buck_tests/test_elp/TARGETS.v2_ | 10 +- .../test_elp_direct_dep/TARGETS.v2_ | 4 +- test/test_projects/buck_tests_2/.elp.toml | 8 +- test/test_projects/diagnostics/.elp.toml | 4 +- test/test_projects/end_to_end/.elp.toml | 2 +- .../test_projects/eqwalizer_callers/.elp.toml | 4 +- test/test_projects/eqwalizer_tests/.elp.toml | 4 +- .../hierarchical_config/.elp.toml | 4 +- test/test_projects/in_place_tests/.elp.toml | 4 +- .../include_lib_dependency_test/.elp.toml | 4 +- test/test_projects/linter/.elp.toml | 4 +- .../test_projects/linter_bad_config/.elp.toml | 4 +- .../linter_bad_config/linter/.elp.toml | 4 +- test/test_projects/parse_error/.elp.toml | 4 +- test/test_projects/standard/.elp.toml | 4 +- test/test_projects/xref/.elp.toml | 4 +- 23 files changed, 180 insertions(+), 130 deletions(-) create mode 100644 test/test_projects/.buckconfig create mode 100644 test/test_projects/.buckroot diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 3391fe2c63..df99eff987 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -605,10 +605,7 @@ mod tests { fn eqwalize_target_diagnostics_match_snapshot_pretty() { if cfg!(feature = "buck") { simple_snapshot( - args_vec![ - "eqwalize-target", - "//whatsapp/elp/test/test_projects/standard:app_a", - ], + args_vec!["eqwalize-target", "//standard:app_a",], "standard", expect_file!("../resources/test/standard/eqwalize_target_diagnostics.pretty"), true, @@ -985,38 +982,13 @@ mod tests { Some(AbsPathBuf::assert(Utf8PathBuf::from_path_buf(abs).unwrap())); let content = normalise_prelude_path(content, buck_config); + let content = sort_json(&content); + expect![[r#" { "apps": [ { - "name": "test_exec", - "dir": "/[prelude]//erlang/common_test/test_exec/src", - "src_dirs": [ - "" - ], - "extra_src_dirs": [], - "include_dirs": [], - "macros": {} - }, - { - "name": "diagnostics_app_a", - "dir": "app_a", - "src_dirs": [ - "src" - ], - "extra_src_dirs": [], - "include_dirs": [ - "include" - ], - "macros": { - "COMMON_TEST": "true", - "TEST": "true" - } - }, - { - "name": "app_a_SUITE", "dir": "app_a/test", - "src_dirs": [], "extra_src_dirs": [ "" ], @@ -1024,61 +996,88 @@ mod tests { "macros": { "COMMON_TEST": "true", "TEST": "true" - } + }, + "name": "app_a_SUITE", + "src_dirs": [] }, { - "name": "common", - "dir": "/[prelude]//erlang/common_test/common", + "dir": "/[prelude]//erlang/common_test/test_exec/src", + "extra_src_dirs": [], + "include_dirs": [], + "macros": {}, + "name": "test_exec", "src_dirs": [ - "src" - ], + "" + ] + }, + { + "dir": "/[prelude]//erlang/common_test/common", "extra_src_dirs": [], "include_dirs": [ "include" ], - "macros": {} + "macros": {}, + "name": "common", + "src_dirs": [ + "src" + ] }, { - "name": "cth_hooks", "dir": "/[prelude]//erlang/common_test/cth_hooks/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [ "" ], - "macros": {} + "macros": {}, + "name": "cth_hooks", + "src_dirs": [ + "" + ] }, { - "name": "buck2_shell_utils", "dir": "/[prelude]//erlang/shell/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "buck2_shell_utils", + "src_dirs": [ + "" + ] + }, + { + "dir": "app_a", + "extra_src_dirs": [], + "include_dirs": [ + "include" + ], + "macros": { + "COMMON_TEST": "true", + "TEST": "true" + }, + "name": "diagnostics_app_a", + "src_dirs": [ + "src" + ] }, { - "name": "test_binary", "dir": "/[prelude]//erlang/common_test/test_binary/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_binary", + "src_dirs": [ + "" + ] }, { - "name": "test_cli_lib", "dir": "/[prelude]//erlang/common_test/test_cli_lib/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_cli_lib", + "src_dirs": [ + "" + ] } ], "deps": [] @@ -1093,6 +1092,12 @@ mod tests { content.replace(prelude_cell, "/[prelude]/") } + fn sort_json(content: &str) -> String { + let mut json: serde_json::Value = serde_json::from_str(content).unwrap(); + json.sort_all_objects(); + serde_json::to_string_pretty(&json).unwrap() + } + #[test] #[ignore] fn build_info_json_buck_bxl_generated() { diff --git a/crates/elp/src/resources/test/xref/unavailable_type.stdout b/crates/elp/src/resources/test/xref/unavailable_type.stdout index b6338343c0..688be17de3 100644 --- a/crates/elp/src/resources/test/xref/unavailable_type.stdout +++ b/crates/elp/src/resources/test/xref/unavailable_type.stdout @@ -1,5 +1,5 @@ Reporting all diagnostics codes module specified: unavailable_type Diagnostics reported: -app_a/src/unavailable_type.erl:10:43-10:58::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test/test_projects/xref:app_a'). -app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'fbcode//whatsapp/elp/test/test_projects/xref:app_a'). +app_a/src/unavailable_type.erl:10:43-10:58::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'root//xref:app_a'). +app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'root//xref:app_a'). diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index bb3ec38b5f..96fc1eac89 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -58,6 +58,7 @@ lazy_static! { } const ERL_EXT: &str = "erl"; +const BUCK_ISOLATION_DIR: &str = "lsp"; #[derive( Debug, @@ -108,7 +109,7 @@ impl BuckConfig { cmd.env_remove("RUST_BACKTRACE") .env_remove("RUST_LIB_BACKTRACE"); cmd.arg("--isolation-dir"); - cmd.arg("lsp"); + cmd.arg(BUCK_ISOLATION_DIR); cmd.current_dir(self.buck_root()); CommandProxy::new(guard, cmd) } @@ -1362,36 +1363,56 @@ fn include_path_from_file(path: &AbsPath) -> AbsPathBuf { } } +fn check_buck_output_success(mut command: CommandProxy<'_>) -> Result { + let output = command.output()?; + if output.status.success() { + return String::from_utf8(output.stdout) + .map_err(|e| anyhow::anyhow!("Invalid UTF-8 in stdout for `{command}`: {e}")); + } + let reason = match output.status.code() { + Some(code) => format!("Exited with status code: {code}"), + None => "Process terminated by signal".to_string(), + }; + let details = String::from_utf8(output.stderr).unwrap_or_default(); + bail!("Command `{command}` failed. Reason: {reason}. Details: {details}"); +} + /// This is used in tests pub fn get_prelude_cell(buck_config: &BuckConfig) -> Result { - let output = buck_config - .buck_command() + let mut command = buck_config.buck_command(); + command .arg("audit") .arg("cell") .arg("prelude") - .output()?; - if !output.status.success() { - let reason = match output.status.code() { - Some(code) => format!("Exited with status code: {code}"), - None => "Process terminated by signal".to_string(), - }; - let details = match String::from_utf8(output.stderr) { - Ok(err) => err, - Err(_) => "".to_string(), - }; - bail!("Error evaluating Buck2 query Reason: {reason}. Details: {details}",); - } - let raw_output = String::from_utf8(output.stdout)?; + .arg("--json"); + let raw_output = check_buck_output_success(command)?; - lazy_static! { - static ref RE: Regex = Regex::new(r"^prelude: ([^\s]+)").unwrap(); + let json: serde_json::Value = serde_json::from_str(&raw_output)?; + let prelude_path = json + .get("prelude") + .and_then(|v| v.as_str()) + .ok_or_else(|| anyhow::anyhow!("Could not find prelude path in Buck2 output"))? + .to_string(); + + if Path::new(&prelude_path).exists() { + Ok(prelude_path) + } else { + get_prelude_cell_bundled(buck_config) } - let string = RE - .captures_iter(&raw_output) - .next() - .map(|c| c[1].to_string()) - .unwrap(); - Ok(string) +} + +fn get_prelude_cell_bundled(buck_config: &BuckConfig) -> Result { + let mut command = buck_config.buck_command(); + command.arg("root"); + let root = check_buck_output_success(command)?; + let root = root.trim(); + let bundled_prelude_path = Path::new(&root) + .join("buck-out") + .join(BUCK_ISOLATION_DIR) + .join("external_cells") + .join("bundled") + .join("prelude"); + Ok(bundled_prelude_path.to_string_lossy().to_string()) } #[cfg(test)] @@ -1655,7 +1676,7 @@ mod tests { .arg("--") .args(generated_args) .arg("--included_targets") - .arg("fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/...") + .arg("root//buck_tests_2/auto_gen/...") .output() .unwrap(); if !output.status.success() { @@ -1679,7 +1700,7 @@ mod tests { false, expect![[r#" { - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, @@ -1697,7 +1718,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, @@ -1842,13 +1863,13 @@ mod tests { fn build_info_buck_bxl_generated_query() { if BUCK_TESTS_ENABLED { // Note that there is now a value for `srcs` in the - // "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" + // "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target check_buck_bxl_query( true, expect![[r#" { - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, @@ -1866,7 +1887,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, diff --git a/test/test_projects/.buckconfig b/test/test_projects/.buckconfig new file mode 100644 index 0000000000..f14e564a7d --- /dev/null +++ b/test/test_projects/.buckconfig @@ -0,0 +1,24 @@ +[cells] + root = . + prelude = prelude + toolchains = toolchains + none = none + +[cell_aliases] + config = prelude + ovr_config = prelude + fbcode = none + fbsource = none + fbcode_macros = none + buck = none + +[external_cells] + prelude = bundled + +[parser] + target_platform_detector_spec = target:root//...->prelude//platforms:default \ + target:prelude//...->prelude//platforms:default \ + target:toolchains//...->prelude//platforms:default + +[build] + execution_platforms = prelude//platforms:default diff --git a/test/test_projects/.buckroot b/test/test_projects/.buckroot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/test_projects/buck_bad_config/.elp.toml b/test/test_projects/buck_bad_config/.elp.toml index a4d80e289f..963072284c 100644 --- a/test/test_projects/buck_bad_config/.elp.toml +++ b/test/test_projects/buck_bad_config/.elp.toml @@ -1,8 +1,8 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_bad_config/..." ] -source_root = "whatsapp/elp/test/test_projects/buck_bad_config" +included_targets = [ "root//buck_bad_config/..." ] +source_root = "buck_bad_config" [eqwalizer] enable_all = false diff --git a/test/test_projects/buck_tests/.elp.toml b/test/test_projects/buck_tests/.elp.toml index 0fbba8f0d6..b38d6f043f 100644 --- a/test/test_projects/buck_tests/.elp.toml +++ b/test/test_projects/buck_tests/.elp.toml @@ -1,9 +1,9 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests/..." ] -excluded_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests:test_elp_ignored" ] -source_root = "whatsapp/elp/test/test_projects/buck_tests" +included_targets = [ "root//buck_tests/..." ] +excluded_targets = [ "buck_tests:test_elp_ignored" ] +source_root = "buck_tests" [eqwalizer] enable_all = false diff --git a/test/test_projects/buck_tests/test_elp/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ index 528f2dbc2a..7e5bd95db5 100644 --- a/test/test_projects/buck_tests/test_elp/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ @@ -7,11 +7,11 @@ erlang_application( ]), app_src = "src/test_elp.app.src", applications = [ - "//whatsapp/elp/test/test_projects/buck_tests/test_elp_direct_dep:test_elp_direct_dep", - "//whatsapp/elp/test/test_projects/buck_tests:test_elp_no_private_headers", - "//whatsapp/elp/test/test_projects/buck_tests:test_elp_no_public_headers", - "//whatsapp/elp/test/test_projects/buck_tests:test_elp_flat_outside_target", - "//whatsapp/elp/test/test_projects/buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", + "//buck_tests/test_elp_direct_dep:test_elp_direct_dep", + "//buck_tests:test_elp_no_private_headers", + "//buck_tests:test_elp_no_public_headers", + "//buck_tests:test_elp_flat_outside_target", + "//buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ index dd46a58842..a752b99087 100644 --- a/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ @@ -5,10 +5,10 @@ erlang_application( "src/*.hrl", ]), applications = [ - "//whatsapp/elp/test/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", + "//buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", ], extra_includes = [ - "//whatsapp/elp/test/test_projects/buck_tests/test_elp:test_elp", + "//buck_tests/test_elp:test_elp", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test/test_projects/buck_tests_2/.elp.toml b/test/test_projects/buck_tests_2/.elp.toml index 102f44c3f5..e71eb6a819 100644 --- a/test/test_projects/buck_tests_2/.elp.toml +++ b/test/test_projects/buck_tests_2/.elp.toml @@ -2,11 +2,11 @@ enabled = true build_deps = false included_targets = [ - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2/util/app_a/...", - "fbcode//whatsapp/elp/test/test_projects/buck_tests_2:check_include" + "root//buck_tests_2/util/app_a/...", + "root//buck_tests_2:check_include" ] -excluded_targets = [ "fbcode//whatsapp/elp/test/test_projects/buck_tests_2:test_elp_ignored" ] -source_root = "whatsapp/elp/test/test_projects/buck_tests_2" +excluded_targets = [ "root//buck_tests_2:test_elp_ignored" ] +source_root = "buck_tests_2" [eqwalizer] enable_all = false diff --git a/test/test_projects/diagnostics/.elp.toml b/test/test_projects/diagnostics/.elp.toml index 44c9516105..504ef547af 100644 --- a/test/test_projects/diagnostics/.elp.toml +++ b/test/test_projects/diagnostics/.elp.toml @@ -1,8 +1,8 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/diagnostics/..." ] -source_root = "whatsapp/elp/test/test_projects/diagnostics" +included_targets = [ "root//diagnostics/..." ] +source_root = "diagnostics" [eqwalizer] enable_all = false diff --git a/test/test_projects/end_to_end/.elp.toml b/test/test_projects/end_to_end/.elp.toml index 4d685a40c7..acbcd6146f 100644 --- a/test/test_projects/end_to_end/.elp.toml +++ b/test/test_projects/end_to_end/.elp.toml @@ -1,7 +1,7 @@ [buck] enabled = true build_deps = false -included_targets = ["fbcode//whatsapp/elp/test/test_projects/end_to_end/..."] +included_targets = ["root//end_to_end/..."] [eqwalizer] enable_all = false diff --git a/test/test_projects/eqwalizer_callers/.elp.toml b/test/test_projects/eqwalizer_callers/.elp.toml index b0c2b55a5b..68476bcf2e 100644 --- a/test/test_projects/eqwalizer_callers/.elp.toml +++ b/test/test_projects/eqwalizer_callers/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/eqwalizer_callers/..." ] -source_root = "whatsapp/elp/test/test_projects/eqwalizer_callers" +included_targets = [ "root//eqwalizer_callers/..." ] +source_root = "eqwalizer_callers" diff --git a/test/test_projects/eqwalizer_tests/.elp.toml b/test/test_projects/eqwalizer_tests/.elp.toml index 9586666619..5dac3aa6a6 100644 --- a/test/test_projects/eqwalizer_tests/.elp.toml +++ b/test/test_projects/eqwalizer_tests/.elp.toml @@ -1,8 +1,8 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/eqwalizer_tests/..." ] -source_root = "whatsapp/elp/test/test_projects/eqwalizer_tests" +included_targets = [ "root//eqwalizer_tests/..." ] +source_root = "eqwalizer_tests" [eqwalizer] enable_all = true diff --git a/test/test_projects/hierarchical_config/.elp.toml b/test/test_projects/hierarchical_config/.elp.toml index a650134c9f..e4d04eb6e0 100644 --- a/test/test_projects/hierarchical_config/.elp.toml +++ b/test/test_projects/hierarchical_config/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/hierarchical_config/..." ] -source_root = "whatsapp/elp/test/test_projects/hierarchical_config" +included_targets = [ "root//hierarchical_config/..." ] +source_root = "hierarchical_config" diff --git a/test/test_projects/in_place_tests/.elp.toml b/test/test_projects/in_place_tests/.elp.toml index 64140d2852..70edcc3f03 100644 --- a/test/test_projects/in_place_tests/.elp.toml +++ b/test/test_projects/in_place_tests/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/in_place_tests/..." ] -source_root = "whatsapp/elp/test/test_projects/in_place_tests" +included_targets = [ "root//in_place_tests/..." ] +source_root = "in_place_tests" diff --git a/test/test_projects/include_lib_dependency_test/.elp.toml b/test/test_projects/include_lib_dependency_test/.elp.toml index 02b5b7d244..9d1c859339 100644 --- a/test/test_projects/include_lib_dependency_test/.elp.toml +++ b/test/test_projects/include_lib_dependency_test/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/include_lib_dependency_test/..." ] -source_root = "whatsapp/elp/test/test_projects/include_lib_dependency_test" +included_targets = [ "root//include_lib_dependency_test/..." ] +source_root = "include_lib_dependency_test" diff --git a/test/test_projects/linter/.elp.toml b/test/test_projects/linter/.elp.toml index bc8b86088c..4820625b2d 100644 --- a/test/test_projects/linter/.elp.toml +++ b/test/test_projects/linter/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] -source_root = "whatsapp/elp/test/test_projects/linter" +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test/test_projects/linter_bad_config/.elp.toml b/test/test_projects/linter_bad_config/.elp.toml index bc8b86088c..4820625b2d 100644 --- a/test/test_projects/linter_bad_config/.elp.toml +++ b/test/test_projects/linter_bad_config/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] -source_root = "whatsapp/elp/test/test_projects/linter" +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test/test_projects/linter_bad_config/linter/.elp.toml b/test/test_projects/linter_bad_config/linter/.elp.toml index bc8b86088c..4820625b2d 100644 --- a/test/test_projects/linter_bad_config/linter/.elp.toml +++ b/test/test_projects/linter_bad_config/linter/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/linter/..." ] -source_root = "whatsapp/elp/test/test_projects/linter" +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test/test_projects/parse_error/.elp.toml b/test/test_projects/parse_error/.elp.toml index a38e24f657..1a06a42f0d 100644 --- a/test/test_projects/parse_error/.elp.toml +++ b/test/test_projects/parse_error/.elp.toml @@ -1,8 +1,8 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/parse_error/..." ] -source_root = "whatsapp/elp/test/test_projects/parse_error" +included_targets = [ "root//parse_error/..." ] +source_root = "parse_error" [eqwalizer] enable_all = false diff --git a/test/test_projects/standard/.elp.toml b/test/test_projects/standard/.elp.toml index 29b2e051a4..f5e2ac1943 100644 --- a/test/test_projects/standard/.elp.toml +++ b/test/test_projects/standard/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/standard/..." ] -source_root = "whatsapp/elp/test/test_projects/standard" +included_targets = [ "root//standard/..." ] +source_root = "standard" diff --git a/test/test_projects/xref/.elp.toml b/test/test_projects/xref/.elp.toml index 0b684fba14..87c2f17334 100644 --- a/test/test_projects/xref/.elp.toml +++ b/test/test_projects/xref/.elp.toml @@ -1,5 +1,5 @@ [buck] enabled = true build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/xref/..." ] -source_root = "whatsapp/elp/test/test_projects/xref" +included_targets = [ "root//xref/..." ] +source_root = "xref" From 8b4c5335271fab6ecbe9642d4db7c392092783bb Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 18 Dec 2025 23:41:12 -0800 Subject: [PATCH 38/46] Convert EDoc syntax to plain comments in codegen_test_SUITE Summary: Convert old EDoc style syntax (%%% doc, end) in test suite to plain comments. EDoc documentation is unnecessary in test suites. Differential Revision: D89526339 fbshipit-source-id: 0111f2ded79a7e802d29b95d6b79889e0a55af32 --- .../codegen_test/app_a/test/codegen_test_SUITE.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl index 797c3c58f3..3c0135dcee 100644 --- a/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl +++ b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl @@ -1,8 +1,4 @@ -%%%------------------------------------------------------------------- -%%% @doc %%% Test suite for code generation functionality -%%% @end -%%%------------------------------------------------------------------- -module(codegen_test_SUITE). -include_lib("stdlib/include/assert.hrl"). From 8cb6ac76207aa6af0def85c2cb17a4cb1226ec63 Mon Sep 17 00:00:00 2001 From: Facebook Community Bot Date: Fri, 19 Dec 2025 02:51:11 -0800 Subject: [PATCH 39/46] Re-sync with internal repository (#144) The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging. fbshipit-source-id: 39bc64c1a96888b1034f7807ced9b7b2365df04e --- test/test_projects/buck_bad_config/BUCK | 14 ++++ test/test_projects/buck_tests_2/BUCK | 41 ++++++++++ .../buck_tests_2/auto_gen/auto_gen_a/BUCK | 25 ++++++ .../test_projects/buck_tests_2/generated/BUCK | 12 +++ .../buck_tests_2/util/app_a/BUCK | 16 ++++ test/test_projects/codegen_test/BUCK | 78 +++++++++++++++++++ test/test_projects/diagnostics/BUCK | 20 +++++ .../end_to_end/assist_examples/BUCK | 14 ++++ .../test_projects/end_to_end/definitions/BUCK | 14 ++++ test/test_projects/end_to_end/docs/BUCK | 16 ++++ test/test_projects/end_to_end/hover/BUCK | 14 ++++ .../end_to_end/single_errors/BUCK | 14 ++++ test/test_projects/eqwalizer_callers/BUCK | 19 +++++ test/test_projects/eqwalizer_tests/BUCK | 77 ++++++++++++++++++ test/test_projects/hierarchical_config/BUCK | 23 ++++++ test/test_projects/in_place_tests/BUCK | 19 +++++ .../include_lib_dependency_test/BUCK | 43 ++++++++++ test/test_projects/linter/BUCK | 41 ++++++++++ test/test_projects/linter_bad_config/BUCK | 12 +++ .../linter_bad_config/linter/BUCK | 41 ++++++++++ test/test_projects/parse_error/BUCK | 9 +++ test/test_projects/standard/BUCK | 49 ++++++++++++ test/test_projects/toolchains/BUCK | 5 ++ test/test_projects/xref/BUCK | 25 ++++++ 24 files changed, 641 insertions(+) create mode 100644 test/test_projects/buck_bad_config/BUCK create mode 100644 test/test_projects/buck_tests_2/BUCK create mode 100644 test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK create mode 100644 test/test_projects/buck_tests_2/generated/BUCK create mode 100644 test/test_projects/buck_tests_2/util/app_a/BUCK create mode 100644 test/test_projects/codegen_test/BUCK create mode 100644 test/test_projects/diagnostics/BUCK create mode 100644 test/test_projects/end_to_end/assist_examples/BUCK create mode 100644 test/test_projects/end_to_end/definitions/BUCK create mode 100644 test/test_projects/end_to_end/docs/BUCK create mode 100644 test/test_projects/end_to_end/hover/BUCK create mode 100644 test/test_projects/end_to_end/single_errors/BUCK create mode 100644 test/test_projects/eqwalizer_callers/BUCK create mode 100644 test/test_projects/eqwalizer_tests/BUCK create mode 100644 test/test_projects/hierarchical_config/BUCK create mode 100644 test/test_projects/in_place_tests/BUCK create mode 100644 test/test_projects/include_lib_dependency_test/BUCK create mode 100644 test/test_projects/linter/BUCK create mode 100644 test/test_projects/linter_bad_config/BUCK create mode 100644 test/test_projects/linter_bad_config/linter/BUCK create mode 100644 test/test_projects/parse_error/BUCK create mode 100644 test/test_projects/standard/BUCK create mode 100644 test/test_projects/toolchains/BUCK create mode 100644 test/test_projects/xref/BUCK diff --git a/test/test_projects/buck_bad_config/BUCK b/test/test_projects/buck_bad_config/BUCK new file mode 100644 index 0000000000..498c769dac --- /dev/null +++ b/test/test_projects/buck_bad_config/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "bad_app", + srcs = glob(["src/*.erl"]), + applications = [ + "root//buck_bad_config/non_existent:missing", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test/test_projects/buck_tests_2/BUCK b/test/test_projects/buck_tests_2/BUCK new file mode 100644 index 0000000000..b01fdb868c --- /dev/null +++ b/test/test_projects/buck_tests_2/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check_include", + srcs = [ + "check_include/src/top_includer.erl", + ], + applications = [ + "common_test", + "stdlib", + "root//buck_tests_2:check_include_separate_1", + "root//buck_tests_2:check_include_separate_2", + ], + includes = [], + labels = [], + resources = [], +) + +erlang_application( + name = "check_include_separate_1", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = [ + "check_include_separate_1/include/top_includer.hrl", + ], + resources = [], +) + +erlang_application( + name = "check_include_separate_2", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = glob(["check_include_separate_2/include/*.hrl"]), + resources = [], +) diff --git a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK new file mode 100644 index 0000000000..f021317020 --- /dev/null +++ b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "auto_gen_a", + srcs = glob([ + "src/*.erl", + "src/*.hrl", + ]), + includes = glob(["include/*.hrl"]), + visibility = ["//buck_tests_2/..."], +) + +erlang_application( + name = "generated_srcs", + srcs = [ + ":generated.erl", + ], + labels = ["generated"], + visibility = ["//buck_tests_2/..."], +) + +export_file( + name = "generated.erl", + src = "out/pretend_generated.erl", +) diff --git a/test/test_projects/buck_tests_2/generated/BUCK b/test/test_projects/buck_tests_2/generated/BUCK new file mode 100644 index 0000000000..8eec40830b --- /dev/null +++ b/test/test_projects/buck_tests_2/generated/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "generated_headers", + includes = [ + "out/generated_header.hrl", + ], + labels = ["generated"], + visibility = [ + "PUBLIC", + ], +) diff --git a/test/test_projects/buck_tests_2/util/app_a/BUCK b/test/test_projects/buck_tests_2/util/app_a/BUCK new file mode 100644 index 0000000000..4231c1c6e8 --- /dev/null +++ b/test/test_projects/buck_tests_2/util/app_a/BUCK @@ -0,0 +1,16 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a_target", + srcs = glob(["src/*.erl"]), + app_name = "app_a", + applications = [ + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a", + "root//buck_tests_2/generated:generated_headers", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test/test_projects/codegen_test/BUCK b/test/test_projects/codegen_test/BUCK new file mode 100644 index 0000000000..3530fca2ff --- /dev/null +++ b/test/test_projects/codegen_test/BUCK @@ -0,0 +1,78 @@ +load("@fbcode_macros//build_defs:native_rules.bzl", "buck_genrule") + +oncall("vscode_erlang") + +# Code generation rule - generates Erlang modules from template files +buck_genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + bash = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", + cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.erl %OUT%\\example_service_types.erl", +) + +buck_genrule( + name = "example_service_client_erl", + srcs = [ + "templates/example_service_client.erl", + ], + outs = { + "example_service_client.erl": ["example_service_client.erl"], + }, + bash = "cp $SRCDIR/templates/example_service_client.erl $OUT/example_service_client.erl", + cmd_exe = "copy %SRCDIR%\\templates\\example_service_client.erl %OUT%\\example_service_client.erl", +) + +buck_genrule( + name = "example_service_types_hrl", + srcs = [ + "templates/example_service_types.hrl", + ], + outs = { + "example_service_types.hrl": ["example_service_types.hrl"], + }, + bash = "cp $SRCDIR/templates/example_service_types.hrl $OUT/example_service_types.hrl", + cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.hrl %OUT%\\example_service_types.hrl", +) + +# Erlang library containing only the generated code +erlang_app( + name = "example_service_generated", + srcs = [ + # Include generated Erlang modules from genrule output + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + # Include generated header files from genrule output + ":example_service_types_hrl[example_service_types.hrl]", + ], +) + +# Erlang application that uses the generated code (non-generated files only) +erlang_application( + name = "codegen_test_app", + srcs = glob(["app_a/src/*.erl"]), + app_name = "codegen_test", + app_src = "app_a/src/codegen_test.app.src", + applications = [ + "kernel", + "stdlib", + ":example_service_generated", + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +# Test to verify the generated code works +erlang_tests( + suites = [ + "app_a/test/codegen_test_SUITE.erl", + ], + deps = [":codegen_test_app"], +) diff --git a/test/test_projects/diagnostics/BUCK b/test/test_projects/diagnostics/BUCK new file mode 100644 index 0000000000..19c15e5acf --- /dev/null +++ b/test/test_projects/diagnostics/BUCK @@ -0,0 +1,20 @@ +oncall("vscode_erlang") + +erlang_application( + name = "diagnostics_app_a_target", + srcs = glob(["app_a/src/*.erl"]), + app_name = "diagnostics_app_a", + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":diagnostics_app_a_target"], +) diff --git a/test/test_projects/end_to_end/assist_examples/BUCK b/test/test_projects/end_to_end/assist_examples/BUCK new file mode 100644 index 0000000000..60d39de125 --- /dev/null +++ b/test/test_projects/end_to_end/assist_examples/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "assist_examples", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/assist_examples.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test/test_projects/end_to_end/definitions/BUCK b/test/test_projects/end_to_end/definitions/BUCK new file mode 100644 index 0000000000..c9e6b204f4 --- /dev/null +++ b/test/test_projects/end_to_end/definitions/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "definitions", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/definitions.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test/test_projects/end_to_end/docs/BUCK b/test/test_projects/end_to_end/docs/BUCK new file mode 100644 index 0000000000..7429863b6e --- /dev/null +++ b/test/test_projects/end_to_end/docs/BUCK @@ -0,0 +1,16 @@ +load("@waserver//buck2/whatsapp:erlang.bzl", "erlang_application") + +oncall("vscode_erlang") + +erlang_application( + name = "docs", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/docs.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test/test_projects/end_to_end/hover/BUCK b/test/test_projects/end_to_end/hover/BUCK new file mode 100644 index 0000000000..7732dc473e --- /dev/null +++ b/test/test_projects/end_to_end/hover/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "hover", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/hover.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test/test_projects/end_to_end/single_errors/BUCK b/test/test_projects/end_to_end/single_errors/BUCK new file mode 100644 index 0000000000..a8f6e478b0 --- /dev/null +++ b/test/test_projects/end_to_end/single_errors/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "single_errors", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/single_errors.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test/test_projects/eqwalizer_callers/BUCK b/test/test_projects/eqwalizer_callers/BUCK new file mode 100644 index 0000000000..32012a0dc0 --- /dev/null +++ b/test/test_projects/eqwalizer_callers/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) diff --git a/test/test_projects/eqwalizer_tests/BUCK b/test/test_projects/eqwalizer_tests/BUCK new file mode 100644 index 0000000000..0817873cb0 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/BUCK @@ -0,0 +1,77 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check", + srcs = glob(["check/src/**/*.erl"]), + applications = [":eqwalizer"], + includes = glob([ + "check/include/*.hrl", + "eqwalizer/include/*.hrl", + ]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "check_gradual", + srcs = glob(["check_gradual/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "check/test/check_SUITE.erl", + ], + deps = [":check"], +) + +erlang_application( + name = "debug", + srcs = glob(["debug/src/*.erl"]), + applications = [":eqwalizer"], + includes = glob(["debug/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elm_core", + srcs = glob(["elm_core/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwater", + srcs = glob(["eqwater/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "fault_tolerance", + srcs = glob(["fault_tolerance/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "options", + srcs = glob(["options/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + includes = glob(["eqwalizer/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/hierarchical_config/BUCK b/test/test_projects/hierarchical_config/BUCK new file mode 100644 index 0000000000..bc78a42d57 --- /dev/null +++ b/test/test_projects/hierarchical_config/BUCK @@ -0,0 +1,23 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + applications = [ + ], + includes = glob(["app_b/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/in_place_tests/BUCK b/test/test_projects/in_place_tests/BUCK new file mode 100644 index 0000000000..f4f354e7ee --- /dev/null +++ b/test/test_projects/in_place_tests/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "in_place_tests_app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":in_place_tests_app_a"], +) diff --git a/test/test_projects/include_lib_dependency_test/BUCK b/test/test_projects/include_lib_dependency_test/BUCK new file mode 100644 index 0000000000..54b81c47b5 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/BUCK @@ -0,0 +1,43 @@ +oncall("vscode_erlang") + +# Main application that will try to include_lib from an app it doesn't depend on +erlang_application( + name = "main_app", + srcs = glob(["main_app/src/*.erl"]), + app_src = "main_app/src/main_app.app.src", + applications = [ + # Note: deliberately NOT including :external_app as a dependency + "root//include_lib_dependency_test:normal_dep", + ], + extra_includes = ["root//include_lib_dependency_test:extra_app"], + labels = ["user_application"], + version = "1.0.0", +) + +# External application that main_app will try to include_lib from +erlang_application( + name = "external_app", + srcs = glob(["external_app/src/*.erl"]), + app_src = "external_app/src/external_app.app.src", + includes = glob(["external_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "extra_app", + srcs = glob(["extra_app/src/*.erl"]), + app_src = "extra_app/src/extra_app.app.src", + includes = glob(["extra_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "normal_dep", + srcs = glob(["normal_dep/src/*.erl"]), + app_src = "normal_dep/src/normal_dep.app.src", + includes = glob(["normal_dep/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/linter/BUCK b/test/test_projects/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test/test_projects/linter_bad_config/BUCK b/test/test_projects/linter_bad_config/BUCK new file mode 100644 index 0000000000..a0817a6ad6 --- /dev/null +++ b/test/test_projects/linter_bad_config/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/linter_bad_config/linter/BUCK b/test/test_projects/linter_bad_config/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test/test_projects/parse_error/BUCK b/test/test_projects/parse_error/BUCK new file mode 100644 index 0000000000..ed65c267d0 --- /dev/null +++ b/test/test_projects/parse_error/BUCK @@ -0,0 +1,9 @@ +oncall("vscode_erlang") + +erlang_application( + name = "elp_test_parse_a", + srcs = glob(["parse_error_a/src/*.erl"]), + app_src = "parse_error_a/src/parse_error_a.app.src", + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/standard/BUCK b/test/test_projects/standard/BUCK new file mode 100644 index 0000000000..3f1e9ee947 --- /dev/null +++ b/test/test_projects/standard/BUCK @@ -0,0 +1,49 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_standard_app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + app_src = "eqwalizer/src/eqwalizer.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test/test_projects/toolchains/BUCK b/test/test_projects/toolchains/BUCK new file mode 100644 index 0000000000..770ac61f67 --- /dev/null +++ b/test/test_projects/toolchains/BUCK @@ -0,0 +1,5 @@ +load("@prelude//toolchains:demo.bzl", "system_demo_toolchains") + +oncall("vscode_erlang") + +system_demo_toolchains() diff --git a/test/test_projects/xref/BUCK b/test/test_projects/xref/BUCK new file mode 100644 index 0000000000..bf44968f02 --- /dev/null +++ b/test/test_projects/xref/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + applications = [ + ":app_b", + ], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_c", + srcs = glob(["app_c/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) From 8e82f1cee4f54dbd3f7fc60a6a4185eb914775a0 Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Fri, 19 Dec 2025 04:09:00 -0800 Subject: [PATCH 40/46] Fix codegen_test test project for new buck root Summary: As title Reviewed By: TheGeorge Differential Revision: D89543670 fbshipit-source-id: 8d4a417a58950ad9f9d8877839561f407d0e9cc0 --- test/test_projects/codegen_test/.elp.toml | 7 +++---- test/test_projects/codegen_test/BUCK | 17 ++++++----------- test/test_projects/codegen_test/README.md | 11 +++++------ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/test/test_projects/codegen_test/.elp.toml b/test/test_projects/codegen_test/.elp.toml index 52ec390ccb..f21fe1bc73 100644 --- a/test/test_projects/codegen_test/.elp.toml +++ b/test/test_projects/codegen_test/.elp.toml @@ -1,8 +1,7 @@ [buck] enabled = true -build_deps = true -included_targets = [ "fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_app" ] -source_root = "whatsapp/elp/test/test_projects/codegen_test" +build_deps = false +included_targets = ["root//codegen_test/..."] [eqwalizer] -enable_all = true +enable_all = false diff --git a/test/test_projects/codegen_test/BUCK b/test/test_projects/codegen_test/BUCK index 3530fca2ff..1df5f86eba 100644 --- a/test/test_projects/codegen_test/BUCK +++ b/test/test_projects/codegen_test/BUCK @@ -1,9 +1,7 @@ -load("@fbcode_macros//build_defs:native_rules.bzl", "buck_genrule") - oncall("vscode_erlang") # Code generation rule - generates Erlang modules from template files -buck_genrule( +genrule( name = "example_service_types_erl", srcs = [ "templates/example_service_types.erl", @@ -11,11 +9,10 @@ buck_genrule( outs = { "example_service_types.erl": ["example_service_types.erl"], }, - bash = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", - cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.erl %OUT%\\example_service_types.erl", + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", ) -buck_genrule( +genrule( name = "example_service_client_erl", srcs = [ "templates/example_service_client.erl", @@ -23,11 +20,10 @@ buck_genrule( outs = { "example_service_client.erl": ["example_service_client.erl"], }, - bash = "cp $SRCDIR/templates/example_service_client.erl $OUT/example_service_client.erl", - cmd_exe = "copy %SRCDIR%\\templates\\example_service_client.erl %OUT%\\example_service_client.erl", + cmd = "cp $SRCDIR/templates/example_service_client.erl $OUT/example_service_client.erl", ) -buck_genrule( +genrule( name = "example_service_types_hrl", srcs = [ "templates/example_service_types.hrl", @@ -35,8 +31,7 @@ buck_genrule( outs = { "example_service_types.hrl": ["example_service_types.hrl"], }, - bash = "cp $SRCDIR/templates/example_service_types.hrl $OUT/example_service_types.hrl", - cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.hrl %OUT%\\example_service_types.hrl", + cmd = "cp $SRCDIR/templates/example_service_types.hrl $OUT/example_service_types.hrl", ) # Erlang library containing only the generated code diff --git a/test/test_projects/codegen_test/README.md b/test/test_projects/codegen_test/README.md index 061fb1914c..1f73475d9d 100644 --- a/test/test_projects/codegen_test/README.md +++ b/test/test_projects/codegen_test/README.md @@ -1,7 +1,7 @@ # Code Generation Test Project This test project demonstrates Buck2-based code generation for Erlang -applications using `buck_genrule`. +applications using `genrule`. ## Project Structure @@ -37,10 +37,10 @@ copied to the output directory during the build. ### 2. Code Generation The code is generated automatically during the build process using a -`buck_genrule`: +`genrule`: ```python -buck_genrule( +genrule( name = "example_service_types_erl", srcs = [ "templates/example_service_types.erl", @@ -48,8 +48,7 @@ buck_genrule( outs = { "example_service_types.erl": ["example_service_types.erl"], }, - bash = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", - cmd_exe = "copy %SRCDIR%\\templates\\example_service_types.erl %OUT%\\example_service_types.erl", + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", ) ``` @@ -58,7 +57,7 @@ The genrule: - Takes the template file as input (`srcs`) - Defines named outputs using `outs` parameter (creates subtargets for each file) -- Uses `cp` (bash) or `copy` (cmd_exe) to copy template files to `$OUT` +- Uses `cp` command to copy template files to `$OUT` - Outputs files to `$OUT` directory in `buck-out/` ### 3. Build Integration From fc09ff94fdda5a6c9fbb3d49764dcd0c7767e76b Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 19 Dec 2025 07:43:01 -0800 Subject: [PATCH 41/46] Enable buck2 tests on GitHub Summary: The test projects are now standalone, so the Buck2 tests can be enabled in CI. Two major fixes to the tests: * Use the project directory to calculate the project root (there's no `.buckconfig` for the ELP crate as a whole in OSS) * Fix expectations for the Buck2 errors (no URL in OSS) Reviewed By: alanz Differential Revision: D89547047 fbshipit-source-id: 76fa8bcc1fadabbeb9ea59245d36d2aacd98a023 --- .github/workflows/ci.yml | 2 +- crates/elp/src/bin/main.rs | 13 +++++++++---- .../buck_bad_config/bxl_error_message_oss.stdout | 1 + crates/project_model/src/buck.rs | 4 +--- 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fcdf48405..e9980283f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,7 +137,7 @@ jobs: - name: Test elp # Do not run the tests in case of cross-compilation or on Windows if: matrix.platform-arch != 'macos-latest-arm' && matrix.os != 'windows' - run: 'cargo test --no-default-features --workspace --target ${{ matrix.target }}' + run: 'cargo test --workspace --target ${{ matrix.target }}' - name: Build elp (No Windows) if: matrix.os != 'windows' run: 'cargo build --release --target ${{ matrix.target }} --config target.aarch64-unknown-linux-gnu.linker=\"aarch64-linux-gnu-gcc\"' diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index df99eff987..721632fc01 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -970,7 +970,9 @@ mod tests { assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); let mut buck_config = BuckConfig::default(); - buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8( + current_dir().unwrap().join(path_str.clone()), + )); let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); let prelude_cell = prelude_cell.strip_prefix("/").unwrap(); let content = content.replace(prelude_cell, "/[prelude]/"); @@ -1111,7 +1113,7 @@ mod tests { "--to", tmp_file.clone(), "--project", - path_str + path_str.clone() ]; let (stdout, stderr, code) = elp(args); assert_eq!( @@ -1126,7 +1128,9 @@ mod tests { assert!(tmp_file.clone().exists()); let content = fs::read_to_string(tmp_file).unwrap(); let mut buck_config = BuckConfig::default(); - buck_config.buck_root = Some(AbsPathBuf::assert_utf8(current_dir().unwrap())); + buck_config.buck_root = Some(AbsPathBuf::assert_utf8( + current_dir().unwrap().join(path_str.clone()), + )); let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); let prelude_cell = prelude_cell.strip_prefix("/").unwrap(); let content = content.replace(prelude_cell, "/[prelude]/"); @@ -1948,7 +1952,8 @@ mod tests { simple_snapshot_expect_stderror( args_vec!["lint",], "buck_bad_config", - expect_file!("../resources/test/buck_bad_config/bxl_error_message.stdout"), + // @fb-only: expect_file!("../resources/test/buck_bad_config/bxl_error_message.stdout"), + expect_file!("../resources/test/buck_bad_config/bxl_error_message_oss.stdout"), // @oss-only true, None, true, diff --git a/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout new file mode 100644 index 0000000000..f5225605a5 --- /dev/null +++ b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout @@ -0,0 +1 @@ +Project Initialisation Failed: invalid or missing buck 2 configuration diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 96fc1eac89..32c6dcccb8 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -1643,9 +1643,7 @@ mod tests { assert_eq!(expected, actual) } - // TODO: enable when buck is properly set up on github project - // @fb-only: const BUCK_TESTS_ENABLED: bool = true; - const BUCK_TESTS_ENABLED: bool = false; // @oss-only + const BUCK_TESTS_ENABLED: bool = true; #[track_caller] fn check_buck_bxl_query(build_generated: bool, expect: Expect) { From 220acfd4f7cfa318c3207db8e5bdefa01eaffccc Mon Sep 17 00:00:00 2001 From: Alan Zimmerman Date: Fri, 19 Dec 2025 08:49:54 -0800 Subject: [PATCH 42/46] Back out "Allow project discovery for OTP" Summary: It breaks navigation to stdlib Reviewed By: TD5 Differential Revision: D89552844 fbshipit-source-id: 676af5f520d9189f98828b4364b0b1665531b245 --- crates/elp/src/project_loader.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/elp/src/project_loader.rs b/crates/elp/src/project_loader.rs index 0f13139201..103353e5db 100644 --- a/crates/elp/src/project_loader.rs +++ b/crates/elp/src/project_loader.rs @@ -100,11 +100,8 @@ impl ProjectLoader { ) -> Option<(ElpConfig, Result, ProjectManifest)> { let mut path_it = path; loop { - if let Some(value) = self.project_roots.get(path_it) { - return match value { - None => Some(self.load_manifest(path_it)), - Some(_) => None, - }; + if self.project_roots.contains_key(path_it) { + return None; } match path_it.parent() { Some(parent) => path_it = parent, From 5c7cfae09d3a89e430cc69a88371939dc2acad42 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 19 Dec 2025 08:58:10 -0800 Subject: [PATCH 43/46] Only enable Buck2 project model tests if feature is enabled Summary: We already have a feature to understand if Buck2 is enabled or not. Let's use that one instead of a constant. This change has no effect anyway since the only two use cases are currently marked as "ignored". Also remove the extra check inside the helper (we already do it in both tests). Reviewed By: alanz Differential Revision: D89549782 fbshipit-source-id: 82e5bbf00c463dca3f4937d2d2a12d16aebc3c19 --- crates/project_model/src/buck.rs | 84 +++++++++++++++----------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 32c6dcccb8..45c6b7f732 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -1643,57 +1643,53 @@ mod tests { assert_eq!(expected, actual) } - const BUCK_TESTS_ENABLED: bool = true; - #[track_caller] fn check_buck_bxl_query(build_generated: bool, expect: Expect) { - if BUCK_TESTS_ENABLED { - let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); - // We only need buck_config to get the buck command, everything but the buck root is ignored. - let buck_config = BuckConfig { - config_path: None, - buck_root: Some(buck_root), - enabled: true, - deps_target: None, - deps_targets: vec![], - build_deps: false, - included_targets: vec![], - excluded_targets: vec![], - source_root: None, - test_application_labels: vec!["test_application".to_string()], - }; - let generated_args = if build_generated { - vec!["--build_generated_code", "true"] - } else { - vec![] - }; - let output = buck_config - .buck_command() - .arg("bxl") - .arg("prelude//erlang/elp.bxl:elp_config") - .arg("--") - .args(generated_args) - .arg("--included_targets") - .arg("root//buck_tests_2/auto_gen/...") - .output() - .unwrap(); - if !output.status.success() { - panic!("{output:#?}"); - } - let string = String::from_utf8(output.stdout).unwrap(); - let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); - let string = string.replace(&prelude_cell, "/[prelude]/"); - - let to_replace = env!("CARGO_WORKSPACE_DIR"); - let string = string.replace(to_replace, "/[..]/"); - expect.assert_eq(&string); + let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); + // We only need buck_config to get the buck command, everything but the buck root is ignored. + let buck_config = BuckConfig { + config_path: None, + buck_root: Some(buck_root), + enabled: true, + deps_target: None, + deps_targets: vec![], + build_deps: false, + included_targets: vec![], + excluded_targets: vec![], + source_root: None, + test_application_labels: vec!["test_application".to_string()], + }; + let generated_args = if build_generated { + vec!["--build_generated_code", "true"] + } else { + vec![] + }; + let output = buck_config + .buck_command() + .arg("bxl") + .arg("prelude//erlang/elp.bxl:elp_config") + .arg("--") + .args(generated_args) + .arg("--included_targets") + .arg("root//buck_tests_2/auto_gen/...") + .output() + .unwrap(); + if !output.status.success() { + panic!("{output:#?}"); } + let string = String::from_utf8(output.stdout).unwrap(); + let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); + let string = string.replace(&prelude_cell, "/[prelude]/"); + + let to_replace = env!("CARGO_WORKSPACE_DIR"); + let string = string.replace(to_replace, "/[..]/"); + expect.assert_eq(&string); } #[test] #[ignore] fn build_info_buck_bxl_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { check_buck_bxl_query( false, expect![[r#" @@ -1859,7 +1855,7 @@ mod tests { #[test] #[ignore] fn build_info_buck_bxl_generated_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { // Note that there is now a value for `srcs` in the // "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target From 6456f325c3a338169e4eed4d0a156db5baf1491f Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 19 Dec 2025 08:58:10 -0800 Subject: [PATCH 44/46] Ignore buck-out in test_projects Summary: So that version control systems do not try to publish the directory. Reviewed By: alanz Differential Revision: D89550389 fbshipit-source-id: d60646ddd4e8f0ae443673dac3c49efbac60dba7 --- test/test_projects/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_projects/.gitignore b/test/test_projects/.gitignore index b34578734c..2672c17cc4 100644 --- a/test/test_projects/.gitignore +++ b/test/test_projects/.gitignore @@ -3,3 +3,4 @@ *wa.build_info *_build/ *rebar.lock +buck-out/ From 4bba415bc1d863764c564ea8545aae98dcf02403 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 22 Dec 2025 09:17:24 -0800 Subject: [PATCH 45/46] Fix diagnostic name Summary: Missed during the original rename. Reviewed By: alanz Differential Revision: D89460380 fbshipit-source-id: 60446aa3f505d635ad5ddc842b2d420c24f0a9d2 --- crates/ide/src/diagnostics/old_edoc_syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide/src/diagnostics/old_edoc_syntax.rs b/crates/ide/src/diagnostics/old_edoc_syntax.rs index e8524b6e6e..da4c3f2430 100644 --- a/crates/ide/src/diagnostics/old_edoc_syntax.rs +++ b/crates/ide/src/diagnostics/old_edoc_syntax.rs @@ -8,7 +8,7 @@ * above-listed licenses. */ -// Diagnostic: edoc +// Diagnostic: old_edoc_syntax use elp_ide_assists::Assist; use elp_ide_assists::helpers; From 7a4eccf14dbf3df942e9d01e0eb0f74d419fbb32 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 23 Dec 2025 03:53:02 -0800 Subject: [PATCH 46/46] Add support for linter-specific excluded_apps Summary: We may want to disable a linter for a certain set of applications. This diff introduces such a mechanism: ``` [linters.my_linter] exclude_apps = ["app_a", "app_2"] ``` Reviewed By: alanz Differential Revision: D89658976 fbshipit-source-id: 78e1cf13ec22f1bf7ed1b6df6892cdad5ca2dfe5 --- crates/elp/src/bin/main.rs | 12 ++ .../resources/test/linter_config/basic.stdout | 5 + crates/ide/src/diagnostics.rs | 119 ++++++++++++++++-- test/test_projects/linter_config/.elp.toml | 5 + .../linter_config/.elp_lint.toml | 5 + test/test_projects/linter_config/BUCK | 25 ++++ .../linter_config/app_a/src/app_a.app.src | 3 + .../linter_config/app_a/src/app_a.erl | 4 + .../linter_config/app_b/src/app_b.app.src | 3 + .../linter_config/app_b/src/app_b.erl | 4 + .../linter_config/app_c/src/app_c.app.src | 3 + .../linter_config/app_c/src/app_c.erl | 4 + test/test_projects/linter_config/rebar.config | 9 ++ 13 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 crates/elp/src/resources/test/linter_config/basic.stdout create mode 100644 test/test_projects/linter_config/.elp.toml create mode 100644 test/test_projects/linter_config/.elp_lint.toml create mode 100644 test/test_projects/linter_config/BUCK create mode 100644 test/test_projects/linter_config/app_a/src/app_a.app.src create mode 100644 test/test_projects/linter_config/app_a/src/app_a.erl create mode 100644 test/test_projects/linter_config/app_b/src/app_b.app.src create mode 100644 test/test_projects/linter_config/app_b/src/app_b.erl create mode 100644 test/test_projects/linter_config/app_c/src/app_c.app.src create mode 100644 test/test_projects/linter_config/app_c/src/app_c.erl create mode 100644 test/test_projects/linter_config/rebar.config diff --git a/crates/elp/src/bin/main.rs b/crates/elp/src/bin/main.rs index 721632fc01..56535e4492 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -2263,6 +2263,18 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn lint_linter_config_basic(buck: bool) { + simple_snapshot_sorted( + args_vec!["lint", "--read-config", "--no-stream"], + "linter_config", + expect_file!("../resources/test/linter_config/basic.stdout"), + buck, + None, + ); + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn eqwalizer_tests_check(buck: bool) { diff --git a/crates/elp/src/resources/test/linter_config/basic.stdout b/crates/elp/src/resources/test/linter_config/basic.stdout new file mode 100644 index 0000000000..946fd7dc9a --- /dev/null +++ b/crates/elp/src/resources/test/linter_config/basic.stdout @@ -0,0 +1,5 @@ +Diagnostics reported: +Reporting all diagnostics codes +app_a/src/app_a.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_A) +app_a/src/app_a.erl:4:9-4:14::[Warning] [L1260] record rec_a is unused +app_b/src/app_b.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_B) \ No newline at end of file diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 7b23da4461..eb6bae611b 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -50,6 +50,7 @@ use elp_ide_db::text_edit::TextEdit; use elp_ide_ssr::Match; use elp_ide_ssr::SsrSearchScope; use elp_ide_ssr::match_pattern; +use elp_project_model::AppName; use elp_syntax::NodeOrToken; use elp_syntax::Parse; use elp_syntax::SourceFile; @@ -550,12 +551,37 @@ pub(crate) trait Linter { } } +fn should_process_app( + app_name: &Option, + config: &DiagnosticsConfig, + diagnostic_code: &DiagnosticCode, +) -> bool { + let app = match app_name { + Some(app) => app.to_string(), + None => return true, + }; + + if let Some(lint_config) = config.lint_config.as_ref() + && let Some(linter_config) = lint_config.linters.get(diagnostic_code) + && let Some(ref excluded) = linter_config.exclude_apps + && excluded.contains(&app) + { + return false; + } + + true +} + fn should_run( linter: &dyn Linter, config: &DiagnosticsConfig, + app_name: &Option, is_generated: bool, is_test: bool, ) -> bool { + if !should_process_app(app_name, config, &linter.id()) { + return false; + } let is_enabled = if let Some(lint_config) = config.lint_config.as_ref() { lint_config .get_is_enabled_override(&linter.id()) @@ -1218,6 +1244,16 @@ impl LintConfig { self.linters.get(diagnostic_code)?.experimental } + /// Get the exclude_apps override for a linter based on its diagnostic code. + pub fn get_exclude_apps_override( + &self, + diagnostic_code: &DiagnosticCode, + ) -> Option> { + self.linters + .get(diagnostic_code) + .and_then(|c| c.exclude_apps.clone()) + } + pub fn get_function_call_linter_config( &self, diagnostic_code: &DiagnosticCode, @@ -1341,6 +1377,7 @@ pub struct LinterConfig { pub include_tests: Option, pub include_generated: Option, pub experimental: Option, + pub exclude_apps: Option>, #[serde(flatten)] pub config: Option, } @@ -1361,6 +1398,7 @@ impl LinterConfig { include_tests: other.include_tests.or(self.include_tests), include_generated: other.include_generated.or(self.include_generated), experimental: other.experimental.or(self.experimental), + exclude_apps: other.exclude_apps.or(self.exclude_apps), config: merged_config, } } @@ -1555,6 +1593,7 @@ pub fn native_diagnostics( } else { FxHashMap::default() }; + let app_name = db.file_app_name(file_id); let metadata = db.elp_metadata(file_id); // TODO: can we ever disable DiagnosticCode::SyntaxError? // In which case we must check labeled_syntax_errors @@ -1563,6 +1602,7 @@ pub fn native_diagnostics( && (config.experimental && d.has_category(Category::Experimental) || !d.has_category(Category::Experimental)) && !d.should_be_suppressed(&metadata, config) + && should_process_app(&app_name, config, &d.code) }); LabeledDiagnostics { @@ -1613,20 +1653,20 @@ pub fn diagnostics_from_descriptors( .db .is_test_suite_or_test_helper(file_id) .unwrap_or(false); + let app_name = sema.db.file_app_name(file_id); descriptors.iter().for_each(|descriptor| { if descriptor.conditions.enabled(config, is_generated, is_test) { - if descriptor.conditions.default_disabled { - // Filter the returned diagnostics to ensure they are - // enabled - let mut diags: Vec = Vec::default(); - (descriptor.checker)(&mut diags, sema, file_id, file_kind); - for diag in diags { - if config.enabled.contains(&diag.code) { - res.push(diag); - } + let mut diags: Vec = Vec::default(); + (descriptor.checker)(&mut diags, sema, file_id, file_kind); + for diag in diags { + // Check if this diagnostic is enabled (for default_disabled descriptors) + // and if the app is not excluded for this diagnostic code + let is_enabled = + !descriptor.conditions.default_disabled || config.enabled.contains(&diag.code); + let app_allowed = should_process_app(&app_name, config, &diag.code); + if is_enabled && app_allowed { + res.push(diag); } - } else { - (descriptor.checker)(res, sema, file_id, file_kind); } } }); @@ -1734,11 +1774,12 @@ fn diagnostics_from_linters( .db .is_test_suite_or_test_helper(file_id) .unwrap_or(false); + let app_name = sema.db.file_app_name(file_id); for l in linters { let linter = l.as_linter(); if linter.should_process_file_id(sema, file_id) - && should_run(linter, config, is_generated, is_test) + && should_run(linter, config, &app_name, is_generated, is_test) { let severity = if let Some(lint_config) = config.lint_config.as_ref() { lint_config @@ -2300,11 +2341,14 @@ pub fn erlang_service_diagnostics( diags }; + let app_name = db.file_app_name(file_id); let metadata = db.elp_metadata(file_id); let diags = diags .into_iter() .filter(|(_file_id, d)| { - !d.should_be_suppressed(&metadata, config) && !config.disabled.contains(&d.code) + !d.should_be_suppressed(&metadata, config) + && !config.disabled.contains(&d.code) + && should_process_app(&app_name, config, &d.code) }) .map(|(file_id, d)| { ( @@ -3995,6 +4039,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4037,6 +4082,7 @@ main(X) -> include_tests: Some(true), include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4078,6 +4124,7 @@ main(X) -> include_tests: None, include_generated: Some(true), experimental: None, + exclude_apps: None, config: None, }, ); @@ -4120,6 +4167,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: Some(true), + exclude_apps: None, config: None, }, ); @@ -4164,6 +4212,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4194,6 +4243,47 @@ main(X) -> ); } + #[test] + fn test_linter_exclude_apps_override() { + let mut lint_config = LintConfig::default(); + lint_config.linters.insert( + DiagnosticCode::NoGarbageCollect, + LinterConfig { + is_enabled: Some(false), + severity: None, + include_tests: None, + include_generated: None, + experimental: None, + exclude_apps: Some(vec!["my_app".to_string()]), + config: None, + }, + ); + + let config = DiagnosticsConfig::default() + .configure_diagnostics( + &lint_config, + &Some("no_garbage_collect".to_string()), + &None, + FallBackToAll::No, + ) + .unwrap(); + check_diagnostics_with_config( + config, + r#" + //- /src/main.erl app:my_app + -module(main). + -export([warning/0]). + + warning() -> + erlang:garbage_collect(). + //- /opt/lib/stdlib-3.17/src/erlang.erl otp_app:/opt/lib/stdlib-3.17 + -module(erlang). + -export([garbage_collect/0]). + garbage_collect() -> ok. + "#, + ); + } + #[test] fn no_unused_macro_in_macro_rhs_for_function_name() { let config = DiagnosticsConfig::default() @@ -4241,6 +4331,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: Some(LinterTraitConfig::FunctionCallLinterConfig( FunctionCallLinterConfig { include: Some(vec![FunctionMatch::mf("mod_a", "func_a")]), @@ -4273,6 +4364,7 @@ main(X) -> include_tests: Some(true), include_generated: None, experimental: None, + exclude_apps: None, config: Some(LinterTraitConfig::FunctionCallLinterConfig( FunctionCallLinterConfig { include: Some(vec![FunctionMatch::mf("mod_b", "func_b")]), @@ -4290,6 +4382,7 @@ main(X) -> include_tests: None, include_generated: Some(true), experimental: None, + exclude_apps: None, config: None, }, ); diff --git a/test/test_projects/linter_config/.elp.toml b/test/test_projects/linter_config/.elp.toml new file mode 100644 index 0000000000..c490cc155e --- /dev/null +++ b/test/test_projects/linter_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter_config/..." ] +source_root = "linter_config" diff --git a/test/test_projects/linter_config/.elp_lint.toml b/test/test_projects/linter_config/.elp_lint.toml new file mode 100644 index 0000000000..47886dd507 --- /dev/null +++ b/test/test_projects/linter_config/.elp_lint.toml @@ -0,0 +1,5 @@ +[linters.L1260] # Unused record, produced by the Erlang Service +exclude_apps = ["app_b", "app_c"] + +[linters.unused_macro] +exclude_apps = ["app_c"] diff --git a/test/test_projects/linter_config/BUCK b/test/test_projects/linter_config/BUCK new file mode 100644 index 0000000000..c1101daea2 --- /dev/null +++ b/test/test_projects/linter_config/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_c", + srcs = glob(["app_c/src/*.erl"]), + app_src = "app_c/src/app_c.app.src", + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test/test_projects/linter_config/app_a/src/app_a.app.src b/test/test_projects/linter_config/app_a/src/app_a.app.src new file mode 100644 index 0000000000..1381435a46 --- /dev/null +++ b/test/test_projects/linter_config/app_a/src/app_a.app.src @@ -0,0 +1,3 @@ +{application, app_a, + [{description, "example app A"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_a/src/app_a.erl b/test/test_projects/linter_config/app_a/src/app_a.erl new file mode 100644 index 0000000000..c374c0b518 --- /dev/null +++ b/test/test_projects/linter_config/app_a/src/app_a.erl @@ -0,0 +1,4 @@ +-module(app_a). + +-define(MACRO_A, a). +-record(rec_a, {a :: atom()}). diff --git a/test/test_projects/linter_config/app_b/src/app_b.app.src b/test/test_projects/linter_config/app_b/src/app_b.app.src new file mode 100644 index 0000000000..4112b4129f --- /dev/null +++ b/test/test_projects/linter_config/app_b/src/app_b.app.src @@ -0,0 +1,3 @@ +{application, app_b, + [{description, "example app B"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_b/src/app_b.erl b/test/test_projects/linter_config/app_b/src/app_b.erl new file mode 100644 index 0000000000..ca7ecf985b --- /dev/null +++ b/test/test_projects/linter_config/app_b/src/app_b.erl @@ -0,0 +1,4 @@ +-module(app_b). + +-define(MACRO_B, b). +-record(rec_b, {b :: atom()}). diff --git a/test/test_projects/linter_config/app_c/src/app_c.app.src b/test/test_projects/linter_config/app_c/src/app_c.app.src new file mode 100644 index 0000000000..c278bbce23 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.app.src @@ -0,0 +1,3 @@ +{application, app_c, + [{description, "example app C"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_c/src/app_c.erl b/test/test_projects/linter_config/app_c/src/app_c.erl new file mode 100644 index 0000000000..57573554b6 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.erl @@ -0,0 +1,4 @@ +-module(app_c). + +-define(MACRO_C, c). +-record(rec_c, {c :: atom()}). diff --git a/test/test_projects/linter_config/rebar.config b/test/test_projects/linter_config/rebar.config new file mode 100644 index 0000000000..d508bd24de --- /dev/null +++ b/test/test_projects/linter_config/rebar.config @@ -0,0 +1,9 @@ +{checkouts_dir, ["."]}. +{project_app_dirs, [ + "app_a", + "app_b", + "app_c" +]}. + +{erl_opts, [debug_info]}. +{deps, []}.