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