update emoji autocorrect entries from po-files
[LibreOffice.git] / compilerplugins / clang / plugin.cxx
blob4a391823bced95900e4cd63b815034ea7b4489db
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.
12 #include "plugin.hxx"
14 #include <cassert>
16 #include <clang/Basic/FileManager.h>
17 #include <clang/Lex/Lexer.h>
19 #include "pluginhandler.hxx"
20 #include "compat.hxx"
23 Base classes for plugin actions.
25 namespace loplugin
28 Plugin::Plugin( const InstantiationData& data )
29 : compiler( data.compiler ), handler( data.handler ), name( data.name )
33 DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) const
35 return handler.report( level, name, message, compiler, loc );
38 bool Plugin::ignoreLocation( SourceLocation loc )
40 SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( loc );
41 if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
42 return true;
43 const char* bufferName = compiler.getSourceManager().getPresumedLoc( expansionLoc ).getFilename();
44 if( bufferName == NULL
45 || strncmp( bufferName, WORKDIR, strlen( WORKDIR )) == 0 )
46 return true;
47 if( strncmp( bufferName, BUILDDIR, strlen( BUILDDIR )) == 0
48 || strncmp( bufferName, SRCDIR, strlen( SRCDIR )) == 0 )
49 return false; // ok
50 return true;
53 void Plugin::registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName, bool isPPCallback, bool byDefault )
55 PluginHandler::registerPlugin( create, optionName, isPPCallback, byDefault );
58 unordered_map< const Stmt*, const Stmt* > Plugin::parents;
60 const Stmt* Plugin::parentStmt( const Stmt* stmt )
62 if( parents.empty())
63 buildParents( compiler );
64 if(parents.count(stmt)!=1)stmt->dump();
65 assert( parents.count( stmt ) == 1 );
66 return parents[ stmt ];
69 Stmt* Plugin::parentStmt( Stmt* stmt )
71 if( parents.empty())
72 buildParents( compiler );
73 assert( parents.count( stmt ) == 1 );
74 return const_cast< Stmt* >( parents[ stmt ] );
78 bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation) const {
79 StringRef name {
80 compiler.getSourceManager().getFilename(spellingLocation) };
81 return compat::isInMainFile(compiler.getSourceManager(), spellingLocation)
82 ? (name == SRCDIR "/cppu/source/cppu/compat.cxx"
83 || name == SRCDIR "/cppuhelper/source/compat.cxx"
84 || name == SRCDIR "/sal/osl/all/compat.cxx")
85 : (name.startswith(SRCDIR "/include/com/")
86 || name.startswith(SRCDIR "/include/cppu/")
87 || name.startswith(SRCDIR "/include/cppuhelper/")
88 || name.startswith(SRCDIR "/include/osl/")
89 || name.startswith(SRCDIR "/include/rtl/")
90 || name.startswith(SRCDIR "/include/sal/")
91 || name.startswith(SRCDIR "/include/salhelper/")
92 || name.startswith(SRCDIR "/include/systools/")
93 || name.startswith(SRCDIR "/include/typelib/")
94 || name.startswith(SRCDIR "/include/uno/")
95 || name.startswith(SRCDIR "/workdir/")
96 || name == SRCDIR "/include/comphelper/implbase_var.hxx");
99 namespace
101 class ParentBuilder
102 : public RecursiveASTVisitor< ParentBuilder >
104 public:
105 bool VisitFunctionDecl( const FunctionDecl* function );
106 bool VisitObjCMethodDecl( const ObjCMethodDecl* method );
107 void walk( const Stmt* stmt );
108 unordered_map< const Stmt*, const Stmt* >* parents;
111 bool ParentBuilder::VisitFunctionDecl( const FunctionDecl* function )
113 // if( ignoreLocation( declaration ))
114 // return true; ???
115 if( function->doesThisDeclarationHaveABody())
117 const Stmt* body = function->getBody();
118 (*parents)[ body ] = NULL; // no parent
119 walk( body );
121 if( const CXXConstructorDecl* ctor = dyn_cast< CXXConstructorDecl >( function ))
123 for( CXXConstructorDecl::init_const_iterator it = ctor->init_begin();
124 it != ctor->init_end();
125 ++it )
127 const Expr* init_expression = (*it)->getInit();
128 (*parents)[ init_expression ] = NULL;
129 walk( init_expression );
132 return true;
135 bool ParentBuilder::VisitObjCMethodDecl( const ObjCMethodDecl* method )
137 // if( ignoreLocation( declaration ))
138 // return true; ???
139 if( method->hasBody())
141 const Stmt* body = method->getBody();
142 (*parents)[ body ] = NULL; // no parent
143 walk( body );
145 return true;
148 void ParentBuilder::walk( const Stmt* stmt )
150 for( ConstStmtIterator it = stmt->child_begin();
151 it != stmt->child_end();
152 ++it )
154 if( *it != NULL )
156 (*parents)[ *it ] = stmt;
157 walk( *it );
162 } // namespace
164 void Plugin::buildParents( CompilerInstance& compiler )
166 assert( parents.empty());
167 ParentBuilder builder;
168 builder.parents = &parents;
169 builder.TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
172 SourceLocation Plugin::locationAfterToken( SourceLocation location )
174 return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts());
177 RewritePlugin::RewritePlugin( const InstantiationData& data )
178 : Plugin( data )
179 , rewriter( data.rewriter )
183 bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
185 assert( rewriter );
186 if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
187 return reportEditFailure( Loc );
188 return true;
191 bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
193 assert( rewriter );
194 if( rewriter->InsertTextAfter( Loc, Str ))
195 return reportEditFailure( Loc );
196 return true;
199 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
201 assert( rewriter );
202 if( rewriter->InsertTextAfterToken( Loc, Str ))
203 return reportEditFailure( Loc );
204 return true;
207 bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
209 assert( rewriter );
210 if( rewriter->InsertTextBefore( Loc, Str ))
211 return reportEditFailure( Loc );
212 return true;
215 bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
217 CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
218 return removeText( range, opts );
221 bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
223 return removeText( CharSourceRange( range, true ), opts );
226 bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts )
228 assert( rewriter );
229 if( rewriter->getRangeSize( range, opts ) == -1 )
230 return reportEditFailure( range.getBegin());
231 if( !handler.addRemoval( range.getBegin() ) )
233 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
234 return true;
236 if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace )
238 if( !adjustRangeForOptions( &range, opts ))
239 return reportEditFailure( range.getBegin());
241 if( rewriter->RemoveText( range, opts ))
242 return reportEditFailure( range.getBegin());
243 return true;
246 bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
248 assert( rewriter );
249 SourceManager& SM = rewriter->getSourceMgr();
250 SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
251 if( fileStartLoc.isInvalid())
252 return false;
253 bool invalid = false;
254 const char* fileBuf = SM.getCharacterData( fileStartLoc, &invalid );
255 if( invalid )
256 return false;
257 const char* startBuf = SM.getCharacterData( range->getBegin(), &invalid );
258 if( invalid )
259 return false;
260 SourceLocation locationEnd = range->getEnd();
261 if( range->isTokenRange())
262 locationEnd = locationAfterToken( locationEnd );
263 const char* endBuf = SM.getCharacterData( locationEnd, &invalid );
264 if( invalid )
265 return false;
266 const char* startPos = startBuf;
267 --startPos;
268 while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
269 --startPos;
270 if( startPos >= fileBuf && *startPos == '\n' )
271 startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
272 const char* endPos = endBuf;
273 while( *endPos == ' ' || *endPos == '\t' )
274 ++endPos;
275 if( opts.flags & RemoveWholeStatement )
277 if( *endPos == ';' )
278 ++endPos;
279 else
280 return false;
282 *range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
283 locationEnd.getLocWithOffset( endPos - endBuf )), false );
284 return true;
287 bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
289 assert( rewriter );
290 if( OrigLength != 0 && !handler.addRemoval( Start ) )
292 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", Start );
293 return true;
295 if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
296 return reportEditFailure( Start );
297 return true;
300 bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
302 assert( rewriter );
303 if( rewriter->getRangeSize( range ) == -1 )
304 return reportEditFailure( range.getBegin());
305 if( !handler.addRemoval( range.getBegin() ) )
307 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin());
308 return true;
310 if( rewriter->ReplaceText( range, NewStr ))
311 return reportEditFailure( range.getBegin());
312 return true;
315 bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
317 assert( rewriter );
318 if( rewriter->getRangeSize( range ) == -1 )
319 return reportEditFailure( range.getBegin());
320 if( !handler.addRemoval( range.getBegin() ) )
322 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin());
323 return true;
325 if( rewriter->ReplaceText( range, replacementRange ))
326 return reportEditFailure( range.getBegin());
327 return true;
330 bool RewritePlugin::reportEditFailure( SourceLocation loc )
332 report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
333 return false;
336 } // namespace
338 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */