UsingInserter.cpp 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. //===---------- UsingInserter.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 "UsingInserter.h"
  9. #include "ASTUtils.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/ASTMatchers/ASTMatchers.h"
  12. #include "clang/Lex/Lexer.h"
  13. #include <optional>
  14. namespace clang::tidy::utils {
  15. using namespace ast_matchers;
  16. static StringRef getUnqualifiedName(StringRef QualifiedName) {
  17. size_t LastSeparatorPos = QualifiedName.rfind("::");
  18. if (LastSeparatorPos == StringRef::npos)
  19. return QualifiedName;
  20. return QualifiedName.drop_front(LastSeparatorPos + 2);
  21. }
  22. UsingInserter::UsingInserter(const SourceManager &SourceMgr)
  23. : SourceMgr(SourceMgr) {}
  24. std::optional<FixItHint> UsingInserter::createUsingDeclaration(
  25. ASTContext &Context, const Stmt &Statement, StringRef QualifiedName) {
  26. StringRef UnqualifiedName = getUnqualifiedName(QualifiedName);
  27. const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
  28. if (!Function)
  29. return std::nullopt;
  30. if (AddedUsing.count(std::make_pair(Function, QualifiedName.str())) != 0)
  31. return std::nullopt;
  32. SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
  33. Function->getBody()->getBeginLoc(), 0, SourceMgr, Context.getLangOpts());
  34. // Only use using declarations in the main file, not in includes.
  35. if (SourceMgr.getFileID(InsertLoc) != SourceMgr.getMainFileID())
  36. return std::nullopt;
  37. // FIXME: This declaration could be masked. Investigate if
  38. // there is a way to avoid using Sema.
  39. bool AlreadyHasUsingDecl =
  40. !match(stmt(hasAncestor(decl(has(usingDecl(hasAnyUsingShadowDecl(
  41. hasTargetDecl(hasName(QualifiedName.str())))))))),
  42. Statement, Context)
  43. .empty();
  44. if (AlreadyHasUsingDecl) {
  45. AddedUsing.emplace(NameInFunction(Function, QualifiedName.str()));
  46. return std::nullopt;
  47. }
  48. // Find conflicting declarations and references.
  49. auto ConflictingDecl = namedDecl(hasName(UnqualifiedName));
  50. bool HasConflictingDeclaration =
  51. !match(findAll(ConflictingDecl), *Function, Context).empty();
  52. bool HasConflictingDeclRef =
  53. !match(findAll(declRefExpr(to(ConflictingDecl))), *Function, Context)
  54. .empty();
  55. if (HasConflictingDeclaration || HasConflictingDeclRef)
  56. return std::nullopt;
  57. std::string Declaration =
  58. (llvm::Twine("\nusing ") + QualifiedName + ";").str();
  59. AddedUsing.emplace(std::make_pair(Function, QualifiedName.str()));
  60. return FixItHint::CreateInsertion(InsertLoc, Declaration);
  61. }
  62. StringRef UsingInserter::getShortName(ASTContext &Context,
  63. const Stmt &Statement,
  64. StringRef QualifiedName) {
  65. const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
  66. if (AddedUsing.count(NameInFunction(Function, QualifiedName.str())) != 0)
  67. return getUnqualifiedName(QualifiedName);
  68. return QualifiedName;
  69. }
  70. } // namespace clang::tidy::utils