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