Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -12,6 +12,7 @@ StaticAssertCheck.cpp SwappedArgumentsCheck.cpp UndelegatedConstructor.cpp + UninitializedFieldCheck.cpp UnusedRAIICheck.cpp UniqueptrResetReleaseCheck.cpp UseOverrideCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -20,6 +20,7 @@ #include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" #include "UndelegatedConstructor.h" +#include "UninitializedFieldCheck.h" #include "UniqueptrResetReleaseCheck.h" #include "UnusedRAIICheck.h" #include "UseOverrideCheck.h" @@ -50,6 +51,8 @@ "misc-swapped-arguments"); CheckFactories.registerCheck( "misc-undelegated-constructor"); + CheckFactories.registerCheck( + "misc-uninitialized-field"); CheckFactories.registerCheck( "misc-uniqueptr-reset-release"); CheckFactories.registerCheck("misc-unused-raii"); Index: clang-tidy/misc/UninitializedFieldCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/UninitializedFieldCheck.h @@ -0,0 +1,38 @@ +//===--- UninitializedFieldCheck.h - clang-tidy------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNINITIALIZED_FIELD_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNINITIALIZED_FIELD_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks that builtin or pointer fields are initialized by explicit +/// constructors. +/// +/// The constructor body is not inspected. Members initialized by assignment or +/// through function calls in the body of the constructor will result in false +/// positives. +/// +/// TODO: See if 'fixes' for false positives are optimized away by the compiler. +class UninitializedFieldCheck : public ClangTidyCheck { +public: + UninitializedFieldCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNINITIALIZED_FIELD_H + Index: clang-tidy/misc/UninitializedFieldCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/UninitializedFieldCheck.cpp @@ -0,0 +1,95 @@ +//===--- UninitializedFieldCheck.cpp - clang-tidy--------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UninitializedFieldCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +namespace { + +static bool fieldRequiresInit(const clang::FieldDecl *F) { + if (F->getType()->isPointerType() || F->getType()->isBuiltinType()) + return true; + return false; +}; + +} // namespace + +void UninitializedFieldCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(constructorDecl(isDefinition()).bind("ctor"), this); +} + +void UninitializedFieldCheck::check(const MatchFinder::MatchResult &Result) { + + if (const auto *Ctor = Result.Nodes.getNodeAs("ctor")) { + // TODO: Add constructor if the compiler-generated one is not sufficient. + if (!Ctor->isUserProvided()) + return; + + auto ParentClass = Ctor->getParent(); + auto MemberFields = ParentClass->fields(); + + std::unordered_set FieldsToInit; + std::copy_if(MemberFields.begin(), MemberFields.end(), + std::inserter(FieldsToInit, FieldsToInit.end()), + &fieldRequiresInit); + + if (FieldsToInit.empty()) + return; + + for (clang::CXXCtorInitializer *Init : Ctor->inits()) { + const FieldDecl *MemberField = Init->getMember(); + if (Init->isDelegatingInitializer()) + return; + + if (!MemberField) + continue; + + FieldsToInit.erase(MemberField); + } + + if (FieldsToInit.empty()) + return; + + std::string Replacement(":"); + llvm::raw_string_ostream ReplacementStream(Replacement); + for (const auto MemberField : MemberFields) + if (FieldsToInit.find(MemberField) != FieldsToInit.end()) + ReplacementStream << MemberField->getNameAsString() << "(),"; + Replacement = ReplacementStream.str(); + + assert(!Replacement.empty()); + Replacement.pop_back(); // remove trailing ','. + + // Do we need a ':' or a ',' at the beginning of our new initializer list? + auto FirstWrittenInitializerLocation = + std::find_if(Ctor->inits().begin(), Ctor->inits().end(), + [](const clang::CXXCtorInitializer *Initializer) { + return Initializer->isWritten(); + }); + + if (FirstWrittenInitializerLocation != Ctor->inits().end()) + Replacement[0] = ','; + + diag(Ctor->getLocStart(), + "constructor '%0' needs to initialize all built-in or pointer fields") + << Ctor->getNameAsString() + << FixItHint::CreateInsertion(Ctor->getBody()->getLocStart(), + Replacement); + } +} + +} // namespace tidy +} // namespace clang Index: test/clang-tidy/misc-uninitialized-field.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-uninitialized-field.cpp @@ -0,0 +1,61 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s misc-uninitialized-field %t +// REQUIRES: shell + +struct A +{ + int a_; + + A() {} +}; +// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor 'A' needs to initialize all built-in or pointer fields + + +struct B +{ + B() {} + + int b_; +}; +// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: constructor 'B' needs to initialize all built-in or pointer fields + + +struct C +{ + int c_; + + C(); +}; + +C::C() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor 'C' needs to initialize all built-in or pointer fields + +struct D +{ + int d_; + + D() : d_() {} +}; + +struct E +{ + int e_; + + E(); +}; +E::E() : e_() {} + +struct F +{ + int f_ = 0; + + F() {} +}; + +struct G +{ + int g_; + + G(int g) : g_(g) {} + G() : G(0) {} +}; +