cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / plugin.cxx
blob143897b499d5349c427cfcaf0b993bd56fd49f2b
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>
15 #include <string>
17 #include <clang/Basic/FileManager.h>
18 #include <clang/Lex/Lexer.h>
20 #include "pluginhandler.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, SRCDIR "/external/", strlen( SRCDIR "/external/" )) == 0 )
46 return true;
47 if( strncmp( bufferName, WORKDIR, strlen( WORKDIR )) == 0 )
49 // workdir/CustomTarget/vcl/unx/kde4/tst_exclude_socket_notifiers.moc
50 // includes
51 // "../../../../../vcl/unx/kde4/tst_exclude_socket_notifiers.hxx",
52 // making the latter file erroneously match here; so strip any ".."
53 // segments:
54 if (strstr(bufferName, "/..") == nullptr) {
55 return true;
57 std::string s(bufferName);
58 normalizeDotDotInFilePath(s);
59 if (strncmp(s.c_str(), WORKDIR, strlen(WORKDIR)) == 0) {
60 return true;
63 if( strncmp( bufferName, BUILDDIR, strlen( BUILDDIR )) == 0
64 || strncmp( bufferName, SRCDIR, strlen( SRCDIR )) == 0 )
65 return false; // ok
66 return true;
69 void Plugin::normalizeDotDotInFilePath( std::string & s )
71 for (std::string::size_type i = 0;;) {
72 i = s.find("/.", i);
73 if (i == std::string::npos) {
74 break;
76 if (i + 2 == s.length() || s[i + 2] == '/') {
77 s.erase(i, 2); // [AAA]/.[/CCC] -> [AAA][/CCC]
78 } else if (s[i + 2] == '.'
79 && (i + 3 == s.length() || s[i + 3] == '/'))
81 if (i == 0) { // /..[/CCC] -> /..[/CCC]
82 break;
84 auto j = s.rfind('/', i - 1);
85 if (j == std::string::npos) {
86 // BBB/..[/CCC] -> BBB/..[/CCC] (instead of BBB/../CCC ->
87 // CCC, to avoid wrong ../../CCC -> CCC; relative paths
88 // shouldn't happen anyway, and even if they did, wouldn't
89 // match against WORKDIR anyway, as WORKDIR should be
90 // absolute):
91 break;
93 s.erase(j, i + 3 - j); // AAA/BBB/..[/CCC] -> AAA[/CCC]
94 i = j;
95 } else {
96 i += 2;
101 void Plugin::registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName, bool isPPCallback, bool byDefault )
103 PluginHandler::registerPlugin( create, optionName, isPPCallback, byDefault );
106 unordered_map< const Stmt*, const Stmt* > Plugin::parents;
108 const Stmt* Plugin::parentStmt( const Stmt* stmt )
110 if( parents.empty())
111 buildParents( compiler );
112 //if(parents.count(stmt)!=1)stmt->dump();
113 //assert( parents.count( stmt ) == 1 );
114 return parents[ stmt ];
117 Stmt* Plugin::parentStmt( Stmt* stmt )
119 if( parents.empty())
120 buildParents( compiler );
121 //assert( parents.count( stmt ) == 1 );
122 return const_cast< Stmt* >( parents[ stmt ] );
125 static const Decl* getDeclContext(ASTContext& context, const Stmt* stmt)
127 auto it = context.getParents(*stmt).begin();
129 if (it == context.getParents(*stmt).end())
130 return nullptr;
132 const Decl *aDecl = it->get<Decl>();
133 if (aDecl)
134 return aDecl;
136 const Stmt *aStmt = it->get<Stmt>();
137 if (aStmt)
138 return getDeclContext(context, aStmt);
140 return nullptr;
143 const FunctionDecl* Plugin::parentFunctionDecl( const Stmt* stmt )
145 const Decl *decl = getDeclContext(compiler.getASTContext(), stmt);
146 if (decl)
147 return static_cast<const FunctionDecl*>(decl->getNonClosureContext());
149 return nullptr;
153 bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation) const {
154 StringRef name {
155 compiler.getSourceManager().getFilename(spellingLocation) };
156 return compiler.getSourceManager().isInMainFile(spellingLocation)
157 ? (name == SRCDIR "/cppu/source/cppu/compat.cxx"
158 || name == SRCDIR "/cppuhelper/source/compat.cxx"
159 || name == SRCDIR "/sal/osl/all/compat.cxx")
160 : (name.startswith(SRCDIR "/include/com/")
161 || name.startswith(SRCDIR "/include/cppu/")
162 || name.startswith(SRCDIR "/include/cppuhelper/")
163 || name.startswith(SRCDIR "/include/osl/")
164 || name.startswith(SRCDIR "/include/rtl/")
165 || name.startswith(SRCDIR "/include/sal/")
166 || name.startswith(SRCDIR "/include/salhelper/")
167 || name.startswith(SRCDIR "/include/systools/")
168 || name.startswith(SRCDIR "/include/typelib/")
169 || name.startswith(SRCDIR "/include/uno/"));
172 bool Plugin::isInUnoIncludeFile(const FunctionDecl* functionDecl) const {
173 return isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(
174 functionDecl->getCanonicalDecl()->getNameInfo().getLoc()));
177 namespace
179 class ParentBuilder
180 : public RecursiveASTVisitor< ParentBuilder >
182 public:
183 bool VisitFunctionDecl( const FunctionDecl* function );
184 bool VisitObjCMethodDecl( const ObjCMethodDecl* method );
185 void walk( const Stmt* stmt );
186 bool shouldVisitTemplateInstantiations () const { return true; }
187 unordered_map< const Stmt*, const Stmt* >* parents;
190 bool ParentBuilder::VisitFunctionDecl( const FunctionDecl* function )
192 // if( ignoreLocation( declaration ))
193 // return true; ???
194 if( function->doesThisDeclarationHaveABody())
196 const Stmt* body = function->getBody();
197 (*parents)[ body ] = NULL; // no parent
198 walk( body );
200 if( const CXXConstructorDecl* ctor = dyn_cast< CXXConstructorDecl >( function ))
202 for( CXXConstructorDecl::init_const_iterator it = ctor->init_begin();
203 it != ctor->init_end();
204 ++it )
206 const Expr* init_expression = (*it)->getInit();
207 (*parents)[ init_expression ] = NULL;
208 walk( init_expression );
211 return true;
214 bool ParentBuilder::VisitObjCMethodDecl( const ObjCMethodDecl* method )
216 // if( ignoreLocation( declaration ))
217 // return true; ???
218 if( method->hasBody())
220 const Stmt* body = method->getBody();
221 (*parents)[ body ] = NULL; // no parent
222 walk( body );
224 return true;
227 void ParentBuilder::walk( const Stmt* stmt )
229 for( ConstStmtIterator it = stmt->child_begin();
230 it != stmt->child_end();
231 ++it )
233 if( *it != NULL )
235 (*parents)[ *it ] = stmt;
236 walk( *it );
241 } // namespace
243 void Plugin::buildParents( CompilerInstance& compiler )
245 assert( parents.empty());
246 ParentBuilder builder;
247 builder.parents = &parents;
248 builder.TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
251 SourceLocation Plugin::locationAfterToken( SourceLocation location )
253 return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts());
256 RewritePlugin::RewritePlugin( const InstantiationData& data )
257 : Plugin( data )
258 , rewriter( data.rewriter )
262 bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
264 assert( rewriter );
265 if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
266 return reportEditFailure( Loc );
267 return true;
270 bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
272 assert( rewriter );
273 if( rewriter->InsertTextAfter( Loc, Str ))
274 return reportEditFailure( Loc );
275 return true;
278 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
280 assert( rewriter );
281 if( rewriter->InsertTextAfterToken( Loc, Str ))
282 return reportEditFailure( Loc );
283 return true;
286 bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
288 assert( rewriter );
289 if( rewriter->InsertTextBefore( Loc, Str ))
290 return reportEditFailure( Loc );
291 return true;
294 bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
296 CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
297 return removeText( range, opts );
300 bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
302 return removeText( CharSourceRange( range, true ), opts );
305 bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts )
307 assert( rewriter );
308 if( rewriter->getRangeSize( range, opts ) == -1 )
309 return reportEditFailure( range.getBegin());
310 if( !handler.addRemoval( range.getBegin() ) )
312 report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
313 return true;
315 if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace )
317 if( !adjustRangeForOptions( &range, opts ))
318 return reportEditFailure( range.getBegin());
320 if( rewriter->RemoveText( range, opts ))
321 return reportEditFailure( range.getBegin());
322 return true;
325 bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
327 assert( rewriter );
328 SourceManager& SM = rewriter->getSourceMgr();
329 SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
330 if( fileStartLoc.isInvalid())
331 return false;
332 bool isInvalid = false;
333 const char* fileBuf = SM.getCharacterData( fileStartLoc, &isInvalid );
334 if( isInvalid )
335 return false;
336 const char* startBuf = SM.getCharacterData( range->getBegin(), &isInvalid );
337 if( isInvalid )
338 return false;
339 SourceLocation locationEnd = range->getEnd();
340 if( range->isTokenRange())
341 locationEnd = locationAfterToken( locationEnd );
342 const char* endBuf = SM.getCharacterData( locationEnd, &isInvalid );
343 if( isInvalid )
344 return false;
345 const char* startPos = startBuf;
346 --startPos;
347 while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
348 --startPos;
349 if( startPos >= fileBuf && *startPos == '\n' )
350 startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
351 const char* endPos = endBuf;
352 while( *endPos == ' ' || *endPos == '\t' )
353 ++endPos;
354 if( opts.flags & RemoveWholeStatement )
356 if( *endPos == ';' )
357 ++endPos;
358 else
359 return false;
361 *range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
362 locationEnd.getLocWithOffset( endPos - endBuf )), false );
363 return true;
366 bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
368 assert( rewriter );
369 if( OrigLength != 0 && !handler.addRemoval( Start ) )
371 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", Start );
372 return true;
374 if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
375 return reportEditFailure( Start );
376 return true;
379 bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
381 assert( rewriter );
382 if( rewriter->getRangeSize( range ) == -1 )
383 return reportEditFailure( range.getBegin());
384 if( !handler.addRemoval( range.getBegin() ) )
386 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin());
387 return true;
389 if( rewriter->ReplaceText( range, NewStr ))
390 return reportEditFailure( range.getBegin());
391 return true;
394 bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
396 assert( rewriter );
397 if( rewriter->getRangeSize( range ) == -1 )
398 return reportEditFailure( range.getBegin());
399 if( !handler.addRemoval( range.getBegin() ) )
401 report( DiagnosticsEngine::Warning, "double code replacement, possible plugin error", range.getBegin());
402 return true;
404 if( rewriter->ReplaceText( range, replacementRange ))
405 return reportEditFailure( range.getBegin());
406 return true;
409 bool RewritePlugin::reportEditFailure( SourceLocation loc )
411 report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
412 return false;
415 } // namespace
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */