mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 02:45:12 +03:00
388 lines
9.8 KiB
D
388 lines
9.8 KiB
D
Ddoc
|
|
|
|
$(D_S Warnings,
|
|
|
|
$(P Depending on one's point of view, warnings are either a symptom
|
|
of broken language design or a useful 'lint' like tool to analyze
|
|
code and look for potential trouble spots.
|
|
Most of the time, those trouble spots will be legitimate code
|
|
intended to be that way. More rarely, it may indicate an
|
|
unintentional error on the part of the programmer.
|
|
)
|
|
|
|
$(P Warnings are not a defined part of the D Programming Language.
|
|
They exist at the discretion of the compiler vendor, and
|
|
will most likely vary from vendor to vendor.
|
|
All constructs for which an implementation may generate a warning
|
|
message are legal D code.
|
|
)
|
|
|
|
$(P These are the warnings generated by the Digital Mars D compiler
|
|
when the $(B -w) switch is thrown.
|
|
Most have generated some spirited debate as to whether it should
|
|
be a warning, an error, and what the correct way to write D code
|
|
in these situations should be. Since no consensus emerged,
|
|
they appear as optional warnings, with alternatives on how to
|
|
write correct code.
|
|
)
|
|
|
|
<h3>warning - implicit conversion of expression $(I expr) of type $(I type) to $(I type) can cause loss of data</h3>
|
|
|
|
$(P D follows the C and C++ rules regarding default integral promotion
|
|
in expressions. This is done for both compatibility reasons
|
|
and because modern CPUs are designed to execute such semantics
|
|
efficiently. These rules also include implicit narrowing rules,
|
|
where the result of such can be cast back into a smaller type,
|
|
perhaps losing significant bits in the process:
|
|
)
|
|
|
|
---
|
|
byte a,b,c;
|
|
...
|
|
a = b + c;
|
|
---
|
|
|
|
$(P The $(I b) and $(I c) are both promoted to type $(B int) using
|
|
the default integral promotion rules. The result of the add is then
|
|
also $(B int). To be assigned to $(I a), that $(B int) gets
|
|
implicitly converted to a $(B byte).
|
|
)
|
|
|
|
$(P Whether it is a bug or not in the program depends on if the values
|
|
for $(I b) and $(I c) can possibly cause an overflow, and
|
|
if that overflow matters or not to the algorithm.
|
|
The warning can be resolved by:
|
|
)
|
|
|
|
$(OL
|
|
$(LI Inserting a cast:
|
|
---
|
|
a = cast(byte)(b + c);
|
|
---
|
|
This eliminates the warning, but since casting is a blunt instrument
|
|
that bypasses type checking, this can mask another bug that could
|
|
be introduced if the types of $(I a), $(I b) or $(I c)
|
|
change in the future, or if their types are set by a template
|
|
instantiation. (In generic code, the $(TT cast(byte)) would
|
|
probably be $(TT cast(typeof(a)))).
|
|
)
|
|
|
|
$(LI Changing the type of $(I a) to $(B int).
|
|
This is generally a better solution, but of course may not work
|
|
if it must be a smaller type, which leads us to:
|
|
)
|
|
|
|
$(LI Doing a runtime check for overflow:
|
|
---
|
|
byte a,b,c;
|
|
int tmp;
|
|
...
|
|
tmp = b + c;
|
|
assert(cast(byte)tmp == tmp);
|
|
a = cast(byte)tmp;
|
|
---
|
|
This ensures that no significant bits get lost.
|
|
)
|
|
)
|
|
|
|
$(P Some proposed language solutions are:)
|
|
|
|
$(OL
|
|
$(LI Having the compiler automatically insert the equivalent of
|
|
the option 3 runtime check.
|
|
)
|
|
|
|
$(LI Add a new construct,
|
|
$(TT $(B implicit_cast)($(I type))$(I expression))
|
|
that is a restricted form of the general cast, and will only
|
|
work where implicit casts would normally be allowed.
|
|
)
|
|
|
|
$(LI Implement the $(B implicit_cast) as a template.
|
|
)
|
|
|
|
)
|
|
|
|
<h3>warning - array 'length' hides other 'length' name in outer scope</h3>
|
|
|
|
$(P Inside the [ ] of an array indexing expression or slice, the
|
|
variable $(I length) gets defined and set to be the length
|
|
of that array. The scope of the $(I length) is from the opening
|
|
'[' to the closing ']'.
|
|
)
|
|
|
|
$(P If $(I length) was declared as a variable name
|
|
in an outer scope, that version may have been intended for
|
|
use between the [ ]. For example:
|
|
)
|
|
|
|
---
|
|
char[10] a;
|
|
int length = 4;
|
|
...
|
|
return a[length - 1]; // returns a[9], not a[3]
|
|
---
|
|
|
|
$(P The warning can be resolved by:)
|
|
|
|
$(OL
|
|
$(LI Renaming the outer $(I length) to another name.
|
|
)
|
|
|
|
$(LI Replacing the use of $(I length) within the [ ]
|
|
with $(I a.length).
|
|
)
|
|
|
|
$(LI If $(I length) is at global or class scope, and that was the
|
|
one intended to be used, use $(I .length) or $(I this.length)
|
|
to disambiguate.
|
|
)
|
|
)
|
|
|
|
$(P Some proposed language solutions are:)
|
|
|
|
$(OL
|
|
$(LI Make the $(I length) a special symbol or a keyword instead
|
|
of an implicitly declared variable.
|
|
)
|
|
)
|
|
|
|
<h3>warning - no return at end of function</h3>
|
|
|
|
$(P Consider the following:)
|
|
|
|
---
|
|
int foo(int k, Collection c)
|
|
{
|
|
foreach (int x; c)
|
|
{
|
|
if (x == k)
|
|
return x + 1;
|
|
}
|
|
}
|
|
---
|
|
|
|
$(P and assume that the nature of the algorithm is that $(I x) will
|
|
always be found in $(I c), so the $(B foreach) never falls
|
|
through the bottom of the code.
|
|
There is no $(B return) at the close of the function, because
|
|
that point is not reachable and any return statement would
|
|
be dead code.
|
|
This is perfectly legitimate code.
|
|
The D compiler, to help with ensuring the robustness of such
|
|
code, will ensure it is correct by automatically inserting
|
|
an $(I assert(0);) statement at the close of the function.
|
|
Then, if there's an error in the assumption that the $(B foreach) will
|
|
never fall through, it will be caught at runtime with
|
|
an obvious assertion failure, which is better than the code just
|
|
falling off the end causing erratic, arbitrary failures.
|
|
)
|
|
|
|
$(P D's behavior on this, however, is not common to other languages,
|
|
and some programmers will be acutely uncomfortable with relying
|
|
on this, or they wish to see the error at compile time rather
|
|
than runtime. They wish to see something explicit expressed in the
|
|
source code that the $(B foreach) cannot fall through.
|
|
Hence the warning.
|
|
)
|
|
|
|
$(P The warning can be resolved by:
|
|
)
|
|
|
|
$(OL
|
|
$(LI Putting a return statement at the close of the function,
|
|
returning some arbitrary value (after all, it will never be
|
|
executed):
|
|
|
|
---
|
|
int foo(int k, Collection c)
|
|
{
|
|
foreach (int x; c)
|
|
{
|
|
if (x == k)
|
|
return x + 1;
|
|
}
|
|
return 0; // suppress warning about no return statement
|
|
}
|
|
---
|
|
|
|
While doing this is surprisingly common, and happens when
|
|
the programmer is inexperienced or in a hurry, it is a
|
|
spectacularly bad solution.
|
|
The trouble happens when the $(B foreach) does fall through
|
|
due to a bug, then instead of the bug being detected, now
|
|
the function returns an unexpected value, which may cause other
|
|
problems or go undetected.
|
|
There's another issue with the maintenance programmer who
|
|
sees this, and upon analyzing the code's behavior, wonders why
|
|
there's a return statement that will never be executed.
|
|
(This can be resolved with comments, but comments are always
|
|
missing, out of date, or just plain wrong.)
|
|
Dead code is always a confusing problem to maintenance programming,
|
|
so deliberately inserting it is a bad idea.
|
|
)
|
|
|
|
$(LI A better solution is to make explicit what the D language
|
|
does implicitly - put an assert there:
|
|
|
|
---
|
|
int foo(int k, Collection c)
|
|
{
|
|
foreach (int x; c)
|
|
{
|
|
if (x == k)
|
|
return x + 1;
|
|
}
|
|
assert(0);
|
|
}
|
|
---
|
|
|
|
Now, if the $(B foreach) does fall through, the error will be
|
|
detected. Furthermore, it is self-documenting.
|
|
)
|
|
|
|
$(LI Another alternative is to do a $(B throw) with a custom
|
|
error class and a more user friendly message:
|
|
|
|
---
|
|
int foo(int k, Collection c)
|
|
{
|
|
foreach (int x; c)
|
|
{
|
|
if (x == k)
|
|
return x + 1;
|
|
}
|
|
$(B throw) new MyErrorClass("fell off the end of foo()");
|
|
}
|
|
---
|
|
)
|
|
)
|
|
|
|
<h3>warning - switch statement has no default</h3>
|
|
|
|
$(P Similar to the no return statement warning is the no default
|
|
in a switch warning. Consider:
|
|
)
|
|
|
|
---
|
|
switch (i)
|
|
{
|
|
case 1: dothis(); break;
|
|
case 2: dothat(); break;
|
|
}
|
|
---
|
|
|
|
$(P According to the D language semantics, this means the only possible
|
|
values for $(I i) are 1 and 2. Any other values represent a bug
|
|
in the program. To detect this bug, the compiler implements the
|
|
switch as if it were written:
|
|
)
|
|
|
|
---
|
|
switch (i)
|
|
{
|
|
case 1: dothis(); break;
|
|
case 2: dothat(); break;
|
|
$(B default: throw new SwitchError();)
|
|
}
|
|
---
|
|
|
|
$(P This is quite different from C and C++ behavior, which is as if
|
|
the switch were written:
|
|
)
|
|
|
|
---
|
|
switch (i)
|
|
{
|
|
case 1: dothis(); break;
|
|
case 2: dothat(); break;
|
|
$(B default: break;)
|
|
}
|
|
---
|
|
|
|
$(P The potential for bugs with that is high, as it is a common error
|
|
to accidentally omit a case, or to add a new value in one part of
|
|
the program and overlook adding a case for it in another part.
|
|
Although D will catch this error at runtime by throwing
|
|
an instance of $(TT std.switcherr.SwitchError), some prefer at least
|
|
a warning so such errors can be caught at compile time.
|
|
)
|
|
|
|
$(P The warning can be resolved by:
|
|
)
|
|
|
|
$(OL
|
|
$(LI Inserting an explict default of the form:
|
|
|
|
---
|
|
switch (i)
|
|
{
|
|
case 1: dothis(); break;
|
|
case 2: dothat(); break;
|
|
$(B default: assert(0);)
|
|
}
|
|
---
|
|
)
|
|
|
|
$(LI As in the missing return, a default with a throw of a custom
|
|
error class can be used.
|
|
)
|
|
)
|
|
|
|
<h3>warning - statement is not reachable</h3>
|
|
|
|
$(P Consider the following code:)
|
|
|
|
---
|
|
int foo(int i)
|
|
{
|
|
return i;
|
|
$(B return i + 1;)
|
|
}
|
|
---
|
|
|
|
$(P The second return statement is not reachable, i.e. it is dead
|
|
code. While dead code is poor style in released code, it can
|
|
legitimately
|
|
happen a lot when rapidly trying to isolate down a bug or experiment
|
|
with different bits of code.
|
|
)
|
|
|
|
$(P The warning can be resolved by:
|
|
)
|
|
|
|
$(OL
|
|
$(LI Commenting out the dead code with /+ ... +/ comments:
|
|
---
|
|
int foo(int i)
|
|
{
|
|
return i;
|
|
/+
|
|
return i + 1;
|
|
+/
|
|
}
|
|
---
|
|
)
|
|
|
|
$(LI Putting the dead code inside a $(TT version(none)) conditional:
|
|
---
|
|
int foo(int i)
|
|
{
|
|
return i;
|
|
$(B version (none))
|
|
{
|
|
return i + 1;
|
|
}
|
|
}
|
|
---
|
|
)
|
|
|
|
$(LI Only compile with warnings enabled when doing release builds.
|
|
)
|
|
)
|
|
|
|
)
|
|
|
|
Macros:
|
|
TITLE=Warnings
|
|
WIKI=Warnings
|