2 * This file is part of the LibreOffice project.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
11 #include "postfixincrementfix.hxx"
16 Change all postfix ++ operators of non-trivial types to prefix if possible.
22 PostfixIncrementFix::PostfixIncrementFix( CompilerInstance
& compiler
, Rewriter
& rewriter
)
23 : RewritePlugin( compiler
, rewriter
)
27 void PostfixIncrementFix::run()
29 TraverseDecl( compiler
.getASTContext().getTranslationUnitDecl());
32 bool PostfixIncrementFix::VisitFunctionDecl( const FunctionDecl
* declaration
)
34 if( ignoreLocation( declaration
))
36 if( !declaration
->doesThisDeclarationHaveABody())
39 fixPostfixOperators( declaration
->getBody(), parents
);
43 void PostfixIncrementFix::fixPostfixOperators( const Stmt
* stmt
, StmtParents
& parents
)
45 if( const CXXOperatorCallExpr
* op
= dyn_cast
<CXXOperatorCallExpr
>( stmt
))
46 { // postfix ++ has two arguments (the operand and the hidden extra int)
47 if( op
->getOperator() == OO_PlusPlus
&& op
->getNumArgs() == 2 )
48 fixPostfixOperator( op
, parents
);
50 // For primitive types it would be UnaryOperatorExpr, but probably no good reason to change those.
51 parents
.push_back( stmt
);
52 for( ConstStmtIterator it
= stmt
->child_begin();
53 it
!= stmt
->child_end();
56 if( *it
!= NULL
) // some children can be apparently NULL
57 fixPostfixOperators( *it
, parents
);
59 assert( parents
.back() == stmt
);
63 void PostfixIncrementFix::fixPostfixOperator( const CXXOperatorCallExpr
* op
, StmtParents
& parents
)
65 if( !canChangePostfixToPrefix( op
, parents
, parents
.size() - 1 ))
67 if( !shouldDoChange( op
->getArg( 0 )))
69 // Adding spaces around the moved ++ should not be necessary
70 // (there might a problem with e.g. a+b++ -> a+++b (i.e. a++ +b),
71 // but we don't change such expressions).
72 if( insertText( op
->getLocStart(), "++" )) // insert is intentionally first, in case it fails
73 removeText( op
->getCallee()->getSourceRange());
76 bool PostfixIncrementFix::canChangePostfixToPrefix( const CXXOperatorCallExpr
* op
, StmtParents
& parents
, int parent_pos
)
78 // check if foo++ can be safely replaced by ++foo
79 switch( parents
[ parent_pos
]->getStmtClass())
81 case Stmt::CompoundStmtClass
:
83 // these mean somebody is going to use it
84 case Stmt::ImplicitCastExprClass
:
85 case Stmt::MaterializeTemporaryExprClass
:
86 case Stmt::BinaryOperatorClass
:
87 case Stmt::UnaryOperatorClass
:
88 case Stmt::CallExprClass
:
89 case Stmt::CXXOperatorCallExprClass
:
91 case Stmt::CXXBindTemporaryExprClass
:
92 // tricky, it may just mean the temporary will be cleaned up
93 // (by ExprWithCleanups), ignore and go up
94 assert( parent_pos
> 0 ); // should not happen
95 return canChangePostfixToPrefix( op
, parents
, parent_pos
- 1 );
96 case Stmt::ExprWithCleanupsClass
:
97 // cleanup of a temporary, should be harmless (if the use
98 // of the postfix ++ operator here relies on the fact that
99 // the dtor for the object will be called, that's pretty insane
100 // code). Ignore and go up.
101 assert( parent_pos
> 0 ); // should not happen
102 return canChangePostfixToPrefix( op
, parents
, parent_pos
- 1 );
103 case Stmt::ParenExprClass
: // parentheses, go up
104 assert( parent_pos
> 0 );
105 return canChangePostfixToPrefix( op
, parents
, parent_pos
- 1 );
106 case Stmt::IfStmtClass
:
107 return canChangeInConditionStatement( op
, cast
< IfStmt
>( parents
[ parent_pos
] )->getCond(),
108 parents
, parent_pos
);
109 case Stmt::WhileStmtClass
:
110 return canChangeInConditionStatement( op
, cast
< WhileStmt
>( parents
[ parent_pos
] )->getCond(),
111 parents
, parent_pos
);
112 case Stmt::DoStmtClass
:
113 return canChangeInConditionStatement( op
, cast
< DoStmt
>( parents
[ parent_pos
] )->getCond(),
114 parents
, parent_pos
);
115 case Stmt::ForStmtClass
:
116 return canChangeInConditionStatement( op
, dyn_cast
< ForStmt
>( parents
[ parent_pos
] )->getCond(),
117 parents
, parent_pos
);
119 report( DiagnosticsEngine::Fatal
, "cannot analyze operator++ (plugin needs fixing)",
120 op
->getLocStart()) << parents
[ parent_pos
]->getSourceRange();
121 // parents[ parent_pos ]->dump();
122 // parents[ std::max( parent_pos - 3, 0 ) ]->dump();
127 bool PostfixIncrementFix::canChangeInConditionStatement( const CXXOperatorCallExpr
* op
, const Expr
* condition
,
128 const StmtParents
& parents
, unsigned int parent_pos
)
130 // cannot be changed in condition, can be changed in statements
131 if( parent_pos
== parents
.size() - 1 )
132 return op
!= condition
;
135 assert( parent_pos
+ 1 < parents
.size());
136 return parents
[ parent_pos
+ 1 ] != condition
;
140 bool PostfixIncrementFix::shouldDoChange( const Expr
* operand
)
142 // TODO Changing 'a->b++' to '++a->b' is technically the same, but the latter probably looks confusing,
143 // so either avoid that, or add parentheses. Avoid for now.
144 const Expr
* expr
= const_cast< Expr
* >( operand
)->IgnoreImplicit(); // does not have const version
145 switch( expr
->getStmtClass())
147 case Stmt::ParenExprClass
:
148 return true; // there are already parentheses, ok to move the ++
149 case Stmt::MemberExprClass
:
150 return false; // ++a->b , avoid
151 case Stmt::DeclRefExprClass
:
155 report( DiagnosticsEngine::Fatal
, "cannot analyze operator++ (plugin needs fixing)",
156 expr
->getLocStart()) << operand
->getSourceRange();
164 static Plugin::Registration
< PostfixIncrementFix
> X( "postfixincrementfix" );