diff --git a/.cargo/config.toml b/.cargo/config.toml index 7e4e7a0f90..eb89fa1e55 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,8 @@ [alias] xtask = "run --package xtask --" -# @fb-only -# @fb-only +# @fb-only: [build] +# @fb-only: target-dir = "../../../buck-out/elp" [profile.release] codegen-units = 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf3834b5ad..e9980283f2 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: @@ -135,7 +137,7 @@ jobs: - name: Test elp # Do not run the tests in case of cross-compilation or on Windows if: matrix.platform-arch != 'macos-latest-arm' && matrix.os != 'windows' - run: 'cargo test --no-default-features --workspace --target ${{ matrix.target }}' + run: 'cargo test --workspace --target ${{ matrix.target }}' - name: Build elp (No Windows) if: matrix.os != 'windows' run: 'cargo build --release --target ${{ matrix.target }} --config target.aarch64-unknown-linux-gnu.linker=\"aarch64-linux-gnu-gcc\"' 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/args.rs b/crates/elp/src/bin/args.rs index db3bc33df2..c9790e9314 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, } @@ -919,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/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/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..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, .. }) @@ -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]); @@ -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/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 e1d26e3e7e..56535e4492 100644 --- a/crates/elp/src/bin/main.rs +++ b/crates/elp/src/bin/main.rs @@ -40,7 +40,7 @@ mod erlang_service_cli; mod explain_cli; mod glean; mod lint_cli; -// @fb-only +// @fb-only: mod meta_only; mod reporting; mod shell; mod ssr_cli; @@ -110,7 +110,7 @@ fn setup_cli_telemetry(args: &Args) { } _ => { // Initialize CLI telemetry, if used - // @fb-only + // @fb-only: meta_only::initialize_telemetry(); } } } @@ -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", @@ -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 } => { @@ -604,10 +605,7 @@ mod tests { fn eqwalize_target_diagnostics_match_snapshot_pretty() { if cfg!(feature = "buck") { simple_snapshot( - args_vec![ - "eqwalize-target", - "//whatsapp/elp/test_projects/standard:app_a", - ], + args_vec!["eqwalize-target", "//standard:app_a",], "standard", expect_file!("../resources/test/standard/eqwalize_target_diagnostics.pretty"), true, @@ -972,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]/"); @@ -984,38 +984,13 @@ mod tests { Some(AbsPathBuf::assert(Utf8PathBuf::from_path_buf(abs).unwrap())); let content = normalise_prelude_path(content, buck_config); + let content = sort_json(&content); + expect![[r#" { "apps": [ { - "name": "test_exec", - "dir": "/[prelude]//erlang/common_test/test_exec/src", - "src_dirs": [ - "" - ], - "extra_src_dirs": [], - "include_dirs": [], - "macros": {} - }, - { - "name": "diagnostics_app_a", - "dir": "app_a", - "src_dirs": [ - "src" - ], - "extra_src_dirs": [], - "include_dirs": [ - "include" - ], - "macros": { - "COMMON_TEST": "true", - "TEST": "true" - } - }, - { - "name": "app_a_SUITE", "dir": "app_a/test", - "src_dirs": [], "extra_src_dirs": [ "" ], @@ -1023,61 +998,88 @@ mod tests { "macros": { "COMMON_TEST": "true", "TEST": "true" - } + }, + "name": "app_a_SUITE", + "src_dirs": [] }, { - "name": "common", - "dir": "/[prelude]//erlang/common_test/common", + "dir": "/[prelude]//erlang/common_test/test_exec/src", + "extra_src_dirs": [], + "include_dirs": [], + "macros": {}, + "name": "test_exec", "src_dirs": [ - "src" - ], + "" + ] + }, + { + "dir": "/[prelude]//erlang/common_test/common", "extra_src_dirs": [], "include_dirs": [ "include" ], - "macros": {} + "macros": {}, + "name": "common", + "src_dirs": [ + "src" + ] }, { - "name": "cth_hooks", "dir": "/[prelude]//erlang/common_test/cth_hooks/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [ "" ], - "macros": {} + "macros": {}, + "name": "cth_hooks", + "src_dirs": [ + "" + ] }, { - "name": "buck2_shell_utils", "dir": "/[prelude]//erlang/shell/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "buck2_shell_utils", + "src_dirs": [ + "" + ] + }, + { + "dir": "app_a", + "extra_src_dirs": [], + "include_dirs": [ + "include" + ], + "macros": { + "COMMON_TEST": "true", + "TEST": "true" + }, + "name": "diagnostics_app_a", + "src_dirs": [ + "src" + ] }, { - "name": "test_binary", "dir": "/[prelude]//erlang/common_test/test_binary/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_binary", + "src_dirs": [ + "" + ] }, { - "name": "test_cli_lib", "dir": "/[prelude]//erlang/common_test/test_cli_lib/src", - "src_dirs": [ - "" - ], "extra_src_dirs": [], "include_dirs": [], - "macros": {} + "macros": {}, + "name": "test_cli_lib", + "src_dirs": [ + "" + ] } ], "deps": [] @@ -1092,6 +1094,12 @@ mod tests { content.replace(prelude_cell, "/[prelude]/") } + fn sort_json(content: &str) -> String { + let mut json: serde_json::Value = serde_json::from_str(content).unwrap(); + json.sort_all_objects(); + serde_json::to_string_pretty(&json).unwrap() + } + #[test] #[ignore] fn build_info_json_buck_bxl_generated() { @@ -1105,7 +1113,7 @@ mod tests { "--to", tmp_file.clone(), "--project", - path_str + path_str.clone() ]; let (stdout, stderr, code) = elp(args); assert_eq!( @@ -1120,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]/"); @@ -1444,7 +1454,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/does_not_exist.toml" + "../../test/test_projects/linter/does_not_exist.toml" ], "linter", expect_file!("../resources/test/linter/parse_elp_lint_custom_config_invalid_output.stdout"), @@ -1456,7 +1466,7 @@ mod tests { &[], false, Some(expect![[r#" - unable to read "../../test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) + unable to read "../../test/test_projects/linter/does_not_exist.toml": No such file or directory (os error 2) "#]]), ) .expect("bad test"); @@ -1472,7 +1482,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 +1508,7 @@ mod tests { "lint", "--experimental", "--config-file", - "../../test_projects/linter/elp_lint_adhoc.toml", + "../../test/test_projects/linter/elp_lint_adhoc.toml", "--module", "app_b", "--apply-fix", @@ -1529,7 +1539,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 +1583,7 @@ mod tests { &[], false, Some(expect![[r#" - failed to read "../../test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 + failed to read "../../test/test_projects/linter_bad_config/.elp_lint.toml":expected a right bracket, found an identifier at line 6 column 4 "#]]), ) .expect("bad test"); @@ -1591,6 +1601,20 @@ mod tests { ); } + #[test_case(false ; "rebar")] + #[test_case(true ; "buck")] + fn lint_no_stream_produces_output(buck: bool) { + if otp::supports_eep66_sigils() { + simple_snapshot_expect_error( + args_vec!["lint", "--no-stream"], + "diagnostics", + expect_file!("../resources/test/diagnostics/lint_no_stream.stdout"), + buck, + None, + ); + } + } + #[test_case(false ; "rebar")] #[test_case(true ; "buck")] fn lint_no_diagnostics_filter_all_enabled_json(buck: bool) { @@ -1625,7 +1649,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"), @@ -1928,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, @@ -1943,7 +1968,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 +1983,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_custom_function_matches.toml", + "../../test/test_projects/linter/elp_lint_custom_function_matches.toml", "--module", "custom_function_matches" ], @@ -1975,7 +2000,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 +2017,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 +2032,7 @@ mod tests { args_vec![ "lint", "--config-file", - "../../test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", + "../../test/test_projects/linter/elp_lint_ssr_adhoc_parse_fail.toml", ], "linter", expect_file!("../resources/test/linter/ssr_ad_hoc_parse_fail.stdout"), @@ -2191,6 +2216,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. @@ -2208,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) { @@ -3069,7 +3136,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/bin/reporting.rs b/crates/elp/src/bin/reporting.rs index fe38287629..7ab10459fe 100644 --- a/crates/elp/src/bin/reporting.rs +++ b/crates/elp/src/bin/reporting.rs @@ -227,9 +227,6 @@ impl Reporter for JsonReporter<'_> { diagnostics: &[EqwalizerDiagnostic], ) -> Result<()> { let line_index = self.analysis.line_index(file_id)?; - // Pass include_Tests = false so that errors for tests files that are not opted-in are tagged as - // arc_types::Severity::Disabled and don't break CI. - let eqwalizer_enabled = self.analysis.is_eqwalizer_enabled(file_id, false).unwrap(); let file_path = &self.loaded.vfs.file_path(file_id); let root_path = &self .analysis @@ -238,12 +235,8 @@ impl Reporter for JsonReporter<'_> { .root_dir; let relative_path = get_relative_path(root_path, file_path); for diagnostic in diagnostics { - let diagnostic = convert::eqwalizer_to_arc_diagnostic( - diagnostic, - &line_index, - relative_path, - eqwalizer_enabled, - ); + let diagnostic = + convert::eqwalizer_to_arc_diagnostic(diagnostic, &line_index, relative_path); let diagnostic = serde_json::to_string(&diagnostic)?; writeln!(self.cli, "{diagnostic}")?; } diff --git a/crates/elp/src/bin/shell.rs b/crates/elp/src/bin/shell.rs index 84ab218710..13ff79ed36 100644 --- a/crates/elp/src/bin/shell.rs +++ b/crates/elp/src/bin/shell.rs @@ -157,10 +157,9 @@ impl ShellCommand { } "eqwalize-app" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-app".into(), @@ -177,7 +176,6 @@ impl ShellCommand { rebar, app: app.into(), include_generated, - include_tests, bail_on_error: false, }))); } @@ -185,10 +183,9 @@ impl ShellCommand { } "eqwalize-all" => { let include_generated = options.contains(&"--include-generated"); - let include_tests = options.contains(&"--include-tests"); if let Some(other) = options .into_iter() - .find(|&opt| opt != "--include-generated" && opt != "--include-tests") + .find(|&opt| opt != "--include-generated") { return Err(ShellError::UnexpectedOption( "eqwalize-all".into(), @@ -204,7 +201,6 @@ impl ShellCommand { rebar, format: None, include_generated, - include_tests, bail_on_error: false, stats: false, list_modules: false, @@ -226,10 +222,8 @@ COMMANDS: eqwalize Eqwalize specified modules --clause-coverage Use experimental clause coverage checker eqwalize-all Eqwalize all modules in the current project - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker eqwalize-app Eqwalize all modules in specified application - --include-tests Also eqwalize test modules from project --clause-coverage Use experimental clause coverage checker "; diff --git a/crates/elp/src/bin/ssr_cli.rs b/crates/elp/src/bin/ssr_cli.rs index 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/config.rs b/crates/elp/src/config.rs index 2637eae5a2..681a022261 100644 --- a/crates/elp/src/config.rs +++ b/crates/elp/src/config.rs @@ -30,7 +30,7 @@ use serde::de::DeserializeOwned; use serde_json::json; use crate::from_json; -// @fb-only +// @fb-only: use crate::meta_only; // Defines the server-side configuration of ELP. We generate *parts* // of VS Code's `package.json` config from this. @@ -180,7 +180,7 @@ impl Config { return; } self.data = ConfigData::from_json(json); - // @fb-only + // @fb-only: meta_only::harmonise_gks(self); } pub fn update_gks(&mut self, json: serde_json::Value) { diff --git a/crates/elp/src/convert.rs b/crates/elp/src/convert.rs index 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/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/resources/test/buck_bad_config/bxl_error_message_oss.stdout b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout new file mode 100644 index 0000000000..f5225605a5 --- /dev/null +++ b/crates/elp/src/resources/test/buck_bad_config/bxl_error_message_oss.stdout @@ -0,0 +1 @@ +Project Initialisation Failed: invalid or missing buck 2 configuration diff --git a/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout new file mode 100644 index 0000000000..eef1c92c6d --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/lint_no_stream.stdout @@ -0,0 +1,138 @@ +Reporting all diagnostics codes +Diagnostics reported: +app_a/src/app_a.erl:52:3-52:23::[Warning] [W0006] this statement has no effect +app_a/src/app_a.erl:3:10-3:21::[WeakWarning] [W0037] Unspecific include. +app_a/src/app_a.erl:27:3-27:9::[Warning] [W0017] Function 'foo:ok/0' is undefined. +app_a/src/app_a.erl:28:4-28:11::[Warning] [W0017] Function 'mod:foo/0' is undefined. +app_a/src/app_a.erl:72:4-72:11::[Warning] [W0017] Function 'foo:bar/2' is undefined. +app_a/src/app_a.erl:37:11-37:28::[Warning] [W0017] Function 'mod_name:fun_name/2' is undefined. +app_a/src/app_a.erl:58:11-58:24::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +app_a/src/app_a.erl:4:1-4:41::[Warning] [W0020] Unused file: inets/include/httpd.hrl +app_a/src/app_a.erl:39:7-39:28::[Error] [L1267] variable 'A' shadowed in 'named fun' +app_a/src/app_a.erl:55:32-55:35::[Error] [L1295] type uri/0 undefined +app_a/src/app_a.erl:56:20-56:26::[Error] [L1295] type binary/1 undefined +app_a/src/app_a.erl:72:3-72:34::[Error] [L1252] record record undefined +app_a/src/app_a.erl:75:5-75:16::[Error] [L1252] record record undefined +app_a/src/app_a.erl:35:1-35:2::[Warning] [L1230] function g/1 is unused +app_a/src/app_a.erl:35:3-35:4::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:36:3-36:4::[Warning] [L1268] variable 'F' is unused +app_a/src/app_a.erl:37:3-37:4::[Warning] [L1268] variable 'G' is unused +app_a/src/app_a.erl:38:3-38:4::[Warning] [L1268] variable 'H' is unused +app_a/src/app_a.erl:39:3-39:4::[Warning] [L1268] variable 'I' is unused +app_a/src/app_a.erl:39:7-39:28::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:41:1-41:2::[Warning] [L1230] function h/0 is unused +app_a/src/app_a.erl:45:1-45:2::[Warning] [L1230] function i/0 is unused +app_a/src/app_a.erl:50:1-50:2::[Warning] [L1230] function j/2 is unused +app_a/src/app_a.erl:50:15-50:16::[Warning] [L1268] variable 'A' is unused +app_a/src/app_a.erl:50:23-50:24::[Warning] [L1268] variable 'B' is unused +app_a/src/app_a.erl:55:1-55:46::[Warning] [L1296] type session(_) is unused +app_a/src/app_a.erl:55:1-55:46::[Warning] [L1313] opaque type session(_) is not exported +app_a/src/app_a.erl:56:7-56:13::[Warning] [L1296] type source(_) is unused +app_a/src/app_a.erl:58:1-58:4::[Warning] [L1230] function map/2 is unused +app_a/src/app_a.erl:60:1-60:9::[Warning] [L1230] function with_dot/0 is unused +app_a/src/app_a.erl:62:1-62:9::[Warning] [L1230] function lang_dir/1 is unused +app_a/src/app_a.erl:66:1-66:7::[Warning] [L1230] function escape/1 is unused +app_a/src/app_a.erl:66:13-66:17::[Warning] [L1268] variable 'T' is unused +app_a/src/app_a.erl:67:9-67:25::[Warning] [L1260] record all_configs_file is unused +app_a/src/app_a.erl:71:1-71:2::[Warning] [L1230] function k/0 is unused +app_a/src/app_a.erl:74:1-74:2::[Warning] [L1230] function l/1 is unused +app_a/src/app_a.erl:77:1-77:2::[Warning] [L1230] function m/0 is unused +app_a/src/broken_parse_trans.erl:10:21-10:22::[Error] [L1256] field b undefined in record a +app_a/src/broken_parse_trans.erl:10:32-10:33::[Error] [L1262] variable 'B' is unbound +app_a/src/cascading.erl:9:5-9:6::[Error] [W0004] Missing ')' + 3:10-3:15: function foo/0 undefined + 6:10-6:15: function foo/0 undefined + 8:7-8:10: spec for undefined function foo/0 +app_a/src/diagnostics.erl:3:10-3:27::[WeakWarning] [W0037] Unspecific include. +app_a/src/diagnostics.erl:4:10-4:34::[WeakWarning] [W0037] Unspecific include. +app_a/src/diagnostics.erl:12:8-12:12::[Warning] [W0060] Match on a bound variable +app_a/src/diagnostics.erl:4:1-4:36::[Error] [L0000] Issue in included file + [app_a/include/broken_diagnostics.hrl] 1:8-1:15: P1702: bad attribute + [app_a/include/broken_diagnostics.hrl] 3:6-3:15: P1702: bad attribute +app_a/src/diagnostics.erl:6:31-6:45::[Error] [L1295] type undefined_type/0 undefined +app_a/src/diagnostics.erl:7:1-7:5::[Warning] [L1230] function main/1 is unused +app_a/src/diagnostics.erl:10:1-10:4::[Warning] [L1230] function foo/0 is unused +app_a/src/lint_recursive.erl:23:5-23:14::[Warning] [W0006] this statement has no effect +app_a/src/lint_recursive.erl:6:5-6:7::[Warning] [W0006] this statement has no effect +app_a/src/lint_recursive.erl:14:5-14:12::[Warning] [L1268] variable 'Config1' is unused +app_a/src/lint_recursive.erl:19:5-19:12::[Warning] [L1268] variable 'Config1' is unused +app_a/src/lints.erl:5:1-5:14::[Error] [P1700] head mismatch 'head_mismatcX' vs 'head_mismatch' + 4:1-4:14: Mismatched clause name +app_a/src/lints.erl:4:22-4:23::[Warning] [W0018] Unexpected ';' +app_a/src/lints.erl:2:10-2:25::[Error] [L1227] function head_mismatch/1 undefined +app_a/src/otp27_docstrings.erl:34:9-34:24::[Warning] [W0002] Unused macro (THIS_IS_THE_END) +app_a/src/otp27_docstrings.erl:24:5-24:6::[Warning] [W0060] Match on a bound variable +app_a/src/otp27_docstrings.erl:30:5-30:6::[Warning] [W0060] Match on a bound variable +app_a/src/otp27_sigils.erl:11:6-11:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:12:5-12:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:13:5-13:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:14:5-14:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:15:5-15:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:17:6-17:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:18:5-18:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:19:5-19:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:20:5-20:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:21:5-21:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:23:6-23:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:24:5-24:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:25:5-25:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:26:5-26:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:27:5-27:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:29:6-29:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:30:5-30:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:31:5-31:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:32:5-32:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:33:5-33:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:35:6-35:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:36:5-36:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:37:5-37:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:38:5-38:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:39:5-39:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:41:6-41:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:42:5-42:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:43:5-43:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:44:5-44:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:45:5-45:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:47:6-47:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:48:5-48:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:49:5-49:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:50:5-50:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:51:5-51:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:53:6-53:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:54:5-54:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:55:5-55:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:56:5-56:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:57:5-57:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:59:6-59:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:60:5-60:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:61:5-61:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:62:5-62:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:63:5-63:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:65:6-65:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:66:5-66:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:67:5-67:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:68:5-68:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:69:5-69:24::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:76:5-79:8::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:80:5-84:9::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:85:5-89:10::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:90:5-94:11::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:95:5-99:12::[Warning] [W0006] this statement has no effect +app_a/src/otp27_sigils.erl:102:5-102:24::[WeakWarning] [W0051] Binary string can be written using sigil syntax. +app_a/src/otp27_sigils.erl:128:9-128:24::[Warning] [W0002] Unused macro (THIS_IS_THE_END) +app_a/src/otp27_sigils.erl:112:4-112:5::[Error] [P1711] syntax error before: X + 4:15-4:18: function g/0 undefined + 74:7-74:8: spec for undefined function g/0 +app_a/src/otp27_sigils.erl:71:5-71:6::[Warning] [L1268] variable 'X' is unused +app_a/src/otp_7655.erl:5:1-5:28::[Error] [L1201] no module definition +app_a/src/parse_error_a_cascade.erl:10:20-11:1::[Error] [W0004] Missing 'atom' + 6:6-6:11: function bar/0 undefined +app_a/src/suppressed.erl:8:5-8:9::[Warning] [L1268] variable 'Life' is unused +app_a/src/syntax.erl:5:46-5:47::[Error] [P1711] syntax error before: ')' +app_a/src/syntax.erl:11:9-11:10::[Error] [W0004] Missing ')' diff --git a/crates/elp/src/resources/test/diagnostics/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_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 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/diagnostics/ssr_exclude_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout new file mode 100644 index 0000000000..1cef26124c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_exclude_generated.stdout @@ -0,0 +1,2 @@ +module specified: erlang_diagnostics_errors_gen +No matches found diff --git a/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout new file mode 100644 index 0000000000..2b88b6e10c --- /dev/null +++ b/crates/elp/src/resources/test/diagnostics/ssr_include_generated.stdout @@ -0,0 +1,5 @@ +module specified: erlang_diagnostics_errors_gen + erlang_diagnostics_errors_gen: 1 + 6:5-6:7::[WeakWarning] [ad-hoc: ssr-match] SSR pattern matched: ssr: ok. + +Matches found in 1 modules diff --git a/crates/elp/src/resources/test/eqwalize_all_help.stdout b/crates/elp/src/resources/test/eqwalize_all_help.stdout index 43f66a0a3a..67497c4000 100644 --- a/crates/elp/src/resources/test/eqwalize_all_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_all_help.stdout @@ -1,11 +1,10 @@ -Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--include-tests] [--bail-on-error] [--stats] [--list-modules] +Usage: [--project PROJECT] [--as PROFILE] [[--format FORMAT]] [--rebar] [--bail-on-error] [--stats] [--list-modules] Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) --format Show diagnostics in JSON format --rebar Run with rebar - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found --stats Print statistics when done --list-modules When printing statistics, include the list of modules parsed diff --git a/crates/elp/src/resources/test/eqwalize_app.stdout b/crates/elp/src/resources/test/eqwalize_app.stdout index eaf1d3126a..bb128f249a 100644 --- a/crates/elp/src/resources/test/eqwalize_app.stdout +++ b/crates/elp/src/resources/test/eqwalize_app.stdout @@ -1,4 +1,4 @@ -Usage: [--project PROJECT] [--as PROFILE] [--include-tests] [--rebar] [--bail-on-error] +Usage: [--project PROJECT] [--as PROFILE] [--rebar] [--bail-on-error] Available positional items: app name @@ -6,7 +6,6 @@ Available positional items: Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) --as Rebar3 profile to pickup (default is test) - --include-tests Also eqwalize test modules from project --rebar Run with rebar --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/eqwalize_target_help.stdout b/crates/elp/src/resources/test/eqwalize_target_help.stdout index eec74e4938..e9a01166dd 100644 --- a/crates/elp/src/resources/test/eqwalize_target_help.stdout +++ b/crates/elp/src/resources/test/eqwalize_target_help.stdout @@ -1,10 +1,9 @@ -Usage: [--project PROJECT] [--include-tests] [--bail-on-error] +Usage: [--project PROJECT] [--bail-on-error] Available positional items: target, like //erl/chatd/... Available options: --project Path to directory with project, or to a JSON file (defaults to `.`) - --include-tests Also eqwalize test modules from project --bail-on-error Exit with a non-zero status code if any errors are found -h, --help Prints help information diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty deleted file mode 100644 index 63be36e638..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-26.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty deleted file mode 100644 index 63be36e638..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-27.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty deleted file mode 100644 index a24c7a6a48..0000000000 --- a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg-OTP-28.pretty +++ /dev/null @@ -1,14 +0,0 @@ -error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) - ┌─ check/src/callbacks3_neg.erl:12:1 - │ -12 │ -behavior(gen_server). - │ ^^^^^^^^^^^^^^^^^^^^^ - │ │ - │ Incorrect return type for implementation of gen_server:handle_cast/2. Expected: {'noreply', term()} | {'noreply', term(), gen_server:action()} | {'stop', term(), term()}, Got: 'wrong_ret'. - │ - - 'wrong_ret' is not compatible with {'noreply', term()} | {'noreply', term(), gen_server:action()} | {'stop', term(), term()} - because - 'wrong_ret' is not compatible with {'noreply', term()} - -1 ERROR diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty new file mode 100644 index 0000000000..6cb2012096 --- /dev/null +++ b/crates/elp/src/resources/test/eqwalizer_tests/check/callbacks3_neg.pretty @@ -0,0 +1,27 @@ +error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) + ┌─ check/src/callbacks3_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ Incorrect return type for implementation of gen_server:handle_cast/2. +Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} +Got: 'wrong_ret' + +error: incorrect_return_type_in_cb_implementation (See https://fb.me/eqwalizer_errors#incorrect_return_type_in_cb_implementation) + ┌─ check/src/callbacks3_neg.erl:13:1 + │ +13 │ -behavior(gen_server). + │ ^^^^^^^^^^^^^^^^^^^^^ + │ │ + │ Incorrect return type for implementation of gen_server:handle_info/2. +Expected: {'noreply', term()} | {'noreply', term(), timeout() | 'hibernate' | {'continue', term()}} | {'stop', term(), term()} +Got: {'noreply', 'ok', 'wrong_atom'} + │ + +Because in the expression's type: + { 'noreply', 'ok', + Here the type is: 'wrong_atom' + Context expects type: 'infinity' | number() | 'hibernate' | {'continue', term()} + No candidate matches in the expected union. + } + +2 ERRORS diff --git a/crates/elp/src/resources/test/eqwalizer_tests/check/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/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 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/elp/src/resources/test/linter_config/basic.stdout b/crates/elp/src/resources/test/linter_config/basic.stdout new file mode 100644 index 0000000000..946fd7dc9a --- /dev/null +++ b/crates/elp/src/resources/test/linter_config/basic.stdout @@ -0,0 +1,5 @@ +Diagnostics reported: +Reporting all diagnostics codes +app_a/src/app_a.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_A) +app_a/src/app_a.erl:4:9-4:14::[Warning] [L1260] record rec_a is unused +app_b/src/app_b.erl:3:9-3:16::[Warning] [W0002] Unused macro (MACRO_B) \ No newline at end of file diff --git a/crates/elp/src/resources/test/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/resources/test/xref/unavailable_type.stdout b/crates/elp/src/resources/test/xref/unavailable_type.stdout index 7064c359ff..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_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 'root//xref:app_a'). +app_a/src/unavailable_type.erl:6:16-6:31::[Warning] [W0059] The type 'app_c:my_type_c/0' is defined in application 'app_c', but the application is not a dependency of 'app_a' (defined in 'root//xref:app_a'). diff --git a/crates/elp/src/server.rs b/crates/elp/src/server.rs index 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/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/elp/src/snapshot.rs b/crates/elp/src/snapshot.rs index e79251f73a..69ea6b97e0 100644 --- a/crates/elp/src/snapshot.rs +++ b/crates/elp/src/snapshot.rs @@ -36,9 +36,11 @@ use parking_lot::Mutex; use parking_lot::RwLock; use serde::Deserialize; use serde::Serialize; +use vfs::AnchoredPathBuf; use crate::config::Config; use crate::convert; +use crate::convert::url_from_abs_path; use crate::line_endings::LineEndings; use crate::mem_docs::MemDocs; use crate::server::EqwalizerTypes; @@ -186,6 +188,14 @@ impl Snapshot { self.line_ending_map.read()[&id] } + pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Option { + let mut base = self.vfs.read().file_path(path.anchor).clone(); + base.pop(); + let path = base.join(&path.path)?; + let path = path.as_path()?; + Some(url_from_abs_path(path)) + } + pub fn update_cache_for_file( &self, file_id: FileId, @@ -193,7 +203,7 @@ impl Snapshot { ) -> Result<()> { let _ = self.analysis.def_map(file_id)?; if optimize_for_eqwalizer { - let should_eqwalize = self.analysis.should_eqwalize(file_id, false)?; + let should_eqwalize = self.analysis.should_eqwalize(file_id)?; if should_eqwalize { let _ = self.analysis.module_ast(file_id)?; } @@ -242,7 +252,7 @@ impl Snapshot { let file_ids: Vec = module_index .iter_own() .filter_map(|(_, _, file_id)| { - if let Ok(true) = self.analysis.should_eqwalize(file_id, false) { + if let Ok(true) = self.analysis.should_eqwalize(file_id) { Some(file_id) } else { None diff --git a/crates/elp/src/to_proto.rs b/crates/elp/src/to_proto.rs index 70d316d5c6..3e5b1fd1e1 100644 --- a/crates/elp/src/to_proto.rs +++ b/crates/elp/src/to_proto.rs @@ -10,6 +10,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. +use std::mem; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -47,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; @@ -121,9 +123,9 @@ pub(crate) fn optional_versioned_text_document_identifier( pub(crate) fn text_document_edit( snap: &Snapshot, file_id: FileId, + text_document: lsp_types::OptionalVersionedTextDocumentIdentifier, edit: TextEdit, ) -> Result { - let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.analysis.line_index(file_id)?; let line_endings = snap.line_endings(file_id); let edits: Vec> = edit @@ -131,34 +133,131 @@ pub(crate) fn text_document_edit( .map(|it| lsp_types::OneOf::Left(text_edit(&line_index, line_endings, it))) .collect(); - // if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { - // for edit in &mut edits { - // edit.annotation_id = Some(outside_workspace_annotation_id()) - // } - // } Ok(lsp_types::TextDocumentEdit { text_document, edits, }) } +pub(crate) fn text_document_ops( + snap: &Snapshot, + file_system_edit: FileSystemEdit, +) -> Cancellable> { + let mut ops = Vec::new(); + match file_system_edit { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + if let Some(uri) = snap.anchored_path(&dst) { + let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile { + uri: uri.clone(), + options: None, + annotation_id: None, + }); + ops.push(lsp_types::DocumentChangeOperation::Op(create_file)); + if !initial_contents.is_empty() { + let text_document = + lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None }; + let text_edit = lsp_types::TextEdit { + range: lsp_types::Range::default(), + new_text: initial_contents, + }; + let edit_file = lsp_types::TextDocumentEdit { + text_document, + edits: vec![lsp_types::OneOf::Left(text_edit)], + }; + ops.push(lsp_types::DocumentChangeOperation::Edit(edit_file)); + } + } else { + log::warn!("create file failed: {:?}", dst); + } + } + FileSystemEdit::MoveFile { src, dst } => { + if let Some(new_uri) = snap.anchored_path(&dst) { + let old_uri = snap.file_id_to_url(src); + let rename_file = lsp_types::RenameFile { + old_uri, + new_uri, + options: None, + annotation_id: None, + }; + ops.push(lsp_types::DocumentChangeOperation::Op( + lsp_types::ResourceOp::Rename(rename_file), + )) + } else { + log::warn!("rename file failed: {:?} -> {:?}", src, dst); + } + } + } + Ok(ops) +} + pub(crate) fn workspace_edit( snap: &Snapshot, - source_change: SourceChange, + mut source_change: SourceChange, ) -> Result { - let mut edits: Vec<_> = vec![]; - for (file_id, edit) in source_change.source_file_edits { - // let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?; - let edit = text_document_edit(snap, file_id, edit)?; - edits.push(lsp_types::TextDocumentEdit { - text_document: edit.text_document, - edits: edit.edits.into_iter().collect(), - }); + let mut document_changes: Vec = Vec::new(); + + // This is copying RA's order of operations, first file creates, + // then edits, then file moves. + + // This allows us to apply edits to the file once it has + // moved. Except we have no FileId at that point + for op in &mut source_change.file_system_edits { + if let FileSystemEdit::CreateFile { + dst, + initial_contents, + } = op + { + // replace with a placeholder to avoid cloning the edit + let op = FileSystemEdit::CreateFile { + dst: dst.clone(), + initial_contents: mem::take(initial_contents), + }; + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } } - let document_changes = lsp_types::DocumentChanges::Edits(edits); + + for op in source_change.file_system_edits { + if !matches!(op, FileSystemEdit::CreateFile { .. }) { + let ops = text_document_ops(snap, op)?; + document_changes.extend_from_slice(&ops); + } + } + + for (file_id, edit) in source_change.source_file_edits { + let text_document = optional_versioned_text_document_identifier(snap, file_id); + let edit = text_document_edit(snap, file_id, text_document, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } + + // Edits on renamed files. The LineIndex from the original can be used. + for (file_ref, edit) in source_change.new_file_edits { + if let Some(uri) = snap.anchored_path(&file_ref.clone().into()) { + let version = snap.url_file_version(&uri); + let text_document = lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version }; + let edit = text_document_edit(snap, file_ref.anchor, text_document, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit( + lsp_types::TextDocumentEdit { + text_document: edit.text_document, + edits: edit.edits.into_iter().collect(), + }, + )); + } else { + log::warn!("new file edit failed: {:?}", file_ref); + } + } + let workspace_edit = lsp_types::WorkspaceEdit { changes: None, - document_changes: Some(document_changes), + document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), change_annotations: None, }; Ok(workspace_edit) @@ -182,10 +281,6 @@ pub(crate) fn code_action( ) -> Result { let mut res = lsp_types::CodeAction { title: assist.label.to_string(), - // group: assist - // .group - // .filter(|_| snap.config.code_action_group()) - // .map(|gr| gr.0), kind: Some(code_action_kind(assist.id.1)), edit: None, is_preferred: None, diff --git a/crates/elp/tests/slow-tests/buck_tests.rs b/crates/elp/tests/slow-tests/buck_tests.rs index e76fdb8bad..91f2b234d9 100644 --- a/crates/elp/tests/slow-tests/buck_tests.rs +++ b/crates/elp/tests/slow-tests/buck_tests.rs @@ -31,7 +31,7 @@ mod tests { #[test] #[ignore] fn test_success_case() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let cli = Fake::default(); @@ -65,7 +65,7 @@ mod tests { let ast = analysis.module_ast(file_id).unwrap(); assert_eq!(ast.errors, vec![]); let eq_enabled = analysis - .is_eqwalizer_enabled(file_id, false) + .is_eqwalizer_enabled(file_id) .unwrap_or_else(|_| panic!("Failed to check if eqwalizer enabled for {module}")); assert_eq!(eq_enabled, eqwalizer_enabled); let project_data = analysis.project_data(file_id).unwrap(); @@ -76,7 +76,7 @@ mod tests { #[test] #[ignore] fn test_load_buck_targets() { - let path_str = "../../test_projects/buck_tests"; + let path_str = "../../test/test_projects/buck_tests"; let path: PathBuf = path_str.into(); let (elp_config, buck_config) = diff --git a/crates/elp/tests/slow-tests/main.rs b/crates/elp/tests/slow-tests/main.rs index 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/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/hir/src/lib.rs b/crates/hir/src/lib.rs index 1ec75fd25e..751a2f4907 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; @@ -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/hir/src/name.rs b/crates/hir/src/name.rs index 00ce2f89f1..f94f139d38 100644 --- a/crates/hir/src/name.rs +++ b/crates/hir/src/name.rs @@ -10,7 +10,7 @@ //! See [`Name`]. -// @fb-only +// @fb-only: pub mod meta_only; use std::borrow::Cow; use std::collections::HashSet; diff --git a/crates/hir/src/sema.rs b/crates/hir/src/sema.rs index 78a7db7c3c..68577d2a2e 100644 --- a/crates/hir/src/sema.rs +++ b/crates/hir/src/sema.rs @@ -102,7 +102,7 @@ use crate::resolver::Resolution; use crate::resolver::Resolver; mod find; -// @fb-only +// @fb-only: pub mod meta_only; pub mod to_def; pub struct ModuleIter(Arc); @@ -1006,6 +1006,28 @@ impl Semantic<'_> { // Folds end // ----------------------------------------------------------------- + pub fn bound_vars_by_function( + &self, + file_id: FileId, + ) -> FxHashMap> { + let bound_vars = self.bound_vars_in_pattern_diagnostic(file_id); + let mut bound_vars_by_function: FxHashMap> = + FxHashMap::default(); + bound_vars.iter().for_each(|(function_id, pat_id, _var)| { + bound_vars_by_function + .entry(function_id.value) + .and_modify(|vars| { + vars.insert(*pat_id); + }) + .or_insert_with(|| { + let mut vars = FxHashSet::default(); + vars.insert(*pat_id); + vars + }); + }); + bound_vars_by_function + } + pub fn bound_vars_in_pattern_diagnostic( &self, file_id: FileId, diff --git a/crates/hir/src/sema/to_def.rs b/crates/hir/src/sema/to_def.rs index 5918054fc5..01d17278a2 100644 --- a/crates/hir/src/sema/to_def.rs +++ b/crates/hir/src/sema/to_def.rs @@ -42,7 +42,7 @@ use crate::macro_exp; use crate::macro_exp::BuiltInMacro; use crate::macro_exp::MacroExpCtx; use crate::resolver::Resolver; -// @fb-only +// @fb-only: use crate::sema::meta_only; pub trait ToDef: Clone { type Def; @@ -567,7 +567,7 @@ pub fn resolve_call_target( let fn_name: Name = sema.db.lookup_atom(body[*name].as_atom()?); let mo = None; // @oss-only - // @fb-only + // @fb-only: meta_only::resolve_handle_call_target(sema, arity, file_id, &module_name, &fn_name); if let Some(r) = mo { r } else { @@ -885,12 +885,183 @@ fn add_dynamic_call_patterns(patterns: &mut FxHashMap Self { + Self { + index, + arg_type: ModuleArgType::Atom, + } + } + + /// Creates a pattern where the argument is a list of module atoms. + pub const fn list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::List, + } + } + + /// Creates a pattern where the argument can be either a single atom or a list. + pub const fn atom_or_list(index: usize) -> Self { + Self { + index, + arg_type: ModuleArgType::AtomOrList, + } + } + + /// Returns true if this pattern accepts a single atom. + pub const fn accepts_atom(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::Atom | ModuleArgType::AtomOrList + ) + } + + /// Returns true if this pattern accepts a list of atoms. + pub const fn accepts_list(&self) -> bool { + matches!( + self.arg_type, + ModuleArgType::List | ModuleArgType::AtomOrList + ) + } +} + +fn add_module_argument_patterns(patterns: &mut FxHashMap) { + // Each entry follows the format: + // (module, function, arity) -> ModuleArgPattern + // + // Where: + // module: Module name (Some("meck"), Some("application"), etc.) + // function: Function name as string literal (e.g., "new", "get_env") + // arity: Number of arguments this function pattern expects + // ModuleArgPattern: Contains the argument index and the expected type + // + // All indexes are 0-based. + + // meck - mocking library + // meck:new/2 accepts either a single module atom or a list of modules + patterns.insert((Some("meck"), "called", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "called", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "capture", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "capture", 6), ModuleArgPattern::atom(1)); + patterns.insert( + (Some("meck"), "delete", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "delete", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 3), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expect", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "expects", 2), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "history", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "history", 2), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "loop", 4), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 1), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "new", 2), ModuleArgPattern::atom_or_list(0)); + patterns.insert((Some("meck"), "num_calls", 3), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "num_calls", 4), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("meck"), "reset", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "sequence", 4), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "unload", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert( + (Some("meck"), "validate", 1), + ModuleArgPattern::atom_or_list(0), + ); + patterns.insert((Some("meck"), "wait", 4), ModuleArgPattern::atom(0)); + patterns.insert((Some("meck"), "wait", 5), ModuleArgPattern::atom(1)); + patterns.insert((Some("meck"), "wait", 6), ModuleArgPattern::atom(1)); + + // code module - module loading and management + // These functions from the Erlang stdlib take module() as their argument + patterns.insert((Some("code"), "load_file", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "ensure_loaded", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "delete", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "soft_purge", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_loaded", 1), ModuleArgPattern::atom(0)); + patterns.insert( + (Some("code"), "get_object_code", 1), + ModuleArgPattern::atom(0), + ); + patterns.insert((Some("code"), "module_md5", 1), ModuleArgPattern::atom(0)); + patterns.insert((Some("code"), "is_sticky", 1), ModuleArgPattern::atom(0)); +} + +// Lazy static initialization for the patterns maps lazy_static! { static ref DYNAMIC_CALL_PATTERNS: FxHashMap = { let mut patterns = FxHashMap::default(); add_dynamic_call_patterns(&mut patterns); - // @fb-only + // @fb-only: meta_only::add_dynamic_call_patterns(&mut patterns); + patterns + }; + static ref MODULE_ARGUMENT_PATTERNS: FxHashMap = { + let mut patterns = FxHashMap::default(); + add_module_argument_patterns(&mut patterns); + // @fb-only: meta_only::add_module_argument_patterns(&mut patterns); + patterns + }; + /// Combined patterns for module argument positions. + /// Merges dynamic call patterns (that have module_arg_index) with simple module argument patterns. + /// Used by rename operations where we only care about the module argument position. + static ref COMBINED_MODULE_ARG_PATTERNS: FxHashMap = { + let mut patterns: FxHashMap = FxHashMap::default(); + // Add module_arg_index from dynamic call patterns (where present) + for (key, pattern) in DYNAMIC_CALL_PATTERNS.iter() { + if let Some(module_idx) = pattern.module_arg_index { + patterns.insert(*key, ModuleArgPattern::atom(module_idx)); + } + } + // Add from simple module argument patterns + for (key, module_arg_pattern) in MODULE_ARGUMENT_PATTERNS.iter() { + patterns.insert(*key, *module_arg_pattern); + } patterns }; } @@ -899,6 +1070,10 @@ fn get_dynamic_call_patterns() -> &'static FxHashMap &'static FxHashMap { + &COMBINED_MODULE_ARG_PATTERNS +} + fn look_for_dynamic_call( sema: &Semantic, file_id: FileId, diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 6cbdd3469c..d0ae18c158 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -17,7 +17,7 @@ use elp_syntax::TextRange; use fxhash::FxHashMap; use fxhash::FxHashSet; -// @fb-only +// @fb-only: use crate::meta_only; use crate::runnables::Runnable; use crate::runnables::runnables; @@ -57,11 +57,11 @@ pub struct Link { } #[rustfmt::skip] -// @fb-only +// @fb-only: pub(crate) fn annotations(db: &RootDatabase, file_id: FileId) -> Vec { pub(crate) fn annotations(_db: &RootDatabase, _file_id: FileId) -> Vec { // @oss-only - // @fb-only + // @fb-only: let mut annotations = Vec::default(); let annotations = Vec::default(); // @oss-only - // @fb-only + // @fb-only: meta_only::annotations(db, file_id, &mut annotations); annotations } diff --git a/crates/ide/src/codemod_helpers.rs b/crates/ide/src/codemod_helpers.rs index e06ceafd61..2ecd21e82c 100644 --- a/crates/ide/src/codemod_helpers.rs +++ b/crates/ide/src/codemod_helpers.rs @@ -573,8 +573,8 @@ pub(crate) fn find_call_in_function( }; if let Some(extra) = check_call(context) { // Got one. - let call_expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let call_expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index e3aa185b19..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; @@ -96,13 +97,13 @@ mod application_env; mod atoms_exhaustion; mod binary_string_to_sigil; mod boolean_precedence; +mod bound_variable; mod could_be_a_string_literal; mod cross_node_eval; mod debugging_function; mod dependent_header; mod deprecated_function; mod duplicate_module; -mod edoc; mod effect_free_statement; mod equality_check_with_unnecessary_operator; mod eqwalizer_assists; @@ -117,7 +118,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; @@ -131,6 +132,7 @@ mod no_garbage_collect; mod no_nowarn_suppressions; mod no_size; mod nonstandard_integer_formatting; +mod old_edoc_syntax; mod record_tuple_match; mod redundant_assignment; mod replace_call; @@ -549,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()) @@ -865,6 +892,7 @@ pub(crate) trait GenericLinter: Linter { fn fixes( &self, _context: &Self::Context, + _range: TextRange, _sema: &Semantic, _file_id: FileId, ) -> Option> { @@ -898,7 +926,7 @@ impl GenericDiagnostics for T { if let Some(matches) = self.matches(sema, file_id) { for matched in matches { let message = self.match_description(&matched.context); - let fixes = self.fixes(&matched.context, sema, file_id); + let fixes = self.fixes(&matched.context, matched.range, sema, file_id); let tag = self.tag(&matched.context); let mut d = Diagnostic::new(self.id(), message, matched.range) .with_fixes(fixes) @@ -1216,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, @@ -1339,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, } @@ -1359,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, } } @@ -1524,7 +1564,7 @@ pub fn native_diagnostics( config .lints_from_config .get_diagnostics(&mut res, &sema, file_id); - // @fb-only + // @fb-only: meta_only::diagnostics(&mut res, &sema, file_id, file_kind, config); syntax_diagnostics(&sema, &parse, &mut res, file_id); diagnostics_from_descriptors( &mut res, @@ -1553,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 @@ -1561,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 { @@ -1611,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); } } }); @@ -1681,11 +1723,12 @@ 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, &boolean_precedence::LINTER, + &bound_variable::LINTER, ]; /// Unified registry for all types of linters @@ -1714,7 +1757,7 @@ pub(crate) fn linters() -> Vec { ); // Add meta-only linters - // @fb-only + // @fb-only: all_linters.extend(meta_only::linters()); all_linters } @@ -1731,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 @@ -2297,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)| { ( @@ -2592,7 +2639,7 @@ pub fn ct_diagnostics( CommonTestInfo::Result { all, groups } => { let testcases = common_test::runnable_names(&sema, file_id, all, groups).ok(); common_test::unreachable_test(&mut res, &sema, file_id, &testcases); - // @fb-only + // @fb-only: meta_only::ct_diagnostics(&mut res, &sema, file_id, testcases); } CommonTestInfo::EvalError(_error) => { // The error currently does not contain anything useful, so we ignore it @@ -3563,7 +3610,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). @@ -3992,6 +4039,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4034,6 +4082,7 @@ main(X) -> include_tests: Some(true), include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4075,6 +4124,7 @@ main(X) -> include_tests: None, include_generated: Some(true), experimental: None, + exclude_apps: None, config: None, }, ); @@ -4117,6 +4167,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: Some(true), + exclude_apps: None, config: None, }, ); @@ -4161,6 +4212,7 @@ main(X) -> include_tests: None, include_generated: None, experimental: None, + exclude_apps: None, config: None, }, ); @@ -4191,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() @@ -4238,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")]), @@ -4270,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")]), @@ -4287,6 +4382,7 @@ main(X) -> include_tests: None, include_generated: Some(true), experimental: None, + exclude_apps: None, config: None, }, ); diff --git a/crates/ide/src/diagnostics/application_env.rs b/crates/ide/src/diagnostics/application_env.rs index 1e4fe7c4cf..615b7801f9 100644 --- a/crates/ide/src/diagnostics/application_env.rs +++ b/crates/ide/src/diagnostics/application_env.rs @@ -28,7 +28,7 @@ use crate::codemod_helpers::CheckCallCtx; use crate::codemod_helpers::FunctionMatch; use crate::codemod_helpers::MatchCtx; use crate::codemod_helpers::find_call_in_function; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::Severity; @@ -36,7 +36,7 @@ pub(crate) static DESCRIPTOR: DiagnosticDescriptor = DiagnosticDescriptor { conditions: DiagnosticConditions { experimental: false, include_generated: true, - include_tests: true, + include_tests: false, default_disabled: false, }, checker: &|diags, sema, file_id, _ext| { @@ -108,7 +108,7 @@ fn check_function(diags: &mut Vec, sema: &Semantic, def: &FunctionDe vec![2, 3], BadEnvCallAction::AppArg(0), ), - // @fb-only + // @fb-only: diagnostics::meta_only::application_env_bad_matches(), ] .into_iter() .flatten() diff --git a/crates/ide/src/diagnostics/atoms_exhaustion.rs b/crates/ide/src/diagnostics/atoms_exhaustion.rs index 4708e35b1f..56a43a50ca 100644 --- a/crates/ide/src/diagnostics/atoms_exhaustion.rs +++ b/crates/ide/src/diagnostics/atoms_exhaustion.rs @@ -13,7 +13,7 @@ use hir::Semantic; use crate::FunctionMatch; use crate::codemod_helpers::CheckCallCtx; -// @fb-only +// @fb-only: use crate::diagnostics; use crate::diagnostics::DiagnosticCode; use crate::diagnostics::FunctionCallLinter; use crate::diagnostics::Linter; @@ -35,9 +35,9 @@ impl Linter for AtomsExhaustionLinter { false } #[rustfmt::skip] - // @fb-only + // @fb-only: fn should_process_file_id(&self, sema: &Semantic, file_id: FileId) -> bool { fn should_process_file_id(&self, _sema: &Semantic, _file_id: FileId) -> bool { // @oss-only - // @fb-only + // @fb-only: diagnostics::meta_only::is_relevant_file(sema.db.upcast(), file_id) true // @oss-only } } @@ -56,16 +56,16 @@ impl FunctionCallLinter for AtomsExhaustionLinter { // FunctionMatch::mfa("erlang", "binary_to_term", 2), ] .into_iter() - // @fb-only + // @fb-only: .chain(diagnostics::meta_only::atoms_exhaustion_matches().into_iter()) .collect::>() ] } fn check_match(&self, context: &CheckCallCtx<'_, ()>) -> Option { #[rustfmt::skip] - // @fb-only - // @fb-only - // @fb-only + // @fb-only: let sema = context.in_clause.sema; + // @fb-only: let is_safe = + // @fb-only: diagnostics::meta_only::atoms_exhaustion_is_safe(sema, context.in_clause, context.parents); let is_safe = false; // @oss-only if !is_safe { match context.args.as_slice() { diff --git a/crates/ide/src/diagnostics/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/bound_variable.rs b/crates/ide/src/diagnostics/bound_variable.rs new file mode 100644 index 0000000000..92f0edadfa --- /dev/null +++ b/crates/ide/src/diagnostics/bound_variable.rs @@ -0,0 +1,178 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is dual-licensed under either the MIT license found in the + * LICENSE-MIT file in the root directory of this source tree or the Apache + * License, Version 2.0 found in the LICENSE-APACHE file in the root directory + * of this source tree. You may select, at your option, one of the + * above-listed licenses. + */ + +// Diagnostic: bound_variable +// +// Return a warning if the LHS of a match already contains a bound variable. +// + +use elp_ide_db::elp_base_db::FileId; +use hir::AnyExpr; +use hir::Expr; +use hir::Semantic; +use hir::Strategy; +use hir::fold::MacroStrategy; +use hir::fold::ParenStrategy; + +use crate::diagnostics::DiagnosticCode; +use crate::diagnostics::GenericLinter; +use crate::diagnostics::GenericLinterMatchContext; +use crate::diagnostics::Linter; + +pub(crate) struct BoundVariableLinter; + +impl Linter for BoundVariableLinter { + fn id(&self) -> DiagnosticCode { + DiagnosticCode::BoundVarInLhs + } + + fn description(&self) -> &'static str { + "Match on a bound variable" + } +} + +impl GenericLinter for BoundVariableLinter { + type Context = (); + + fn matches( + &self, + sema: &Semantic, + file_id: FileId, + ) -> Option>> { + let bound_vars_by_function = sema.bound_vars_by_function(file_id); + let mut res = Vec::new(); + sema.def_map(file_id) + .get_function_clauses() + .for_each(|(_, def)| { + if def.file.file_id == file_id + && let Some(bound_vars) = bound_vars_by_function.get(&def.function_clause_id) + { + let in_clause = def.in_clause(sema, def); + in_clause.fold_clause( + Strategy { + macros: MacroStrategy::ExpandButIncludeMacroCall, + parens: ParenStrategy::InvisibleParens, + }, + (), + &mut |acc, ctx| { + if let AnyExpr::Expr(Expr::Match { lhs, rhs: _ }) = ctx.item + && bound_vars.contains(&lhs) + && let Some(range) = in_clause.range_for_pat(lhs) + && range.file_id == def.file.file_id + && ctx.in_macro.is_none() + { + res.push(GenericLinterMatchContext { + range: range.range, + context: (), + }); + }; + acc + }, + ); + } + }); + + Some(res) + } +} + +pub static LINTER: BoundVariableLinter = BoundVariableLinter; + +#[cfg(test)] +mod test { + use elp_ide_db::DiagnosticCode; + use expect_test::Expect; + + use crate::diagnostics::DiagnosticsConfig; + use crate::tests::check_diagnostics_with_config; + use crate::tests::check_fix_with_config; + + #[track_caller] + pub(crate) fn check_diagnostics(fixture: &str) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_diagnostics_with_config(config, fixture) + } + + #[track_caller] + pub(crate) fn check_fix(fixture_before: &str, fixture_after: Expect) { + let config = DiagnosticsConfig::default().disable(DiagnosticCode::UndefinedFunction); + check_fix_with_config(config, fixture_before, fixture_after) + } + #[test] + fn bound_variable() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + AA = bar(). + %% ^^ 💡 warning: W0060: Match on a bound variable + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_case() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + + foo(Val) -> + case Val of + undefined -> ok; + Val when is_list(Val) -> ok + end. + + "#, + ) + } + + #[test] + fn bound_variable_not_reported_in_macro() { + check_diagnostics( + r#" + //- /src/bound.erl + -module(bound). + -include("inc.hrl"). + + foo(Val) -> + ?A_MACRO(Val). + //- /src/inc.hrl + -define(A_MACRO(X), X=X). + "#, + ) + } + + #[test] + fn bound_variable_ignore_fix() { + check_fix( + r#" + //- /src/bound.erl + -module(bound). + + foo() -> + AA = bar(), + A~A = bar(). + "#, + expect_test::expect![[r#" + -module(bound). + + foo() -> + AA = bar(), + % elp:ignore W0060 (bound_var_in_lhs) + AA = bar(). + "#]], + ) + } +} diff --git a/crates/ide/src/diagnostics/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..b353d824d2 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() @@ -134,8 +134,8 @@ fn check_function( ); let details = match_result.map(|(_match, details)| details.clone()); if target_def.deprecated || match_result.is_some() { - let expr_id = if let Some(expr_id) = ctx.in_macro { - expr_id.idx + let expr_id = if let Some((hir_idx, _macro_def)) = ctx.in_macro { + hir_idx.idx } else { ctx.item_id }; diff --git a/crates/ide/src/diagnostics/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/mutable_variable.rs b/crates/ide/src/diagnostics/mutable_variable.rs index 52fe32eccf..6878c90d57 100644 --- a/crates/ide/src/diagnostics/mutable_variable.rs +++ b/crates/ide/src/diagnostics/mutable_variable.rs @@ -27,12 +27,8 @@ // use elp_ide_db::elp_base_db::FileId; -use fxhash::FxHashMap; -use fxhash::FxHashSet; use hir::AnyExpr; use hir::Expr; -use hir::FunctionClauseId; -use hir::PatId; use hir::Semantic; use hir::Strategy; use hir::fold::MacroStrategy; @@ -60,21 +56,7 @@ fn mutable_variable_bug( sema: &Semantic, file_id: FileId, ) -> Option<()> { - let mut bound_vars_by_function: FxHashMap> = - FxHashMap::default(); - let bound_vars = sema.bound_vars_in_pattern_diagnostic(file_id); - bound_vars.iter().for_each(|(function_id, pat_id, _var)| { - bound_vars_by_function - .entry(function_id.value) - .and_modify(|vars| { - vars.insert(pat_id); - }) - .or_insert_with(|| { - let mut vars = FxHashSet::default(); - vars.insert(pat_id); - vars - }); - }); + let bound_vars_by_function = sema.bound_vars_by_function(file_id); sema.def_map(file_id) .get_function_clauses() .for_each(|(_, def)| { diff --git a/crates/ide/src/diagnostics/no_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 { diff --git a/crates/ide/src/diagnostics/edoc.rs b/crates/ide/src/diagnostics/old_edoc_syntax.rs similarity index 97% rename from crates/ide/src/diagnostics/edoc.rs rename to crates/ide/src/diagnostics/old_edoc_syntax.rs index ead44d7331..da4c3f2430 100644 --- a/crates/ide/src/diagnostics/edoc.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; @@ -31,11 +31,10 @@ use super::DiagnosticCode; use super::GenericLinter; use super::GenericLinterMatchContext; use super::Linter; -use super::Severity; -pub(crate) struct EdocLinter; +pub(crate) struct OldEdocSyntaxLinter; -impl Linter for EdocLinter { +impl Linter for OldEdocSyntaxLinter { fn id(&self) -> DiagnosticCode { DiagnosticCode::OldEdocSyntax } @@ -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 } } @@ -56,10 +52,9 @@ impl Linter for EdocLinter { pub struct Context { header_ptr: Option>, doc_start: TextSize, - range: TextRange, } -impl GenericLinter for EdocLinter { +impl GenericLinter for OldEdocSyntaxLinter { type Context = Context; fn matches( @@ -77,7 +72,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: doc.range, }, }); } @@ -88,7 +82,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: equiv.range, }, }); } @@ -99,7 +92,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: deprecated.range, }, }); } @@ -111,7 +103,6 @@ impl GenericLinter for EdocLinter { context: Context { header_ptr: Some(*header_ptr), doc_start, - range: hidden.range, }, }); } @@ -123,6 +114,7 @@ impl GenericLinter for EdocLinter { fn fixes( &self, context: &Self::Context, + range: TextRange, sema: &Semantic, file_id: FileId, ) -> Option> { @@ -134,12 +126,12 @@ impl GenericLinter for EdocLinter { file_id, header, context.doc_start, - context.range, + range, )]) } } -pub static LINTER: EdocLinter = EdocLinter; +pub static LINTER: OldEdocSyntaxLinter = OldEdocSyntaxLinter; fn old_edoc_syntax_fix( sema: &Semantic, @@ -302,22 +294,6 @@ mod tests { ) } - #[test] - fn test_function_doc_in_test_file() { - check_diagnostics( - r#" - //- /test/main_SUITE.erl extra:test - -module(main_SUITE). - %% @doc This is the main function documentation. - %% ^^^^ 💡 weak: W0038: EDoc style comments are deprecated. Please use Markdown instead. - main() -> - dep(). - - dep() -> ok. - "#, - ) - } - #[test] fn test_function_doc_different_arities() { check_diagnostics( diff --git a/crates/ide/src/diagnostics/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 { 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/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/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, 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 dcaff6de70..ad5c1bb680 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -110,7 +110,7 @@ pub mod diagnostics; pub mod diagnostics_collection; pub mod diff; mod highlight_related; -// @fb-only +// @fb-only: pub mod meta_only; pub use annotations::Annotation; pub use annotations::AnnotationKind; @@ -251,9 +251,9 @@ impl Analysis { }) } - pub fn should_eqwalize(&self, file_id: FileId, include_tests: bool) -> Cancellable { + pub fn should_eqwalize(&self, file_id: FileId) -> Cancellable { let is_in_app = self.file_app_type(file_id).ok() == Some(Some(AppType::App)); - Ok(is_in_app && self.is_eqwalizer_enabled(file_id, include_tests)?) + Ok(is_in_app && self.is_eqwalizer_enabled(file_id)?) } /// Computes the set of eqwalizer diagnostics for the given files, @@ -383,8 +383,8 @@ impl Analysis { /// - the app (the module belongs to) has `.eqwalizer` marker in the roof /// - or the module has `-typing([eqwalizer]).` pragma /// - or the whole project has `enable_all=true` in its `.elp.toml` file - pub fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> Cancellable { - self.with_db(|db| db.is_eqwalizer_enabled(file_id, include_tests)) + pub fn is_eqwalizer_enabled(&self, file_id: FileId) -> Cancellable { + self.with_db(|db| db.is_eqwalizer_enabled(file_id)) } /// ETF for the module's abstract forms diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 0cedbe1d90..ee5bfb7219 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -194,35 +194,53 @@ pub fn rename_var( #[cfg(test)] pub(crate) mod tests { + use elp_ide_db::RootDatabase; + use elp_ide_db::elp_base_db::AnchoredPathBuf; + use elp_ide_db::elp_base_db::FileId; + use elp_ide_db::elp_base_db::VfsPath; use elp_ide_db::elp_base_db::assert_eq_text; + use elp_ide_db::elp_base_db::fixture::ChangeFixture; use elp_ide_db::elp_base_db::fixture::WithFixture as _; + use elp_ide_db::source_change::FileSystemEdit; use elp_ide_db::text_edit::TextEdit; use elp_project_model::test_fixture::trim_indent; use elp_syntax::AstNode; use elp_syntax::algo; use elp_syntax::ast; + use fxhash::FxHashSet; use hir::AnyExprId; use hir::InFile; use hir::Semantic; use super::rename_var; + use crate::AnalysisHost; use crate::fixture; #[track_caller] pub(crate) fn check_rename(new_name: &str, fixture_before: &str, fixture_after_str: &str) { let fixture_after_str = &trim_indent(fixture_after_str); - let analysis_after = fixture::multi_file(fixture_after_str); - let (analysis, position, _) = fixture::position(fixture_before); + let (db_before, fixture) = RootDatabase::with_fixture(fixture_before); + let host_before = AnalysisHost { db: db_before }; + let analysis = host_before.analysis(); + let position = fixture.position(); + + let (db_after, fixture_after) = RootDatabase::with_fixture(fixture_after_str); + let host_after = AnalysisHost { db: db_after }; + let analysis_after = host_after.analysis(); + let rename_result = analysis .rename(position, new_name) .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}")); match rename_result { Ok(source_change) => { + let mut file_ids: FxHashSet = FxHashSet::default(); for edit in source_change.source_file_edits { let mut text_edit_builder = TextEdit::builder(); let file_id = edit.0; + // New and old file_id are the same + file_ids.insert(file_id); for indel in edit.1.into_iter() { text_edit_builder.replace(indel.delete, indel.insert); } @@ -232,6 +250,82 @@ pub(crate) mod tests { let expected = analysis_after.file_text(file_id).unwrap().to_string(); assert_eq_text!(&*expected, &*result); } + for op in source_change.file_system_edits { + let expected; + let new_file_id; + match op { + FileSystemEdit::CreateFile { + dst, + initial_contents, + } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &dst.path + ) + }); + new_file_id = *new_file.1; + expected = initial_contents; + let actual = analysis_after.file_text(new_file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*actual); + } + FileSystemEdit::MoveFile { src: _, dst } => { + let new_file = + find_new_file_id(&fixture_after, &dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file renamed to '{}'", + &dst.path + ) + }); + new_file_id = *new_file.1; + // We simply record the new file id for checking in `fixture_after``. + // The expected value will be updated by the new_file_edits below, + // and the result asserted there + } + } + file_ids.insert(new_file_id); + } + for (dst, op) in source_change.new_file_edits { + // When renaming a module, we move the original file, then apply fixup edits + // to the new file + let anchored_dst = AnchoredPathBuf { + anchor: dst.anchor, + path: dst.path, + }; + let new_file = + find_new_file_id(&fixture_after, &anchored_dst).unwrap_or_else(|| { + panic!( + "Fixture after:could not find file created as '{}'", + &anchored_dst.path + ) + }); + + let mut text_edit_builder = TextEdit::builder(); + let file_id = *new_file.1; + // New and old file_id are the same + file_ids.insert(file_id); + for indel in op.iter() { + text_edit_builder.replace(indel.delete, indel.insert.to_string()); + } + let mut result = analysis.file_text(file_id).unwrap().to_string(); + let edit = text_edit_builder.finish(); + edit.apply(&mut result); + let expected = analysis_after.file_text(file_id).unwrap().to_string(); + assert_eq_text!(&*expected, &*result); + } + // Check the balance of the expectations in the new fixture. + for file_id in &fixture_after.files { + if !file_ids.contains(file_id) { + let actual = analysis_after.file_text(*file_id).unwrap().to_string(); + let expected = if fixture.files.contains(file_id) { + analysis.file_text(*file_id).unwrap().to_string() + } else { + format!("File {:?} not present in original fixture", file_id) + }; + assert_eq_text!(&*expected, &*actual); + } + } } Err(err) => { if fixture_after_str.starts_with("error:") { @@ -247,6 +341,16 @@ pub(crate) mod tests { }; } + fn find_new_file_id<'a>( + fixture: &'a ChangeFixture, + dst: &'a AnchoredPathBuf, + ) -> Option<(&'a VfsPath, &'a FileId)> { + fixture + .files_by_path + .iter() + .find(|(name, _)| name.as_path().unwrap().to_string().ends_with(&dst.path)) + } + #[test] fn test_rename_var_1() { check_rename("Y", r#"main() -> I~ = 1."#, r#"main() -> Y = 1."#); @@ -1135,6 +1239,326 @@ pub(crate) mod tests { ); } + // --------------------------------- + // Renaming modules + + #[test] + fn rename_module_fails_name_exists() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + + #[test] + fn rename_module_fails_bad_name_1() { + check_rename( + "Main", + r#" + //- /app_a/src/main.erl + -module(ma~in). + //- /app_a_/src/main_2.erl + -module(main_2). + "#, + r#"error: Invalid new module name: 'Main'"#, + ); + } + + #[test] + fn rename_module_simple() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + "#, + r#" + //- /app_a/src/main_2.erl + -module(main_2). + "#, + ); + } + + #[test] + fn rename_module_fails_dup_name() { + check_rename( + "main_2", + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + r#"error: module 'main_2' already exists"#, + ); + } + + #[test] + fn rename_module_with_usage_internal() { + check_rename( + "main_2", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/0]). + foo() -> ok. + bar() -> main:foo(). + baz() -> main:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main:foo(). + "#, + //------------------ + r#" + //- /app_a/src/main_2.erl + -module(main_2). + -export([foo/0]). + foo() -> ok. + bar() -> main_2:foo(). + baz() -> main_2:bar(). + + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + bar() -> main_2:foo(). + "#, + ); + } + #[test] + fn rename_module_with_usage_type() { + // TODO: check for compile errors in the fixture + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_record() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun_arg() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main:foo(). + bar() -> + meck:new(main, [passthrough]), + meck:new([other, main] , [passthrough]), + meck:unload(main), + apply(main, foo, []), + ok. + -record(main, {field :: main:foo()}). + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export_type([foo/0]). + -type foo() :: ok. + //- /app_a/src/other.erl + -module(other). + -export([bar/0]). + -spec bar() -> main_3:foo(). + bar() -> + meck:new(main_3, [passthrough]), + meck:new([other, main_3] , [passthrough]), + meck:unload(main_3), + apply(main_3, foo, []), + ok. + -record(main, {field :: main_3:foo()}). + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:foo/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:foo/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_fun_as_module() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main:main/1, [U], []} || U <- UStrings], + ok. + "#, + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([main/1]). + main(X) -> {X}. + //- /app_a/src/other.erl + -module(other). + -export([bar/1]). + -spec bar(term()) -> ok. + bar(UStrings) -> + Jobs = [{fun main_3:main/1, [U], []} || U <- UStrings], + ok. + "#, + ); + } + + #[test] + fn rename_module_with_usage_define() { + check_rename( + "main_3", + r#" + //- /app_a/src/main.erl + -module(ma~in). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main:foo(U), + ?FOO(U), + ok. + "#, + //------------------ + r#" + //- /app_a/src/main_3.erl + -module(main_3). + -export([foo/1]). + foo(X) -> {X}. + + //- /app_a/src/definer.hrl + -define(FOO(X), main_3:foo(X)). + + //- /app_a/src/other.erl + -module(other). + -include("definer.hrl"). + -export([bar/0]). + -spec bar(term()) -> ok. + bar(U) -> + main_3:foo(U), + ?FOO(U), + ok. + "#, + ); + } + // --------------------------------- #[track_caller] 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_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..ec1e1d76cf 100644 --- a/crates/ide_db/src/diagnostic_code.rs +++ b/crates/ide_db/src/diagnostic_code.rs @@ -20,9 +20,9 @@ use serde::de; use strum::IntoEnumIterator; use strum_macros::EnumIter; -// @fb-only +// @fb-only: use crate::meta_only::MetaOnlyDiagnosticCode; -// @fb-only +// @fb-only: pub const BASE_URL: &str = crate::meta_only::BASE_URL; pub const BASE_URL: &str = "https://whatsapp.github.io/erlang-language-platform/docs"; // @oss-only #[derive(Clone, Debug, PartialEq, Eq, Hash, EnumIter)] @@ -93,6 +93,7 @@ pub enum DiagnosticCode { ListsReverseAppend, HirUnresolvedMacro, HirUnresolvedInclude, + BoundVarInLhs, // Wrapper for erlang service diagnostic codes ErlangService(String), @@ -100,7 +101,7 @@ pub enum DiagnosticCode { Eqwalizer(String), // Used for ad-hoc diagnostics via lints/codemods AdHoc(String), - // @fb-only + // @fb-only: MetaOnly(MetaOnlyDiagnosticCode), } // These namespaces map the error codes returned by the Erlang Service. @@ -116,7 +117,7 @@ pub enum Namespace { Parser, EDoc, WhatsApp, - // @fb-only + // @fb-only: MetaOnly, } impl fmt::Display for Namespace { @@ -131,7 +132,7 @@ impl fmt::Display for Namespace { Namespace::Parser => "p", Namespace::EDoc => "o", Namespace::WhatsApp => "w", - // @fb-only + // @fb-only: Namespace::MetaOnly => "meta_only", }; write!(f, "{namespace}") } @@ -164,7 +165,7 @@ impl Namespace { pub fn supports_doc_path(&self) -> bool { match self { Namespace::WhatsApp => true, - // @fb-only + // @fb-only: Namespace::MetaOnly => true, _ => false, } } @@ -256,10 +257,11 @@ 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}"), - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(c) => c.as_code(), } } @@ -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(), @@ -360,7 +363,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 +374,7 @@ impl DiagnosticCode { pub fn maybe_from_string(s: &str) -> Option { DIAGNOSTIC_CODE_LOOKUPS .get(s).cloned() - // @fb-only + // @fb-only: .or_else(|| MetaOnlyDiagnosticCode::from_str(s).ok().map(DiagnosticCode::MetaOnly)) .or_else( || // Look for ErlangService and AdHoc if let Some(code) = Self::is_adhoc(s) { @@ -388,7 +391,7 @@ impl DiagnosticCode { match self { DiagnosticCode::DefaultCodeForEnumIter => None, DiagnosticCode::AdHoc(_) => None, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(_) => Some(Namespace::MetaOnly), DiagnosticCode::ErlangService(code) => Namespace::from_str(code).ok(), _ => Namespace::from_str(&self.as_code()).ok(), } @@ -397,7 +400,7 @@ impl DiagnosticCode { pub fn supports_doc_path(&self) -> bool { match self { DiagnosticCode::DefaultCodeForEnumIter => false, - // @fb-only + // @fb-only: DiagnosticCode::MetaOnly(MetaOnlyDiagnosticCode::DefaultCodeForEnumIter) => false, _ => true, } } @@ -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, @@ -541,7 +545,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/eqwalizer.rs b/crates/ide_db/src/eqwalizer.rs index 4deed79778..fca577bb21 100644 --- a/crates/ide_db/src/eqwalizer.rs +++ b/crates/ide_db/src/eqwalizer.rs @@ -12,7 +12,6 @@ use std::sync::Arc; use elp_base_db::FileId; use elp_base_db::FileRange; -use elp_base_db::FileSource; use elp_base_db::ModuleName; use elp_base_db::ProjectId; use elp_base_db::SourceDatabase; @@ -89,7 +88,7 @@ pub trait EqwalizerDatabase: fn types_for_file(&self, file_id: FileId) -> Option>>; fn has_eqwalizer_module_marker(&self, file_id: FileId) -> bool; fn has_eqwalizer_ignore_marker(&self, file_id: FileId) -> bool; - fn is_eqwalizer_enabled(&self, file_id: FileId, include_tests: bool) -> bool; + fn is_eqwalizer_enabled(&self, file_id: FileId) -> bool; } pub fn eqwalizer_diagnostics_by_project( @@ -114,7 +113,7 @@ fn type_at_position( db: &dyn EqwalizerDatabase, range: FileRange, ) -> Option> { - if !db.is_eqwalizer_enabled(range.file_id, false) { + if !db.is_eqwalizer_enabled(range.file_id) { return None; } let project_id = db.file_app_data(range.file_id)?.project_id; @@ -149,7 +148,7 @@ fn type_at_position( } fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option>> { - if !db.is_eqwalizer_enabled(file_id, false) { + if !db.is_eqwalizer_enabled(file_id) { return None; } let project_id = db.file_app_data(file_id)?.project_id; @@ -162,7 +161,7 @@ fn types_for_file(db: &dyn EqwalizerDatabase, file_id: FileId) -> Option bool { +fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId) -> bool { if !otp_supported_by_eqwalizer() { return false; } @@ -178,11 +177,8 @@ fn is_eqwalizer_enabled(db: &dyn EqwalizerDatabase, file_id: FileId, include_tes let project = db.project_data(project_id); let eqwalizer_config = &project.eqwalizer_config; let module_index = db.module_index(project_id); - let is_src = module_index.file_source_for_file(file_id) == Some(FileSource::Src); - let is_test_opted_in = db.is_test_suite_or_test_helper(file_id) == Some(true) && include_tests; let global_opt_in = eqwalizer_config.enable_all; - let opt_in = - (global_opt_in && (is_src || is_test_opted_in)) || db.has_eqwalizer_module_marker(file_id); + let opt_in = global_opt_in || db.has_eqwalizer_module_marker(file_id); let ignored_in_config = if let Some(module_name) = module_index.module_for_file(file_id) { eqwalizer_config .ignore_modules_compiled_patterns diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index c92be86f5f..038b41f0be 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -43,11 +43,22 @@ pub fn pick_best_token( tokens.max_by_key(move |t| f(t.kind())) } +/// Given a syntax node, check it it is immediately enclosed in a call, +/// which can represent a function call or a type. +/// For a remote call, the node can be the module or the function name. +/// In the former case, there is an extra level of nesting, so we need +/// to check up to 3 steps up pub fn get_call(syntax: &SyntaxNode) -> Option { - if let Some(call) = ast::Call::cast(syntax.parent()?) { - Some(call) + ast::Call::cast(syntax.parent()?) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?)) + .or_else(|| ast::Call::cast(syntax.parent()?.parent()?.parent()?)) +} + +pub fn get_external_fun(syntax: &SyntaxNode) -> Option { + if let Some(external_fun) = ast::ExternalFun::cast(syntax.parent()?) { + Some(external_fun) } else { - ast::Call::cast(syntax.parent()?.parent()?) + ast::ExternalFun::cast(syntax.parent()?.parent()?) } } diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 9b64d09b31..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; @@ -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![]); } diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs index 889569ca4e..bd5a49f8cf 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; @@ -25,9 +27,12 @@ use hir::Semantic; use crate::SymbolDefinition; use crate::helpers::get_call; +use crate::helpers::get_external_fun; use crate::search::NameLike; +use crate::source_change::FileSystemEdit; use crate::source_change::SourceChange; use crate::text_edit::TextEdit; +use crate::text_edit::TextEditBuilder; pub type RenameResult = Result; @@ -106,6 +111,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 +139,10 @@ impl SymbolDefinition { ) -> RenameResult { match self.clone() { SymbolDefinition::Module(_) => { - rename_error!("Cannot rename module") + if safety_check == SafetyChecks::Yes && !is_valid_module_name(new_name) { + rename_error!("Invalid new module name: '{}'", new_name); + } + self.rename_reference(sema, new_name, parens_needed_in_context, safety_check) } SymbolDefinition::Function(fun) => { if safety_check == SafetyChecks::Yes && !is_valid_function_name(new_name) { @@ -375,6 +395,7 @@ impl SymbolDefinition { ); Ok(source_change) } + SymbolDefinition::Module(_module) => self.rename_module(sema, new_name, safety_check), // Note: This is basically an internal error, this function is called from // SymbolDefinition::rename which already weeds them out _ => { @@ -382,6 +403,184 @@ impl SymbolDefinition { } } } + + fn rename_module( + &self, + sema: &Semantic, + new_name: &str, + safety_check: SafetyChecks, + ) -> RenameResult { + let file_id = self.file().file_id; + if let Some(project_id) = sema.db.file_project_id(file_id) { + let module_index = sema.db.module_index(project_id); + if safety_check == SafetyChecks::Yes { + let new_name_module = ModuleName::new(new_name); + if module_index + .all_modules() + .iter() + .any(|name| name == &new_name_module) + { + rename_error!("module '{}' already exists", new_name); + } + } + + let mut source_change = SourceChange::default(); + // Step 1, rename all references + let usages = self.clone().usages(sema).all(); + let mut renamed_module_edit: TextEdit = TextEdit::default(); + rename_remote_module_call_refs( + usages, + file_id, + new_name, + &mut source_change, + &mut renamed_module_edit, + ); + + // Step 2: Rename the module attribute in the module being renamed + let form_list = sema.form_list(file_id); + if let Some(module_attribute) = form_list.module_attribute() { + let ast = module_attribute.form_id.get_ast(sema.db, file_id); + if let Some(name) = ast.name() { + let range = name.syntax().text_range(); + let mut builder = TextEdit::builder(); + builder.replace(range, new_name.to_string()); + renamed_module_edit + .union(builder.finish()) + .expect("Could not combine TextEdits"); + } + } + + let anchor = file_id; + let path = format!("{new_name}.erl"); + let dst = AnchoredPathBuf { anchor, path }; + source_change.insert_new_source_edit(dst.clone().into(), renamed_module_edit); + source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst }); + Ok(source_change) + } else { + rename_error!( + "Could not find project for '{:?}'", + self.file().name(sema.db.upcast()) + ) + } + } +} + +fn rename_remote_module_call_refs( + usages: crate::UsageSearchResult, + file_id: FileId, + new_name: &str, + source_change: &mut SourceChange, + renamed_module_edit: &mut TextEdit, +) { + usages.iter().for_each(|(usage_file_id, refs)| { + if let Some(edit) = rename_module_in_refs(refs, new_name) { + if usage_file_id == file_id { + renamed_module_edit + .union(edit) + .expect("Could not combine TextEdits"); + } else { + source_change.insert_source_edit(usage_file_id, edit); + } + }; + }); +} + +fn rename_module_in_refs(refs: &[NameLike], new_name: &str) -> Option { + let mut builder = TextEdit::builder(); + for usage in refs { + // Note: we cannot blindly replace all occurrences of an + // atom that happens to be a module name + // We will flesh out other usages as we need them + let _ = rename_call_module_in_ref(usage, &mut builder, new_name); + let _ = rename_external_fun_module_in_ref(usage, &mut builder, new_name); + } + Some(builder.finish()) +} + +fn rename_call_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let call = get_call(usage.syntax())?; + // We can only rename an atom usage + let usage_atom = match usage { + NameLike::Name(ast::Name::Atom(atom)) => atom, + _ => return Some(()), + }; + + // First check if this is the module part of a remote call (e.g., module:function()) + if let Some(ast::Expr::Remote(remote)) = call.expr() + && let Some(module) = remote.module() + && let Some(ast::ExprMax::Atom(mod_atom)) = module.module() + && mod_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + return Some(()); + } + + // Check if this is a known function call that takes a module as an argument + // Extract function name and optional module name based on call type + let (module_name, function_name) = match call.expr()? { + ast::Expr::Remote(remote) => { + let module = remote.module()?; + let mod_atom = match module.module()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + let fun_atom = match remote.fun()? { + ast::ExprMax::Atom(atom) => atom, + _ => return Some(()), + }; + (Some(mod_atom.text()?), fun_atom.text()?) + } + ast::Expr::ExprMax(ast::ExprMax::Atom(fun_atom)) => (None, fun_atom.text()?), + _ => return Some(()), + }; + + let args = call.args()?; + let args_vec: Vec<_> = args.args().collect(); + let arity = args_vec.len(); + let pattern_key = (module_name.as_deref(), function_name.as_str(), arity); + + // Use combined patterns that merge dynamic call patterns and module argument patterns + let combined_patterns = hir::sema::to_def::get_module_arg_patterns(); + if let Some(pattern) = combined_patterns.get(&pattern_key) + && let Some(arg) = args_vec.get(pattern.index) + { + match arg { + ast::Expr::ExprMax(ast::ExprMax::Atom(arg_atom)) + if pattern.accepts_atom() && arg_atom.syntax() == usage_atom.syntax() => + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + } + ast::Expr::ExprMax(ast::ExprMax::List(list)) if pattern.accepts_list() => { + // Handle list of modules (e.g., meck:new([mod1, mod2], Options)) + for expr in list.exprs() { + if let ast::Expr::ExprMax(ast::ExprMax::Atom(list_atom)) = expr + && list_atom.syntax() == usage_atom.syntax() + { + builder.replace(usage_atom.syntax().text_range(), new_name.to_string()); + break; + } + } + } + _ => {} + } + } + + Some(()) +} + +fn rename_external_fun_module_in_ref( + usage: &NameLike, + builder: &mut TextEditBuilder, + new_name: &str, +) -> Option<()> { + let external_fun = get_external_fun(usage.syntax())?; + let module = external_fun.module()?; + builder.replace(module.name()?.syntax().text_range(), new_name.to_string()); + Some(()) } fn source_edit_from_usages( diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 20b4f050b4..1e66598359 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -325,7 +325,7 @@ impl<'a> FindUsages<'a> { /// Represents possible ast reference points - /// a string for header, or ast::Name for everything else -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NameLike { Name(ast::Name), String(ast::String), diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index 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, } diff --git a/crates/project_model/src/buck.rs b/crates/project_model/src/buck.rs index 013f3cbc87..45c6b7f732 100644 --- a/crates/project_model/src/buck.rs +++ b/crates/project_model/src/buck.rs @@ -58,6 +58,7 @@ lazy_static! { } const ERL_EXT: &str = "erl"; +const BUCK_ISOLATION_DIR: &str = "lsp"; #[derive( Debug, @@ -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)] @@ -1622,72 +1643,66 @@ mod tests { assert_eq!(expected, actual) } - // TODO: enable when buck is properly set up on github project - // @fb-only - const BUCK_TESTS_ENABLED: bool = false; // @oss-only - #[track_caller] fn check_buck_bxl_query(build_generated: bool, expect: Expect) { - if BUCK_TESTS_ENABLED { - let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); - // We only need buck_config to get the buck command, everything but the buck root is ignored. - let buck_config = BuckConfig { - config_path: None, - buck_root: Some(buck_root), - enabled: true, - deps_target: None, - deps_targets: vec![], - build_deps: false, - included_targets: vec![], - excluded_targets: vec![], - source_root: None, - 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("fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/...") - .output() - .unwrap(); - if !output.status.success() { - panic!("{output:#?}"); - } - let string = String::from_utf8(output.stdout).unwrap(); - let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); - let string = string.replace(&prelude_cell, "/[prelude]/"); - - let to_replace = env!("CARGO_WORKSPACE_DIR"); - let string = string.replace(to_replace, "/[..]/"); - expect.assert_eq(&string); + let buck_root = to_abs_path_buf(&std::env::current_dir().unwrap()).unwrap(); + // We only need buck_config to get the buck command, everything but the buck root is ignored. + let buck_config = BuckConfig { + config_path: None, + buck_root: Some(buck_root), + enabled: true, + deps_target: None, + deps_targets: vec![], + build_deps: false, + included_targets: vec![], + excluded_targets: vec![], + source_root: None, + test_application_labels: vec!["test_application".to_string()], + }; + let generated_args = if build_generated { + vec!["--build_generated_code", "true"] + } else { + vec![] + }; + let output = buck_config + .buck_command() + .arg("bxl") + .arg("prelude//erlang/elp.bxl:elp_config") + .arg("--") + .args(generated_args) + .arg("--included_targets") + .arg("root//buck_tests_2/auto_gen/...") + .output() + .unwrap(); + if !output.status.success() { + panic!("{output:#?}"); } + let string = String::from_utf8(output.stdout).unwrap(); + let prelude_cell = get_prelude_cell(&buck_config).expect("could not get prelude"); + let string = string.replace(&prelude_cell, "/[prelude]/"); + + let to_replace = env!("CARGO_WORKSPACE_DIR"); + let string = string.replace(to_replace, "/[..]/"); + expect.assert_eq(&string); } #[test] #[ignore] fn build_info_buck_bxl_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { check_buck_bxl_query( false, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1697,7 +1712,7 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, @@ -1840,23 +1855,23 @@ mod tests { #[test] #[ignore] fn build_info_buck_bxl_generated_query() { - if BUCK_TESTS_ENABLED { + if cfg!(feature = "buck") { // Note that there is now a value for `srcs` in the - // "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs" + // "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs" // target check_buck_bxl_query( true, expect![[r#" { - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a": { "name": "auto_gen_a", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl" ], "includes": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include" ], "labels": [ "user_application" @@ -1866,12 +1881,12 @@ mod tests { "included_apps": [], "origin": "app" }, - "fbcode//whatsapp/elp/test_projects/buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { + "root//buck_tests_2/auto_gen/auto_gen_a:generated_srcs": { "name": "generated_srcs", "app_name": null, "suite": null, "srcs": [ - "/[..]/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" + "/[..]/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl" ], "includes": [], "labels": [ diff --git a/eqwalizer b/eqwalizer index a835ce03b0..0f514eb389 160000 --- a/eqwalizer +++ b/eqwalizer @@ -1 +1 @@ -Subproject commit a835ce03b0308a9869af964e35a24466b49cda51 +Subproject commit 0f514eb3893fa7070835c83ecb49fbea31b0426d diff --git a/erlang_service/src/elp_lint.erl b/erlang_service/src/elp_lint.erl index 609e8c0712..31d2c70248 100644 --- a/erlang_service/src/elp_lint.erl +++ b/erlang_service/src/elp_lint.erl @@ -4372,14 +4372,14 @@ is_format_function(io, fwrite) -> true; is_format_function(io, format) -> true; is_format_function(io_lib, fwrite) -> true; is_format_function(io_lib, format) -> true; -% @fb-only -% @fb-only +% @fb-only: is_format_function(wa_log, send_if) -> true; +% @fb-only: is_format_function(wa_string, format) -> true; is_format_function(M, F) when is_atom(M), is_atom(F) -> false. %% check_format_1([Arg]) -> ok | {warn,Level,Format,[Arg]}. -% @fb-only -% @fb-only +% @fb-only[end= ]: format_args(wa_log, send_if, [_Level, _Meta, _Opts, Format, Args]) -> [Format, Args]; +% @fb-only[end= ]: format_args(wa_string, format, [Format, Args, _Options]) -> [Format, Args]; format_args(_M, _F, As) -> As. diff --git a/test/test_projects/.buckconfig b/test/test_projects/.buckconfig new file mode 100644 index 0000000000..f14e564a7d --- /dev/null +++ b/test/test_projects/.buckconfig @@ -0,0 +1,24 @@ +[cells] + root = . + prelude = prelude + toolchains = toolchains + none = none + +[cell_aliases] + config = prelude + ovr_config = prelude + fbcode = none + fbsource = none + fbcode_macros = none + buck = none + +[external_cells] + prelude = bundled + +[parser] + target_platform_detector_spec = target:root//...->prelude//platforms:default \ + target:prelude//...->prelude//platforms:default \ + target:toolchains//...->prelude//platforms:default + +[build] + execution_platforms = prelude//platforms:default diff --git a/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer b/test/test_projects/.buckroot similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer rename to test/test_projects/.buckroot diff --git a/test_projects/.gitignore b/test/test_projects/.gitignore similarity index 85% rename from test_projects/.gitignore rename to test/test_projects/.gitignore index b34578734c..2672c17cc4 100644 --- a/test_projects/.gitignore +++ b/test/test_projects/.gitignore @@ -3,3 +3,4 @@ *wa.build_info *_build/ *rebar.lock +buck-out/ diff --git a/test_projects/README.md b/test/test_projects/README.md similarity index 100% rename from test_projects/README.md rename to test/test_projects/README.md diff --git a/test/test_projects/buck_bad_config/.elp.toml b/test/test_projects/buck_bad_config/.elp.toml new file mode 100644 index 0000000000..963072284c --- /dev/null +++ b/test/test_projects/buck_bad_config/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//buck_bad_config/..." ] +source_root = "buck_bad_config" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/buck_bad_config/BUCK b/test/test_projects/buck_bad_config/BUCK new file mode 100644 index 0000000000..498c769dac --- /dev/null +++ b/test/test_projects/buck_bad_config/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "bad_app", + srcs = glob(["src/*.erl"]), + applications = [ + "root//buck_bad_config/non_existent:missing", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test_projects/buck_bad_config/src/bad_app.erl b/test/test_projects/buck_bad_config/src/bad_app.erl similarity index 100% rename from test_projects/buck_bad_config/src/bad_app.erl rename to test/test_projects/buck_bad_config/src/bad_app.erl diff --git a/test/test_projects/buck_tests/.elp.toml b/test/test_projects/buck_tests/.elp.toml new file mode 100644 index 0000000000..b38d6f043f --- /dev/null +++ b/test/test_projects/buck_tests/.elp.toml @@ -0,0 +1,9 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//buck_tests/..." ] +excluded_targets = [ "buck_tests:test_elp_ignored" ] +source_root = "buck_tests" + +[eqwalizer] +enable_all = false diff --git a/test_projects/buck_tests/TARGETS.v2_ b/test/test_projects/buck_tests/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/TARGETS.v2_ rename to test/test_projects/buck_tests/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ similarity index 77% rename from test_projects/buck_tests/test_elp/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp/TARGETS.v2_ index 83089813da..7e5bd95db5 100644 --- a/test_projects/buck_tests/test_elp/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp/TARGETS.v2_ @@ -7,11 +7,11 @@ erlang_application( ]), app_src = "src/test_elp.app.src", applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_direct_dep:test_elp_direct_dep", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_private_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_no_public_headers", - "//whatsapp/elp/test_projects/buck_tests:test_elp_flat_outside_target", - "//whatsapp/elp/test_projects/buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", + "//buck_tests/test_elp_direct_dep:test_elp_direct_dep", + "//buck_tests:test_elp_no_private_headers", + "//buck_tests:test_elp_no_public_headers", + "//buck_tests:test_elp_flat_outside_target", + "//buck_tests/test_elp_flat_inside_target:test_elp_flat_inside_target", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp/include/test_elp.hrl b/test/test_projects/buck_tests/test_elp/include/test_elp.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/include/test_elp.hrl rename to test/test_projects/buck_tests/test_elp/include/test_elp.hrl diff --git a/test_projects/buck_tests/test_elp/src/test_elp.app.src b/test/test_projects/buck_tests/test_elp/src/test_elp.app.src similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.app.src rename to test/test_projects/buck_tests/test_elp/src/test_elp.app.src diff --git a/test_projects/buck_tests/test_elp/src/test_elp.erl b/test/test_projects/buck_tests/test_elp/src/test_elp.erl similarity index 100% rename from test_projects/buck_tests/test_elp/src/test_elp.erl rename to test/test_projects/buck_tests/test_elp/src/test_elp.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test1.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/handle_update_test2.json diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_header.hrl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_SUITE_data/untracked_module.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.erl diff --git a/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl b/test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl similarity index 100% rename from test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl rename to test/test_projects/buck_tests/test_elp/test/test_elp_test_utils.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ similarity index 62% rename from test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ index d0fea8149b..a752b99087 100644 --- a/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ +++ b/test/test_projects/buck_tests/test_elp_direct_dep/TARGETS.v2_ @@ -5,10 +5,10 @@ erlang_application( "src/*.hrl", ]), applications = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", + "//buck_tests/test_elp_transitive_dep:test_elp_transitive_dep", ], extra_includes = [ - "//whatsapp/elp/test_projects/buck_tests/test_elp:test_elp", + "//buck_tests/test_elp:test_elp", ], includes = glob(["include/*.hrl"]), version = "1.0.0", diff --git a/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/include/test_elp_direct_dep.hrl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep.erl diff --git a/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl b/test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_direct_dep/src/test_elp_direct_dep_private.hrl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_flat_inside_target/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_inside_target/test_elp_flat_inside_target.hrl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.erl diff --git a/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl b/test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl rename to test/test_projects/buck_tests/test_elp_flat_outside_target/test_elp_flat_outside_target.hrl diff --git a/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl b/test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl similarity index 100% rename from test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl rename to test/test_projects/buck_tests/test_elp_ignored/test_elp_ignored.erl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl b/test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl rename to test/test_projects/buck_tests/test_elp_no_private_headers/include/test_elp_no_private_headers.hrl diff --git a/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl b/test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl rename to test/test_projects/buck_tests/test_elp_no_private_headers/src/test_elp_no_private_headers.erl diff --git a/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl b/test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl similarity index 100% rename from test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl rename to test/test_projects/buck_tests/test_elp_no_public_headers/src/test_elp_no_headers.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ b/test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ rename to test/test_projects/buck_tests/test_elp_transitive_dep/TARGETS.v2_ diff --git a/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/include/test_elp_transitive_dep.hrl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep.erl diff --git a/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl b/test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl similarity index 100% rename from test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl rename to test/test_projects/buck_tests/test_elp_transitive_dep/src/test_elp_transitive_dep_private.hrl diff --git a/test/test_projects/buck_tests_2/.elp.toml b/test/test_projects/buck_tests_2/.elp.toml new file mode 100644 index 0000000000..e71eb6a819 --- /dev/null +++ b/test/test_projects/buck_tests_2/.elp.toml @@ -0,0 +1,12 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ + "root//buck_tests_2/util/app_a/...", + "root//buck_tests_2:check_include" + ] +excluded_targets = [ "root//buck_tests_2:test_elp_ignored" ] +source_root = "buck_tests_2" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/buck_tests_2/BUCK b/test/test_projects/buck_tests_2/BUCK new file mode 100644 index 0000000000..b01fdb868c --- /dev/null +++ b/test/test_projects/buck_tests_2/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check_include", + srcs = [ + "check_include/src/top_includer.erl", + ], + applications = [ + "common_test", + "stdlib", + "root//buck_tests_2:check_include_separate_1", + "root//buck_tests_2:check_include_separate_2", + ], + includes = [], + labels = [], + resources = [], +) + +erlang_application( + name = "check_include_separate_1", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = [ + "check_include_separate_1/include/top_includer.hrl", + ], + resources = [], +) + +erlang_application( + name = "check_include_separate_2", + srcs = glob([ + "src/*.erl", + ]), + applications = [ + ], + includes = glob(["check_include_separate_2/include/*.hrl"]), + resources = [], +) diff --git a/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK new file mode 100644 index 0000000000..f021317020 --- /dev/null +++ b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "auto_gen_a", + srcs = glob([ + "src/*.erl", + "src/*.hrl", + ]), + includes = glob(["include/*.hrl"]), + visibility = ["//buck_tests_2/..."], +) + +erlang_application( + name = "generated_srcs", + srcs = [ + ":generated.erl", + ], + labels = ["generated"], + visibility = ["//buck_tests_2/..."], +) + +export_file( + name = "generated.erl", + src = "out/pretend_generated.erl", +) diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/include/auto_gen_a.hrl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/out/pretend_generated.erl diff --git a/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl b/test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl similarity index 100% rename from test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl rename to test/test_projects/buck_tests_2/auto_gen/auto_gen_a/src/auto_gen_a.erl diff --git a/test_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/test_projects/buck_tests_2/generated/BUCK b/test/test_projects/buck_tests_2/generated/BUCK new file mode 100644 index 0000000000..8eec40830b --- /dev/null +++ b/test/test_projects/buck_tests_2/generated/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "generated_headers", + includes = [ + "out/generated_header.hrl", + ], + labels = ["generated"], + visibility = [ + "PUBLIC", + ], +) diff --git a/test_projects/buck_tests_2/generated/out/generated_header.hrl b/test/test_projects/buck_tests_2/generated/out/generated_header.hrl similarity index 100% rename from test_projects/buck_tests_2/generated/out/generated_header.hrl rename to test/test_projects/buck_tests_2/generated/out/generated_header.hrl diff --git a/test/test_projects/buck_tests_2/util/app_a/BUCK b/test/test_projects/buck_tests_2/util/app_a/BUCK new file mode 100644 index 0000000000..4231c1c6e8 --- /dev/null +++ b/test/test_projects/buck_tests_2/util/app_a/BUCK @@ -0,0 +1,16 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a_target", + srcs = glob(["src/*.erl"]), + app_name = "app_a", + applications = [ + "root//buck_tests_2/auto_gen/auto_gen_a:auto_gen_a", + "root//buck_tests_2/generated:generated_headers", + ], + includes = glob( + ["include/*.hrl"], + exclude = ["include/junk.hrl"], + ), + version = "1.0.0", +) diff --git a/test_projects/buck_tests_2/util/app_a/include/junk.hrl b/test/test_projects/buck_tests_2/util/app_a/include/junk.hrl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/include/junk.hrl rename to test/test_projects/buck_tests_2/util/app_a/include/junk.hrl diff --git a/test_projects/buck_tests_2/util/app_a/src/app_a.erl b/test/test_projects/buck_tests_2/util/app_a/src/app_a.erl similarity index 100% rename from test_projects/buck_tests_2/util/app_a/src/app_a.erl rename to test/test_projects/buck_tests_2/util/app_a/src/app_a.erl diff --git a/test_projects/end_to_end/.elp.toml b/test/test_projects/codegen_test/.elp.toml similarity index 50% rename from test_projects/end_to_end/.elp.toml rename to test/test_projects/codegen_test/.elp.toml index f015956e45..f21fe1bc73 100644 --- a/test_projects/end_to_end/.elp.toml +++ b/test/test_projects/codegen_test/.elp.toml @@ -1,7 +1,7 @@ [buck] enabled = true build_deps = false -included_targets = ["fbcode//whatsapp/elp/test_projects/end_to_end/..."] +included_targets = ["root//codegen_test/..."] [eqwalizer] enable_all = false diff --git a/test/test_projects/codegen_test/BUCK b/test/test_projects/codegen_test/BUCK new file mode 100644 index 0000000000..1df5f86eba --- /dev/null +++ b/test/test_projects/codegen_test/BUCK @@ -0,0 +1,73 @@ +oncall("vscode_erlang") + +# Code generation rule - generates Erlang modules from template files +genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", +) + +genrule( + name = "example_service_client_erl", + srcs = [ + "templates/example_service_client.erl", + ], + outs = { + "example_service_client.erl": ["example_service_client.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_client.erl $OUT/example_service_client.erl", +) + +genrule( + name = "example_service_types_hrl", + srcs = [ + "templates/example_service_types.hrl", + ], + outs = { + "example_service_types.hrl": ["example_service_types.hrl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.hrl $OUT/example_service_types.hrl", +) + +# Erlang library containing only the generated code +erlang_app( + name = "example_service_generated", + srcs = [ + # Include generated Erlang modules from genrule output + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + # Include generated header files from genrule output + ":example_service_types_hrl[example_service_types.hrl]", + ], +) + +# Erlang application that uses the generated code (non-generated files only) +erlang_application( + name = "codegen_test_app", + srcs = glob(["app_a/src/*.erl"]), + app_name = "codegen_test", + app_src = "app_a/src/codegen_test.app.src", + applications = [ + "kernel", + "stdlib", + ":example_service_generated", + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +# Test to verify the generated code works +erlang_tests( + suites = [ + "app_a/test/codegen_test_SUITE.erl", + ], + deps = [":codegen_test_app"], +) diff --git a/test/test_projects/codegen_test/README.md b/test/test_projects/codegen_test/README.md new file mode 100644 index 0000000000..1f73475d9d --- /dev/null +++ b/test/test_projects/codegen_test/README.md @@ -0,0 +1,138 @@ +# Code Generation Test Project + +This test project demonstrates Buck2-based code generation for Erlang +applications using `genrule`. + +## Project Structure + +``` +codegen_test/ +├── .elp.toml # ELP configuration +├── BUCK # Buck build configuration with genrule +├── README.md # This file +├── templates/ # Erlang template files (input) +│ ├── example_service_types.erl # Type definitions template +│ ├── example_service_client.erl # Client stubs template +│ └── example_service_types.hrl # Header file template +├── generated/ # Pre-generated code (for reference) +│ ├── example_service_types.erl # Generated type module +│ ├── example_service_client.erl # Generated client stubs +│ └── example_service_types.hrl # Generated header file +└── app_a/ + ├── src/ + │ ├── codegen_test.app.src # Application metadata + │ └── example_usage.erl # Example using generated code + └── test/ + └── codegen_test_SUITE.erl # Test suite +``` + +## How It Works + +### 1. Template Files + +The templates directory contains well-formed Erlang files that serve as input to +the code generation process. These are complete, valid Erlang files that are +copied to the output directory during the build. + +### 2. Code Generation + +The code is generated automatically during the build process using a +`genrule`: + +```python +genrule( + name = "example_service_types_erl", + srcs = [ + "templates/example_service_types.erl", + ], + outs = { + "example_service_types.erl": ["example_service_types.erl"], + }, + cmd = "cp $SRCDIR/templates/example_service_types.erl $OUT/example_service_types.erl", +) +``` + +The genrule: + +- Takes the template file as input (`srcs`) +- Defines named outputs using `outs` parameter (creates subtargets for each + file) +- Uses `cp` command to copy template files to `$OUT` +- Outputs files to `$OUT` directory in `buck-out/` + +### 3. Build Integration + +The `BUCK` file references the generated files using subtarget syntax: + +```python +erlang_app( + name = "example_service_generated", + srcs = [ + ":example_service_types_erl[example_service_types.erl]", + ":example_service_client_erl[example_service_client.erl]", + ], + includes = [ + ":example_service_types_hrl[example_service_types.hrl]", + ], +) +``` + +When Buck builds the application: + +1. The genrule runs first, copying the template Erlang files to the output +2. The generated files are placed in + `buck-out/v2/gen/.../example_service_types_erl/` +3. The `erlang_app` consumes these files via subtarget references (`[filename]` + syntax) +4. The files are copied to the application's build directory in `buck-out/` + +### 4. Using Generated Code + +Application code includes the generated header and uses the types: + +```erlang +-module(example_usage). +-include("example_service_types.hrl"). + +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +get_user_by_id(UserId) -> + example_service_client:get_user(UserId). +``` + +## Building and Testing + +```bash +# Build just the code generation step +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:example_service_types_erl + +# Build the application (automatically runs code generation) +buck2 build fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_app + +# Run tests +buck2 test fbcode//whatsapp/elp/test/test_projects/codegen_test:codegen_test_SUITE + +# View generated files in buck-out +find buck-out -path "*codegen_test*" -name "example_service_*.erl" +``` + +## Test Coverage + +The test suite (`app_a/test/codegen_test_SUITE.erl`) verifies: + +- Generated record types work correctly +- Generated client functions are callable +- Record fields have correct types and defaults +- Integration with application code + +All tests pass: + +``` +Tests finished: Pass 3. Fail 0. Fatal 0. Skip 0. Build failure 0 +``` diff --git a/test/test_projects/codegen_test/app_a/src/codegen_test.app.src b/test/test_projects/codegen_test/app_a/src/codegen_test.app.src new file mode 100644 index 0000000000..7a38d49a24 --- /dev/null +++ b/test/test_projects/codegen_test/app_a/src/codegen_test.app.src @@ -0,0 +1,14 @@ +{application, codegen_test, + [{description, "Test application demonstrating code generation"}, + {vsn, "1.0.0"}, + {registered, []}, + {applications, + [kernel, + stdlib, + example_service_generated + ]}, + {env,[]}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/test/test_projects/codegen_test/app_a/src/example_usage.erl b/test/test_projects/codegen_test/app_a/src/example_usage.erl new file mode 100644 index 0000000000..a624c2089b --- /dev/null +++ b/test/test_projects/codegen_test/app_a/src/example_usage.erl @@ -0,0 +1,51 @@ +-module(example_usage). +-compile([warn_missing_spec_all]). +-moduledoc """ +Example module that demonstrates using generated code. +This module uses the types and client functions generated from +the example_service.schema file. +""". + +%% Include the generated header file +-include_lib("example_service_generated/include/example_service_types.hrl"). + +%% API exports +-export([ + create_sample_user/0, + create_query_request/1, + get_user_by_id/1 +]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-doc """ +Creates a sample user_info record using generated types +""". +-spec create_sample_user() -> #user_info{}. +create_sample_user() -> + #user_info{ + user_id = <<"user_123">>, + username = <<"john_doe">>, + age = 25, + is_active = true + }. + +-doc """ +Creates a query_request record +""". +-spec create_query_request(binary()) -> #query_request{}. +create_query_request(QueryId) -> + #query_request{ + query_id = QueryId, + filters = [{age, greater_than, 18}] + }. + +-doc """ +Uses the generated client to get user information +""". +-spec get_user_by_id(binary()) -> {ok, term()} | {error, term()}. +get_user_by_id(UserId) -> + %% This calls the generated client function + example_service_client:get_user(UserId). diff --git a/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl new file mode 100644 index 0000000000..3c0135dcee --- /dev/null +++ b/test/test_projects/codegen_test/app_a/test/codegen_test_SUITE.erl @@ -0,0 +1,73 @@ +%%% Test suite for code generation functionality +-module(codegen_test_SUITE). + +-include_lib("stdlib/include/assert.hrl"). +-include("example_service_types.hrl"). + +%% CT callbacks +-export([all/0, init_per_suite/1, end_per_suite/1]). + +%% Test cases +-export([ + test_generated_types/1, + test_generated_client/1, + test_user_record_creation/1 +]). + +%%%=================================================================== +%%% CT Callbacks +%%%=================================================================== + +all() -> + [ + test_generated_types, + test_generated_client, + test_user_record_creation + ]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +%%%=================================================================== +%%% Test Cases +%%%=================================================================== + +test_generated_types(_Config) -> + %% Test that we can create a user_info record + User = #user_info{ + user_id = <<"test_123">>, + username = <<"testuser">>, + age = 30, + is_active = true + }, + + %% Verify the record fields + ?assertEqual(<<"test_123">>, User#user_info.user_id), + ?assertEqual(<<"testuser">>, User#user_info.username), + ?assertEqual(30, User#user_info.age), + ?assertEqual(true, User#user_info.is_active), + + ok. + +test_generated_client(_Config) -> + %% Test that the generated client module exists and can be called + Result = example_service_client:get_user(<<"user_123">>), + + %% The generated stub returns {ok, generated_response} + ?assertMatch({ok, _}, Result), + + ok. + +test_user_record_creation(_Config) -> + %% Test the example_usage module + User = example_usage:create_sample_user(), + + %% Verify it's a valid user_info record + ?assertMatch(#user_info{}, User), + ?assertEqual(<<"user_123">>, User#user_info.user_id), + ?assertEqual(25, User#user_info.age), + + ok. diff --git a/test/test_projects/codegen_test/generated/example_service_client.erl b/test/test_projects/codegen_test/generated/example_service_client.erl new file mode 100644 index 0000000000..6b38c39425 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_client.erl @@ -0,0 +1,37 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. diff --git a/test/test_projects/codegen_test/generated/example_service_types.erl b/test/test_projects/codegen_test/generated/example_service_types.erl new file mode 100644 index 0000000000..3f89adcba9 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_types.erl @@ -0,0 +1,55 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. diff --git a/test/test_projects/codegen_test/generated/example_service_types.hrl b/test/test_projects/codegen_test/generated/example_service_types.hrl new file mode 100644 index 0000000000..4c957a0906 --- /dev/null +++ b/test/test_projects/codegen_test/generated/example_service_types.hrl @@ -0,0 +1,27 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). diff --git a/test/test_projects/codegen_test/templates/example_service_client.erl b/test/test_projects/codegen_test/templates/example_service_client.erl new file mode 100644 index 0000000000..ab30a89f74 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_client.erl @@ -0,0 +1,38 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated client for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_client). + +-include("example_service_types.hrl"). + +%% API exports +-export([get_user/1]). +-export([query_users/1]). +-export([update_user/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + + +%% @doc Retrieves user information by user ID +-spec get_user(term()) -> {ok, term()} | {error, term()}. +get_user(_UserId) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Queries users with given filters +-spec query_users(term()) -> {ok, term()} | {error, term()}. +query_users(_Request) -> + %% Auto-generated client stub + {ok, generated_response}. + +%% @doc Updates user information +-spec update_user(term(), term()) -> {ok, term()} | {error, term()}. +update_user(_UserId, _UserInfo) -> + %% Auto-generated client stub + {ok, generated_response}. + diff --git a/test/test_projects/codegen_test/templates/example_service_types.erl b/test/test_projects/codegen_test/templates/example_service_types.erl new file mode 100644 index 0000000000..833df167c6 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_types.erl @@ -0,0 +1,56 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated type definitions for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- +-module(example_service_types). + +%% API exports +-export([new_user_info/0]). +-export([new_query_request/0]). +-export([new_query_response/0]). +-export([get_version/0]). + +%% Type exports +-export_type([user_info/0]). +-export_type([query_request/0]). +-export_type([query_response/0]). + +%% Record definitions +-record(user_info, {user_id = <<>>, username = <<>>, age = 0, is_active = false}). +-record(query_request, {query_id = <<>>, filters = []}). +-record(query_response, {results = [], count = 0}). + +%% Type definitions +-type user_info() :: #user_info{}. +-type query_request() :: #query_request{}. +-type query_response() :: #query_response{}. + +%%%=================================================================== +%%% API +%%%=================================================================== + +%% @doc Returns the schema version +-spec get_version() -> binary(). +get_version() -> + <<"1.0.0">>. + + +%% @doc Creates a new user_info record +-spec new_user_info() -> #user_info{}. +new_user_info() -> + #user_info{}. + + +%% @doc Creates a new query_request record +-spec new_query_request() -> #query_request{}. +new_query_request() -> + #query_request{}. + + +%% @doc Creates a new query_response record +-spec new_query_response() -> #query_response{}. +new_query_response() -> + #query_response{}. + diff --git a/test/test_projects/codegen_test/templates/example_service_types.hrl b/test/test_projects/codegen_test/templates/example_service_types.hrl new file mode 100644 index 0000000000..88a2e7aed2 --- /dev/null +++ b/test/test_projects/codegen_test/templates/example_service_types.hrl @@ -0,0 +1,28 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% Auto-generated header file for example_service +%%% DO NOT EDIT - Generated by generate_erlang.sh +%%% @end +%%%------------------------------------------------------------------- + + +-record(user_info, { + + user_id = <<>> :: binary(), + username = <<>> :: binary(), + age = 0 :: integer(), + is_active = false :: boolean() +}). + +-record(query_request, { + + query_id = <<>> :: binary(), + filters = [] :: list() +}). + +-record(query_response, { + + results = [] :: list(), + count = 0 :: integer() +}). + diff --git a/test_projects/custom_build_tool/.elp.toml b/test/test_projects/custom_build_tool/.elp.toml similarity index 100% rename from test_projects/custom_build_tool/.elp.toml rename to test/test_projects/custom_build_tool/.elp.toml diff --git a/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl b/test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/include/app_a.hrl rename to test/test_projects/custom_build_tool/apps/app_a/include/app_a.hrl diff --git a/test_projects/custom_build_tool/apps/app_a/src/app_a.erl b/test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_a/src/app_a.erl rename to test/test_projects/custom_build_tool/apps/app_a/src/app_a.erl diff --git a/test_projects/custom_build_tool/apps/app_b/src/app_b.erl b/test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/custom_build_tool/apps/app_b/src/app_b.erl rename to test/test_projects/custom_build_tool/apps/app_b/src/app_b.erl diff --git a/test/test_projects/diagnostics/.elp.toml b/test/test_projects/diagnostics/.elp.toml new file mode 100644 index 0000000000..504ef547af --- /dev/null +++ b/test/test_projects/diagnostics/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//diagnostics/..." ] +source_root = "diagnostics" + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/diagnostics/BUCK b/test/test_projects/diagnostics/BUCK new file mode 100644 index 0000000000..19c15e5acf --- /dev/null +++ b/test/test_projects/diagnostics/BUCK @@ -0,0 +1,20 @@ +oncall("vscode_erlang") + +erlang_application( + name = "diagnostics_app_a_target", + srcs = glob(["app_a/src/*.erl"]), + app_name = "diagnostics_app_a", + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":diagnostics_app_a_target"], +) diff --git a/test_projects/diagnostics/README.md b/test/test_projects/diagnostics/README.md similarity index 100% rename from test_projects/diagnostics/README.md rename to test/test_projects/diagnostics/README.md diff --git a/test_projects/diagnostics/app_a/extra/app_a.erl b/test/test_projects/diagnostics/app_a/extra/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/extra/app_a.erl rename to test/test_projects/diagnostics/app_a/extra/app_a.erl diff --git a/test_projects/diagnostics/app_a/include/app_a.hrl b/test/test_projects/diagnostics/app_a/include/app_a.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/app_a.hrl rename to test/test_projects/diagnostics/app_a/include/app_a.hrl diff --git a/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/broken_diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/include/diagnostics.hrl b/test/test_projects/diagnostics/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/diagnostics/app_a/include/diagnostics.hrl rename to test/test_projects/diagnostics/app_a/include/diagnostics.hrl diff --git a/test_projects/diagnostics/app_a/src/app_a.app.src b/test/test_projects/diagnostics/app_a/src/app_a.app.src similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.app.src rename to test/test_projects/diagnostics/app_a/src/app_a.app.src diff --git a/test_projects/diagnostics/app_a/src/app_a.erl b/test/test_projects/diagnostics/app_a/src/app_a.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/app_a.erl rename to test/test_projects/diagnostics/app_a/src/app_a.erl diff --git a/test_projects/diagnostics/app_a/src/broken_parse_trans.erl b/test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/broken_parse_trans.erl rename to test/test_projects/diagnostics/app_a/src/broken_parse_trans.erl diff --git a/test_projects/diagnostics/app_a/src/cascading.erl b/test/test_projects/diagnostics/app_a/src/cascading.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/cascading.erl rename to test/test_projects/diagnostics/app_a/src/cascading.erl diff --git a/test_projects/diagnostics/app_a/src/crlf.erl b/test/test_projects/diagnostics/app_a/src/crlf.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/crlf.erl rename to test/test_projects/diagnostics/app_a/src/crlf.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.erl b/test/test_projects/diagnostics/app_a/src/diagnostics.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.erl rename to test/test_projects/diagnostics/app_a/src/diagnostics.erl diff --git a/test_projects/diagnostics/app_a/src/diagnostics.escript b/test/test_projects/diagnostics/app_a/src/diagnostics.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_errors.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_errors.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_errors.escript diff --git a/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript b/test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript similarity index 100% rename from test_projects/diagnostics/app_a/src/diagnostics_warnings.escript rename to test/test_projects/diagnostics/app_a/src/diagnostics_warnings.escript diff --git a/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl b/test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl rename to test/test_projects/diagnostics/app_a/src/erlang_diagnostics_errors_gen.erl diff --git a/test_projects/diagnostics/app_a/src/file_attribute.erl b/test/test_projects/diagnostics/app_a/src/file_attribute.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/file_attribute.erl rename to test/test_projects/diagnostics/app_a/src/file_attribute.erl diff --git a/test_projects/diagnostics/app_a/src/lint_recursive.erl b/test/test_projects/diagnostics/app_a/src/lint_recursive.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lint_recursive.erl rename to test/test_projects/diagnostics/app_a/src/lint_recursive.erl diff --git a/test_projects/diagnostics/app_a/src/lints.erl b/test/test_projects/diagnostics/app_a/src/lints.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/lints.erl rename to test/test_projects/diagnostics/app_a/src/lints.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_docstrings.erl b/test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_docstrings.erl rename to test/test_projects/diagnostics/app_a/src/otp27_docstrings.erl diff --git a/test_projects/diagnostics/app_a/src/otp27_sigils.erl b/test/test_projects/diagnostics/app_a/src/otp27_sigils.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp27_sigils.erl rename to test/test_projects/diagnostics/app_a/src/otp27_sigils.erl diff --git a/test_projects/diagnostics/app_a/src/otp_7655.erl b/test/test_projects/diagnostics/app_a/src/otp_7655.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/otp_7655.erl rename to test/test_projects/diagnostics/app_a/src/otp_7655.erl diff --git a/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl b/test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl rename to test/test_projects/diagnostics/app_a/src/parse_error_a_cascade.erl diff --git a/test_projects/diagnostics/app_a/src/suppressed.erl b/test/test_projects/diagnostics/app_a/src/suppressed.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/suppressed.erl rename to test/test_projects/diagnostics/app_a/src/suppressed.erl diff --git a/test_projects/diagnostics/app_a/src/syntax.erl b/test/test_projects/diagnostics/app_a/src/syntax.erl similarity index 100% rename from test_projects/diagnostics/app_a/src/syntax.erl rename to test/test_projects/diagnostics/app_a/src/syntax.erl diff --git a/test_projects/diagnostics/app_a/test/app_a_SUITE.erl b/test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/diagnostics/app_a/test/app_a_SUITE.erl rename to test/test_projects/diagnostics/app_a/test/app_a_SUITE.erl diff --git a/test_projects/diagnostics/erlang_ls.config b/test/test_projects/diagnostics/erlang_ls.config similarity index 100% rename from test_projects/diagnostics/erlang_ls.config rename to test/test_projects/diagnostics/erlang_ls.config diff --git a/test_projects/diagnostics/rebar.config b/test/test_projects/diagnostics/rebar.config similarity index 100% rename from test_projects/diagnostics/rebar.config rename to test/test_projects/diagnostics/rebar.config diff --git a/test_projects/diagnostics/test_build_info.json b/test/test_projects/diagnostics/test_build_info.json similarity index 100% rename from test_projects/diagnostics/test_build_info.json rename to test/test_projects/diagnostics/test_build_info.json diff --git a/test/test_projects/end_to_end/.elp.toml b/test/test_projects/end_to_end/.elp.toml new file mode 100644 index 0000000000..acbcd6146f --- /dev/null +++ b/test/test_projects/end_to_end/.elp.toml @@ -0,0 +1,7 @@ +[buck] +enabled = true +build_deps = false +included_targets = ["root//end_to_end/..."] + +[eqwalizer] +enable_all = false diff --git a/test/test_projects/end_to_end/assist_examples/BUCK b/test/test_projects/end_to_end/assist_examples/BUCK new file mode 100644 index 0000000000..60d39de125 --- /dev/null +++ b/test/test_projects/end_to_end/assist_examples/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "assist_examples", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/assist_examples.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/assist_examples/src/add_doc.erl b/test/test_projects/end_to_end/assist_examples/src/add_doc.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/add_doc.erl rename to test/test_projects/end_to_end/assist_examples/src/add_doc.erl diff --git a/test_projects/end_to_end/assist_examples/src/assist_examples.app.src b/test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src similarity index 100% rename from test_projects/end_to_end/assist_examples/src/assist_examples.app.src rename to test/test_projects/end_to_end/assist_examples/src/assist_examples.app.src diff --git a/test_projects/end_to_end/assist_examples/src/code_completion.erl b/test/test_projects/end_to_end/assist_examples/src/code_completion.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/code_completion.erl rename to test/test_projects/end_to_end/assist_examples/src/code_completion.erl diff --git a/test_projects/end_to_end/assist_examples/src/extract_function.erl b/test/test_projects/end_to_end/assist_examples/src/extract_function.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/extract_function.erl rename to test/test_projects/end_to_end/assist_examples/src/extract_function.erl diff --git a/test_projects/end_to_end/assist_examples/src/head_mismatch.erl b/test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl similarity index 100% rename from test_projects/end_to_end/assist_examples/src/head_mismatch.erl rename to test/test_projects/end_to_end/assist_examples/src/head_mismatch.erl diff --git a/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl b/test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl similarity index 100% rename from test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl rename to test/test_projects/end_to_end/caf/configerator/source/whatsapp/my_caf.erl diff --git a/test/test_projects/end_to_end/definitions/BUCK b/test/test_projects/end_to_end/definitions/BUCK new file mode 100644 index 0000000000..c9e6b204f4 --- /dev/null +++ b/test/test_projects/end_to_end/definitions/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "definitions", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/definitions.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/definitions/README.md b/test/test_projects/end_to_end/definitions/README.md similarity index 100% rename from test_projects/end_to_end/definitions/README.md rename to test/test_projects/end_to_end/definitions/README.md diff --git a/test_projects/end_to_end/definitions/src/definitions.app.src b/test/test_projects/end_to_end/definitions/src/definitions.app.src similarity index 100% rename from test_projects/end_to_end/definitions/src/definitions.app.src rename to test/test_projects/end_to_end/definitions/src/definitions.app.src diff --git a/test_projects/end_to_end/definitions/src/local_def.erl b/test/test_projects/end_to_end/definitions/src/local_def.erl similarity index 100% rename from test_projects/end_to_end/definitions/src/local_def.erl rename to test/test_projects/end_to_end/definitions/src/local_def.erl diff --git a/test/test_projects/end_to_end/docs/BUCK b/test/test_projects/end_to_end/docs/BUCK new file mode 100644 index 0000000000..7429863b6e --- /dev/null +++ b/test/test_projects/end_to_end/docs/BUCK @@ -0,0 +1,16 @@ +load("@waserver//buck2/whatsapp:erlang.bzl", "erlang_application") + +oncall("vscode_erlang") + +erlang_application( + name = "docs", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/docs.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/docs/src/docs.app.src b/test/test_projects/end_to_end/docs/src/docs.app.src similarity index 100% rename from test_projects/end_to_end/docs/src/docs.app.src rename to test/test_projects/end_to_end/docs/src/docs.app.src diff --git a/test_projects/end_to_end/docs/src/docs.erl b/test/test_projects/end_to_end/docs/src/docs.erl similarity index 100% rename from test_projects/end_to_end/docs/src/docs.erl rename to test/test_projects/end_to_end/docs/src/docs.erl diff --git a/test_projects/end_to_end/erlang_ls.config b/test/test_projects/end_to_end/erlang_ls.config similarity index 100% rename from test_projects/end_to_end/erlang_ls.config rename to test/test_projects/end_to_end/erlang_ls.config diff --git a/test_projects/end_to_end/fixtures/erlang-stacktrace.txt b/test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt similarity index 100% rename from test_projects/end_to_end/fixtures/erlang-stacktrace.txt rename to test/test_projects/end_to_end/fixtures/erlang-stacktrace.txt diff --git a/test/test_projects/end_to_end/hover/BUCK b/test/test_projects/end_to_end/hover/BUCK new file mode 100644 index 0000000000..7732dc473e --- /dev/null +++ b/test/test_projects/end_to_end/hover/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "hover", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/hover.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/hover/README.md b/test/test_projects/end_to_end/hover/README.md similarity index 100% rename from test_projects/end_to_end/hover/README.md rename to test/test_projects/end_to_end/hover/README.md diff --git a/test_projects/end_to_end/hover/src/doc_examples.erl b/test/test_projects/end_to_end/hover/src/doc_examples.erl similarity index 100% rename from test_projects/end_to_end/hover/src/doc_examples.erl rename to test/test_projects/end_to_end/hover/src/doc_examples.erl diff --git a/test_projects/end_to_end/hover/src/hover.app.src b/test/test_projects/end_to_end/hover/src/hover.app.src similarity index 100% rename from test_projects/end_to_end/hover/src/hover.app.src rename to test/test_projects/end_to_end/hover/src/hover.app.src diff --git a/test_projects/end_to_end/rebar.config b/test/test_projects/end_to_end/rebar.config similarity index 100% rename from test_projects/end_to_end/rebar.config rename to test/test_projects/end_to_end/rebar.config diff --git a/test/test_projects/end_to_end/single_errors/BUCK b/test/test_projects/end_to_end/single_errors/BUCK new file mode 100644 index 0000000000..a8f6e478b0 --- /dev/null +++ b/test/test_projects/end_to_end/single_errors/BUCK @@ -0,0 +1,14 @@ +oncall("vscode_erlang") + +erlang_application( + name = "single_errors", + srcs = glob([ + "src/*.erl", + ]), + app_src = "src/single_errors.app.src", + applications = [], + includes = [], + labels = ["user_application"], + version = "1.0.0", + visibility = ["PUBLIC"], +) diff --git a/test_projects/end_to_end/single_errors/README.md b/test/test_projects/end_to_end/single_errors/README.md similarity index 100% rename from test_projects/end_to_end/single_errors/README.md rename to test/test_projects/end_to_end/single_errors/README.md diff --git a/test_projects/end_to_end/single_errors/src/as_you_type.erl b/test/test_projects/end_to_end/single_errors/src/as_you_type.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/as_you_type.erl rename to test/test_projects/end_to_end/single_errors/src/as_you_type.erl diff --git a/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl b/test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl rename to test/test_projects/end_to_end/single_errors/src/compiler_diagnostics.erl diff --git a/test_projects/end_to_end/single_errors/src/single_errors.app.src b/test/test_projects/end_to_end/single_errors/src/single_errors.app.src similarity index 100% rename from test_projects/end_to_end/single_errors/src/single_errors.app.src rename to test/test_projects/end_to_end/single_errors/src/single_errors.app.src diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl diff --git a/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 b/test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 similarity index 100% rename from test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 rename to test/test_projects/end_to_end/single_errors/src/spec_mismatch.erl.2 diff --git a/test_projects/end_to_end/single_errors/src/types_on_hover.erl b/test/test_projects/end_to_end/single_errors/src/types_on_hover.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/types_on_hover.erl rename to test/test_projects/end_to_end/single_errors/src/types_on_hover.erl diff --git a/test_projects/end_to_end/single_errors/src/unused_macro.erl b/test/test_projects/end_to_end/single_errors/src/unused_macro.erl similarity index 100% rename from test_projects/end_to_end/single_errors/src/unused_macro.erl rename to test/test_projects/end_to_end/single_errors/src/unused_macro.erl diff --git a/test_projects/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer/src/eqwalizer_specs.erl diff --git a/test/test_projects/eqwalizer_callers/.elp.toml b/test/test_projects/eqwalizer_callers/.elp.toml new file mode 100644 index 0000000000..68476bcf2e --- /dev/null +++ b/test/test_projects/eqwalizer_callers/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//eqwalizer_callers/..." ] +source_root = "eqwalizer_callers" diff --git a/test_projects/eqwalizer_callers/.gitignore b/test/test_projects/eqwalizer_callers/.gitignore similarity index 100% rename from test_projects/eqwalizer_callers/.gitignore rename to test/test_projects/eqwalizer_callers/.gitignore diff --git a/test/test_projects/eqwalizer_callers/BUCK b/test/test_projects/eqwalizer_callers/BUCK new file mode 100644 index 0000000000..32012a0dc0 --- /dev/null +++ b/test/test_projects/eqwalizer_callers/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) diff --git a/test_projects/eqwalizer_callers/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_callers/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.app.src b/test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.app.src rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.app.src diff --git a/test_projects/eqwalizer_callers/app_a/src/app_a.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_callers/app_a/src/app_b.erl b/test/test_projects/eqwalizer_callers/app_a/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/src/app_b.erl rename to test/test_projects/eqwalizer_callers/app_a/src/app_b.erl diff --git a/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl b/test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl rename to test/test_projects/eqwalizer_callers/app_a/test/app_a_SUITE.erl diff --git a/test_projects/eqwalizer_callers/rebar.config b/test/test_projects/eqwalizer_callers/rebar.config similarity index 100% rename from test_projects/eqwalizer_callers/rebar.config rename to test/test_projects/eqwalizer_callers/rebar.config diff --git a/test_projects/eqwalizer_ignore_modules/.elp.toml b/test/test_projects/eqwalizer_ignore_modules/.elp.toml similarity index 100% rename from test_projects/eqwalizer_ignore_modules/.elp.toml rename to test/test_projects/eqwalizer_ignore_modules/.elp.toml diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/include/app_a.hrl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/another_module_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_a/src/app_a.erl diff --git a/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl b/test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl similarity index 100% rename from test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl rename to test/test_projects/eqwalizer_ignore_modules/apps/app_b/src/app_b.erl diff --git a/test/test_projects/eqwalizer_tests/.elp.toml b/test/test_projects/eqwalizer_tests/.elp.toml new file mode 100644 index 0000000000..5dac3aa6a6 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//eqwalizer_tests/..." ] +source_root = "eqwalizer_tests" + +[eqwalizer] +enable_all = true diff --git a/test_projects/eqwalizer_tests/.gitignore b/test/test_projects/eqwalizer_tests/.gitignore similarity index 100% rename from test_projects/eqwalizer_tests/.gitignore rename to test/test_projects/eqwalizer_tests/.gitignore diff --git a/test_projects/eqwalizer_tests/.rebar.root b/test/test_projects/eqwalizer_tests/.rebar.root similarity index 100% rename from test_projects/eqwalizer_tests/.rebar.root rename to test/test_projects/eqwalizer_tests/.rebar.root diff --git a/test/test_projects/eqwalizer_tests/BUCK b/test/test_projects/eqwalizer_tests/BUCK new file mode 100644 index 0000000000..0817873cb0 --- /dev/null +++ b/test/test_projects/eqwalizer_tests/BUCK @@ -0,0 +1,77 @@ +oncall("vscode_erlang") + +erlang_application( + name = "check", + srcs = glob(["check/src/**/*.erl"]), + applications = [":eqwalizer"], + includes = glob([ + "check/include/*.hrl", + "eqwalizer/include/*.hrl", + ]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "check_gradual", + srcs = glob(["check_gradual/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "check/test/check_SUITE.erl", + ], + deps = [":check"], +) + +erlang_application( + name = "debug", + srcs = glob(["debug/src/*.erl"]), + applications = [":eqwalizer"], + includes = glob(["debug/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elm_core", + srcs = glob(["elm_core/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwater", + srcs = glob(["eqwater/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "fault_tolerance", + srcs = glob(["fault_tolerance/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "options", + srcs = glob(["options/src/*.erl"]), + applications = [":eqwalizer"], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + includes = glob(["eqwalizer/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/eqwalizer_tests/check/include/my_header.hrl b/test/test_projects/eqwalizer_tests/check/include/my_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/check/include/my_header.hrl rename to test/test_projects/eqwalizer_tests/check/include/my_header.hrl diff --git a/test_projects/eqwalizer_tests/check/src/any_fun_type.erl b/test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/any_fun_type.erl rename to test/test_projects/eqwalizer_tests/check/src/any_fun_type.erl diff --git a/test_projects/eqwalizer_tests/check/src/apply_none.erl b/test/test_projects/eqwalizer_tests/check/src/apply_none.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/apply_none.erl rename to test/test_projects/eqwalizer_tests/check/src/apply_none.erl diff --git a/test_projects/eqwalizer_tests/check/src/approx.erl b/test/test_projects/eqwalizer_tests/check/src/approx.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/approx.erl rename to test/test_projects/eqwalizer_tests/check/src/approx.erl diff --git a/test_projects/eqwalizer_tests/check/src/as_pat.erl b/test/test_projects/eqwalizer_tests/check/src/as_pat.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/as_pat.erl rename to test/test_projects/eqwalizer_tests/check/src/as_pat.erl diff --git a/test_projects/eqwalizer_tests/check/src/auto_imports.erl b/test/test_projects/eqwalizer_tests/check/src/auto_imports.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/auto_imports.erl rename to test/test_projects/eqwalizer_tests/check/src/auto_imports.erl diff --git a/test_projects/eqwalizer_tests/check/src/behave.erl b/test/test_projects/eqwalizer_tests/check/src/behave.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/behave.erl rename to test/test_projects/eqwalizer_tests/check/src/behave.erl diff --git a/test_projects/eqwalizer_tests/check/src/binaries.erl b/test/test_projects/eqwalizer_tests/check/src/binaries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/binaries.erl rename to test/test_projects/eqwalizer_tests/check/src/binaries.erl diff --git a/test_projects/eqwalizer_tests/check/src/booleans.erl b/test/test_projects/eqwalizer_tests/check/src/booleans.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/booleans.erl rename to test/test_projects/eqwalizer_tests/check/src/booleans.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks1_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl similarity index 70% rename from test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl index 8152487669..854feec923 100644 --- a/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl +++ b/test/test_projects/eqwalizer_tests/check/src/callbacks3_neg.erl @@ -3,11 +3,12 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(callbacks3_neg). % @OTPVersionDependent +-module(callbacks3_neg). % @OTP27Only -export([ init/1, handle_call/3, - handle_cast/2 + handle_cast/2, + handle_info/2 ]). -behavior(gen_server). @@ -19,3 +20,7 @@ handle_call(_, _From, State) -> -spec handle_cast(ok, ok) -> wrong_ret. handle_cast(_, _) -> wrong_ret. + +-spec handle_info(ok, ok) -> {noreply, ok, wrong_atom}. +handle_info(_, _) -> + {noreply, ok, wrong_atom}. diff --git a/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks4_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks5_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks6_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl b/test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/callbacks7_overload_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/case_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/case_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/case_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/case_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/check.app.src b/test/test_projects/eqwalizer_tests/check/src/check.app.src similarity index 100% rename from test_projects/eqwalizer_tests/check/src/check.app.src rename to test/test_projects/eqwalizer_tests/check/src/check.app.src diff --git a/test_projects/eqwalizer_tests/check/src/compiler_macro.erl b/test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/compiler_macro.erl rename to test/test_projects/eqwalizer_tests/check/src/compiler_macro.erl diff --git a/test_projects/eqwalizer_tests/check/src/complex_maps.erl b/test/test_projects/eqwalizer_tests/check/src/complex_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/complex_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/complex_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/comprehensions.erl b/test/test_projects/eqwalizer_tests/check/src/comprehensions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/comprehensions.erl rename to test/test_projects/eqwalizer_tests/check/src/comprehensions.erl diff --git a/test_projects/eqwalizer_tests/check/src/contravariant.erl b/test/test_projects/eqwalizer_tests/check/src/contravariant.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/contravariant.erl rename to test/test_projects/eqwalizer_tests/check/src/contravariant.erl diff --git a/test_projects/eqwalizer_tests/check/src/custom.erl b/test/test_projects/eqwalizer_tests/check/src/custom.erl similarity index 99% rename from test_projects/eqwalizer_tests/check/src/custom.erl rename to test/test_projects/eqwalizer_tests/check/src/custom.erl index 911bfa3a0a..b6910b0fc7 100644 --- a/test_projects/eqwalizer_tests/check/src/custom.erl +++ b/test/test_projects/eqwalizer_tests/check/src/custom.erl @@ -3,7 +3,7 @@ %%% This source code is licensed under the Apache 2.0 license found in %%% the LICENSE file in the root directory of this source tree. --module(custom). % @OTPVersionDependent +-module(custom). % @OTP27Only -import(maps, [get/2, get/3]). -compile([export_all, nowarn_export_all]). diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs1.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs1.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs1.erl diff --git a/test_projects/eqwalizer_tests/check/src/detached_specs2.erl b/test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/detached_specs2.erl rename to test/test_projects/eqwalizer_tests/check/src/detached_specs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dyn_remote_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_1.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_callbacks_2.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_calls.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_calls.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_local_funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_refine.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl b/test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl rename to test/test_projects/eqwalizer_tests/check/src/dynamic_try_catch.erl diff --git a/test_projects/eqwalizer_tests/check/src/elab_clause.erl b/test/test_projects/eqwalizer_tests/check/src/elab_clause.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/elab_clause.erl rename to test/test_projects/eqwalizer_tests/check/src/elab_clause.erl diff --git a/test_projects/eqwalizer_tests/check/src/error_messages.erl b/test/test_projects/eqwalizer_tests/check/src/error_messages.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/error_messages.erl rename to test/test_projects/eqwalizer_tests/check/src/error_messages.erl diff --git a/test_projects/eqwalizer_tests/check/src/fancy_generics.erl b/test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fancy_generics.erl rename to test/test_projects/eqwalizer_tests/check/src/fancy_generics.erl diff --git a/test_projects/eqwalizer_tests/check/src/format.erl b/test/test_projects/eqwalizer_tests/check/src/format.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/format.erl rename to test/test_projects/eqwalizer_tests/check/src/format.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats.erl diff --git a/test_projects/eqwalizer_tests/check/src/fun_stats2.erl b/test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/fun_stats2.erl rename to test/test_projects/eqwalizer_tests/check/src/fun_stats2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs.erl b/test/test_projects/eqwalizer_tests/check/src/funs.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs.erl rename to test/test_projects/eqwalizer_tests/check/src/funs.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs2.erl b/test/test_projects/eqwalizer_tests/check/src/funs2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs2.erl rename to test/test_projects/eqwalizer_tests/check/src/funs2.erl diff --git a/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl b/test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/funs_uncommon.erl rename to test/test_projects/eqwalizer_tests/check/src/funs_uncommon.erl diff --git a/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl b/test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generic_fun_application.erl rename to test/test_projects/eqwalizer_tests/check/src/generic_fun_application.erl diff --git a/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl b/test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/generics_with_unions.erl rename to test/test_projects/eqwalizer_tests/check/src/generics_with_unions.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_bounded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_bounded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_custom.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_custom.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_custom.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_lambdas.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_misc.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_misc.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_regression_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl b/test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/gradual_untyped.erl rename to test/test_projects/eqwalizer_tests/check/src/gradual_untyped.erl diff --git a/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl b/test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guard_b_connections.erl rename to test/test_projects/eqwalizer_tests/check/src/guard_b_connections.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards.erl b/test/test_projects/eqwalizer_tests/check/src/guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards.erl rename to test/test_projects/eqwalizer_tests/check/src/guards.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_logic.erl b/test/test_projects/eqwalizer_tests/check/src/guards_logic.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_logic.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_logic.erl diff --git a/test_projects/eqwalizer_tests/check/src/guards_simple.erl b/test/test_projects/eqwalizer_tests/check/src/guards_simple.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/guards_simple.erl rename to test/test_projects/eqwalizer_tests/check/src/guards_simple.erl diff --git a/test_projects/eqwalizer_tests/check/src/hints.erl b/test/test_projects/eqwalizer_tests/check/src/hints.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/hints.erl rename to test/test_projects/eqwalizer_tests/check/src/hints.erl diff --git a/test_projects/eqwalizer_tests/check/src/index1.erl b/test/test_projects/eqwalizer_tests/check/src/index1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index1.erl rename to test/test_projects/eqwalizer_tests/check/src/index1.erl diff --git a/test_projects/eqwalizer_tests/check/src/index2.erl b/test/test_projects/eqwalizer_tests/check/src/index2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/index2.erl rename to test/test_projects/eqwalizer_tests/check/src/index2.erl diff --git a/test_projects/eqwalizer_tests/check/src/iolists.erl b/test/test_projects/eqwalizer_tests/check/src/iolists.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/iolists.erl rename to test/test_projects/eqwalizer_tests/check/src/iolists.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_01.erl b/test/test_projects/eqwalizer_tests/check/src/kp_01.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_01.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_01.erl diff --git a/test_projects/eqwalizer_tests/check/src/kp_02.erl b/test/test_projects/eqwalizer_tests/check/src/kp_02.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/kp_02.erl rename to test/test_projects/eqwalizer_tests/check/src/kp_02.erl diff --git a/test_projects/eqwalizer_tests/check/src/lists_tests.erl b/test/test_projects/eqwalizer_tests/check/src/lists_tests.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/lists_tests.erl rename to test/test_projects/eqwalizer_tests/check/src/lists_tests.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc.erl b/test/test_projects/eqwalizer_tests/check/src/misc.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc.erl rename to test/test_projects/eqwalizer_tests/check/src/misc.erl diff --git a/test_projects/eqwalizer_tests/check/src/misc_lib.erl b/test/test_projects/eqwalizer_tests/check/src/misc_lib.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/misc_lib.erl rename to test/test_projects/eqwalizer_tests/check/src/misc_lib.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl b/test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl rename to test/test_projects/eqwalizer_tests/check/src/my_gradual_behaviour.erl diff --git a/test_projects/eqwalizer_tests/check/src/my_header.erl b/test/test_projects/eqwalizer_tests/check/src/my_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/my_header.erl rename to test/test_projects/eqwalizer_tests/check/src/my_header.erl diff --git a/test_projects/eqwalizer_tests/check/src/neg.erl b/test/test_projects/eqwalizer_tests/check/src/neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/neg.erl rename to test/test_projects/eqwalizer_tests/check/src/neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/misc_nested1.erl diff --git a/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl b/test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl rename to test/test_projects/eqwalizer_tests/check/src/nested1/nested2/misc_nested2.erl diff --git a/test_projects/eqwalizer_tests/check/src/nowarn.erl b/test/test_projects/eqwalizer_tests/check/src/nowarn.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/nowarn.erl rename to test/test_projects/eqwalizer_tests/check/src/nowarn.erl diff --git a/test_projects/eqwalizer_tests/check/src/number_comparisons.erl b/test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/number_comparisons.erl rename to test/test_projects/eqwalizer_tests/check/src/number_comparisons.erl diff --git a/test_projects/eqwalizer_tests/check/src/numbers.erl b/test/test_projects/eqwalizer_tests/check/src/numbers.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/numbers.erl rename to test/test_projects/eqwalizer_tests/check/src/numbers.erl diff --git a/test_projects/eqwalizer_tests/check/src/opaque.erl b/test/test_projects/eqwalizer_tests/check/src/opaque.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/opaque.erl rename to test/test_projects/eqwalizer_tests/check/src/opaque.erl diff --git a/test_projects/eqwalizer_tests/check/src/other.erl b/test/test_projects/eqwalizer_tests/check/src/other.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/other.erl rename to test/test_projects/eqwalizer_tests/check/src/other.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp28.erl b/test/test_projects/eqwalizer_tests/check/src/otp28.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp28.erl rename to test/test_projects/eqwalizer_tests/check/src/otp28.erl diff --git a/test_projects/eqwalizer_tests/check/src/otp_opaques.erl b/test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/otp_opaques.erl rename to test/test_projects/eqwalizer_tests/check/src/otp_opaques.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded.erl diff --git a/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl b/test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl rename to test/test_projects/eqwalizer_tests/check/src/overloaded_specs_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/parametricity.erl b/test/test_projects/eqwalizer_tests/check/src/parametricity.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/parametricity.erl rename to test/test_projects/eqwalizer_tests/check/src/parametricity.erl diff --git a/test_projects/eqwalizer_tests/check/src/pats.erl b/test/test_projects/eqwalizer_tests/check/src/pats.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pats.erl rename to test/test_projects/eqwalizer_tests/check/src/pats.erl diff --git a/test_projects/eqwalizer_tests/check/src/pinned.erl b/test/test_projects/eqwalizer_tests/check/src/pinned.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pinned.erl rename to test/test_projects/eqwalizer_tests/check/src/pinned.erl diff --git a/test_projects/eqwalizer_tests/check/src/pos.erl b/test/test_projects/eqwalizer_tests/check/src/pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/pos.erl rename to test/test_projects/eqwalizer_tests/check/src/pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/records.erl b/test/test_projects/eqwalizer_tests/check/src/records.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/records.erl rename to test/test_projects/eqwalizer_tests/check/src/records.erl diff --git a/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/recursive_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/recursive_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/refine.erl b/test/test_projects/eqwalizer_tests/check/src/refine.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/refine.erl rename to test/test_projects/eqwalizer_tests/check/src/refine.erl diff --git a/test_projects/eqwalizer_tests/check/src/scoping.erl b/test/test_projects/eqwalizer_tests/check/src/scoping.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/scoping.erl rename to test/test_projects/eqwalizer_tests/check/src/scoping.erl diff --git a/test_projects/eqwalizer_tests/check/src/skip.erl b/test/test_projects/eqwalizer_tests/check/src/skip.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/skip.erl rename to test/test_projects/eqwalizer_tests/check/src/skip.erl diff --git a/test_projects/eqwalizer_tests/check/src/static_maybe.erl b/test/test_projects/eqwalizer_tests/check/src/static_maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/static_maybe.erl rename to test/test_projects/eqwalizer_tests/check/src/static_maybe.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl b/test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_complex_types.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_complex_types.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_fun.erl b/test/test_projects/eqwalizer_tests/check/src/strict_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/strict_receive.erl b/test/test_projects/eqwalizer_tests/check/src/strict_receive.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/strict_receive.erl rename to test/test_projects/eqwalizer_tests/check/src/strict_receive.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_neg.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_neg.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_neg.erl diff --git a/test_projects/eqwalizer_tests/check/src/subtype_pos.erl b/test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/subtype_pos.erl rename to test/test_projects/eqwalizer_tests/check/src/subtype_pos.erl diff --git a/test_projects/eqwalizer_tests/check/src/t_maps.erl b/test/test_projects/eqwalizer_tests/check/src/t_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/t_maps.erl rename to test/test_projects/eqwalizer_tests/check/src/t_maps.erl diff --git a/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl b/test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tagged_tuples.erl rename to test/test_projects/eqwalizer_tests/check/src/tagged_tuples.erl diff --git a/test_projects/eqwalizer_tests/check/src/test.erl b/test/test_projects/eqwalizer_tests/check/src/test.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/test.erl rename to test/test_projects/eqwalizer_tests/check/src/test.erl diff --git a/test_projects/eqwalizer_tests/check/src/tries.erl b/test/test_projects/eqwalizer_tests/check/src/tries.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tries.erl rename to test/test_projects/eqwalizer_tests/check/src/tries.erl diff --git a/test_projects/eqwalizer_tests/check/src/tuple_union.erl b/test/test_projects/eqwalizer_tests/check/src/tuple_union.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/tuple_union.erl rename to test/test_projects/eqwalizer_tests/check/src/tuple_union.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_aliases.erl b/test/test_projects/eqwalizer_tests/check/src/type_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_aliases.erl rename to test/test_projects/eqwalizer_tests/check/src/type_aliases.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_asserts.erl b/test/test_projects/eqwalizer_tests/check/src/type_asserts.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_asserts.erl rename to test/test_projects/eqwalizer_tests/check/src/type_asserts.erl diff --git a/test_projects/eqwalizer_tests/check/src/type_predicates.erl b/test/test_projects/eqwalizer_tests/check/src/type_predicates.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/type_predicates.erl rename to test/test_projects/eqwalizer_tests/check/src/type_predicates.erl diff --git a/test_projects/eqwalizer_tests/check/src/united_fun.erl b/test/test_projects/eqwalizer_tests/check/src/united_fun.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/united_fun.erl rename to test/test_projects/eqwalizer_tests/check/src/united_fun.erl diff --git a/test_projects/eqwalizer_tests/check/src/unspecced.erl b/test/test_projects/eqwalizer_tests/check/src/unspecced.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/unspecced.erl rename to test/test_projects/eqwalizer_tests/check/src/unspecced.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_gradual.erl diff --git a/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl b/test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl rename to test/test_projects/eqwalizer_tests/check/src/use_dynamic_strict.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars1.erl b/test/test_projects/eqwalizer_tests/check/src/vars1.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars1.erl rename to test/test_projects/eqwalizer_tests/check/src/vars1.erl diff --git a/test_projects/eqwalizer_tests/check/src/vars2.erl b/test/test_projects/eqwalizer_tests/check/src/vars2.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/src/vars2.erl rename to test/test_projects/eqwalizer_tests/check/src/vars2.erl diff --git a/test_projects/eqwalizer_tests/check/test/check_SUITE.erl b/test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl similarity index 100% rename from test_projects/eqwalizer_tests/check/test/check_SUITE.erl rename to test/test_projects/eqwalizer_tests/check/test/check_SUITE.erl diff --git a/test_projects/eqwalizer_tests/debug/include/debug_header.hrl b/test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl similarity index 100% rename from test_projects/eqwalizer_tests/debug/include/debug_header.hrl rename to test/test_projects/eqwalizer_tests/debug/include/debug_header.hrl diff --git a/test_projects/eqwalizer_tests/debug/src/attributes.erl b/test/test_projects/eqwalizer_tests/debug/src/attributes.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/attributes.erl rename to test/test_projects/eqwalizer_tests/debug/src/attributes.erl diff --git a/test_projects/eqwalizer_tests/debug/src/debug.app.src b/test/test_projects/eqwalizer_tests/debug/src/debug.app.src similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug.app.src rename to test/test_projects/eqwalizer_tests/debug/src/debug.app.src diff --git a/test_projects/eqwalizer_tests/debug/src/debug_header.erl b/test/test_projects/eqwalizer_tests/debug/src/debug_header.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/debug_header.erl rename to test/test_projects/eqwalizer_tests/debug/src/debug_header.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expand.erl b/test/test_projects/eqwalizer_tests/debug/src/expand.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expand.erl rename to test/test_projects/eqwalizer_tests/debug/src/expand.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr1.erl b/test/test_projects/eqwalizer_tests/debug/src/expr1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr1.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/expr2.erl b/test/test_projects/eqwalizer_tests/debug/src/expr2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/expr2.erl rename to test/test_projects/eqwalizer_tests/debug/src/expr2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/records_wip.erl b/test/test_projects/eqwalizer_tests/debug/src/records_wip.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/records_wip.erl rename to test/test_projects/eqwalizer_tests/debug/src/records_wip.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types1.erl b/test/test_projects/eqwalizer_tests/debug/src/types1.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types1.erl rename to test/test_projects/eqwalizer_tests/debug/src/types1.erl diff --git a/test_projects/eqwalizer_tests/debug/src/types2.erl b/test/test_projects/eqwalizer_tests/debug/src/types2.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/types2.erl rename to test/test_projects/eqwalizer_tests/debug/src/types2.erl diff --git a/test_projects/eqwalizer_tests/debug/src/wip_maps.erl b/test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/debug/src/wip_maps.erl rename to test/test_projects/eqwalizer_tests/debug/src/wip_maps.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/basics.erl b/test/test_projects/eqwalizer_tests/elm_core/src/basics.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/basics.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/basics.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src b/test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src rename to test/test_projects/eqwalizer_tests/elm_core/src/elm_core.app.src diff --git a/test_projects/eqwalizer_tests/elm_core/src/list.erl b/test/test_projects/eqwalizer_tests/elm_core/src/list.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/list.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/list.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl b/test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/map_ffi.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/maybe.erl b/test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/maybe.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/maybe.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/result.erl b/test/test_projects/eqwalizer_tests/elm_core/src/result.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/result.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/result.erl diff --git a/test_projects/eqwalizer_tests/elm_core/src/tuple.erl b/test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl similarity index 100% rename from test_projects/eqwalizer_tests/elm_core/src/tuple.erl rename to test/test_projects/eqwalizer_tests/elm_core/src/tuple.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl b/test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl rename to test/test_projects/eqwalizer_tests/eqwalizer/include/eqwalizer.hrl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/eqwalizer_tests/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_build_info_prv.erl diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.app.src diff --git a/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl b/test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl rename to test/test_projects/eqwalizer_tests/eqwalizer_rebar3/src/eqwalizer_rebar3.erl diff --git a/test_projects/eqwalizer_tests/eqwater/readme.md b/test/test_projects/eqwalizer_tests/eqwater/readme.md similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/readme.md rename to test/test_projects/eqwalizer_tests/eqwater/readme.md diff --git a/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl b/test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/deep_tuples.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.app.src diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_aliases.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_generics.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl similarity index 100% rename from test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl rename to test/test_projects/eqwalizer_tests/eqwater/src/eqwater_lists.erl diff --git a/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl b/test/test_projects/eqwalizer_tests/eqwater/src/eqwater_maps.erl similarity index 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/standard/app_a/.eqwalizer b/test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer similarity index 100% rename from test_projects/standard/app_a/.eqwalizer rename to test/test_projects/eqwalizer_tests/fault_tolerance/.eqwalizer diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.app.src diff --git a/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl b/test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl similarity index 100% rename from test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl rename to test/test_projects/eqwalizer_tests/fault_tolerance/src/fault_tolerance.erl diff --git a/test_projects/eqwalizer_tests/options/src/bad_maps.erl b/test/test_projects/eqwalizer_tests/options/src/bad_maps.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/bad_maps.erl rename to test/test_projects/eqwalizer_tests/options/src/bad_maps.erl diff --git a/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl b/test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl rename to test/test_projects/eqwalizer_tests/options/src/dynamic_lambdas.erl diff --git a/test_projects/eqwalizer_tests/options/src/options.app.src b/test/test_projects/eqwalizer_tests/options/src/options.app.src similarity index 100% rename from test_projects/eqwalizer_tests/options/src/options.app.src rename to test/test_projects/eqwalizer_tests/options/src/options.app.src diff --git a/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl b/test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl rename to test/test_projects/eqwalizer_tests/options/src/overloaded_specs_dynamic_result.erl diff --git a/test_projects/eqwalizer_tests/options/src/redundant_guards.erl b/test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/redundant_guards.erl rename to test/test_projects/eqwalizer_tests/options/src/redundant_guards.erl diff --git a/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl b/test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl similarity index 100% rename from test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl rename to test/test_projects/eqwalizer_tests/options/src/uncovered_clauses.erl diff --git a/test_projects/eqwalizer_tests/rebar.config b/test/test_projects/eqwalizer_tests/rebar.config similarity index 100% rename from test_projects/eqwalizer_tests/rebar.config rename to test/test_projects/eqwalizer_tests/rebar.config diff --git a/test/test_projects/hierarchical_config/.elp.toml b/test/test_projects/hierarchical_config/.elp.toml new file mode 100644 index 0000000000..e4d04eb6e0 --- /dev/null +++ b/test/test_projects/hierarchical_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//hierarchical_config/..." ] +source_root = "hierarchical_config" diff --git a/test_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/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_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..70edcc3f03 --- /dev/null +++ b/test/test_projects/in_place_tests/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//in_place_tests/..." ] +source_root = "in_place_tests" diff --git a/test/test_projects/in_place_tests/BUCK b/test/test_projects/in_place_tests/BUCK new file mode 100644 index 0000000000..f4f354e7ee --- /dev/null +++ b/test/test_projects/in_place_tests/BUCK @@ -0,0 +1,19 @@ +oncall("vscode_erlang") + +erlang_application( + name = "in_place_tests_app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":in_place_tests_app_a"], +) diff --git a/test_projects/in_place_tests/README.md b/test/test_projects/in_place_tests/README.md similarity index 100% rename from test_projects/in_place_tests/README.md rename to test/test_projects/in_place_tests/README.md diff --git a/test_projects/in_place_tests/app_a/extra/app_a.erl b/test/test_projects/in_place_tests/app_a/extra/app_a.erl similarity index 100% rename from test_projects/in_place_tests/app_a/extra/app_a.erl rename to test/test_projects/in_place_tests/app_a/extra/app_a.erl diff --git a/test_projects/in_place_tests/app_a/include/app_a.hrl b/test/test_projects/in_place_tests/app_a/include/app_a.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/app_a.hrl rename to test/test_projects/in_place_tests/app_a/include/app_a.hrl diff --git a/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/broken_diagnostics.hrl diff --git a/test_projects/in_place_tests/app_a/include/diagnostics.hrl b/test/test_projects/in_place_tests/app_a/include/diagnostics.hrl similarity index 100% rename from test_projects/in_place_tests/app_a/include/diagnostics.hrl rename to test/test_projects/in_place_tests/app_a/include/diagnostics.hrl diff --git a/test_projects/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..9d1c859339 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//include_lib_dependency_test/..." ] +source_root = "include_lib_dependency_test" diff --git a/test/test_projects/include_lib_dependency_test/BUCK b/test/test_projects/include_lib_dependency_test/BUCK new file mode 100644 index 0000000000..54b81c47b5 --- /dev/null +++ b/test/test_projects/include_lib_dependency_test/BUCK @@ -0,0 +1,43 @@ +oncall("vscode_erlang") + +# Main application that will try to include_lib from an app it doesn't depend on +erlang_application( + name = "main_app", + srcs = glob(["main_app/src/*.erl"]), + app_src = "main_app/src/main_app.app.src", + applications = [ + # Note: deliberately NOT including :external_app as a dependency + "root//include_lib_dependency_test:normal_dep", + ], + extra_includes = ["root//include_lib_dependency_test:extra_app"], + labels = ["user_application"], + version = "1.0.0", +) + +# External application that main_app will try to include_lib from +erlang_application( + name = "external_app", + srcs = glob(["external_app/src/*.erl"]), + app_src = "external_app/src/external_app.app.src", + includes = glob(["external_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "extra_app", + srcs = glob(["extra_app/src/*.erl"]), + app_src = "extra_app/src/extra_app.app.src", + includes = glob(["extra_app/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "normal_dep", + srcs = glob(["normal_dep/src/*.erl"]), + app_src = "normal_dep/src/normal_dep.app.src", + includes = glob(["normal_dep/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl b/test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/include/external_header.hrl rename to test/test_projects/include_lib_dependency_test/external_app/include/external_header.hrl diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.app.src rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.app.src diff --git a/test_projects/include_lib_dependency_test/external_app/src/external_app.erl b/test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/external_app/src/external_app.erl rename to test/test_projects/include_lib_dependency_test/external_app/src/external_app.erl diff --git a/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl b/test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl rename to test/test_projects/include_lib_dependency_test/extra_app/include/extra_header.hrl diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.app.src diff --git a/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl b/test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl rename to test/test_projects/include_lib_dependency_test/extra_app/src/extra_app.erl diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.app.src rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.app.src diff --git a/test_projects/include_lib_dependency_test/main_app/src/main_app.erl b/test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl similarity index 100% rename from test_projects/include_lib_dependency_test/main_app/src/main_app.erl rename to test/test_projects/include_lib_dependency_test/main_app/src/main_app.erl diff --git a/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl b/test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl rename to test/test_projects/include_lib_dependency_test/normal_dep/include/assert.hrl diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.app.src diff --git a/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl b/test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl similarity index 100% rename from test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl rename to test/test_projects/include_lib_dependency_test/normal_dep/src/normal_dep.erl diff --git a/test_projects/include_lib_dependency_test/rebar.config b/test/test_projects/include_lib_dependency_test/rebar.config similarity index 100% rename from test_projects/include_lib_dependency_test/rebar.config rename to test/test_projects/include_lib_dependency_test/rebar.config diff --git a/test/test_projects/linter/.elp.toml b/test/test_projects/linter/.elp.toml new file mode 100644 index 0000000000..4820625b2d --- /dev/null +++ b/test/test_projects/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter/.elp_lint.toml b/test/test_projects/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter/.elp_lint.toml rename to test/test_projects/linter/.elp_lint.toml diff --git a/test_projects/linter/.gitignore b/test/test_projects/linter/.gitignore similarity index 100% rename from test_projects/linter/.gitignore rename to test/test_projects/linter/.gitignore diff --git a/test/test_projects/linter/BUCK b/test/test_projects/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test_projects/linter/app_a/include/app_a.hrl b/test/test_projects/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter/app_a/include/app_a.hrl rename to test/test_projects/linter/app_a/include/app_a.hrl diff --git a/test_projects/linter/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..4820625b2d --- /dev/null +++ b/test/test_projects/linter_bad_config/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter_bad_config/.elp_lint.toml b/test/test_projects/linter_bad_config/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/.elp_lint.toml rename to test/test_projects/linter_bad_config/.elp_lint.toml diff --git a/test_projects/linter_bad_config/.gitignore b/test/test_projects/linter_bad_config/.gitignore similarity index 100% rename from test_projects/linter_bad_config/.gitignore rename to test/test_projects/linter_bad_config/.gitignore diff --git a/test/test_projects/linter_bad_config/BUCK b/test/test_projects/linter_bad_config/BUCK new file mode 100644 index 0000000000..a0817a6ad6 --- /dev/null +++ b/test/test_projects/linter_bad_config/BUCK @@ -0,0 +1,12 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/linter_bad_config/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/app_a/include/app_a.hrl diff --git a/test_projects/linter_bad_config/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..4820625b2d --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//linter/..." ] +source_root = "linter" diff --git a/test_projects/linter_bad_config/linter/.elp_lint.toml b/test/test_projects/linter_bad_config/linter/.elp_lint.toml similarity index 100% rename from test_projects/linter_bad_config/linter/.elp_lint.toml rename to test/test_projects/linter_bad_config/linter/.elp_lint.toml diff --git a/test_projects/linter_bad_config/linter/.gitignore b/test/test_projects/linter_bad_config/linter/.gitignore similarity index 100% rename from test_projects/linter_bad_config/linter/.gitignore rename to test/test_projects/linter_bad_config/linter/.gitignore diff --git a/test/test_projects/linter_bad_config/linter/BUCK b/test/test_projects/linter_bad_config/linter/BUCK new file mode 100644 index 0000000000..2db8750582 --- /dev/null +++ b/test/test_projects/linter_bad_config/linter/BUCK @@ -0,0 +1,41 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl b/test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl similarity index 100% rename from test_projects/linter_bad_config/linter/app_a/include/app_a.hrl rename to test/test_projects/linter_bad_config/linter/app_a/include/app_a.hrl diff --git a/test_projects/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/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_projects/standard/app_a/src/app_a.app.src b/test/test_projects/linter_config/app_a/src/app_a.app.src similarity index 100% rename from test_projects/standard/app_a/src/app_a.app.src rename to test/test_projects/linter_config/app_a/src/app_a.app.src 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_projects/standard/app_b/src/app_b.app.src b/test/test_projects/linter_config/app_b/src/app_b.app.src similarity index 100% rename from test_projects/standard/app_b/src/app_b.app.src rename to test/test_projects/linter_config/app_b/src/app_b.app.src diff --git a/test/test_projects/linter_config/app_b/src/app_b.erl b/test/test_projects/linter_config/app_b/src/app_b.erl new file mode 100644 index 0000000000..ca7ecf985b --- /dev/null +++ b/test/test_projects/linter_config/app_b/src/app_b.erl @@ -0,0 +1,4 @@ +-module(app_b). + +-define(MACRO_B, b). +-record(rec_b, {b :: atom()}). diff --git a/test/test_projects/linter_config/app_c/src/app_c.app.src b/test/test_projects/linter_config/app_c/src/app_c.app.src new file mode 100644 index 0000000000..c278bbce23 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.app.src @@ -0,0 +1,3 @@ +{application, app_c, + [{description, "example app C"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test/test_projects/linter_config/app_c/src/app_c.erl b/test/test_projects/linter_config/app_c/src/app_c.erl new file mode 100644 index 0000000000..57573554b6 --- /dev/null +++ b/test/test_projects/linter_config/app_c/src/app_c.erl @@ -0,0 +1,4 @@ +-module(app_c). + +-define(MACRO_C, c). +-record(rec_c, {c :: atom()}). diff --git a/test/test_projects/linter_config/rebar.config b/test/test_projects/linter_config/rebar.config new file mode 100644 index 0000000000..d508bd24de --- /dev/null +++ b/test/test_projects/linter_config/rebar.config @@ -0,0 +1,9 @@ +{checkouts_dir, ["."]}. +{project_app_dirs, [ + "app_a", + "app_b", + "app_c" +]}. + +{erl_opts, [debug_info]}. +{deps, []}. diff --git a/test/test_projects/parse_error/.elp.toml b/test/test_projects/parse_error/.elp.toml new file mode 100644 index 0000000000..1a06a42f0d --- /dev/null +++ b/test/test_projects/parse_error/.elp.toml @@ -0,0 +1,8 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//parse_error/..." ] +source_root = "parse_error" + +[eqwalizer] +enable_all = false diff --git a/test_projects/parse_error/.gitignore b/test/test_projects/parse_error/.gitignore similarity index 100% rename from test_projects/parse_error/.gitignore rename to test/test_projects/parse_error/.gitignore diff --git a/test_projects/parse_error/.rebar.root b/test/test_projects/parse_error/.rebar.root similarity index 100% rename from test_projects/parse_error/.rebar.root rename to test/test_projects/parse_error/.rebar.root diff --git a/test/test_projects/parse_error/BUCK b/test/test_projects/parse_error/BUCK new file mode 100644 index 0000000000..ed65c267d0 --- /dev/null +++ b/test/test_projects/parse_error/BUCK @@ -0,0 +1,9 @@ +oncall("vscode_erlang") + +erlang_application( + name = "elp_test_parse_a", + srcs = glob(["parse_error_a/src/*.erl"]), + app_src = "parse_error_a/src/parse_error_a.app.src", + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/parse_error/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.app.src rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.app.src diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_reference_bad.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_syntax_error.erl diff --git a/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl b/test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl similarity index 100% rename from test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl rename to test/test_projects/parse_error/parse_error_a/src/parse_error_a_worst.erl diff --git a/test_projects/parse_error/rebar.config b/test/test_projects/parse_error/rebar.config similarity index 100% rename from test_projects/parse_error/rebar.config rename to test/test_projects/parse_error/rebar.config diff --git a/test/test_projects/standard/.elp.toml b/test/test_projects/standard/.elp.toml new file mode 100644 index 0000000000..f5e2ac1943 --- /dev/null +++ b/test/test_projects/standard/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//standard/..." ] +source_root = "standard" diff --git a/test_projects/standard/.gitignore b/test/test_projects/standard/.gitignore similarity index 100% rename from test_projects/standard/.gitignore rename to test/test_projects/standard/.gitignore diff --git a/test_projects/standard/.rebar.root b/test/test_projects/standard/.rebar.root similarity index 100% rename from test_projects/standard/.rebar.root rename to test/test_projects/standard/.rebar.root diff --git a/test/test_projects/standard/BUCK b/test/test_projects/standard/BUCK new file mode 100644 index 0000000000..3f1e9ee947 --- /dev/null +++ b/test/test_projects/standard/BUCK @@ -0,0 +1,49 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + app_src = "app_a/src/app_a.app.src", + applications = [ + ], + includes = glob(["app_a/include/*.hrl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_standard_app_b", + srcs = glob(["app_b/src/*.erl"]), + app_src = "app_b/src/app_b.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "elp_test_eqwalizer", + srcs = glob(["eqwalizer/src/*.erl"]), + app_src = "eqwalizer/src/eqwalizer.app.src", + labels = ["user_application"], + version = "1.0.0", +) + +erlang_tests( + suites = [ + "app_a/test/app_a_SUITE.erl", + ], + deps = [":app_a"], +) + +erlang_application( + name = "app_a_test_utils", + srcs = [ + "app_a/test/app_a_test_helpers.erl", + "app_a/test/app_a_test_helpers_not_opted_in.erl", + "app_a/test/app_test_helpers_no_errors.erl", + ], + applications = [":app_a"], + labels = [ + "test_application", + "test_utils", + ], +) diff --git a/test/test_projects/standard/app_a/.eqwalizer b/test/test_projects/standard/app_a/.eqwalizer new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test_projects/standard/app_a/extra/app_a.erl b/test/test_projects/standard/app_a/extra/app_a.erl similarity index 100% rename from test_projects/standard/app_a/extra/app_a.erl rename to test/test_projects/standard/app_a/extra/app_a.erl diff --git a/test_projects/standard/app_a/include/app_a.hrl b/test/test_projects/standard/app_a/include/app_a.hrl similarity index 100% rename from test_projects/standard/app_a/include/app_a.hrl rename to test/test_projects/standard/app_a/include/app_a.hrl diff --git a/test/test_projects/standard/app_a/src/app_a.app.src b/test/test_projects/standard/app_a/src/app_a.app.src new file mode 100644 index 0000000000..1381435a46 --- /dev/null +++ b/test/test_projects/standard/app_a/src/app_a.app.src @@ -0,0 +1,3 @@ +{application, app_a, + [{description, "example app A"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test_projects/standard/app_a/src/app_a.erl b/test/test_projects/standard/app_a/src/app_a.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a.erl rename to test/test_projects/standard/app_a/src/app_a.erl diff --git a/test_projects/standard/app_a/src/app_a_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_fixme.erl b/test/test_projects/standard/app_a/src/app_a_fixme.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_fixme.erl rename to test/test_projects/standard/app_a/src/app_a_fixme.erl diff --git a/test_projects/standard/app_a/src/app_a_ignored.erl b/test/test_projects/standard/app_a/src/app_a_ignored.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_ignored.erl rename to test/test_projects/standard/app_a/src/app_a_ignored.erl diff --git a/test_projects/standard/app_a/src/app_a_lists.erl b/test/test_projects/standard/app_a/src/app_a_lists.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_lists.erl rename to test/test_projects/standard/app_a/src/app_a_lists.erl diff --git a/test_projects/standard/app_a/src/app_a_mod2.erl b/test/test_projects/standard/app_a/src/app_a_mod2.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_mod2.erl rename to test/test_projects/standard/app_a/src/app_a_mod2.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors.erl b/test/test_projects/standard/app_a/src/app_a_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_generated.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_generated.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_generated.erl diff --git a/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl b/test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl rename to test/test_projects/standard/app_a/src/app_a_no_errors_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_a_SUITE.erl b/test/test_projects/standard/app_a/test/app_a_SUITE.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_SUITE.erl rename to test/test_projects/standard/app_a/test/app_a_SUITE.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers.erl diff --git a/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl b/test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl similarity index 100% rename from test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl rename to test/test_projects/standard/app_a/test/app_a_test_helpers_not_opted_in.erl diff --git a/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl b/test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl similarity index 100% rename from test_projects/standard/app_a/test/app_test_helpers_no_errors.erl rename to test/test_projects/standard/app_a/test/app_test_helpers_no_errors.erl diff --git a/test/test_projects/standard/app_b/src/app_b.app.src b/test/test_projects/standard/app_b/src/app_b.app.src new file mode 100644 index 0000000000..4112b4129f --- /dev/null +++ b/test/test_projects/standard/app_b/src/app_b.app.src @@ -0,0 +1,3 @@ +{application, app_b, + [{description, "example app B"}, {vsn, "inplace"}, {applications, [kernel, stdlib]}] +}. diff --git a/test_projects/standard/app_b/src/app_b.erl b/test/test_projects/standard/app_b/src/app_b.erl similarity index 100% rename from test_projects/standard/app_b/src/app_b.erl rename to test/test_projects/standard/app_b/src/app_b.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.app.src b/test/test_projects/standard/eqwalizer/src/eqwalizer.app.src similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.app.src rename to test/test_projects/standard/eqwalizer/src/eqwalizer.app.src diff --git a/test_projects/standard/eqwalizer/src/eqwalizer.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer.erl diff --git a/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl b/test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl similarity index 100% rename from test_projects/standard/eqwalizer/src/eqwalizer_specs.erl rename to test/test_projects/standard/eqwalizer/src/eqwalizer_specs.erl diff --git a/test_projects/standard/erlang_ls.config b/test/test_projects/standard/erlang_ls.config similarity index 100% rename from test_projects/standard/erlang_ls.config rename to test/test_projects/standard/erlang_ls.config diff --git a/test_projects/standard/rebar.config b/test/test_projects/standard/rebar.config similarity index 100% rename from test_projects/standard/rebar.config rename to test/test_projects/standard/rebar.config diff --git a/test/test_projects/toolchains/BUCK b/test/test_projects/toolchains/BUCK new file mode 100644 index 0000000000..770ac61f67 --- /dev/null +++ b/test/test_projects/toolchains/BUCK @@ -0,0 +1,5 @@ +load("@prelude//toolchains:demo.bzl", "system_demo_toolchains") + +oncall("vscode_erlang") + +system_demo_toolchains() diff --git a/test/test_projects/xref/.elp.toml b/test/test_projects/xref/.elp.toml new file mode 100644 index 0000000000..87c2f17334 --- /dev/null +++ b/test/test_projects/xref/.elp.toml @@ -0,0 +1,5 @@ +[buck] +enabled = true +build_deps = false +included_targets = [ "root//xref/..." ] +source_root = "xref" diff --git a/test/test_projects/xref/BUCK b/test/test_projects/xref/BUCK new file mode 100644 index 0000000000..bf44968f02 --- /dev/null +++ b/test/test_projects/xref/BUCK @@ -0,0 +1,25 @@ +oncall("vscode_erlang") + +erlang_application( + name = "app_a", + srcs = glob(["app_a/src/*.erl"]), + applications = [ + ":app_b", + ], + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_b", + srcs = glob(["app_b/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) + +erlang_application( + name = "app_c", + srcs = glob(["app_c/src/*.erl"]), + labels = ["user_application"], + version = "1.0.0", +) diff --git a/test_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/diagnostics/.elp.toml b/test_projects/diagnostics/.elp.toml deleted file mode 100644 index e5ee70c992..0000000000 --- a/test_projects/diagnostics/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/diagnostics/..." ] -source_root = "whatsapp/elp/test_projects/diagnostics" - -[eqwalizer] -enable_all = false diff --git a/test_projects/eqwalizer_callers/.elp.toml b/test_projects/eqwalizer_callers/.elp.toml deleted file mode 100644 index f4b7f6f6ac..0000000000 --- a/test_projects/eqwalizer_callers/.elp.toml +++ /dev/null @@ -1,5 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_callers/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_callers" diff --git a/test_projects/eqwalizer_tests/.elp.toml b/test_projects/eqwalizer_tests/.elp.toml deleted file mode 100644 index ed5c60502f..0000000000 --- a/test_projects/eqwalizer_tests/.elp.toml +++ /dev/null @@ -1,8 +0,0 @@ -[buck] -enabled = true -build_deps = false -included_targets = [ "fbcode//whatsapp/elp/test_projects/eqwalizer_tests/..." ] -source_root = "whatsapp/elp/test_projects/eqwalizer_tests" - -[eqwalizer] -enable_all = true diff --git a/test_projects/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" 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.