[cfe-commits] Clang change to detect and warn about unused private members
Daniel Jasper
djasper at google.com
Tue May 8 23:37:19 PDT 2012
Finally, a new version of this patch.
+ typedef llvm::SmallPtrSet<const NamedDecl*, 16> NamedDeclSetType;
> +
> + /// \brief Set containing all declared private fields that are not used.
> + NamedDeclSetType UnusedPrivateFields;
>
> How big does this set tend to get with, say, a typical .cpp file in LLVM
> or Clang? Big enough that we should be concerned about a performance impact?
>
While compiling files from Sema, I got the following statistics: The
maximum size of this structure was 560, with 1600 total inserts and 14000
attempted deletes. This should not be a performance impact, but I don't
really know with respect to LLVM/Clang.
> + if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
> + if (const Type *T = TSI->getType().getTypePtrOrNull()) {
>
> With a non-NULL TSI, TSI->getType() will always return a non-NULL
> QualType. You can just collapse this into the next line to get
>
> if (CXXRecordDecl *FriendDecl =
> TSI->getType()->getAsCXXRecordDecl()) {
>
Done.
> + if (const FunctionDecl *FD =
> + dyn_cast_or_null<FunctionDecl>((*I)->getFriendDecl()))
> + Complete = FD->isDefined();
>
> I don't think the "_or_null" is needed here, because a FriendDecl either
> befriends a type or a decl.
>
Done.
> I note that these three loops:
>
> + for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
> + E = RD->friend_end();
> + I != E && Complete; ++I) {
>
> + for (CXXRecordDecl::method_iterator M = RD->method_begin(),
> + E = RD->method_end();
> + M != E && Complete; ++M) {
>
> + for (record_iterator I(RD->decls_begin()), E(RD->decls_begin());
> + I != E && Complete; ++I) {
>
> All make a complete pass over all of the declarations within each
> CXXRecordDecl, which is rather inefficient. It would be better to make one
> pass over decls_begin/decls_end and deal with all of the potential cases.
>
Done.
+bool FieldDecl::hasSideEffects() const {
> + if (!getType().isNull()) {
> + if (const CXXRecordDecl *RD = getType()->getAsCXXRecordDecl()) {
> + return !RD->isCompleteDefinition() ||
> + !RD->hasTrivialDefaultConstructor() ||
> + !RD->hasTrivialDestructor();
> + }
> + }
> + return false;
> +}
>
> I don't think that hasSideEffects() belongs on FieldDecl. It can be some
> kind of Sema utility routine, perhaps, that makes it obvious that we mean
> "no side effects due to its initialization."
I have now made it a local function in SemaDeclCXX.cpp and named it more
appropriately. It is not reused for templates anymore (see below).
> Note that there's a C++11 case missing here, for fields with an initializer
>
> struct X {
> int i = 7;
> };
>
>
Added test and implementation for this.
> + llvm::SmallPtrSet<const CXXRecordDecl*, 16> NotFullyDefined;
> + llvm::SmallPtrSet<const CXXRecordDecl*, 16> FullyDefined;
> + for (NamedDeclSetType::iterator I = UnusedPrivateFields.begin(),
> + E = UnusedPrivateFields.end(); I != E;
> ++I) {
>
> This will give a non-deterministic ordering of warnings. You probably want
> to use a SetVector here to keep things in order.
>
Done.
> + const NamedDecl *D = *I;
> + const CXXRecordDecl *RD = cast<const
> CXXRecordDecl>(D->getDeclContext());
> + if (RD && IsRecordFullyDefined(RD, NotFullyDefined,
> + FullyDefined)) {
> + Diag(D->getLocation(), diag::warn_unused_private_field)
> + << D->getNameAsString();
>
> There's no need for D->getNameAsString(). You can just use
> D->getDeclName() and the diagnostic system will format it.
>
Done.
> --- a/lib/Sema/SemaDeclCXX.cpp
> +++ b/lib/Sema/SemaDeclCXX.cpp
> @@ -1643,8 +1643,17 @@ Sema::ActOnCXXMemberDeclarator(Scope *S,
> AccessSpecifier AS, Declarator &D,
>
> assert((Name || isInstField) && "No identifier for non-field ?");
>
> - if (isInstField)
> - FieldCollector->Add(cast<FieldDecl>(Member));
> + if (isInstField) {
> + FieldDecl *FD = cast<FieldDecl>(Member);
> + FieldCollector->Add(FD);
> + // Remember all private FieldDecls that have no side effects and are
> not
> + // part of a dependent type declaration.
> + if (FD->getAccess() == AS_private &&
> + !FD->getParent()->getTypeForDecl()->isDependentType() &&
> + !FD->hasSideEffects())
> + UnusedPrivateFields.insert(FD);
> + }
> +
>
> Shouldn't you also check that FD actually has a name?
>
Yes. Done.
> + // Mark FieldDecl as being used if it is a non-primitive type and the
> + // initializer does not call the default constructor (which is trivial
> + // for all entries in UnusedPrivateFields).
> + // FIXME: Make this smarter once more side effect-free types can be
> + // determined.
> + ValueDecl *VD = cast<ValueDecl>(Member);
> + if (!VD->getType()->isBuiltinType() &&
> + !VD->getType()->isAnyPointerType() &&
> + Args.begin() != Args.end()) {
> + UnusedPrivateFields.erase(VD);
> + }
> +
>
> I find the check for pointer + built-in types odd. Isn't there a more
> obvious, existing type predicate that checks what you want?
>
Yes. I actually want to check that it is not a record-type. Done. Also, I
now check that the initializer itself does not have side-effects.
> diff --git a/lib/Sema/SemaTemplateInstantiate.cpp
> b/lib/Sema/SemaTemplateInstantiate.cpp
> index dde7826..f30a521 100644
> --- a/lib/Sema/SemaTemplateInstantiate.cpp
> +++ b/lib/Sema/SemaTemplateInstantiate.cpp
> @@ -1809,6 +1809,9 @@ Sema::InstantiateClass(SourceLocation
> PointOfInstantiation,
> if (OldField->getInClassInitializer())
> FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
> Field));
> + // Remember all private FieldDecls that have no side effects.
> + if (Field->getAccess() == AS_private && !Field->hasSideEffects())
> + UnusedPrivateFields.insert(Field);
>
> Templates are an interesting case, because it's actually quite unlikely
> that we'll ever see the definition of all of the member functions of a
> class template, since no translation unit is likely to instantiate all of
> them. Perhaps we shouldn't bother even trying to make this warning work for
> templates?
>
Probably not as this cannot be solved locally for a TU. Another TU might
declare an explicit specialization using the otherwise unused private
member. Removed.
Kind regards,
Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120509/e6d9cc56/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 13062 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120509/e6d9cc56/attachment.obj>
More information about the cfe-commits
mailing list