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,32 @@ +//===--- 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. +// +// TODO: Describe what check does +// +//===----------------------------------------------------------------------===// + +#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 { + +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,118 @@ +//===--- 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 +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +namespace { + +static void +registerMatchersForConstructorDefinition(MatchFinder *Finder, + MatchFinder::MatchCallback *Callback) { + Finder->addMatcher(constructorDecl(isDefinition()).bind("ctor"), Callback); +} + +static bool fieldRequiresInit(const clang::FieldDecl *f) { + if (f->getType()->isPointerType()) { + return true; + } + if (f->getType()->isBuiltinType()) { + return true; + } + return false; +}; + +} // namespace + +void UninitializedFieldCheck::registerMatchers(MatchFinder *Finder) { + registerMatchersForConstructorDefinition(Finder, this); +} + +void UninitializedFieldCheck::check(const MatchFinder::MatchResult &Result) { + + if (const auto *Ctor = Result.Nodes.getNodeAs("ctor")) { + if (!Ctor->isUserProvided()) { + // TODO: Add constructor if the compiler-generated one is not sufficient + 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::stringstream ss; + for (const auto MemberField : MemberFields) { + if (FieldsToInit.find(MemberField) != FieldsToInit.end()) { + ss << MemberField->getNameAsString() << "(),"; + } + } + + auto Replacement = ss.str(); + assert(!Replacement.empty()); + + Replacement.back() = '{'; // remove training ',' TODO: Avoid replacing the constructor body's leading '{' + + // Do we need a ':' or a ',' at the beginning of our new initializer list? + auto it_WrittenInitializer = + std::find_if(Ctor->inits().begin(), Ctor->inits().end(), + [](const clang::CXXCtorInitializer *Initializer) { + return Initializer->isWritten(); + }); + + if (it_WrittenInitializer == Ctor->inits().end()) { + Replacement = ":" + Replacement; + } else { + Replacement = ", " + Replacement; + } + + diag(Ctor->getLocStart(), + "Constructor '%0' needs to initialize all built-in or pointer fields.") + << Ctor->getNameAsString() + << FixItHint::CreateReplacement(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) {} +}; +