Index: clang-tidy/abseil/AbseilTidyModule.cpp =================================================================== --- clang-tidy/abseil/AbseilTidyModule.cpp +++ clang-tidy/abseil/AbseilTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "DurationDivisionCheck.h" #include "FasterStrsplitDelimiterCheck.h" +#include "NoInternalDepsCheck.h" #include "NoNamespaceCheck.h" #include "StringFindStartswithCheck.h" @@ -26,6 +27,8 @@ "abseil-duration-division"); CheckFactories.registerCheck( "abseil-faster-strsplit-delimiter"); + CheckFactories.registerCheck( + "abseil-no-internal-deps"); CheckFactories.registerCheck("abseil-no-namespace"); CheckFactories.registerCheck( "abseil-string-find-startswith"); Index: clang-tidy/abseil/CMakeLists.txt =================================================================== --- clang-tidy/abseil/CMakeLists.txt +++ clang-tidy/abseil/CMakeLists.txt @@ -4,6 +4,7 @@ AbseilTidyModule.cpp DurationDivisionCheck.cpp FasterStrsplitDelimiterCheck.cpp + NoInternalDepsCheck.cpp NoNamespaceCheck.cpp StringFindStartswithCheck.cpp Index: clang-tidy/abseil/NoInternalDepsCheck.h =================================================================== --- clang-tidy/abseil/NoInternalDepsCheck.h +++ clang-tidy/abseil/NoInternalDepsCheck.h @@ -0,0 +1,37 @@ +//===--- NoInternalDepsCheck.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_ABSEIL_NOINTERNALDEPSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NOINTERNALDEPSCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds instances where the user depends on internal details and warns them +/// against doing so.Should not run on internal Abseil files or Abseil source +/// code. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-no-internal-deps.html +class NoInternalDepsCheck : public ClangTidyCheck { +public: + NoInternalDepsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NOINTERNALDEPSCHECK_H Index: clang-tidy/abseil/NoInternalDepsCheck.cpp =================================================================== --- clang-tidy/abseil/NoInternalDepsCheck.cpp +++ clang-tidy/abseil/NoInternalDepsCheck.cpp @@ -0,0 +1,48 @@ +//===--- NoInternalDepsCheck.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 "NoInternalDepsCheck.h" +#include "AbseilMatcher.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void NoInternalDepsCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // TODO: refactor matcher to be configurable or just match on any internal + // access from outside the enclosing namespace. + + Finder->addMatcher( + nestedNameSpecifierLoc(loc(specifiesNamespace(namespaceDecl( + matchesName("internal"), + hasParent(namespaceDecl(hasName("absl")))))), + unless(isInAbseilFile())) + .bind("InternalDep"), + this); +} + +void NoInternalDepsCheck::check(const MatchFinder::MatchResult &Result) { + const auto *InternalDependency = + Result.Nodes.getNodeAs("InternalDep"); + + diag(InternalDependency->getBeginLoc(), + "do not reference any 'internal' namespaces; those implementation " + "details are reserved to Abseil"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -60,6 +60,11 @@ - New :doc:`abseil-duration-division ` check. +- New :doc:`abseil-no-internal-deps + ` check. + + Gives a warning if code using Abseil depends on internal details. + Checks for uses of ``absl::Duration`` division that is done in a floating-point context, and recommends the use of a function that returns a floating-point value. Index: docs/clang-tidy/checks/abseil-no-internal-deps.rst =================================================================== --- docs/clang-tidy/checks/abseil-no-internal-deps.rst +++ docs/clang-tidy/checks/abseil-no-internal-deps.rst @@ -0,0 +1,24 @@ +subl.. title:: clang-tidy - abseil-no-internal-deps + +abseil-no-internal-deps +======================= + +Warns if code using Abseil depends on internal details. If something is in a +namespace that includes the word “internal”, code is not allowed to depend upon +it beaucse it’s an implementation detail. They cannot friend it, include it, +you mention it or refer to it in any way. Doing so violates Abseil's +compatibility guidelines and may result in breakage. See +https://abseil.io/about/compatibility for more information. + +The following cases will result in warnings: + +.. code-block:: c++ + + absl::strings_internal::foo(); + // warning triggered on this line + class foo { + friend struct absl::container_internal::faa; + // warning triggered on this line + }; + absl::memory_internal::MakeUniqueResult(); + // warning triggered on this line Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -6,6 +6,7 @@ .. toctree:: abseil-duration-division abseil-faster-strsplit-delimiter + abseil-no-internal-deps abseil-no-namespace abseil-string-find-startswith android-cloexec-accept Index: test/clang-tidy/Inputs/absl/external-file.h =================================================================== --- test/clang-tidy/Inputs/absl/external-file.h +++ test/clang-tidy/Inputs/absl/external-file.h @@ -1 +1,6 @@ -namespace absl {} +namespace absl { +namespace base_internal { +void InternalFunction() {} +} // namespace base_internal +} //namespace absl +void DirectAccess2() { absl::base_internal::InternalFunction(); } Index: test/clang-tidy/Inputs/absl/strings/internal-file.h =================================================================== --- test/clang-tidy/Inputs/absl/strings/internal-file.h +++ test/clang-tidy/Inputs/absl/strings/internal-file.h @@ -1 +1,33 @@ -namespace absl {} +namespace std { +struct string { + string(const char *); + ~string(); +}; +} // namespace std + +namespace absl { +std::string StringsFunction(std::string s1) { return s1; } +class SomeContainer {}; +namespace strings_internal { +void InternalFunction() {} +template P InternalTemplateFunction(P a) {} +} // namespace strings_internal + +namespace container_internal { +struct InternalStruct {}; +} // namespace container_internal +} // namespace absl + +// should not trigger warnings because inside Abseil files +void DirectAcessInternal() { + absl::strings_internal::InternalFunction(); + absl::strings_internal::InternalTemplateFunction("a"); +} + +class FriendUsageInternal { + friend struct absl::container_internal::InternalStruct; +}; + +namespace absl { +void OpeningNamespaceInternally() { strings_internal::InternalFunction(); } +} // namespace absl Index: test/clang-tidy/abseil-no-internal-deps.cpp =================================================================== --- test/clang-tidy/abseil-no-internal-deps.cpp +++ test/clang-tidy/abseil-no-internal-deps.cpp @@ -0,0 +1,39 @@ +// RUN: %check_clang_tidy %s abseil-no-internal-deps %t, -- -- -I %S/Inputs +// RUN: clang-tidy -checks='-*, abseil-no-internal-deps' -header-filter='.*' %s -- -I %S/Inputs 2>&1 | FileCheck %s + +#include "absl/strings/internal-file.h" +// CHECK-NOT: warning: + +#include "absl/external-file.h" +// CHECK: absl/external-file.h:6:24: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil [abseil-no-internal-deps] + +void DirectAcess() { + absl::strings_internal::InternalFunction(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil + + absl::strings_internal::InternalTemplateFunction("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +} + +class FriendUsage { + friend struct absl::container_internal::InternalStruct; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +}; + +namespace absl { +void OpeningNamespace() { + strings_internal::InternalFunction(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +} +} // namespace absl + +// should not trigger warnings +void CorrectUsage() { + std::string Str = absl::StringsFunction("a"); + absl::SomeContainer b; +} + +namespace absl { +SomeContainer b; +std::string Str = absl::StringsFunction("a"); +} // namespace absl