Merge pull request 'No-op code correctness improvements detected by Elixir 1.19 compiler' (#7863) from elixir-1.19-cherrypick into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7863
This commit is contained in:
feld 2026-03-25 19:38:16 +00:00
commit 85d311adcf
15 changed files with 119 additions and 88 deletions

View file

@ -0,0 +1 @@
No-op code correctness improvements detected by Elixir 1.19 compiler

View file

@ -78,7 +78,7 @@ defmodule Pleroma.Marker do
defp get_marker(user, timeline) do
case Repo.find_resource(get_query(user, timeline)) do
{:ok, marker} -> %__MODULE__{marker | user: user}
{:ok, %__MODULE__{} = marker} -> %{marker | user: user}
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
end
end

View file

@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do
alias Pleroma.User
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
settings =
%Settings{} =
settings =
changeset
|> Ecto.Changeset.apply_changes()
|> MFA.fetch_settings()
@ -20,20 +21,20 @@ defmodule Pleroma.MFA.Changeset do
end
end
def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do
def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
user
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
end
def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do
totp_settings = %Settings.TOTP{settings.totp | confirmed: true}
def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true}
user
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
end
def setup_totp(%User{} = user, attrs) do
mfa_settings = MFA.fetch_settings(user)
%Settings{} = mfa_settings = MFA.fetch_settings(user)
totp_settings =
%Settings.TOTP{}
@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do
def cast_backup_codes(%User{} = user, codes) do
user
|> put_change(%Settings{
user.multi_factor_authentication_settings
(%Settings{} = user.multi_factor_authentication_settings)
| backup_codes: codes
})
end

View file

@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
def store(upload, opts \\ []) do
opts = get_opts(opts)
with {:ok, upload} <- prepare_upload(upload, opts),
with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts),
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
description = get_description(upload),

View file

