bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / store / svstreamoutputoperators.cxx
blobdcabbf5df3bbe70e63b1048a3230b60c14ddf487
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 RecursiveASTVisitor< SvStreamOutputOperators >
37 , public RewritePlugin
39 public:
40 explicit SvStreamOutputOperators( InstantiationData const & data );
41 virtual void run() override;
42 bool VisitCallExpr( const CallExpr* call );
43 private:
44 SourceLocation after(const SourceLocation& loc);
47 SvStreamOutputOperators::SvStreamOutputOperators( InstantiationData const & data )
48 : RewritePlugin( data )
52 void SvStreamOutputOperators::run()
54 TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
57 bool SvStreamOutputOperators::VisitCallExpr( const CallExpr* callExpr )
59 if( ignoreLocation( callExpr ))
60 return true;
61 if( callExpr->getNumArgs() < 2 )
62 return true;
63 const FunctionDecl* func = dyn_cast_or_null< FunctionDecl >( callExpr->getCalleeDecl() );
64 if ( func == NULL )
65 return true;
66 if( func->getNumParams() != 1 )
67 return true;
68 string qualifiedName = func->getQualifiedNameAsString();
69 bool bOutputOperator;
70 if( qualifiedName == "SvStream::operator<<" )
71 bOutputOperator = true;
72 else if( qualifiedName == "SvStream::operator>>" )
73 bOutputOperator = false;
74 else
75 return true;
77 string arg0 = func->getParamDecl( 0 )->getType().getAsString();
78 string newIOMethod;
79 if (bOutputOperator)
81 if( arg0 == "sal_uInt16" )
82 newIOMethod = "WriteUInt16";
83 else if( arg0 == "sal_uInt32" )
84 newIOMethod = "WriteUInt32";
85 else if( arg0 == "sal_uInt64" )
86 newIOMethod = "WriteUInt64";
87 else if( arg0 == "sal_Int16" )
88 newIOMethod = "WriteInt16";
89 else if( arg0 == "sal_Int32" )
90 newIOMethod = "WriteInt32";
91 else if( arg0 == "sal_Int64" )
92 newIOMethod = "WriteInt64";
93 else if( arg0 == "sal_uInt8" )
94 newIOMethod = "WriteUInt8";
95 else if( arg0 == "sal_Unicode" )
96 newIOMethod = "WriteUnicode";
97 else if( arg0 == "rtl::OString" )
98 newIOMethod = "WriteOString";
99 else if( arg0 == "bool" )
100 newIOMethod = "WriteBool";
101 else if( arg0 == "signed char" )
102 newIOMethod = "WriteSChar";
103 else if( arg0 == "char" )
104 newIOMethod = "WriteChar";
105 else if( arg0 == "unsigned char" )
106 newIOMethod = "WriteUChar";
107 else if( arg0 == "float" )
108 newIOMethod = "WriteFloat";
109 else if( arg0 == "double" )
110 newIOMethod = "WriteDouble";
111 else if( arg0 == "const double &" )
112 newIOMethod = "WriteDouble";
113 else if( arg0 == "const char *" )
114 newIOMethod = "WriteCharPtr";
115 else if( arg0 == "char *" )
116 newIOMethod = "WriteCharPtr";
117 else if( arg0 == "const unsigned char *" )
118 newIOMethod = "WriteUCharPtr";
119 else if( arg0 == "unsigned char *" )
120 newIOMethod = "WriteUCharPtr";
121 else if( arg0 == "class SvStream &" )
122 newIOMethod = "WriteStream";
123 else
125 report( DiagnosticsEngine::Warning,
126 "found call to operator<< that I cannot convert with type: " + arg0,
127 callExpr->getLocStart());
128 return true;
131 else
133 if( arg0 == "sal_uInt16 &" )
134 newIOMethod = "ReadUInt16";
135 else if( arg0 == "sal_uInt32 &" )
136 newIOMethod = "ReadUInt32";
137 else if( arg0 == "sal_uInt64 &" )
138 newIOMethod = "ReadUInt64";
139 else if( arg0 == "sal_Int16 &" )
140 newIOMethod = "ReadInt16";
141 else if( arg0 == "sal_Int32 &" )
142 newIOMethod = "ReadInt32";
143 else if( arg0 == "sal_Int64 &" )
144 newIOMethod = "ReadInt64";
145 else if( arg0 == "sal_uInt8 &" )
146 newIOMethod = "ReadUInt8";
147 else if( arg0 == "signed char &" )
148 newIOMethod = "ReadSChar";
149 else if( arg0 == "char &" )
150 newIOMethod = "ReadChar";
151 else if( arg0 == "unsigned char &" )
152 newIOMethod = "ReadUChar";
153 else if( arg0 == "float &" )
154 newIOMethod = "ReadFloat";
155 else if( arg0 == "double &" )
156 newIOMethod = "ReadDouble";
157 else if( arg0 == "class SvStream &" )
158 newIOMethod = "ReadStream";
159 else
161 report( DiagnosticsEngine::Warning,
162 "found call to operator>> that I cannot convert with type: " + arg0,
163 callExpr->getLocStart());
164 return true;
168 // CallExpr overrides the children() method from Stmt, but not the const variant of it, so we need to cast const away.
169 StmtRange range = const_cast<CallExpr*>(callExpr)->children();
170 const Stmt* child1 = *range; // ImplicitCastExpr
171 ++range;
172 const Stmt* child2 = *range; // ImplicitCastExpr
174 if( dyn_cast_or_null< UnaryOperator >( child2 ) != NULL )
176 // remove the "*" before the stream variable
177 if( !replaceText( callExpr->getLocStart(), 1, "" ) )
178 return true;
179 if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "->" ) )
180 return true;
182 else
184 if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "." ) )
185 return true;
188 if( !insertTextBefore( callExpr->getArg( 1 )->getLocStart(), newIOMethod + "( " ) )
189 return true;
190 if( !insertTextAfter( after( callExpr->getLocEnd() ), " )" ) )
191 return true;
193 //TODO for some reason this is currently removing too much text
194 // if there was a cast e.g. "r << (sal_Int32) 1", then remove the cast
195 // const CStyleCastExpr* cast = dyn_cast_or_null< CStyleCastExpr >( callExpr->getArg(1) );
196 // if (cast != NULL)
197 // {
198 // replaceText( SourceRange( cast->getLParenLoc(), cast->getRParenLoc() ), "" );
199 // }
201 // if there was already parentheses around the expression, remove them
202 const ParenExpr* paren = dyn_cast_or_null< ParenExpr >( callExpr->getArg(1) );
203 if (paren != NULL)
205 if( !replaceText( paren->getLocStart(), 1, "" ) )
206 return true;
207 if( !replaceText( paren->getLocEnd(), 1, "" ) )
208 return true;
211 // report( DiagnosticsEngine::Note, "found", callExpr->getLocStart());
212 return true;
215 SourceLocation SvStreamOutputOperators::after( const SourceLocation& loc )
217 return Lexer::getLocForEndOfToken( loc, 0, compiler.getASTContext().getSourceManager(), compiler.getASTContext().getLangOpts() );
220 static Plugin::Registration< SvStreamOutputOperators > X( "svstreamoutputoperators" );
222 } // namespace
224 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */