blob: 3a7a3ff5d99267e1f2d4073690aae59e3e0f3771 [file] [log] [blame]
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +00001//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
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 "clang/Tooling/Refactoring/Transformer.h"
10#include "clang/AST/Expr.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/Basic/Diagnostic.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Rewrite/Core/Rewriter.h"
16#include "clang/Tooling/Refactoring/AtomicChange.h"
17#include "clang/Tooling/Refactoring/SourceCode.h"
18#include "llvm/ADT/Optional.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/Errc.h"
21#include "llvm/Support/Error.h"
22#include <deque>
23#include <string>
24#include <utility>
25#include <vector>
26
27using namespace clang;
28using namespace tooling;
29
30using ast_matchers::MatchFinder;
31using ast_type_traits::ASTNodeKind;
32using ast_type_traits::DynTypedNode;
33using llvm::Error;
34using llvm::Expected;
35using llvm::Optional;
36using llvm::StringError;
37using llvm::StringRef;
38using llvm::Twine;
39
40using MatchResult = MatchFinder::MatchResult;
41
42// Did the text at this location originate in a macro definition (aka. body)?
43// For example,
44//
45// #define NESTED(x) x
46// #define MACRO(y) { int y = NESTED(3); }
47// if (true) MACRO(foo)
48//
49// The if statement expands to
50//
51// if (true) { int foo = 3; }
52// ^ ^
53// Loc1 Loc2
54//
55// For SourceManager SM, SM.isMacroArgExpansion(Loc1) and
56// SM.isMacroArgExpansion(Loc2) are both true, but isOriginMacroBody(sm, Loc1)
57// is false, because "foo" originated in the source file (as an argument to a
58// macro), whereas isOriginMacroBody(SM, Loc2) is true, because "3" originated
59// in the definition of MACRO.
60static bool isOriginMacroBody(const clang::SourceManager &SM,
61 clang::SourceLocation Loc) {
62 while (Loc.isMacroID()) {
63 if (SM.isMacroBodyExpansion(Loc))
64 return true;
65 // Otherwise, it must be in an argument, so we continue searching up the
66 // invocation stack. getImmediateMacroCallerLoc() gives the location of the
67 // argument text, inside the call text.
68 Loc = SM.getImmediateMacroCallerLoc(Loc);
69 }
70 return false;
71}
72
73static llvm::Error invalidArgumentError(Twine Message) {
74 return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
75}
76
77static llvm::Error typeError(StringRef Id, const ASTNodeKind &Kind,
78 Twine Message) {
79 return invalidArgumentError(
80 Message + " (node id=" + Id + " kind=" + Kind.asStringRef() + ")");
81}
82
83static llvm::Error missingPropertyError(StringRef Id, Twine Description,
84 StringRef Property) {
85 return invalidArgumentError(Description + " requires property '" + Property +
86 "' (node id=" + Id + ")");
87}
88
89static Expected<CharSourceRange>
90getTargetRange(StringRef Target, const DynTypedNode &Node, ASTNodeKind Kind,
91 NodePart TargetPart, ASTContext &Context) {
92 switch (TargetPart) {
93 case NodePart::Node: {
94 // For non-expression statements, associate any trailing semicolon with the
95 // statement text. However, if the target was intended as an expression (as
96 // indicated by its kind) then we do not associate any trailing semicolon
97 // with it. We only associate the exact expression text.
98 if (Node.get<Stmt>() != nullptr) {
99 auto ExprKind = ASTNodeKind::getFromNodeKind<clang::Expr>();
100 if (!ExprKind.isBaseOf(Kind))
101 return getExtendedRange(Node, tok::TokenKind::semi, Context);
102 }
103 return CharSourceRange::getTokenRange(Node.getSourceRange());
104 }
105 case NodePart::Member:
106 if (auto *M = Node.get<clang::MemberExpr>())
107 return CharSourceRange::getTokenRange(
108 M->getMemberNameInfo().getSourceRange());
109 return typeError(Target, Node.getNodeKind(),
110 "NodePart::Member applied to non-MemberExpr");
111 case NodePart::Name:
112 if (const auto *D = Node.get<clang::NamedDecl>()) {
113 if (!D->getDeclName().isIdentifier())
114 return missingPropertyError(Target, "NodePart::Name", "identifier");
115 SourceLocation L = D->getLocation();
116 auto R = CharSourceRange::getTokenRange(L, L);
117 // Verify that the range covers exactly the name.
118 // FIXME: extend this code to support cases like `operator +` or
119 // `foo<int>` for which this range will be too short. Doing so will
120 // require subcasing `NamedDecl`, because it doesn't provide virtual
121 // access to the \c DeclarationNameInfo.
122 if (getText(R, Context) != D->getName())
123 return CharSourceRange();
124 return R;
125 }
126 if (const auto *E = Node.get<clang::DeclRefExpr>()) {
127 if (!E->getNameInfo().getName().isIdentifier())
128 return missingPropertyError(Target, "NodePart::Name", "identifier");
129 SourceLocation L = E->getLocation();
130 return CharSourceRange::getTokenRange(L, L);
131 }
132 if (const auto *I = Node.get<clang::CXXCtorInitializer>()) {
133 if (!I->isMemberInitializer() && I->isWritten())
134 return missingPropertyError(Target, "NodePart::Name",
135 "explicit member initializer");
136 SourceLocation L = I->getMemberLocation();
137 return CharSourceRange::getTokenRange(L, L);
138 }
139 return typeError(
140 Target, Node.getNodeKind(),
141 "NodePart::Name applied to neither DeclRefExpr, NamedDecl nor "
142 "CXXCtorInitializer");
143 }
144 llvm_unreachable("Unexpected case in NodePart type.");
145}
146
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000147Expected<SmallVector<Transformation, 1>>
148tooling::translateEdits(const MatchResult &Result,
149 llvm::ArrayRef<ASTEdit> Edits) {
150 SmallVector<Transformation, 1> Transformations;
151 auto &NodesMap = Result.Nodes.getMap();
152 for (const auto &Edit : Edits) {
153 auto It = NodesMap.find(Edit.Target);
154 assert(It != NodesMap.end() && "Edit target must be bound in the match.");
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000155
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000156 Expected<CharSourceRange> RangeOrErr = getTargetRange(
157 Edit.Target, It->second, Edit.Kind, Edit.Part, *Result.Context);
158 if (auto Err = RangeOrErr.takeError())
159 return std::move(Err);
160 Transformation T;
161 T.Range = *RangeOrErr;
162 if (T.Range.isInvalid() ||
163 isOriginMacroBody(*Result.SourceManager, T.Range.getBegin()))
164 return SmallVector<Transformation, 0>();
165 T.Replacement = Edit.Replacement(Result);
166 Transformations.push_back(std::move(T));
167 }
168 return Transformations;
169}
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000170
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000171RewriteRule tooling::makeRule(ast_matchers::internal::DynTypedMatcher M,
172 SmallVector<ASTEdit, 1> Edits) {
173 M.setAllowBind(true);
174 // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
175 return RewriteRule{*M.tryBind(RewriteRule::RootId), std::move(Edits)};
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000176}
177
178constexpr llvm::StringLiteral RewriteRule::RootId;
179
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000180void Transformer::registerMatchers(MatchFinder *MatchFinder) {
181 MatchFinder->addDynamicMatcher(Rule.Matcher, this);
182}
183
184void Transformer::run(const MatchResult &Result) {
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000185 if (Result.Context->getDiagnostics().hasErrorOccurred())
186 return;
187
188 // Verify the existence and validity of the AST node that roots this rule.
189 auto &NodesMap = Result.Nodes.getMap();
190 auto Root = NodesMap.find(RewriteRule::RootId);
191 assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
192 SourceLocation RootLoc = Result.SourceManager->getExpansionLoc(
193 Root->second.getSourceRange().getBegin());
194 assert(RootLoc.isValid() && "Invalid location for Root node of match.");
195
196 auto TransformationsOrErr = translateEdits(Result, Rule.Edits);
197 if (auto Err = TransformationsOrErr.takeError()) {
198 llvm::errs() << "Transformation failed: " << llvm::toString(std::move(Err))
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000199 << "\n";
200 return;
201 }
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000202 auto &Transformations = *TransformationsOrErr;
203 if (Transformations.empty()) {
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000204 // No rewrite applied (but no error encountered either).
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000205 RootLoc.print(llvm::errs() << "note: skipping match at loc ",
206 *Result.SourceManager);
207 llvm::errs() << "\n";
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000208 return;
209 }
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000210
211 // Convert the result to an AtomicChange.
212 AtomicChange AC(*Result.SourceManager, RootLoc);
213 for (const auto &T : Transformations) {
214 if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) {
215 AC.setError(llvm::toString(std::move(Err)));
216 break;
217 }
218 }
219
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000220 Consumer(AC);
221}