[cfe-commits] [Patch] Add a warning for for loops with conditions that do not change
Richard Trieu
rtrieu at google.com
Mon Mar 12 16:33:04 PDT 2012
On Mon, Mar 12, 2012 at 1:52 PM, Douglas Gregor <dgregor at apple.com> wrote:
>
> On Mar 12, 2012, at 1:38 PM, Richard Trieu wrote:
>
>
>
> On Sun, Mar 11, 2012 at 1:58 PM, Douglas Gregor <dgregor at apple.com> wrote:
>
>>
>> On Feb 14, 2012, at 3:42 PM, Richard Trieu wrote:
>>
>> On Tue, Feb 7, 2012 at 10:38 AM, Douglas Gregor <dgregor at apple.com>wrote:
>>
>>>
>>> On Feb 6, 2012, at 1:00 PM, Eli Friedman wrote:
>>>
>>> > On Mon, Feb 6, 2012 at 2:50 PM, Richard Trieu <rtrieu at google.com>
>>> wrote:
>>> >> On Mon, Feb 6, 2012 at 2:02 PM, Eli Friedman <eli.friedman at gmail.com>
>>> wrote:
>>> >>>
>>> >>> On Mon, Feb 6, 2012 at 1:55 PM, Richard Trieu <rtrieu at google.com>
>>> wrote:
>>> >>>> The motivation of this path is to catch code like this:
>>> >>>>
>>> >>>> for (int i = 0; i < 10; ++i)
>>> >>>> for (int j = 0; j < 10; ++i)
>>> >>>> { }
>>> >>>>
>>> >>>> The second for loop increments i instead of j causing an infinite
>>> loop.
>>> >>>> The
>>> >>>> warning also checks the body of the for loop so it will trigger on:
>>> >>>>
>>> >>>> for (int i; i <10; ) { }
>>> >>>>
>>> >>>> But not trigger on:
>>> >>>>
>>> >>>> for (int i; i< 10; ) { ++i; }
>>> >>>>
>>> >>>> I'm still fine-tuning the trigger conditions, but would like some
>>> >>>> feedback
>>> >>>> on this patch.
>>> >>>
>>> >>> Adding an additional recursive traversal of the body of every parsed
>>> >>> for loop is very expensive.
>>> >>>
>>> >>> -Eli
>>> >>
>>> >>
>>> >> Is it that expensive? I would think that once the AST was
>>> constructed, the
>>> >> visitor would be pretty fast, especially if no action is taken on
>>> most of
>>> >> the nodes. I also made the warning default ignore and put in an early
>>> >> return to prevent the visitors from running in the default case.
>>> >>
>>> >> Do you have any suggestions on removing the recursive traversal?
>>> >
>>> > Okay, thinking about it a bit more, maybe it's not that expensive, but
>>> > you should at least measure to make sure.
>>>
>>> I'm still very nervous about adding such an AST traversal. Presumably,
>>> it's not going to be a concern in practice because most of the time, the
>>> increment statement of the 'for' loop will mention one of the variables in
>>> the condition, and therefore we'll short-circuit the expensive walk of the
>>> loop body. It would be helpful to know that's the case.
>>>
>>> > I don't have any good suggestion for an alternative.
>>>
>>>
>>> Nor do I.
>>>
>>> A couple more comments:
>>>
>>> + ValueDecl *VD = E->getDecl();
>>> + for (SmallVectorImpl<ValueDecl*>::iterator I = Decls.begin(),
>>> + E = Decls.end();
>>> + I != E; ++I)
>>> + if (*I == VD) {
>>> + FoundDecl = true;
>>> + return;
>>> + }
>>>
>>> This is linear; please use a SmallPtrSet instead.
>>>
>> Done.
>>
>>>
>>> Plus, I think you want to narrow this check to only consider VarDecls.
>>> Functions and enumerators are not interesting.
>>>
>> Done.
>>
>>>
>>> + S.Diag(Second->getLocStart(),
>>> diag::warn_variables_not_in_loop_body)
>>> + << Second->getSourceRange();
>>>
>>> This warning needs to specify which variables were not used or point to
>>> them in the source.
>>>
>> The diagnostic now underlines all the variables in the condition.
>>
>>>
>>> Do we want to actually look for modification, e.g., any use of the
>>> variable that isn't immediately consumed by an lvalue-to-rvalue conversion?
>>>
>> Yes, that is exactly what we are checking for. The VisitCastExpr checks
>> for LvalueToRvalue casts and skips any DeclRefExpr's that are direct
>> sub-expressions.
>>
>>>
>>> - Doug
>>>
>>
>> I also did a comparison between runs with and without -Wloop-analysis.
>> [snip]
>>
>>
>> Thanks for all of the performance testing. A few more comments on the
>> actual code:
>>
>> Index: include/clang/Basic/DiagnosticSemaKinds.td
>> ===================================================================
>> --- include/clang/Basic/DiagnosticSemaKinds.td (revision 150501)
>> +++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)
>> @@ -14,6 +14,11 @@
>> let Component = "Sema" in {
>> let CategoryName = "Semantic Issue" in {
>>
>> +// For loop analysis
>> +def warn_variables_not_in_loop_body : Warning<
>> + "variables used in loop condition not modified in loop body">,
>> + InGroup<DiagGroup<"loop-analysis">>, DefaultIgnore;
>>
>> It would be really nice if this diagnostic could mention the variables by
>> name ("variables 'i' and 'n' used in loop condition are not modified in
>> loop body"), like we do with missing enumerators in a switch. It'll take
>> some special casing for 1/2/many variables, but it will be much nicer for
>> the user.
>>
>
> I will look into this.
>
>
>> + void VisitDeclRefExpr(DeclRefExpr *E) {
>> + VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
>> + if (!VD) return;
>> +
>> + PDiag << E->getSourceRange();
>> +
>> + // Dont do analysis on pointers.
>> + if (VD->getType()->isAnyPointerType()) {
>> + Simple = false;
>> + return;
>> + }
>> +
>> + Decls.insert(VD);
>> + }
>>
>> Presumably, anything volatile-qualified should make the expression
>> non-simple.
>>
> The check for volatile is performed after the DeclMatcher finishes.
>
>>
>> + void VisitCastExpr(CastExpr *E) {
>> + // Don't do analysis on function calls or arrays.
>> + if (E->getCastKind() == CK_FunctionToPointerDecay ||
>> + E->getCastKind() == CK_ArrayToPointerDecay) {
>> + Simple = false;
>> + return;
>> + }
>> +
>> + Visit(E->getSubExpr());
>> + }
>>
>> If you want to avoid calls, why not just intercept CallExpr and
>> CXXConstructExpr and call those non-simple?
>>
>> Backing up a step, it seems strange that DeclExtractor assumes that an
>> expression is 'simple' unless it sees some very small number of cases that
>> aren't consider simple. That feels very brittle to me because, e.g.,
>> CXXConstructExpr is certainly not simple (for non-trivial constructors),
>> yet it would be considered simple by this approach. Similarly for
>> CXXNewExpr and a host of other non-trivial expressions that aren't covered.
>> If we have to have the notion of 'simple', then we need to build it
>> constructively by white-listing expressions that are known to be simple and
>> banning (by default) all other expressions.
>>
>> I'm a bit worried about how many visitor methods I need to write to, but
> I'll give it a try and see what happens.
>
>
> I could settle for a full audit of all of the expressions, but the fact
> that there are several obvious missing cases worries me a lot.
>
> + // DeclMatcher checks to see if the decls are used in a non-evauluated
>> + // context.
>> + class DeclMatcher : public StmtVisitor<DeclMatcher> {
>>
>> Should this use an EvaluatedExprVisitor to provide more accurate
>> diagnostics? e.g., the presence of sizeof(x) doesn't mean that 'x' has been
>> used in a meaningful way.
>>
>
> An EvaluatedExprVisitor is used to find the decls used in the loop
> condition, which is called the DeclExtractor. A separate visitor, the
> DeclMatch, is used on the loop conditional, the loop increment, and the
> loop body to see if the decls are in use. For this, non-evaluated contexts
> that could change the decl are important, so an EvaluatedExprVisitor can
> not be used here.
>
>
> How would a non-evaluated context change the value of the variable?
>
> - Doug
>
>
> Oops, I seemed to have taken part of this conversation off list. I was a
little fuzzy on the definition of an evaluated context, and a bit of
testing shows that an EvaluatedExprVisitor will work for this warning. I
will drop the StmtVisitor.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120312/ad77888b/attachment.html>
More information about the cfe-commits
mailing list