From db176eb2a9d1a567339d795a58837e1906375e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Wed, 21 Feb 2024 12:33:23 +0100 Subject: [PATCH 01/11] dirty macro warn if function does not match any --- lib/unifex/specs.ex | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 6564a4b..a419809 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -70,17 +70,36 @@ defmodule Unifex.Specs do {_res, binds} = Code.eval_string(specs_code, [{:unifex_config__, []}], make_env(specs_file)) config = binds |> Keyword.fetch!(:unifex_config__) |> Enum.reverse() - {functions_args, functions_results} = + {functions_args_arity, functions_results} = config |> Keyword.get_values(:function) - |> Enum.map(fn {name, args, results} -> {{name, args}, {name, results}} end) + |> Enum.map(fn {name, args, results} -> + {{{name, args}, {name, Enum.count(args)}}, {name, results}} + end) |> Enum.unzip() + {functions_args, functions_arity} = Enum.unzip(functions_args_arity) + functions_results = Enum.flat_map(functions_results, fn {name, results} -> Enum.map(results, &{name, &1}) end) functions_docs = parse_docs(config, specs_file) + dirty_functions = + config |> Keyword.get_values(:dirty_functions) |> List.flatten() |> Map.new() + + dirty_functions + |> Map.keys() + |> Enum.each(fn dirty_function -> + if not Enum.member?(functions_arity, dirty_function) do + {name, arity} = dirty_function + + Logger.warning( + "Function #{name} with arity #{arity} marked as dirty that does not correspond to any function defined in spec." + ) + end + end) + %__MODULE__{ name: name, module: Keyword.get(config, :module), @@ -88,8 +107,7 @@ defmodule Unifex.Specs do functions_results: functions_results, functions_docs: functions_docs, sends: Keyword.get_values(config, :sends), - dirty_functions: - config |> Keyword.get_values(:dirty_functions) |> List.flatten() |> Map.new(), + dirty_functions: dirty_functions, callbacks: config |> Keyword.get_values(:callback) |> Map.new(), interface: Keyword.get(config, :interface), state_type: Keyword.get(config, :state_type, nil), From 5f4665950c7ce3d4aca422f4a69ca7319988a821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Wed, 21 Feb 2024 12:59:05 +0100 Subject: [PATCH 02/11] display file that generates warning --- lib/unifex/specs.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index a419809..976c262 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -95,7 +95,7 @@ defmodule Unifex.Specs do {name, arity} = dirty_function Logger.warning( - "Function #{name} with arity #{arity} marked as dirty that does not correspond to any function defined in spec." + "Function #{name} with arity #{arity} marked as dirty that does not correspond to any function defined in spec (#{specs_file})." ) end end) From 6a13e4a7074d7f8fb1c1dc5f78d9f7c3f532c794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Wed, 21 Feb 2024 17:13:55 +0100 Subject: [PATCH 03/11] support no-arity --- lib/unifex/specs.ex | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 976c262..41d9afc 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -79,26 +79,35 @@ defmodule Unifex.Specs do |> Enum.unzip() {functions_args, functions_arity} = Enum.unzip(functions_args_arity) - + IO.inspect(functions_arity, label: "functions_arity") functions_results = Enum.flat_map(functions_results, fn {name, results} -> Enum.map(results, &{name, &1}) end) functions_docs = parse_docs(config, specs_file) dirty_functions = - config |> Keyword.get_values(:dirty_functions) |> List.flatten() |> Map.new() - - dirty_functions - |> Map.keys() - |> Enum.each(fn dirty_function -> - if not Enum.member?(functions_arity, dirty_function) do - {name, arity} = dirty_function - - Logger.warning( - "Function #{name} with arity #{arity} marked as dirty that does not correspond to any function defined in spec (#{specs_file})." - ) - end - end) + config + |> Keyword.get_values(:dirty_functions) + |> List.flatten() + |> IO.inspect(label: "dirty_functions list before") + |> Enum.flat_map(fn {dirty_func, type} -> + cond do + is_atom(dirty_func) -> + matched_func = List.keyfind(functions_arity, dirty_func, 0) + if matched_func == nil do + Logger.warning( + "Function #{dirty_func} marked as dirty that does not correspond to any function defined in spec (#{specs_file})." + ) + [] + else + [{matched_func, type}] + end + is_tuple(dirty_func) -> + [{dirty_func, type}] + end + end) + |> IO.inspect(label: "dirty_functions list after") + |> Map.new() %__MODULE__{ name: name, From b306a2f7a9c91ec350dc5b316f6a9aed4900c622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Wed, 21 Feb 2024 17:24:35 +0100 Subject: [PATCH 04/11] wip --- lib/unifex/specs.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 41d9afc..a979f34 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -93,22 +93,22 @@ defmodule Unifex.Specs do |> Enum.flat_map(fn {dirty_func, type} -> cond do is_atom(dirty_func) -> - matched_func = List.keyfind(functions_arity, dirty_func, 0) - if matched_func == nil do + matched_func_arity = List.keyfind(functions_arity, dirty_func, 0) + if matched_func_arity == nil do Logger.warning( "Function #{dirty_func} marked as dirty that does not correspond to any function defined in spec (#{specs_file})." ) [] else - [{matched_func, type}] + {name, arity} = matched_func_arity + [{name, type}] end is_tuple(dirty_func) -> [{dirty_func, type}] end end) - |> IO.inspect(label: "dirty_functions list after") |> Map.new() - + |> IO.inspect(label: "dirty_functions list after") %__MODULE__{ name: name, module: Keyword.get(config, :module), From c73071fd1985b1e6c4e1dda09c2be6bbefc4498c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 13:31:51 +0100 Subject: [PATCH 05/11] wip --- lib/unifex/specs.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index a979f34..030433b 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -104,7 +104,8 @@ defmodule Unifex.Specs do [{name, type}] end is_tuple(dirty_func) -> - [{dirty_func, type}] + {name, arity} = dirty_func + [{name, type}] end end) |> Map.new() From e91af63d1d598016f749de8408e6e2da2860ee49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 14:14:42 +0100 Subject: [PATCH 06/11] works --- lib/unifex/specs.ex | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 030433b..9e00bc2 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -90,26 +90,37 @@ defmodule Unifex.Specs do |> Keyword.get_values(:dirty_functions) |> List.flatten() |> IO.inspect(label: "dirty_functions list before") - |> Enum.flat_map(fn {dirty_func, type} -> + |> Enum.map(fn {dirty_func, type} -> cond do + is_tuple(dirty_func) -> + {dirty_name, _dirty_arity} = dirty_func + matched_func = List.keyfind(functions_arity, dirty_name, 0) + if matched_func == dirty_func do + {dirty_func, type} + else + Logger.warning( + "Function #{dirty_name} marked as dirty does not correspond to any function defined in spec (#{specs_file})." + ) + nil + end is_atom(dirty_func) -> - matched_func_arity = List.keyfind(functions_arity, dirty_func, 0) - if matched_func_arity == nil do + matched_func = List.keyfind(functions_arity, dirty_func, 0) + if matched_func == nil do Logger.warning( - "Function #{dirty_func} marked as dirty that does not correspond to any function defined in spec (#{specs_file})." + "Function #{dirty_func} marked as dirty does not correspond to any function defined in spec (#{specs_file})." ) - [] + nil else - {name, arity} = matched_func_arity - [{name, type}] + {name, arity} = matched_func + {{name, arity}, type} end - is_tuple(dirty_func) -> - {name, arity} = dirty_func - [{name, type}] + true -> nil end end) + |> Enum.reject(fn el -> el == nil end) |> Map.new() - |> IO.inspect(label: "dirty_functions list after") + |> IO.inspect(label: "dirty_functions map") + %__MODULE__{ name: name, module: Keyword.get(config, :module), From 851c87ab40cae1d492f4ee049b514b3e996a75f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 14:56:58 +0100 Subject: [PATCH 07/11] optimization, credo etc --- lib/unifex/specs.ex | 47 +++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 9e00bc2..69b1818 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -79,7 +79,7 @@ defmodule Unifex.Specs do |> Enum.unzip() {functions_args, functions_arity} = Enum.unzip(functions_args_arity) - IO.inspect(functions_arity, label: "functions_arity") + functions_results = Enum.flat_map(functions_results, fn {name, results} -> Enum.map(results, &{name, &1}) end) @@ -89,37 +89,30 @@ defmodule Unifex.Specs do config |> Keyword.get_values(:dirty_functions) |> List.flatten() - |> IO.inspect(label: "dirty_functions list before") |> Enum.map(fn {dirty_func, type} -> - cond do - is_tuple(dirty_func) -> - {dirty_name, _dirty_arity} = dirty_func - matched_func = List.keyfind(functions_arity, dirty_name, 0) - if matched_func == dirty_func do - {dirty_func, type} - else - Logger.warning( - "Function #{dirty_name} marked as dirty does not correspond to any function defined in spec (#{specs_file})." - ) - nil - end - is_atom(dirty_func) -> - matched_func = List.keyfind(functions_arity, dirty_func, 0) - if matched_func == nil do - Logger.warning( - "Function #{dirty_func} marked as dirty does not correspond to any function defined in spec (#{specs_file})." - ) - nil - else - {name, arity} = matched_func - {{name, arity}, type} - end - true -> nil + {dirty_name, dirty_arity} = + cond do + is_tuple(dirty_func) -> + dirty_func + + is_atom(dirty_func) -> + {dirty_func, nil} + end + + {matched_name, matched_arity} = List.keyfind(functions_arity, dirty_name, 0, {nil, nil}) + + if matched_name == dirty_name and (matched_arity == dirty_arity or dirty_arity == nil) do + {{dirty_name, matched_arity}, type} + else + Logger.warning( + "Function #{dirty_name} marked as dirty does not match any function defined in spec (#{specs_file})." + ) + + nil end end) |> Enum.reject(fn el -> el == nil end) |> Map.new() - |> IO.inspect(label: "dirty_functions map") %__MODULE__{ name: name, From aa28ca5c39a97f168fb42a9e9c717abd2adaa26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 17:58:56 +0100 Subject: [PATCH 08/11] flat map --- lib/unifex/specs.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 69b1818..7413b45 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -89,7 +89,7 @@ defmodule Unifex.Specs do config |> Keyword.get_values(:dirty_functions) |> List.flatten() - |> Enum.map(fn {dirty_func, type} -> + |> Enum.flat_map(fn {dirty_func, type} -> {dirty_name, dirty_arity} = cond do is_tuple(dirty_func) -> @@ -102,16 +102,15 @@ defmodule Unifex.Specs do {matched_name, matched_arity} = List.keyfind(functions_arity, dirty_name, 0, {nil, nil}) if matched_name == dirty_name and (matched_arity == dirty_arity or dirty_arity == nil) do - {{dirty_name, matched_arity}, type} + [{{dirty_name, matched_arity}, type}] else Logger.warning( "Function #{dirty_name} marked as dirty does not match any function defined in spec (#{specs_file})." ) - nil + [] end end) - |> Enum.reject(fn el -> el == nil end) |> Map.new() %__MODULE__{ From 00894edb41369ed59c2476e8e5fb19bed2e07769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 18:18:20 +0100 Subject: [PATCH 09/11] code refactor --- lib/unifex/specs.ex | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 7413b45..33c52c5 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -70,16 +70,12 @@ defmodule Unifex.Specs do {_res, binds} = Code.eval_string(specs_code, [{:unifex_config__, []}], make_env(specs_file)) config = binds |> Keyword.fetch!(:unifex_config__) |> Enum.reverse() - {functions_args_arity, functions_results} = + {functions_args, functions_results} = config |> Keyword.get_values(:function) - |> Enum.map(fn {name, args, results} -> - {{{name, args}, {name, Enum.count(args)}}, {name, results}} - end) + |> Enum.map(fn {name, args, results} -> {{name, args}, {name, results}} end) |> Enum.unzip() - {functions_args, functions_arity} = Enum.unzip(functions_args_arity) - functions_results = Enum.flat_map(functions_results, fn {name, results} -> Enum.map(results, &{name, &1}) end) @@ -90,19 +86,16 @@ defmodule Unifex.Specs do |> Keyword.get_values(:dirty_functions) |> List.flatten() |> Enum.flat_map(fn {dirty_func, type} -> - {dirty_name, dirty_arity} = - cond do - is_tuple(dirty_func) -> - dirty_func - - is_atom(dirty_func) -> - {dirty_func, nil} + dirty_name = + case dirty_func do + {name, _arity} -> name + name -> name end - {matched_name, matched_arity} = List.keyfind(functions_arity, dirty_name, 0, {nil, nil}) + {matched_name, matched_args} = List.keyfind(functions_args, dirty_name, 0, {nil, nil}) - if matched_name == dirty_name and (matched_arity == dirty_arity or dirty_arity == nil) do - [{{dirty_name, matched_arity}, type}] + if matched_name != nil and matched_args != nil and dirty_func in [matched_name, {matched_name, length(matched_args)}] do + [{{dirty_name, length(matched_args)}, type}] else Logger.warning( "Function #{dirty_name} marked as dirty does not match any function defined in spec (#{specs_file})." From e5b32fa66e5627b0f22475f12e96c0f9cca0ade7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 18:19:12 +0100 Subject: [PATCH 10/11] format --- lib/unifex/specs.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index 33c52c5..dfb22f4 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -94,7 +94,8 @@ defmodule Unifex.Specs do {matched_name, matched_args} = List.keyfind(functions_args, dirty_name, 0, {nil, nil}) - if matched_name != nil and matched_args != nil and dirty_func in [matched_name, {matched_name, length(matched_args)}] do + if matched_name != nil and matched_args != nil and + dirty_func in [matched_name, {matched_name, length(matched_args)}] do [{{dirty_name, length(matched_args)}, type}] else Logger.warning( From 750625780a7f0d53ec88039603a5be679ccc2b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Chali=C5=84ski?= Date: Thu, 22 Feb 2024 19:39:08 +0100 Subject: [PATCH 11/11] Update lib/unifex/specs.ex Co-authored-by: Mateusz Front --- lib/unifex/specs.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/unifex/specs.ex b/lib/unifex/specs.ex index dfb22f4..0b41139 100644 --- a/lib/unifex/specs.ex +++ b/lib/unifex/specs.ex @@ -92,10 +92,9 @@ defmodule Unifex.Specs do name -> name end - {matched_name, matched_args} = List.keyfind(functions_args, dirty_name, 0, {nil, nil}) + {matched_name, matched_args} = List.keyfind(functions_args, dirty_name, 0, {nil, []}) - if matched_name != nil and matched_args != nil and - dirty_func in [matched_name, {matched_name, length(matched_args)}] do + if dirty_func in [matched_name, {matched_name, length(matched_args)}] do [{{dirty_name, length(matched_args)}, type}] else Logger.warning(