@ -17,7 +17,7 @@ defmodule Pleroma.Utils do
dir
|> File.ls!()
|> Enum.map(&Path.join(dir, &1))
|> Kernel.ParallelCompiler.compile()
|> Kernel.ParallelCompiler.compile(return_diagnostics: true)
end
@doc """

View file

@ -106,7 +106,14 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end
defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
defp cast_and_validate(
spec,
operation,
%Conn{} = conn,
content_type,
false = _strict,
cast_opts
) do
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
{:ok, conn} ->
{:ok, conn}

View file

@ -17,11 +17,11 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
def call(conn, errors) do
errors =
Enum.map(errors, fn
%{name: nil, reason: :invalid_enum} = err ->
%OpenApiSpex.Cast.Error{err | name: err.value}
%OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err ->
%{err | name: err.value}
%{name: nil} = err ->
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
%OpenApiSpex.Cast.Error{name: nil} = err ->
%{err | name: List.last(err.path)}
err ->
err

View file

@ -88,7 +88,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> validate()
end
defp listen_object(draft) do
defp listen_object(%__MODULE__{} = draft) do
object =
draft.params
|> Map.take([:album, :artist, :title, :length])
@ -99,34 +99,34 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Map.put("cc", draft.cc)
|> Map.put("actor", draft.user.ap_id)
%__MODULE__{draft | object: object}
%{draft | object: object}
end
defp put_params(draft, params) do
defp put_params(%__MODULE__{} = draft, params) do
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
%__MODULE__{draft | params: params}
%{draft | params: params}
end
defp status(%{params: %{status: status}} = draft) do
%__MODULE__{draft | status: String.trim(status)}
defp status(%__MODULE__{params: %{status: status}} = draft) do
%{draft | status: String.trim(status)}
end
defp summary(%{params: params} = draft) do
%__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}
defp summary(%__MODULE__{params: params} = draft) do
%{draft | summary: Map.get(params, :spoiler_text, "")}
end
defp full_payload(%{status: status, summary: summary} = draft) do
defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do
full_payload = String.trim(status <> summary)
case Utils.validate_character_limit(full_payload, draft.attachments) do
:ok -> %__MODULE__{draft | full_payload: full_payload}
:ok -> %{draft | full_payload: full_payload}
{:error, message} -> add_error(draft, message)
end
end
defp attachments(%{params: params} = draft) do
defp attachments(%__MODULE__{params: params} = draft) do
attachments = Utils.attachments_from_ids(params, draft.user)
draft = %__MODULE__{draft | attachments: attachments}
draft = %{draft | attachments: attachments}
case Utils.validate_attachments_count(attachments) do
:ok -> draft
@ -134,9 +134,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft
defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft)
when is_binary(id) do
# If a post was deleted all its activities (except the newly added Delete) are purged too,
# thus lookup by Create db ID will yield nil just as if it never existed in the first place.
#
@ -148,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, draft.user),
{_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do
%__MODULE__{draft | in_reply_to: activity}
%{draft | in_reply_to: activity}
else
nil ->
add_error(draft, dgettext("errors", "Cannot reply to a deleted status"))
@ -166,40 +167,43 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do
%__MODULE__{draft | in_reply_to: in_reply_to}
defp in_reply_to(
%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft
) do
%{draft | in_reply_to: in_reply_to}
end
defp in_reply_to(draft), do: draft
defp quote_post(%{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do
defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft)
when not_empty_string(id) do
case Activity.get_by_id_with_object(id) do
%Activity{} = activity ->
%__MODULE__{draft | quote_post: activity}
%{draft | quote_post: activity}
_ ->
draft
end
end
defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do
defp quote_post(%__MODULE__{params: %{quote_id: id}} = draft) when not_empty_string(id) do
quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)})
end
defp quote_post(draft), do: draft
defp in_reply_to_conversation(draft) do
defp in_reply_to_conversation(%__MODULE__{} = draft) do
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
%{draft | in_reply_to_conversation: in_reply_to_conversation}
end
defp visibility(%{params: params} = draft) do
defp visibility(%__MODULE__{params: params} = draft) do
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
{visibility, "direct"} when visibility != "direct" ->
add_error(draft, dgettext("errors", "The message visibility must be direct"))
{visibility, _} ->
%__MODULE__{draft | visibility: visibility}
%{draft | visibility: visibility}
end
end
@ -215,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
false
end
defp quoting_visibility(%{quote_post: %Activity{}} = draft) do
defp quoting_visibility(%__MODULE__{quote_post: %Activity{}} = draft) do
with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false),
true <- can_quote?(draft, object, Visibility.get_visibility(object)) do
draft
@ -226,24 +230,24 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
defp quoting_visibility(draft), do: draft
defp expires_at(draft) do
defp expires_at(%__MODULE__{} = draft) do
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
{:ok, expires_at} -> %{draft | expires_at: expires_at}
{:error, message} -> add_error(draft, message)
end
end
defp poll(draft) do
defp poll(%__MODULE__{} = draft) do
case Utils.make_poll_data(draft.params) do
{:ok, {poll, poll_emoji}} ->
%__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
%{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
{:error, message} ->
add_error(draft, message)
end
end
defp content(%{mentions: mentions} = draft) do
defp content(%__MODULE__{mentions: mentions} = draft) do
{content_html, mentioned_users, tags} = Utils.make_content_html(draft)
mentioned_ap_ids =
@ -254,25 +258,25 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Kernel.++(mentioned_ap_ids)
|> Utils.get_addressed_users(draft.params[:to])
%__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags}
%{draft | content_html: content_html, mentions: mentions, tags: tags}
end
defp to_and_cc(draft) do
defp to_and_cc(%__MODULE__{} = draft) do
{to, cc} = Utils.get_to_and_cc(draft)
%__MODULE__{draft | to: to, cc: cc}
%{draft | to: to, cc: cc}
end
defp context(draft) do
defp context(%__MODULE__{} = draft) do
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
%__MODULE__{draft | context: context}
%{draft | context: context}
end
defp sensitive(draft) do
defp sensitive(%__MODULE__{} = draft) do
sensitive = draft.params[:sensitive]
%__MODULE__{draft | sensitive: sensitive}
%{draft | sensitive: sensitive}
end
defp language(draft) do
defp language(%__MODULE__{} = draft) do
language =
with language <- draft.params[:language],
true <- good_locale_code?(language) do
@ -281,10 +285,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
_ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
end
%__MODULE__{draft | language: language}
%{draft | language: language}
end
defp object(draft) do
defp object(%__MODULE__{} = draft) do
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
# Sometimes people create posts with subject containing emoji,
@ -325,15 +329,15 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Map.put("generator", draft.params[:generator])
|> Map.put("language", draft.language)
%__MODULE__{draft | object: object}
%{draft | object: object}
end
defp preview?(draft) do
defp preview?(%__MODULE__{} = draft) do
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
%__MODULE__{draft | preview?: preview?}
%{draft | preview?: preview?}
end
defp changes(draft) do
defp changes(%__MODULE__{} = draft) do
direct? = draft.visibility == "direct"
additional = %{"cc" => draft.cc, "directMessage" => direct?}
@ -353,14 +357,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
}
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
%__MODULE__{draft | changes: changes}
%{draft | changes: changes}
end
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
defp with_valid(draft, _func), do: draft
defp add_error(draft, message) do
%__MODULE__{draft | valid?: false, errors: [message | draft.errors]}
defp add_error(%__MODULE__{} = draft, message) do
%{draft | valid?: false, errors: [message | draft.errors]}
end
defp validate(%{valid?: true} = draft), do: {:ok, draft}

View file

@ -273,24 +273,28 @@ defmodule Pleroma.ConfigDBTest do
end
test "sigil" do
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source ==
~r/comp[lL][aA][iI][nN]er/.source
end
test "link sigil" do
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source ==
~r/https:\/\/example.com/.source
end
test "link sigil with um modifiers" do
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
~r/https:\/\/example.com/um
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um").source ==
~r/https:\/\/example.com/um.source
end
test "link sigil with i modifier" do
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source ==
~r/https:\/\/example.com/i.source
end
test "link sigil with s modifier" do
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source ==
~r/https:\/\/example.com/s.source
end
test "raise if valid delimiter not found" do
@ -460,11 +464,11 @@ defmodule Pleroma.ConfigDBTest do
test "complex keyword with sigil" do
assert ConfigDB.to_elixir_types([
%{"tuple" => [":federated_timeline_removal", []]},
%{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
%{"tuple" => [":reject", [~r/comp[lL][aA][iI][nN]er/.source]]},
%{"tuple" => [":replace", []]}
]) == [
federated_timeline_removal: [],
reject: [~r/comp[lL][aA][iI][nN]er/],
reject: [~r/comp[lL][aA][iI][nN]er/.source],
replace: []
]
end

View file

@ -36,11 +36,12 @@ defmodule Pleroma.MarkerTest do
insert(:notification, user: user, activity: insert(:note_activity))
insert(:notification, user: user, activity: insert(:note_activity))
insert(:marker, timeline: "home", user: user)
%Marker{} = refreshed_marker = refresh_record(marker)
assert Marker.get_markers(
user,
["notifications"]
) == [%Marker{refresh_record(marker) | unread_count: 2}]
) == [%{refreshed_marker | unread_count: 2}]
end
end

View file

@ -24,7 +24,8 @@ defmodule Pleroma.RepoTest do
describe "get_assoc/2" do
test "get assoc from preloaded data" do
user = %User{name: "Agent Smith"}
token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user}
%Pleroma.Web.OAuth.Token{} = token = insert(:oauth_token)
token = %{token | user: user}
assert Repo.get_assoc(token, :user) == {:ok, user}
end

View file

@ -1524,8 +1524,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
%{test_file: test_file}
end
test "strips / from filename", %{test_file: file} do
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
test "strips / from filename", %{test_file: %Plug.Upload{} = file} do
file = %{file | filename: "../../../../../nested/bad.jpg"}
{:ok, %Object{} = object} = ActivityPub.upload(file)
[%{"href" => href}] = object.data["url"]
assert Regex.match?(~r"/bad.jpg$", href)
@ -1786,10 +1786,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
{:ok, list} = Pleroma.List.follow(list, member)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
{:ok, %Activity{} = activity} =
CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
activity = Repo.preload(activity, :bookmark)
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
activity = %{activity | thread_muted?: !!activity.thread_muted?}
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
end
@ -1989,7 +1990,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert User.following?(follower, old_user)
assert User.following?(follower_move_opted_out, old_user)
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
assert {:ok, %Activity{} = activity} = ActivityPub.move(old_user, new_user)
assert %Activity{
actor: ^old_ap_id,
@ -2021,7 +2022,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert User.following?(follower_move_opted_out, old_user)
refute User.following?(follower_move_opted_out, new_user)
activity = %Activity{activity | object: nil}
activity = %{activity | object: nil}
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)

View file

@ -10,18 +10,25 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
alias Pleroma.Web.ActivityPub.MRF
defp regexes_match!([],[]), do: true
defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do
authority.source == checked.source and regexes_match!(authority_rest, checked_rest)
end
defp regexes_match!(_, _), do: false
test "subdomains_regex/1" do
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
~r/^unsafe.tld$/i,
~r/^(.*\.)*unsafe.tld$/i
]
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i])
end
describe "subdomain_match/2" do
test "common domains" do
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i])
assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
@ -32,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "wildcard domains with one subdomain" do
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
@ -43,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "wildcard domains with two subdomains" do
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
assert MRF.subdomain_match?(regexes, "unsafe.tld")
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
@ -54,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
test "matches are case-insensitive" do
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]
assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i])
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")

View file

@ -129,8 +129,8 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end
test "Do not allow nested filename", %{conn: conn, image: image} do
image = %Plug.Upload{
test "Do not allow nested filename", %{conn: conn, image: %Plug.Upload{} = image} do
image = %{
image
| filename: "../../../../../nested/file.jpg"
}

View file

@ -3068,7 +3068,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|> json_response_and_validate_schema(:ok)
{:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
assert DateTime.diff(expires_at, a_expires_at) == 0
assert DateTime.diff(
DateTime.truncate(expires_at, :second),
DateTime.truncate(a_expires_at, :second)
) == 0
%{conn: conn} = oauth_access(["read:statuses"])