bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / store / svstreamoutputoperators.cxx
blob6e0ff616844df076d562a89fefad3ae9c5f23db8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
13 This is a rewriter.
15 It changes the SvStream operator<< calls into calls to more explicitly named
16 methods, which reduces the casting needed, and makes it less likely that we
17 will accidentally write data to a file using the wrong data-type-size.
19 TODO we don't currently cope with macro expansion e.g. if the constant on the RHS is a #define
21 TODO we don't currently cope with code like "(*this) << 1;"
23 TODO we don't currently cope with code like "aStream << x << endl;" the "endl" parts ends up dangling.
25 TODO we don't currently cope with custom overloads of operator<< in some of the use-sites.
28 #include "plugin.hxx"
29 #include <clang/Lex/Lexer.h>
30 #include <iostream>
32 namespace loplugin
35 class SvStreamOutputOperators
36 : public loplugin::FilteringRewritePlugin< SvStreamOutputOperators >
38 public:
39 explicit SvStreamOutputOperators( InstantiationData const & data );
40 virtual void run() override;
41 bool VisitCallExpr( const CallExpr* call );
42 private:
43 SourceLocation after(const SourceLocation& loc);
46 SvStreamOutputOperators::SvStreamOutputOperators( InstantiationData const & data )
47 : FilteringRewritePlugin( data )
51 void SvStreamOutputOperators::run()
53 TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
56 bool SvStreamOutputOperators::VisitCallExpr( const CallExpr* callExpr )
58 if( ignoreLocation( callExpr ))
59 return true;
60 if( callExpr->getNumArgs() < 2 )
61 return true;
62 const FunctionDecl* func = dyn_cast_or_null< FunctionDecl >( callExpr->getCalleeDecl() );
63 if ( func == NULL )
64 return true;
65 if( func->getNumParams() != 1 )
66 return true;
67 string qualifiedName = func->getQualifiedNameAsString();
68 bool bOutputOperator;
69 if( qualifiedName == "SvStream::operator<<" )
70 bOutputOperator = true;
71 else if( qualifiedName == "SvStream::operator>>" )
72 bOutputOperator = false;
73 else
74 return true;
76 string arg0 = func->getParamDecl( 0 )->getType().getAsString();
77 string newIOMethod;
78 if (bOutputOperator)
80 if( arg0 == "sal_uInt16" )
81 newIOMethod = "WriteUInt16";
82 else if( arg0 == "sal_uInt32" )
83 newIOMethod = "WriteUInt32";
84 else if( arg0 == "sal_uInt64" )
85 newIOMethod = "WriteUInt64";
86 else if( arg0 == "sal_Int16" )
87 newIOMethod = "WriteInt16";
88 else if( arg0 == "sal_Int32" )
89 newIOMethod = "WriteInt32";
90 else if( arg0 == "sal_Int64" )
91 newIOMethod = "WriteInt64";
92 else if( arg0 == "sal_uInt8" )
93 newIOMethod = "WriteUInt8";
94 else if( arg0 == "sal_Unicode" )
95 newIOMethod = "WriteUnicode";
96 else if( arg0 == "rtl::OString" )
97 newIOMethod = "WriteOString";
98 else if( arg0 == "bool" )
99 newIOMethod = "WriteBool";
100 else if( arg0 == "signed char" )
101 newIOMethod = "WriteSChar";
102 else if( arg0 == "char" )
103 newIOMethod = "WriteChar";
104 else if( arg0 == "unsigned char" )
105 newIOMethod = "WriteUChar";
106 else if( arg0 == "float" )
107 newIOMethod = "WriteFloat";
108 else if( arg0 == "double" )
109 newIOMethod = "WriteDouble";
110 else if( arg0 == "const double &" )
111 newIOMethod = "WriteDouble";
112 else if( arg0 == "const char *" )
113 newIOMethod = "WriteCharPtr";
114 else if( arg0 == "char *" )
115 newIOMethod = "WriteCharPtr";
116 else if( arg0 == "const unsigned char *" )
117 newIOMethod = "WriteUCharPtr";
118 else if( arg0 == "unsigned char *" )
119 newIOMethod = "WriteUCharPtr";
120 else if( arg0 == "class SvStream &" )
121 newIOMethod = "WriteStream";
122 else
124 report( DiagnosticsEngine::Warning,
125 "found call to operator<< that I cannot convert with type: " + arg0,
126 callExpr->getLocStart());
127 return true;
130 else
132 if( arg0 == "sal_uInt16 &" )
133 newIOMethod = "ReadUInt16";
134 else if( arg0 == "sal_uInt32 &" )
135 newIOMethod = "ReadUInt32";
136 else if( arg0 == "sal_uInt64 &" )
137 newIOMethod = "ReadUInt64";
138 else if( arg0 == "sal_Int16 &" )
139 newIOMethod = "ReadInt16";
140 else if( arg0 == "sal_Int32 &" )
141 newIOMethod = "ReadInt32";
142 else if( arg0 == "sal_Int64 &" )
143 newIOMethod = "ReadInt64";
144 else if( arg0 == "sal_uInt8 &" )
145 newIOMethod = "ReadUInt8";
146 else if( arg0 == "signed char &" )
147 newIOMethod = "ReadSChar";
148 else if( arg0 == "char &" )
149 newIOMethod = "ReadChar";
150 else if( arg0 == "unsigned char &" )
151 newIOMethod = "ReadUChar";
152 else if( arg0 == "float &" )
153 newIOMethod = "ReadFloat";
154 else if( arg0 == "double &" )
155 newIOMethod = "ReadDouble";
156 else if( arg0 == "class SvStream &" )
157 newIOMethod = "ReadStream";
158 else
160 report( DiagnosticsEngine::Warning,
161 "found call to operator>> that I cannot convert with type: " + arg0,
162 callExpr->getLocStart());
163 return true;
167 // CallExpr overrides the children() method from Stmt, but not the const variant of it, so we need to cast const away.
168 StmtRange range = const_cast<CallExpr*>(callExpr)->children();
169 const Stmt* child1 = *range; // ImplicitCastExpr
170 ++range;
171 const Stmt* child2 = *range; // ImplicitCastExpr
173 if( dyn_cast_or_null< UnaryOperator >( child2 ) != NULL )
175 // remove the "*" before the stream variable
176 if( !replaceText( callExpr->getLocStart(), 1, "" ) )
177 return true;
178 if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "->" ) )
179 return true;
181 else
183 if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "." ) )
184 return true;
187 if( !insertTextBefore( callExpr->getArg( 1 )->getLocStart(), newIOMethod + "( " ) )
188 return true;
189 if( !insertTextAfter( after( callExpr->getLocEnd() ), " )" ) )
190 return true;
192 //TODO for some reason this is currently removing too much text
193 // if there was a cast e.g. "r << (sal_Int32) 1", then remove the cast
194 // const CStyleCastExpr* cast = dyn_cast_or_null< CStyleCastExpr >( callExpr->getArg(1) );
195 // if (cast != NULL)
196 // {
197 // replaceText( SourceRange( cast->getLParenLoc(), cast->getRParenLoc() ), "" );
198 // }
200 // if there was already parentheses around the expression, remove them
201 const ParenExpr* paren = dyn_cast_or_null< ParenExpr >( callExpr->getArg(1) );
202 if (paren != NULL)
204 if( !replaceText( paren->getLocStart(), 1, "" ) )
205 return true;
206 if( !replaceText( paren->getLocEnd(), 1, "" ) )
207 return true;
210 // report( DiagnosticsEngine::Note, "found", callExpr->getLocStart());
211 return true;
214 SourceLocation SvStreamOutputOperators::after( const SourceLocation& loc )
216 return Lexer::getLocForEndOfToken( loc, 0, compiler.getASTContext().getSourceManager(), compiler.getASTContext().getLangOpts() );
219 static Plugin::Registration< SvStreamOutputOperators > X( "svstreamoutputoperators" );
221 } // namespace
223 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */