1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * Based on LLVM/Clang.
6  *
7  * This file is distributed under the University of Illinois Open Source
8  * License. See LICENSE.TXT for details.
9  *
10  */
11 
12 #pragma once
13 
14 #include <clang/AST/ASTContext.h>
15 #include <clang/AST/RecursiveASTVisitor.h>
16 #include <clang/Basic/FileManager.h>
17 #include <clang/Basic/SourceManager.h>
18 #include <clang/Frontend/CompilerInstance.h>
19 #include <clang/Lex/Preprocessor.h>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include <clang/Rewrite/Core/Rewriter.h>
24 
25 #include "compat.hxx"
26 #include "pluginhandler.hxx"
27 
28 using namespace clang;
29 using namespace llvm;
30 
31 namespace loplugin
32 {
33 
34 struct InstantiationData
35 {
36     const char* name;
37     PluginHandler& handler;
38     CompilerInstance& compiler;
39     Rewriter* rewriter;
40 };
41 
42 /**
43     Base class for plugins.
44 
45     If you want to create a non-rewriter action, inherit from this class. Remember to also
46     use Plugin::Registration.
47 */
48 class Plugin
49 {
50 public:
51     explicit Plugin( const InstantiationData& data );
~Plugin()52     virtual ~Plugin() {}
53     // The main function of the plugin.
54     // Note that for shared plugins, its functionality must be split into preRun() and postRun(),
55     // see sharedvisitor/generator.cxx .
56     virtual void run() = 0;
57     // Should be called from run() before TraverseDecl().
58     // If returns false, run() should not do anything.
preRun()59     virtual bool preRun() { return true; }
postRun()60     virtual void postRun() {}
61     template< typename T > class Registration;
62     // Returns location right after the end of the token that starts at the given location.
63     SourceLocation locationAfterToken( SourceLocation location );
setSharedPlugin(Plugin *,const char *)64     virtual bool setSharedPlugin( Plugin* /*plugin*/, const char* /*name*/ ) { return false; }
65     enum { isPPCallback = false };
66     enum { isSharedPlugin = false };
67 protected:
68     DiagnosticBuilder report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc = SourceLocation()) const;
ignoreLocation(SourceLocation loc) const69     bool ignoreLocation( SourceLocation loc ) const
70     { return handler.ignoreLocation(loc); }
71     bool ignoreLocation( const Decl* decl ) const;
72     bool ignoreLocation( const Stmt* stmt ) const;
73     bool ignoreLocation(TypeLoc tloc) const;
74     CompilerInstance& compiler;
75     PluginHandler& handler;
76     /**
77      Returns the parent of the given AST node. Clang's internal AST representation doesn't provide this information,
78      it can only provide children, but getting the parent is often useful for inspecting a part of the AST.
79     */
80     const Stmt* getParentStmt( const Stmt* stmt );
81     Stmt* getParentStmt( Stmt* stmt );
82     const FunctionDecl* getParentFunctionDecl( const Stmt* stmt );
83 
84     /**
85      Get filename of the given location. Use this instead of SourceManager::getFilename(), as that one
86      does not handle source with expanded #inline directives (used by Icecream for remote compilation).
87     */
88     StringRef getFilenameOfLocation(SourceLocation spellingLocation) const;
89     /**
90      Checks if the location is inside a UNO file, more specifically, if it forms part of the URE stable interface,
91      which is not allowed to be changed.
92     */
93     bool isInUnoIncludeFile(SourceLocation spellingLocation) const;
94     bool isInUnoIncludeFile(const FunctionDecl*) const;
95 
isDebugMode() const96     bool isDebugMode() const { return handler.isDebugMode(); }
97 
98     static bool isUnitTestMode();
99 
100     bool containsPreprocessingConditionalInclusion(SourceRange range);
101 
102     enum class IdenticalDefaultArgumentsResult { No, Yes, Maybe };
103     IdenticalDefaultArgumentsResult checkIdenticalDefaultArguments(
104         Expr const * argument1, Expr const * argument2);
105 
106 private:
107     static void registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName,
108         bool isPPCallback, bool isSharedPlugin, bool byDefault );
109     template< typename T > static Plugin* createHelper( const InstantiationData& data );
110     bool evaluate(const Expr* expr, APSInt& x);
111 
112     enum { isRewriter = false };
113     const char* name;
114 };
115 
116 template<typename Derived>
117 class FilteringPlugin : public RecursiveASTVisitor<Derived>, public Plugin
118 {
119 public:
FilteringPlugin(const InstantiationData & data)120     explicit FilteringPlugin( const InstantiationData& data ) : Plugin(data) {}
121 
TraverseNamespaceDecl(NamespaceDecl * decl)122     bool TraverseNamespaceDecl(NamespaceDecl * decl) {
123         if (ignoreLocation(compat::getBeginLoc(decl)))
124             return true;
125         return RecursiveASTVisitor<Derived>::TraverseNamespaceDecl(decl);
126     }
127 };
128 
129 /**
130     Base class for rewriter plugins.
131 
132     Remember to also use Plugin::Registration.
133 */
134 class RewritePlugin
135     : public Plugin
136 {
137 public:
138     explicit RewritePlugin( const InstantiationData& data );
139 protected:
140     enum RewriteOption
141     {
142         // This enum allows passing just 'RemoveLineIfEmpty' to functions below.
143         // If the resulting line would be completely empty, it'll be removed.
144         RemoveLineIfEmpty    = 1 << 0,
145         // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement
146         // and the trailing semicolon (is not part of the AST element range itself).
147         // The trailing semicolon must be present.
148         RemoveWholeStatement = 1 << 1,
149         // Removes also all whitespace preceding and following the expression (completely, so that
150         // the preceding and following tokens would be right next to each other, follow with insertText( " " )
151         // if this is not wanted). Despite the name, indentation whitespace is not removed.
152         RemoveAllWhitespace  = 1 << 2
153     };
154     struct RewriteOptions
155         : public Rewriter::RewriteOptions
156     {
RewriteOptionsloplugin::RewritePlugin::RewriteOptions157         RewriteOptions() : flags( 0 ) {}
158         explicit RewriteOptions( RewriteOption option );
159         const int flags;
160     };
161     // syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement'
162     friend RewriteOption operator|( RewriteOption option1, RewriteOption option2 );
163     // These following insert/remove/replaceText functions map to functions
164     // in clang::Rewriter, with these differences:
165     // - they (more intuitively) return false on failure rather than true
166     // - they report a warning when the change cannot be done
167     // - There are more options for easier removal of surroundings of a statement/expression.
168     bool insertText( SourceLocation Loc, StringRef Str,
169     bool InsertAfter = true, bool indentNewLines = false );
170     bool insertTextAfter( SourceLocation Loc, StringRef Str );
171     bool insertTextAfterToken( SourceLocation Loc, StringRef Str );
172     bool insertTextBefore( SourceLocation Loc, StringRef Str );
173     bool removeText( SourceLocation Start, unsigned Length, RewriteOptions opts = RewriteOptions());
174     bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions());
175     bool removeText( SourceRange range, RewriteOptions opts = RewriteOptions());
176     bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr );
177     bool replaceText( SourceRange range, StringRef NewStr );
178     bool replaceText( SourceRange range, SourceRange replacementRange );
179     Rewriter* rewriter;
180 private:
181     template< typename T > friend class Plugin::Registration;
182     enum { isRewriter = true };
183     bool wouldRewriteWorkdir(SourceLocation loc);
184     bool reportEditFailure( SourceLocation loc );
185     bool adjustRangeForOptions( CharSourceRange* range, RewriteOptions options );
186 };
187 
188 /**
189     Plugin registration helper.
190 
191     If you create a new helper class, create also an instance of this class to automatically register it.
192     The passed argument is name of the plugin, used for explicitly invoking rewriter plugins
193     (it is ignored for non-rewriter plugins).
194 
195     @code
196     static Plugin::Registration< NameOfClass > X( "nameofclass" );
197     @endcode
198 */
199 template< typename T >
200 class Plugin::Registration
201 {
202 public:
203     Registration( const char* optionName, bool byDefault = !T::isRewriter );
204 };
205 
206 class RegistrationCreate
207 {
208 public:
209     template< typename T, bool > static T* create( const InstantiationData& data );
210 };
211 
212 inline
ignoreLocation(const Decl * decl) const213 bool Plugin::ignoreLocation( const Decl* decl ) const
214 {
215     return ignoreLocation( decl->getLocation());
216 }
217 
218 inline
ignoreLocation(const Stmt * stmt) const219 bool Plugin::ignoreLocation( const Stmt* stmt ) const
220 {
221     // Invalid location can happen at least for ImplicitCastExpr of
222     // ImplicitParam 'self' in Objective C method declarations:
223     return compat::getBeginLoc(stmt).isValid() && ignoreLocation( compat::getBeginLoc(stmt));
224 }
225 
ignoreLocation(TypeLoc tloc) const226 inline bool Plugin::ignoreLocation(TypeLoc tloc) const
227 {
228     // Invalid locations appear to happen at least with Clang 5.0.2 (but no longer with at least
229     // recent Clang 10 trunk):
230     auto const loc = tloc.getBeginLoc();
231     return loc.isValid() && ignoreLocation(loc);
232 }
233 
234 template< typename T >
createHelper(const InstantiationData & data)235 Plugin* Plugin::createHelper( const InstantiationData& data )
236  {
237     return new T( data );
238 }
239 
240 template< typename T >
241 inline
Registration(const char * optionName,bool byDefault)242 Plugin::Registration< T >::Registration( const char* optionName, bool byDefault )
243 {
244     registerPlugin( &T::template createHelper< T >, optionName, T::isPPCallback, T::isSharedPlugin, byDefault );
245 }
246 
247 inline
RewriteOptions(RewriteOption option)248 RewritePlugin::RewriteOptions::RewriteOptions( RewriteOption option )
249     : flags( option )
250 {
251     // Note that 'flags' stores also RemoveLineIfEmpty, it must be kept in sync with the base class.
252     if( flags & RewritePlugin::RemoveLineIfEmpty )
253         RemoveLineIfEmpty = true;
254 }
255 
256 inline
operator |(RewritePlugin::RewriteOption option1,RewritePlugin::RewriteOption option2)257 RewritePlugin::RewriteOption operator|( RewritePlugin::RewriteOption option1, RewritePlugin::RewriteOption option2 )
258 {
259     return static_cast< RewritePlugin::RewriteOption >( int( option1 ) | int( option2 ));
260 }
261 
262 template<typename Derived>
263 class FilteringRewritePlugin : public RecursiveASTVisitor<Derived>, public RewritePlugin
264 {
265 public:
FilteringRewritePlugin(const InstantiationData & data)266     explicit FilteringRewritePlugin( const InstantiationData& data ) : RewritePlugin(data) {}
267 
TraverseNamespaceDecl(NamespaceDecl * decl)268     bool TraverseNamespaceDecl(NamespaceDecl * decl) {
269         if (ignoreLocation(compat::getBeginLoc(decl)))
270             return true;
271         return RecursiveASTVisitor<Derived>::TraverseNamespaceDecl(decl);
272     }
273 };
274 
275 void normalizeDotDotInFilePath(std::string&);
276 
277 // Same as pathname.startswith(prefix), except on Windows, where pathname and
278 // prefix may also contain backslashes:
279 bool hasPathnamePrefix(StringRef pathname, StringRef prefix);
280 
281 // Same as pathname == other, except on Windows, where pathname and other may
282 // also contain backslashes:
283 bool isSamePathname(StringRef pathname, StringRef other);
284 
285 // It appears that, given a function declaration, there is no way to determine
286 // the language linkage of the function's type, only of the function's name
287 // (via FunctionDecl::isExternC); however, in a case like
288 //
289 //   extern "C" { static void f(); }
290 //
291 // the function's name does not have C language linkage while the function's
292 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
293 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
294 // "Language linkage of function type":
295 bool hasCLanguageLinkageType(FunctionDecl const * decl);
296 
297 // Count the number of times the base class is present in the subclass hierarchy
298 //
299 int derivedFromCount(clang::QualType subclassType, clang::QualType baseclassType);
300 int derivedFromCount(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
301 
302 // It looks like Clang wrongly implements DR 4
303 // (<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#4>) and treats
304 // a variable declared in an 'extern "..." {...}'-style linkage-specification as
305 // if it contained the 'extern' specifier:
306 bool hasExternalLinkage(VarDecl const * decl);
307 
308 bool isSmartPointerType(const Expr*);
309 
310 const Decl* getFunctionDeclContext(ASTContext& context, const Stmt* stmt);
311 
312 } // namespace
313 
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
315