Skip to content

could_be_immutable: fix false positives for ref/out parameters and member access - #978

Open
cschlote wants to merge 1 commit into
dlang-community:masterfrom
cschlote:fix/could-be-immutable-false-positives
Open

could_be_immutable: fix false positives for ref/out parameters and member access#978
cschlote wants to merge 1 commit into
dlang-community:masterfrom
cschlote:fix/could-be-immutable-false-positives

Conversation

@cschlote

@cschlote cschlote commented Aug 4, 2026

Copy link
Copy Markdown

Fixes #366, fixes #325 (also covers the case reported in #298).

The check suggests const/immutable declarations that do not compile in two situations.

1. Arguments bound to ref/out parameters (#298 / #366)

bool foo()
{
    bool overflowed;
    significand = mulu(significand, pow, overflowed); // "could be immutable" — FALSE
    if (overflowed)
        return false;
}

D-Scanner cannot know mulu's signature (see the discussion in #366: "this limitation is unlikely to fall"), so a by-value and a ref/out binding cannot be distinguished. Arguments of function calls and new expressions are therefore now treated as potential modifications regardless of their type (new callArgument counter disabling the value-type early return in variableMightBeModified). The #640 behavior (new Foo(i1) does not warn) is preserved.

2. Member access without a call (#325)

import std.range : iota;
auto d = iota(2);
d.popFront;                        // paren-less call — previously flagged
assert(d.front == 1);

As noted in #325, the AST cannot tell whether d.popFront is a call, and the member might be a non-const method/property (range accessors) or return something that requires a mutable base — e.g. Nullable.get on a struct holding an array, where immutable doc fails with "contains pointers or references". The base of any member access (a.b, chained a.b.c) is now treated as potentially modified (new markMemberAccessBase).

Preserved behavior / trade-off

  • Bare reads of value types are still reported: int i = 1; return i;
  • Index reads are still reported: int i = 0; return arr[i];
  • Variables used only through member access are no longer reported (Point p; return p.x;) — member const-ness cannot be proven syntactically.

Testing

  • All cases above added as unittests in unmodified.d, plus regression tests for the preserved warnings.
  • make test (54 modules) and tests/it.sh pass.
  • Running the patched binary over this repository's own sources removes exactly 7 could_be_immutable warnings — all confirmed false positives, e.g. the equalRange result range in unmodified.d itself — and adds none.

…mber access

The check treated simple value types passed to a function as unmodified,
but the argument may be bound to a `ref` or `out` parameter, which
modifies it regardless of its type. Treat function call and
new-expression arguments as potential modifications.

Member accesses (`a.b`) were only considered when they appeared in an
otherwise interesting context, but a member can be a non-const method or
property (e.g. range accessors like `empty`/`front`) or return a mutable
copy that requires a mutable base (e.g. `Nullable.get` on a struct
holding an array). Without semantic analysis, const-ness of the base
cannot be proven, so treat the base of a member access as potentially
modified.

Index expressions, return statements and bare reads of value types are
still reported as before.
immutable id = produce(createdNow);
return id + (createdNow ? 1 : 0);
}
}, sac);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a test showing that a regular function parameter won't suppress the lint (out and ref parameters are not that common, they shouldn't be assumed to always be there and break the const lint)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive for Unmodified Variable don't check for 'could be immutable' if object methods are called

2 participants