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