1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
17 #include <clang/Basic/FileManager.h>
18 #include <clang/Lex/Lexer.h>
20 #include "pluginhandler.hxx"
23 Base classes for plugin actions.
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
))
43 const char* bufferName
= compiler
.getSourceManager().getPresumedLoc( expansionLoc
).getFilename();
44 if( bufferName
== NULL
45 || strncmp( bufferName
, SRCDIR
"/external/", strlen( SRCDIR
"/external/" )) == 0 )
47 if( strncmp( bufferName
, WORKDIR
, strlen( WORKDIR
)) == 0 )
49 // workdir/CustomTarget/vcl/unx/kde4/tst_exclude_socket_notifiers.moc
51 // "../../../../../vcl/unx/kde4/tst_exclude_socket_notifiers.hxx",
52 // making the latter file erroneously match here; so strip any ".."
54 if (strstr(bufferName
, "/..") == nullptr) {
57 std::string
s(bufferName
);
58 normalizeDotDotInFilePath(s
);
59 if (strncmp(s
.c_str(), WORKDIR
, strlen(WORKDIR
)) == 0) {
63 if( strncmp( bufferName
, BUILDDIR
, strlen( BUILDDIR
)) == 0
64 || strncmp( bufferName
, SRCDIR
, strlen( SRCDIR
)) == 0 )
69 void Plugin::normalizeDotDotInFilePath( std::string
& s
)
71 for (std::string::size_type i
= 0;;) {
73 if (i
== std::string::npos
) {
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]
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
93 s
.erase(j
, i
+ 3 - j
); // AAA/BBB/..[/CCC] -> AAA[/CCC]
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
)
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
)
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())
132 const Decl
*aDecl
= it
->get
<Decl
>();
136 const Stmt
*aStmt
= it
->get
<Stmt
>();
138 return getDeclContext(context
, aStmt
);
143 const FunctionDecl
* Plugin::parentFunctionDecl( const Stmt
* stmt
)
145 const Decl
*decl
= getDeclContext(compiler
.getASTContext(), stmt
);
147 return static_cast<const FunctionDecl
*>(decl
->getNonClosureContext());
153 bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation
) const {
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()));
180 : public RecursiveASTVisitor
< ParentBuilder
>
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 ))
194 if( function
->doesThisDeclarationHaveABody())
196 const Stmt
* body
= function
->getBody();
197 (*parents
)[ body
] = NULL
; // no parent
200 if( const CXXConstructorDecl
* ctor
= dyn_cast
< CXXConstructorDecl
>( function
))
202 for( CXXConstructorDecl::init_const_iterator it
= ctor
->init_begin();
203 it
!= ctor
->init_end();
206 const Expr
* init_expression
= (*it
)->getInit();
207 (*parents
)[ init_expression
] = NULL
;
208 walk( init_expression
);
214 bool ParentBuilder::VisitObjCMethodDecl( const ObjCMethodDecl
* method
)
216 // if( ignoreLocation( declaration ))
218 if( method
->hasBody())
220 const Stmt
* body
= method
->getBody();
221 (*parents
)[ body
] = NULL
; // no parent
227 void ParentBuilder::walk( const Stmt
* stmt
)
229 for( ConstStmtIterator it
= stmt
->child_begin();
230 it
!= stmt
->child_end();
235 (*parents
)[ *it
] = stmt
;
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
)
258 , rewriter( data
.rewriter
)
262 bool RewritePlugin::insertText( SourceLocation Loc
, StringRef Str
, bool InsertAfter
, bool indentNewLines
)
265 if( rewriter
->InsertText( Loc
, Str
, InsertAfter
, indentNewLines
))
266 return reportEditFailure( Loc
);
270 bool RewritePlugin::insertTextAfter( SourceLocation Loc
, StringRef Str
)
273 if( rewriter
->InsertTextAfter( Loc
, Str
))
274 return reportEditFailure( Loc
);
278 bool RewritePlugin::insertTextAfterToken( SourceLocation Loc
, StringRef Str
)
281 if( rewriter
->InsertTextAfterToken( Loc
, Str
))
282 return reportEditFailure( Loc
);
286 bool RewritePlugin::insertTextBefore( SourceLocation Loc
, StringRef Str
)
289 if( rewriter
->InsertTextBefore( Loc
, Str
))
290 return reportEditFailure( Loc
);
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
)
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());
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());
325 bool RewritePlugin::adjustRangeForOptions( CharSourceRange
* range
, RewriteOptions opts
)
328 SourceManager
& SM
= rewriter
->getSourceMgr();
329 SourceLocation fileStartLoc
= SM
.getLocForStartOfFile( SM
.getFileID( range
->getBegin()));
330 if( fileStartLoc
.isInvalid())
332 bool isInvalid
= false;
333 const char* fileBuf
= SM
.getCharacterData( fileStartLoc
, &isInvalid
);
336 const char* startBuf
= SM
.getCharacterData( range
->getBegin(), &isInvalid
);
339 SourceLocation locationEnd
= range
->getEnd();
340 if( range
->isTokenRange())
341 locationEnd
= locationAfterToken( locationEnd
);
342 const char* endBuf
= SM
.getCharacterData( locationEnd
, &isInvalid
);
345 const char* startPos
= startBuf
;
347 while( startPos
>= fileBuf
&& ( *startPos
== ' ' || *startPos
== '\t' ))
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' )
354 if( opts
.flags
& RemoveWholeStatement
)
361 *range
= CharSourceRange( SourceRange( range
->getBegin().getLocWithOffset( startPos
- startBuf
+ 1 ),
362 locationEnd
.getLocWithOffset( endPos
- endBuf
)), false );
366 bool RewritePlugin::replaceText( SourceLocation Start
, unsigned OrigLength
, StringRef NewStr
)
369 if( OrigLength
!= 0 && !handler
.addRemoval( Start
) )
371 report( DiagnosticsEngine::Warning
, "double code replacement, possible plugin error", Start
);
374 if( rewriter
->ReplaceText( Start
, OrigLength
, NewStr
))
375 return reportEditFailure( Start
);
379 bool RewritePlugin::replaceText( SourceRange range
, StringRef NewStr
)
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());
389 if( rewriter
->ReplaceText( range
, NewStr
))
390 return reportEditFailure( range
.getBegin());
394 bool RewritePlugin::replaceText( SourceRange range
, SourceRange replacementRange
)
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());
404 if( rewriter
->ReplaceText( range
, replacementRange
))
405 return reportEditFailure( range
.getBegin());
409 bool RewritePlugin::reportEditFailure( SourceLocation loc
)
411 report( DiagnosticsEngine::Warning
, "cannot perform source modification (macro expansion involved?)", loc
);
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */