ConcatNestedNamespacesCheck.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. //===--- ConcatNestedNamespacesCheck.cpp - clang-tidy----------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "ConcatNestedNamespacesCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/Lex/Lexer.h"
  12. #include <algorithm>
  13. namespace clang::tidy::modernize {
  14. static bool locationsInSameFile(const SourceManager &Sources,
  15. SourceLocation Loc1, SourceLocation Loc2) {
  16. return Loc1.isFileID() && Loc2.isFileID() &&
  17. Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
  18. }
  19. static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) {
  20. return ND.isAnonymousNamespace() || ND.isInlineNamespace();
  21. }
  22. static bool singleNamedNamespaceChild(const NamespaceDecl &ND) {
  23. NamespaceDecl::decl_range Decls = ND.decls();
  24. if (std::distance(Decls.begin(), Decls.end()) != 1)
  25. return false;
  26. const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
  27. return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace);
  28. }
  29. static bool alreadyConcatenated(std::size_t NumCandidates,
  30. const SourceRange &ReplacementRange,
  31. const SourceManager &Sources,
  32. const LangOptions &LangOpts) {
  33. // FIXME: This logic breaks when there is a comment with ':'s in the middle.
  34. CharSourceRange TextRange =
  35. Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts);
  36. StringRef CurrentNamespacesText =
  37. Lexer::getSourceText(TextRange, Sources, LangOpts);
  38. return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2;
  39. }
  40. ConcatNestedNamespacesCheck::NamespaceString
  41. ConcatNestedNamespacesCheck::concatNamespaces() {
  42. NamespaceString Result("namespace ");
  43. Result.append(Namespaces.front()->getName());
  44. std::for_each(std::next(Namespaces.begin()), Namespaces.end(),
  45. [&Result](const NamespaceDecl *ND) {
  46. Result.append("::");
  47. Result.append(ND->getName());
  48. });
  49. return Result;
  50. }
  51. void ConcatNestedNamespacesCheck::registerMatchers(
  52. ast_matchers::MatchFinder *Finder) {
  53. Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this);
  54. }
  55. void ConcatNestedNamespacesCheck::reportDiagnostic(
  56. const SourceRange &FrontReplacement, const SourceRange &BackReplacement) {
  57. diag(Namespaces.front()->getBeginLoc(),
  58. "nested namespaces can be concatenated", DiagnosticIDs::Warning)
  59. << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces())
  60. << FixItHint::CreateReplacement(BackReplacement, "}");
  61. }
  62. void ConcatNestedNamespacesCheck::check(
  63. const ast_matchers::MatchFinder::MatchResult &Result) {
  64. const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
  65. const SourceManager &Sources = *Result.SourceManager;
  66. if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc()))
  67. return;
  68. if (anonymousOrInlineNamespace(ND))
  69. return;
  70. Namespaces.push_back(&ND);
  71. if (singleNamedNamespaceChild(ND))
  72. return;
  73. SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
  74. Namespaces.back()->getLocation());
  75. SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
  76. Namespaces.front()->getRBraceLoc());
  77. if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
  78. getLangOpts()))
  79. reportDiagnostic(FrontReplacement, BackReplacement);
  80. Namespaces.clear();
  81. }
  82. } // namespace clang::tidy::modernize