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 | |
21 | static StringRef getUnqualifiedName(StringRef QualifiedName) { |
22 | size_t LastSeparatorPos = QualifiedName.rfind(Str: "::" ); |
23 | if (LastSeparatorPos == StringRef::npos) |
24 | return QualifiedName; |
25 | return QualifiedName.drop_front(N: LastSeparatorPos + 2); |
26 | } |
27 | |
28 | UsingInserter::UsingInserter(const SourceManager &SourceMgr) |
29 | : SourceMgr(SourceMgr) {} |
30 | |
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(x: std::make_pair(x&: Function, y: QualifiedName.str())) != 0) |
39 | return std::nullopt; |
40 | |
41 | SourceLocation InsertLoc = Lexer::getLocForEndOfToken( |
42 | Loc: Function->getBody()->getBeginLoc(), Offset: 0, SM: SourceMgr, LangOpts: Context.getLangOpts()); |
43 | |
44 | // Only use using declarations in the main file, not in includes. |
45 | if (SourceMgr.getFileID(SpellingLoc: 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(Matcher: stmt(hasAncestor(decl(has(usingDecl(hasAnyUsingShadowDecl( |
52 | InnerMatcher: hasTargetDecl(InnerMatcher: hasName(Name: QualifiedName.str())))))))), |
53 | Node: Statement, Context) |
54 | .empty(); |
55 | if (AlreadyHasUsingDecl) { |
56 | AddedUsing.emplace(args&: Function, args: QualifiedName.str()); |
57 | return std::nullopt; |
58 | } |
59 | // Find conflicting declarations and references. |
60 | auto ConflictingDecl = namedDecl(hasName(Name: UnqualifiedName)); |
61 | bool HasConflictingDeclaration = |
62 | !match(Matcher: findAll(Matcher: ConflictingDecl), Node: *Function, Context).empty(); |
63 | bool HasConflictingDeclRef = |
64 | !match(Matcher: findAll(Matcher: declRefExpr(to(InnerMatcher: ConflictingDecl))), Node: *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(args&: Function, args: QualifiedName.str()); |
73 | return FixItHint::CreateInsertion(InsertionLoc: InsertLoc, Code: Declaration); |
74 | } |
75 | |
76 | StringRef UsingInserter::getShortName(ASTContext &Context, |
77 | const Stmt &Statement, |
78 | StringRef QualifiedName) { |
79 | const FunctionDecl *Function = getSurroundingFunction(Context, Statement); |
80 | if (AddedUsing.count(x: NameInFunction(Function, QualifiedName.str())) != 0) |
81 | return getUnqualifiedName(QualifiedName); |
82 | return QualifiedName; |
83 | } |
84 | |
85 | } // namespace clang::tidy::utils |
86 | |