[patch] Implementing -Wunused-local-typedefs

Richard Smith richard at metafoo.co.uk
Fri Sep 5 19:17:38 PDT 2014


On Fri, Sep 5, 2014 at 6:37 PM, Nico Weber <thakis at chromium.org> wrote:

> On Fri, Sep 5, 2014 at 6:01 PM, Richard Smith <richard at metafoo.co.uk>
> wrote:
>
>> On Fri, Sep 5, 2014 at 4:44 PM, Nico Weber <thakis at chromium.org> wrote:
>>
>>> On Fri, Sep 5, 2014 at 2:44 PM, Richard Smith <richard at metafoo.co.uk>
>>> wrote:
>>>
>>>> Thanks, this is looking really good.
>>>>
>>>> +  if (Diags.isIgnored(diag::warn_unused_local_typedef,
>>>> SourceLocation()))
>>>> +    return;
>>>>
>>>> It would seem better to consider the diagnostic state at the point
>>>> where the typedef is declared. (Maybe check this when adding typedefs to
>>>> the set rather than when diagnosing?)
>>>>
>>>
>>> Great catch. -Wunused-private-fields probably gets this wrong too. I
>>> added a test for this (see the very bottom of
>>> warn-unused-local-typedef.cpp, and the pragma diagnostic push/pop). The
>>> test sadly showed that adding when adding the typedefs is wrong too: For
>>> template instantiations, the instantiation is done at end-of-file, but a
>>> pragma diag at end of file shouldn't disable warnings in templates that
>>> physically are further up. So I just removed this check.
>>>
>>>
>>>> +    // Warnigns emitted in ActOnEndOfTranslationUnit() should be
>>>> emitted for
>>>>
>>>> Typo 'Warnigns'.
>>>>
>>>
>>> Doen.
>>>
>>>
>>>> +  // Except for labels, we only care about unused decls that are local
>>>> to
>>>> +  // functions.
>>>> +  bool WithinFunction = D->getDeclContext()->isFunctionOrMethod();
>>>> +  if (const auto *R = dyn_cast<CXXRecordDecl>(D->getDeclContext()))
>>>> +    WithinFunction =
>>>> +        WithinFunction || (R->isLocalClass() && !R->isDependentType());
>>>>
>>>> Why are dependent local classes skipped here? I'd like at least a
>>>> comment to explain this so future readers aren't surprised :)
>>>>
>>>
>>> I added a short comment. This is covered by my tests, so removing it
>>> does make something fail.
>>> (SemaTemplateInstantiateDecl.cpp's Sema::BuildVariableInstantiation() calls
>>> this method for instantiated fields, so the warnings are deferred until
>>> after instantiation.)
>>>
>>>
>>>>
>>>> +  for (auto *TmpD : D->decls()) {
>>>> +    if (const auto* T = dyn_cast<TypedefNameDecl>(TmpD))
>>>> +      DiagnoseUnusedDecl(T);
>>>> +    else if(const auto *R = dyn_cast<RecordDecl>(TmpD))
>>>>
>>>> '*' on the right, please.
>>>>
>>>
>>>
>>>     Done.
>>>
>>>
>>>>
>>>> +  if (auto* TD = dyn_cast<TypedefNameDecl>(D)) {
>>>> +    // typedefs can be referenced later on, so the diagnostics are
>>>> emitted
>>>> +    // at end-of-translation-unit.
>>>>
>>>> Likewise.
>>>>
>>>
>>>
>>>     Done.
>>>
>>>
>>>>
>>>> +void Sema::DiagnoseUnusedNestedTypedefs(const RecordDecl *D) {
>>>> +  if (D->getTypeForDecl()->isDependentType())
>>>> +    return;
>>>> +
>>>> +  for (auto *TmpD : D->decls()) {
>>>> +    if (const auto* T = dyn_cast<TypedefNameDecl>(TmpD))
>>>> +      DiagnoseUnusedDecl(T);
>>>> +    else if(const auto *R = dyn_cast<RecordDecl>(TmpD))
>>>> +      DiagnoseUnusedNestedTypedefs(R);
>>>> +  }
>>>> +}
>>>> [...]
>>>> -    if (!S->hasUnrecoverableErrorOccurred())
>>>> +    if (!S->hasUnrecoverableErrorOccurred()) {
>>>>        DiagnoseUnusedDecl(D);
>>>> +      if (const auto *RD = dyn_cast<RecordDecl>(D))
>>>> +        DiagnoseUnusedNestedTypedefs(RD);
>>>> +    }
>>>>
>>>> Would it make sense for DiagnoseUnusedDecl to do the recursive walk
>>>> over child decls itself?
>>>>
>>>
>>> Doesn't matter too much either way I guess? Left it as is for now.
>>>
>>>
>>>>
>>>> +/// In a function like
>>>> +/// auto f() {
>>>> +///   struct S { typedef int a; };
>>>> +///   return S;
>>>> +/// }
>>>>
>>>> Should be 'return S();' or similar here.
>>>>
>>>
>>> Done.
>>>
>>>
>>>> +/// others. Pretend that all local typedefs are always references, to
>>>> not warn
>>>>
>>>> Typo 'references'.
>>>>
>>>
>>> Done.
>>>
>>>
>>>> +class LocalTypedefNameReferencer
>>>> +    : public RecursiveASTVisitor<LocalTypedefNameReferencer> {
>>>> +public:
>>>> +  LocalTypedefNameReferencer(Sema &S) : S(S) {}
>>>> +  bool VisitRecordType(const RecordType *RT);
>>>> +private:
>>>> +  Sema &S;
>>>> +};
>>>>
>>>> I think you'll also need to handle MemberPointerType here; the `Class`
>>>> in `T Class::*` is just modeled as a CXXRecordDecl, not a RecordType.
>>>>
>>>
>>> I added the (slightly contrived :-) ) test you showed me, it seems to
>>> pass as-is.
>>>
>>
>> Does it still not warn if you remove the g() function and its use?
>>
>
> Yes, as discussed on IRC, RecursiveASTVisitor gets this right for me.
>
> As also discussed on IRC, landed in r217298.
>

(For posterity: LGTM)


> Thanks for the great review, and for realizing that this warning is
> trickier to get right than I (and the person who implemented it in gcc ;-)
> ) thought – I learned quite a bit about clang while working on this :-)
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20140905/602462a5/attachment.html>


More information about the cfe-commits mailing list