1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
17 * Changes calls to tools::Rectangle/Point/Size methods that return a ref to instead call the setter methods.
20 * make COMPILER_PLUGIN_TOOL=changetoolsgen UPDATE_FILES=all FORCE_COMPILE_ALL=1
22 * make <module> COMPILER_PLUGIN_TOOL=changetoolsgen FORCE_COMPILE_ALL=1
27 class ChangeToolsGen
: public loplugin::FilteringRewritePlugin
<ChangeToolsGen
>
30 explicit ChangeToolsGen(loplugin::InstantiationData
const& data
)
31 : FilteringRewritePlugin(data
)
34 virtual void run() override
;
35 bool VisitCXXMemberCallExpr(CXXMemberCallExpr
const* call
);
38 bool ChangeAssignment(Stmt
const* parent
, std::string
const& methodName
,
39 std::string
const& setPrefix
);
40 bool ChangeBinaryOperatorPlusMinus(BinaryOperator
const* parent
, CXXMemberCallExpr
const* call
,
41 std::string
const& methodName
);
42 bool ChangeBinaryOperatorOther(BinaryOperator
const* parent
, CXXMemberCallExpr
const* call
,
43 std::string
const& methodName
, std::string
const& setPrefix
);
44 bool ChangeUnaryOperator(UnaryOperator
const* parent
, CXXMemberCallExpr
const* call
,
45 std::string
const& methodName
);
46 std::string
extractCode(SourceLocation startLoc
, SourceLocation endLoc
);
49 void ChangeToolsGen::run() { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
51 bool ChangeToolsGen::VisitCXXMemberCallExpr(CXXMemberCallExpr
const* call
)
53 if (ignoreLocation(call
))
55 const CXXMethodDecl
* func
= call
->getMethodDecl();
60 auto dc
= loplugin::DeclCheck(func
);
61 std::string methodName
;
62 std::string setPrefix
;
63 if (dc
.Function("Top").Class("Rectangle").Namespace("tools").GlobalNamespace())
68 else if (dc
.Function("Bottom").Class("Rectangle").Namespace("tools").GlobalNamespace())
70 methodName
= "Bottom";
73 else if (dc
.Function("Left").Class("Rectangle").Namespace("tools").GlobalNamespace())
78 else if (dc
.Function("Right").Class("Rectangle").Namespace("tools").GlobalNamespace())
83 else if (dc
.Function("X").Class("Point").GlobalNamespace())
88 else if (dc
.Function("Y").Class("Point").GlobalNamespace())
93 else if (dc
.Function("Width").Class("Size").GlobalNamespace())
98 else if (dc
.Function("Height").Class("Size").GlobalNamespace())
100 methodName
= "Height";
105 if (!loplugin::TypeCheck(func
->getReturnType()).LvalueReference())
108 auto parent
= getParentStmt(call
);
111 if (auto unaryOp
= dyn_cast
<UnaryOperator
>(parent
))
113 if (!ChangeUnaryOperator(unaryOp
, call
, methodName
))
114 report(DiagnosticsEngine::Warning
, "Could not fix, unary", compat::getBeginLoc(call
));
117 auto binaryOp
= dyn_cast
<BinaryOperator
>(parent
);
123 auto opcode
= binaryOp
->getOpcode();
124 if (opcode
== BO_Assign
)
126 // Check for assignments embedded inside other expressions
127 auto parent2
= getParentStmt(parent
);
128 if (dyn_cast_or_null
<ExprWithCleanups
>(parent2
))
129 parent2
= getParentStmt(parent2
);
130 if (parent2
&& isa
<Expr
>(parent2
))
132 report(DiagnosticsEngine::Warning
, "Could not fix, embedded assign",
133 compat::getBeginLoc(call
));
137 // X.Width() = X.Height() = 1;
138 if (auto rhs
= dyn_cast
<BinaryOperator
>(binaryOp
->getRHS()->IgnoreParenImpCasts()))
139 if (rhs
->getOpcode() == BO_Assign
)
141 report(DiagnosticsEngine::Warning
, "Could not fix, double assign",
142 compat::getBeginLoc(call
));
145 if (!ChangeAssignment(parent
, methodName
, setPrefix
))
146 report(DiagnosticsEngine::Warning
, "Could not fix, assign", compat::getBeginLoc(call
));
149 if (opcode
== BO_AddAssign
|| opcode
== BO_SubAssign
)
151 if (!ChangeBinaryOperatorPlusMinus(binaryOp
, call
, methodName
))
152 report(DiagnosticsEngine::Warning
, "Could not fix, assign-and-change",
153 compat::getBeginLoc(call
));
156 else if (opcode
== BO_RemAssign
|| opcode
== BO_MulAssign
|| opcode
== BO_DivAssign
)
158 if (!ChangeBinaryOperatorOther(binaryOp
, call
, methodName
, setPrefix
))
159 report(DiagnosticsEngine::Warning
, "Could not fix, assign-and-change",
160 compat::getBeginLoc(call
));
168 bool ChangeToolsGen::ChangeAssignment(Stmt
const* parent
, std::string
const& methodName
,
169 std::string
const& setPrefix
)
171 // Look for expressions like
172 // aRect.Left() = ...;
174 // aRect.SetLeft( ... );
175 SourceManager
& SM
= compiler
.getSourceManager();
176 SourceLocation startLoc
= SM
.getExpansionLoc(compat::getBeginLoc(parent
));
177 SourceLocation endLoc
= SM
.getExpansionLoc(compat::getEndLoc(parent
));
178 const char* p1
= SM
.getCharacterData(startLoc
);
179 const char* p2
= SM
.getCharacterData(endLoc
);
180 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
181 if (p2
< p1
) // clang is misbehaving, appears to be macro constant related
183 std::string
callText(p1
, p2
- p1
+ n
);
184 auto originalLength
= callText
.size();
186 auto newText
= std::regex_replace(callText
, std::regex(methodName
+ " *\\( *\\) *="),
187 setPrefix
+ methodName
+ "(");
188 if (newText
== callText
)
192 return replaceText(startLoc
, originalLength
, newText
);
195 bool ChangeToolsGen::ChangeBinaryOperatorPlusMinus(BinaryOperator
const* binaryOp
,
196 CXXMemberCallExpr
const* call
,
197 std::string
const& methodName
)
199 // Look for expressions like
200 // aRect.Left() += ...;
202 // aRect.MoveLeft( ... );
203 SourceManager
& SM
= compiler
.getSourceManager();
204 SourceLocation startLoc
= SM
.getExpansionLoc(compat::getBeginLoc(binaryOp
));
205 SourceLocation endLoc
= SM
.getExpansionLoc(compat::getEndLoc(binaryOp
));
206 const char* p1
= SM
.getCharacterData(startLoc
);
207 const char* p2
= SM
.getCharacterData(endLoc
);
208 if (p2
< p1
) // clang is misbehaving, appears to be macro constant related
210 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
211 std::string
callText(p1
, p2
- p1
+ n
);
212 auto originalLength
= callText
.size();
215 if (binaryOp
->getOpcode() == BO_AddAssign
)
217 newText
= std::regex_replace(callText
, std::regex(methodName
+ " *\\( *\\) *\\+= *"),
218 "Adjust" + methodName
+ "(");
223 newText
= std::regex_replace(callText
, std::regex(methodName
+ " *\\( *\\) *\\-= *"),
224 "Adjust" + methodName
+ "( -(");
228 if (newText
== callText
)
230 report(DiagnosticsEngine::Warning
, "binaryop-plusminus regex match failed",
231 compat::getBeginLoc(call
));
235 return replaceText(startLoc
, originalLength
, newText
);
238 bool ChangeToolsGen::ChangeBinaryOperatorOther(BinaryOperator
const* binaryOp
,
239 CXXMemberCallExpr
const* call
,
240 std::string
const& methodName
,
241 std::string
const& setPrefix
)
243 // Look for expressions like
244 // aRect.Left() += ...;
246 // aRect.SetLeft( aRect.GetLeft() + ... );
247 SourceManager
& SM
= compiler
.getSourceManager();
248 SourceLocation startLoc
= SM
.getExpansionLoc(compat::getBeginLoc(binaryOp
));
249 SourceLocation endLoc
= SM
.getExpansionLoc(compat::getEndLoc(binaryOp
));
250 const char* p1
= SM
.getCharacterData(startLoc
);
251 const char* p2
= SM
.getCharacterData(endLoc
);
252 if (p2
< p1
) // clang is misbehaving, appears to be macro constant related
254 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
255 std::string
callText(p1
, p2
- p1
+ n
);
256 auto originalLength
= callText
.size();
258 std::string regexOpname
;
259 std::string replaceOpname
;
260 switch (binaryOp
->getOpcode())
263 regexOpname
= "\\%=";
267 regexOpname
= "\\*=";
271 regexOpname
= "\\/=";
278 auto implicitObjectText
= extractCode(call
->getImplicitObjectArgument()->getExprLoc(),
279 call
->getImplicitObjectArgument()->getExprLoc());
280 std::string
reString(methodName
+ " *\\( *\\) *" + regexOpname
);
281 auto newText
= std::regex_replace(callText
, std::regex(reString
),
282 setPrefix
+ methodName
+ "( " + implicitObjectText
+ "."
283 + methodName
+ "() " + replaceOpname
+ " (");
284 if (newText
== callText
)
286 report(DiagnosticsEngine::Warning
, "binaryop-other regex match failed %0",
287 compat::getBeginLoc(call
))
291 // sometimes we end up with duplicate spaces after the opname
293 = std::regex_replace(newText
, std::regex(methodName
+ "\\(\\) \\" + replaceOpname
+ " "),
294 methodName
+ "() " + replaceOpname
+ " ");
297 return replaceText(startLoc
, originalLength
, newText
);
300 bool ChangeToolsGen::ChangeUnaryOperator(UnaryOperator
const* unaryOp
,
301 CXXMemberCallExpr
const* call
,
302 std::string
const& methodName
)
304 // Look for expressions like
308 // aRect.MoveLeft( 1 );
310 SourceManager
& SM
= compiler
.getSourceManager();
311 SourceLocation startLoc
= SM
.getExpansionLoc(compat::getBeginLoc(unaryOp
));
312 SourceLocation endLoc
= SM
.getExpansionLoc(compat::getEndLoc(unaryOp
));
313 const char* p1
= SM
.getCharacterData(startLoc
);
314 const char* p2
= SM
.getCharacterData(endLoc
);
315 if (p2
< p1
) // clang is misbehaving, appears to be macro constant related
317 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
318 std::string
callText(p1
, p2
- p1
+ n
);
319 auto originalLength
= callText
.size();
321 auto implicitObjectText
= extractCode(call
->getImplicitObjectArgument()->getExprLoc(),
322 call
->getImplicitObjectArgument()->getExprLoc());
323 auto op
= unaryOp
->getOpcode();
324 std::string regexOpname
;
325 std::string replaceOp
;
331 regexOpname
= "\\+\\+";
336 regexOpname
= "\\-\\-";
342 std::string reString
;
343 if (op
== UO_PostInc
|| op
== UO_PostDec
)
345 reString
= methodName
+ " *\\( *\\) *" + regexOpname
;
346 newText
= std::regex_replace(callText
, std::regex(reString
),
347 "Adjust" + methodName
+ "( " + replaceOp
);
351 newText
= implicitObjectText
+ "." + "Adjust" + methodName
+ "( " + replaceOp
;
353 if (newText
== callText
)
355 report(DiagnosticsEngine::Warning
, "unaryop regex match failed %0",
356 compat::getBeginLoc(call
))
361 return replaceText(startLoc
, originalLength
, newText
);
364 std::string
ChangeToolsGen::extractCode(SourceLocation startLoc
, SourceLocation endLoc
)
366 SourceManager
& SM
= compiler
.getSourceManager();
367 const char* p1
= SM
.getCharacterData(SM
.getExpansionLoc(startLoc
));
368 const char* p2
= SM
.getCharacterData(SM
.getExpansionLoc(endLoc
));
369 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
370 return std::string(p1
, p2
- p1
+ n
);
373 static loplugin::Plugin::Registration
<ChangeToolsGen
> X("changetoolsgen", false);
377 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */