[patch] Implementing -Wunused-local-typedefs

Richard Smith richard at metafoo.co.uk
Fri Sep 5 18:01:49 PDT 2014


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?

+      if (T->getAccess() != AS_private)
>>
>> You should also check if the class has any friends; if so, they might use
>> the member.
>>
>
> Good point. Done, added a test for this.
>
>
>> +  }
>>    else if (TypeDecl *TD = dyn_cast<TypeDecl>(FoundDecl))
>>
>> No newline between } and else.
>>
>
> Done.
>
>
>> +  // Build a record containing all of the
>> UnusedLocalTypedefNameCandidates.
>> +  RecordData UnusedLocalTypedefNameCandidates;
>> +  for (const TypedefNameDecl *TD :
>> SemaRef.UnusedLocalTypedefNameCandidates)
>> +    AddDeclRef(TD, UnusedLocalTypedefNameCandidates);
>>
>> Maybe wrap this in a `if (!isModule)`
>>
>
> UnusedLocalTypedefNameCandidates should've been clear()ed for modules at
> this point I think, so should be fine as is.
>
>
>> +  // FIXME: typedef that's only used in a constexpr
>>
>> Do you mean "in a constant expression"? constexpr is an adjective, not a
>> noun.
>>
>
> Done (by fixing the FIXME).
>
> Thanks for the thorough review!
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20140905/4d108a15/attachment.html>


More information about the cfe-commits mailing list