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