diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -45,6 +45,7 @@ #include "StringIntegerAssignmentCheck.h" #include "StringLiteralWithEmbeddedNulCheck.h" #include "SuspiciousEnumUsageCheck.h" +#include "SuspiciousIncludeCheck.h" #include "SuspiciousMemsetUsageCheck.h" #include "SuspiciousMissingCommaCheck.h" #include "SuspiciousSemicolonCheck.h" @@ -140,6 +141,8 @@ "bugprone-string-literal-with-embedded-nul"); CheckFactories.registerCheck( "bugprone-suspicious-enum-usage"); + CheckFactories.registerCheck( + "bugprone-suspicious-include"); CheckFactories.registerCheck( "bugprone-suspicious-memset-usage"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -37,6 +37,7 @@ StringIntegerAssignmentCheck.cpp StringLiteralWithEmbeddedNulCheck.cpp SuspiciousEnumUsageCheck.cpp + SuspiciousIncludeCheck.cpp SuspiciousMemsetUsageCheck.cpp SuspiciousMissingCommaCheck.cpp SuspiciousSemicolonCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.h @@ -0,0 +1,39 @@ +//===--- SuspiciousIncludeCheck.h - clang-tidy ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSINCLUDECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSINCLUDECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Warns on inclusion of files whose names suggest that they're implementation +/// files, instead of headers. E.g: +/// +/// #include "foo.cpp" // warning +/// #include "bar.c" // warning +/// #include "baz.h" // no diagnostic +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-include.html +class SuspiciousIncludeCheck : public ClangTidyCheck { +public: + SuspiciousIncludeCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSINCLUDECHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousIncludeCheck.cpp @@ -0,0 +1,74 @@ +//===--- SuspiciousIncludeCheck.cpp - clang-tidy --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SuspiciousIncludeCheck.h" +#include "clang/AST/ASTContext.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +namespace { +class SuspiciousIncludePPCallbacks : public PPCallbacks { +public: + explicit SuspiciousIncludePPCallbacks(ClangTidyCheck &Check, + const SourceManager &SM, + Preprocessor *PP) + : Check(Check), PP(PP) {} + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + +private: + ClangTidyCheck &Check; + Preprocessor *PP; +}; +} // namespace + +void SuspiciousIncludeCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + PP->addPPCallbacks(::std::make_unique(*this, SM, PP)); +} + +void SuspiciousIncludePPCallbacks::InclusionDirective( + SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) { + const char *SuspiciousExtensions[] = {".c", ".cpp", ".cxx", ".cc"}; + const char *RecommendedExtensions[] = {"", ".h", ".hpp", ".hh"}; + + for (const char *SE : SuspiciousExtensions) { + if (FileName.consume_back(SE)) { + Check.diag(HashLoc, "suspicious #include of file with %0 extension") + << SE; + + for (const char *RE : RecommendedExtensions) { + const DirectoryLookup *CurDir; + Optional File = PP->LookupFile(HashLoc /* FIXME: lies */, + (FileName + RE).str(), + IsAngled, nullptr, nullptr, + CurDir, nullptr, nullptr, + nullptr, nullptr, nullptr); + if (File) { + Check.diag(HashLoc, "did you mean to include '%0'?", DiagnosticIDs::Note) + << (FileName + RE).str(); + } + } + } + } +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a.hpp b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a.hpp new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a.cpp b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/a.cpp new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.c b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.c new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.cc b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.cc new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.cxx b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/c.cxx new file mode 100644 diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-include.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-include.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-include.cpp @@ -0,0 +1,19 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-include %t -- -- -isystem %S/Inputs/Headers + +// CHECK-MESSAGES: [[@LINE+4]]:1: warning: suspicious #include of file with .cpp extension +// CHECK-MESSAGES: [[@LINE+3]]:1: note: did you mean to include 'a'? +// CHECK-MESSAGES: [[@LINE+2]]:1: note: did you mean to include 'a.h'? +// CHECK-MESSAGES: [[@LINE+1]]:1: note: did you mean to include 'a.hpp'? +#include "a.cpp" + +#include "b.h" + +// CHECK-MESSAGES: [[@LINE+1]]:1: warning: suspicious #include of file with .c extension +#include + +// CHECK-MESSAGES: [[@LINE+1]]:1: warning: suspicious #include of file with .cc extension +#include + +// CHECK-MESSAGES: [[@LINE+1]]:1: warning: suspicious #include of file with .cxx extension +#include +