New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Diagnostics: Equal sub-expressions and sub-statements [skip ci] #11553
base: master
Are you sure you want to change the base?
Conversation
Thanks for your pull request and interest in making D better, @nordlow! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + dmd#11553" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a test case and/or updated test suite with AUTO_UPDATE=1
Edit: I overlooked that you mentioned this in your PR description. Sorry.
I don't think flat-out disallowing self-assignment is very sensible. The underlying assumption is that those are no-op and mistakes, but they don't necessarily are. It could be generic code, or it could be a variable with a side effect on Also, comparing the ident is just not going to work. See the current failure on: Lines 403 to 404 in e9cc3dd
Since the main issue is in constructor, I would suggest to only disallow self-assignment of fields within constructors, and leave the rest be. |
This change would break the following example which tests whether
Not the best way to do it but it's probably out there. |
FWIW, "self-assignment of variables is deprecated" is a horrible error message. It does tell me why or what I can do about it. |
This is a more intrusive change than it appears:
This is not self-assignment. the second declaration of This kind of change absolutely needs a precise definition of what semantics it has before we can evaluate it. |
This case is not triggered in my latest commit as I'm comparing the lhs-var and rhs-var for equivalence. I'm not using the Currently pure unittest
{
int x;
x = x; // diagnose
int y;
y = x;
int* xp;
xp = xp; // diagnose
*(&x) = *(&x); // should diagnose
}
int x;
void test() @safe nothrow @nogc
{
int x = x; // shouldn't this give a shadowing warning?
} is diagnosed as
BTW, shouldn't the declaration of |
This
passes with last commit and with |
How about
? |
7104444
to
90cc743
Compare
Still need some sort of specification of what the intended behavior is. |
I strongly second this. So far the cons of this far out-weight the pros IMO. Limiting it to constructors (perhaps even methods) would be one way to improve this, because that's where the error is most common. |
How common is this mistake in practice? |
It's happened to me a handful number of times. |
Now correctly analyses this struct S
{
pure nothrow:
this(this) { count += 1;} // posblit
int count;
}
pure unittest
{
S s;
s = s;
int x;
x = x; // diagnose
int y;
y = x;
int* xp;
xp = xp; // diagnose
*xp = *xp; // diagnose
*&x = *&x; // diagnose
static assert(__traits(compiles, { int t; t = t; }));
}
int x;
void test() @safe nothrow @nogc
{
int x = x; // shouldn't this give a shadowing warning?
} |
Test has been added. No ready for review yet, though. |
This new check is super useful. I was several time facing segmentation faults. It was always caused by self assignments in constructors (when assignment of an argument was meant). |
Those are motivating words. Thanks. I'm planning more checks. |
New diagnostics now finds the following two suspicious lines that breaks the autotester: Line 1530 in paranoia.d containing
triggers error
Line 6245 in interpret3.d containing
triggers error
Are these two statements intentional or bugs? Is it ok to modify them to silence the warnings? |
For reference, the code int x = 0;
x = x; triggers a similar diagnostics
for Clang. Should I stick to my diagnostics naming "from itself"? Furthermore, in Clang, its worth noticing that class C
{
C(int x)
{
x = x;
_x = _x;
}
int _x;
}; diagnoses as warnings only:
|
Is it ok for int g_x;
alias g_y = g_x;
g_y = g_y; to diagnose as
or is it better to print
? What's the preferred way of handling aliases in diagnostics? |
This looks like a bug to me (showing the merits of your PR).
Yes in general it's ok.
AFAICT there's no this context, so at least that part of your detection is wrong.
I personally prefer "to".
Why not mention both? "assignment of g_x to g_y ..." |
I thought so too at first but look again. The indentation is flattened. The scope of |
The test is against an ICE with the assignment, so it should not be removed, but replaced with something similar, e.g.
Most of the module is wrapped in a struct, so the message is ok. The assignment can probably be removed. |
Can you give a complete replacement for struct Chunk13831
{
this(Coord13831)
{
coord = coord;
}
Coord13831 coord;
static const Chunk13831* unknownChunk = new Chunk13831(Coord13831());
} , please? @rainers? My guess is struct Chunk13831
{
this(Coord13831)
{
Coord13831 x;
coord = x;
}
Coord13831 coord;
static const Chunk13831* unknownChunk = new Chunk13831(Coord13831());
} . Is this ok? |
@nordlow Are you still pursuing this? |
Not at the moment, no. I'm uncertain about the scope of the implementation. |
Diagnostics Experimentation Playground
Inspired by warning flags in GCC, Clang, and CppCheck. I see the need for helper functions. So I join these in a PR mergeable with master for now so we can experiment with these all at once in existing projects. Helper functions are
equalsExp
By evaluating checks in an iterative process we can incrementally adjust sensible rules for when to exclude diagnostics, for instance, in assert statements, unittests and (unittest) calls of overloaded aggregate logical, bitwise and assignment operators. In the hope that these checks, in the long run, can be enabled by default. This because
false positives
are more harmful to the developer experience thanfalse negative
.N-ary Expressions giving No-Op for Equal Sub-Expressions
Detects mistakes such as
After having tested these on dmd, druntime and phobos I've decided to disable this diagnostics for enums because (generic) unittests may test behaviour of overridden (binary) operators using constants inside assert statements. It might be better to exclude this check for overridden operators and for any expression inside a unittest.
Check for equality of sub-expressions must be maximally fast so we can have it by default. Therefore I'm only using the
isXXX
members ofExpression
for now to avoid any virtual member calls such asequals(const RootObject)
. Later onequalsExp
should be merged withequals
or moved into afinal Expression.equals(const Expression)
.Self-Assignment of Variables
Comments:
This diagnostics is especially important in constructors when one accidentally write
this(T)(T someMember) { someMember = someMember; }
when one really meant
For reference, the C code
triggers a similar diagnostics
when compiled with
clang-10
.Furthermore, in Clang, its worth noticing that in cases such as
diagnoses as warnings only for both the normal variable and member variable cases: