summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/plugin.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/plugin.hxx')
-rw-r--r--compilerplugins/clang/plugin.hxx321
1 files changed, 321 insertions, 0 deletions
diff --git a/compilerplugins/clang/plugin.hxx b/compilerplugins/clang/plugin.hxx
new file mode 100644
index 0000000000..7980e79f7b
--- /dev/null
+++ b/compilerplugins/clang/plugin.hxx
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * Based on LLVM/Clang.
+ *
+ * This file is distributed under the University of Illinois Open Source
+ * License. See LICENSE.TXT for details.
+ *
+ */
+
+#pragma once
+
+#include <clang/AST/ASTContext.h>
+#include <clang/AST/RecursiveASTVisitor.h>
+#include <clang/Basic/FileManager.h>
+#include <clang/Basic/SourceManager.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Lex/Preprocessor.h>
+#include <unordered_map>
+#include <vector>
+
+#include <clang/Rewrite/Core/Rewriter.h>
+
+#include "pluginhandler.hxx"
+
+using namespace clang;
+using namespace llvm;
+
+namespace loplugin
+{
+
+struct InstantiationData
+{
+ const char* name;
+ PluginHandler& handler;
+ CompilerInstance& compiler;
+ Rewriter* rewriter;
+};
+
+/**
+ Base class for plugins.
+
+ If you want to create a non-rewriter action, inherit from this class. Remember to also
+ use Plugin::Registration.
+*/
+class Plugin
+{
+public:
+ explicit Plugin( const InstantiationData& data );
+ virtual ~Plugin() {}
+ // The main function of the plugin.
+ // Note that for shared plugins, its functionality must be split into preRun() and postRun(),
+ // see sharedvisitor/generator.cxx .
+ virtual void run() = 0;
+ // Should be called from run() before TraverseDecl().
+ // If returns false, run() should not do anything.
+ virtual bool preRun() { return true; }
+ virtual void postRun() {}
+ template< typename T > class Registration;
+ // Returns location right after the end of the token that starts at the given location.
+ SourceLocation locationAfterToken( SourceLocation location );
+ virtual bool setSharedPlugin( Plugin* /*plugin*/, const char* /*name*/ ) { return false; }
+ enum { isPPCallback = false };
+ enum { isSharedPlugin = false };
+protected:
+ DiagnosticBuilder report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc = SourceLocation()) const;
+ // Look at the line containing location and the previous line for any comments that overlap
+ // either of those two lines and that contain "[-loplugin:<name>]" (with the name of this
+ // plugin), indicating that warnings from this plugin should be suppressed here:
+ bool suppressWarningAt(SourceLocation location) const;
+ bool ignoreLocation( SourceLocation loc ) const
+ { return handler.ignoreLocation(loc); }
+ bool ignoreLocation( const Decl* decl ) const;
+ bool ignoreLocation( const Stmt* stmt ) const;
+ bool ignoreLocation(TypeLoc tloc) const;
+ CompilerInstance& compiler;
+ PluginHandler& handler;
+ /**
+ Returns the parent of the given AST node. Clang's internal AST representation doesn't provide this information,
+ it can only provide children, but getting the parent is often useful for inspecting a part of the AST.
+ */
+ const Stmt* getParentStmt( const Stmt* stmt );
+ Stmt* getParentStmt( Stmt* stmt );
+ const FunctionDecl* getParentFunctionDecl( const Stmt* stmt );
+
+ /**
+ Get filename of the given location. Use this instead of SourceManager::getFilename(), as that one
+ does not handle source with expanded #inline directives (used by Icecream for remote compilation).
+ */
+ StringRef getFilenameOfLocation(SourceLocation spellingLocation) const;
+ /**
+ Checks if the location is inside a UNO file, more specifically, if it forms part of the URE stable interface,
+ which is not allowed to be changed.
+ */
+ bool isInUnoIncludeFile(SourceLocation spellingLocation) const;
+ bool isInUnoIncludeFile(const FunctionDecl*) const;
+
+ bool isDebugMode() const { return handler.isDebugMode(); }
+
+ static bool isUnitTestMode();
+
+ bool containsPreprocessingConditionalInclusion(SourceRange range);
+
+ bool containsComment(SourceRange range);
+
+ enum class IdenticalDefaultArgumentsResult { No, Yes, Maybe };
+ IdenticalDefaultArgumentsResult checkIdenticalDefaultArguments(
+ Expr const * argument1, Expr const * argument2);
+
+private:
+ static void registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName,
+ bool isPPCallback, bool isSharedPlugin, bool byDefault );
+ template< typename T > static Plugin* createHelper( const InstantiationData& data );
+ bool evaluate(const Expr* expr, APSInt& x);
+
+ enum { isRewriter = false };
+ const char* name;
+};
+
+template<typename Derived>
+class FilteringPlugin : public RecursiveASTVisitor<Derived>, public Plugin
+{
+public:
+ explicit FilteringPlugin( const InstantiationData& data ) : Plugin(data) {}
+
+ bool TraverseNamespaceDecl(NamespaceDecl * decl) {
+ if (ignoreLocation(decl->getBeginLoc()))
+ return true;
+ return RecursiveASTVisitor<Derived>::TraverseNamespaceDecl(decl);
+ }
+};
+
+/**
+ Base class for rewriter plugins.
+
+ Remember to also use Plugin::Registration.
+*/
+class RewritePlugin
+ : public Plugin
+{
+public:
+ explicit RewritePlugin( const InstantiationData& data );
+protected:
+ enum RewriteOption
+ {
+ // This enum allows passing just 'RemoveLineIfEmpty' to functions below.
+ // If the resulting line would be completely empty, it'll be removed.
+ RemoveLineIfEmpty = 1 << 0,
+ // Use this to remove the declaration/statement as a whole, i.e. all whitespace before the statement
+ // and the trailing semicolon (is not part of the AST element range itself).
+ // The trailing semicolon must be present.
+ RemoveWholeStatement = 1 << 1,
+ // Removes also all whitespace preceding and following the expression (completely, so that
+ // the preceding and following tokens would be right next to each other, follow with insertText( " " )
+ // if this is not wanted). Despite the name, indentation whitespace is not removed.
+ RemoveAllWhitespace = 1 << 2
+ };
+ struct RewriteOptions
+ : public Rewriter::RewriteOptions
+ {
+ RewriteOptions() : flags( 0 ) {}
+ explicit RewriteOptions( RewriteOption option );
+ const int flags;
+ };
+ // syntactic sugar to be able to write 'RemoveLineIfEmpty | RemoveWholeStatement'
+ friend RewriteOption operator|( RewriteOption option1, RewriteOption option2 );
+ // These following insert/remove/replaceText functions map to functions
+ // in clang::Rewriter, with these differences:
+ // - they (more intuitively) return false on failure rather than true
+ // - they report a warning when the change cannot be done
+ // - There are more options for easier removal of surroundings of a statement/expression.
+ bool insertText( SourceLocation Loc, StringRef Str,
+ bool InsertAfter = true, bool indentNewLines = false );
+ bool insertTextAfter( SourceLocation Loc, StringRef Str );
+ bool insertTextAfterToken( SourceLocation Loc, StringRef Str );
+ bool insertTextBefore( SourceLocation Loc, StringRef Str );
+ bool removeText( SourceLocation Start, unsigned Length, RewriteOptions opts = RewriteOptions());
+ bool removeText( CharSourceRange range, RewriteOptions opts = RewriteOptions());
+ bool removeText( SourceRange range, RewriteOptions opts = RewriteOptions());
+ bool replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr );
+ bool replaceText( SourceRange range, StringRef NewStr );
+ bool replaceText( SourceRange range, SourceRange replacementRange );
+ Rewriter* rewriter;
+private:
+ template< typename T > friend class Plugin::Registration;
+ enum { isRewriter = true };
+ bool wouldRewriteWorkdir(SourceLocation loc);
+ bool reportEditFailure( SourceLocation loc );
+ bool adjustRangeForOptions( CharSourceRange* range, RewriteOptions options );
+};
+
+/**
+ Plugin registration helper.
+
+ If you create a new helper class, create also an instance of this class to automatically register it.
+ The passed argument is name of the plugin, used for explicitly invoking rewriter plugins
+ (it is ignored for non-rewriter plugins).
+
+ @code
+ static Plugin::Registration< NameOfClass > X( "nameofclass" );
+ @endcode
+*/
+template< typename T >
+class Plugin::Registration
+{
+public:
+ Registration( const char* optionName, bool byDefault = !T::isRewriter );
+};
+
+class RegistrationCreate
+{
+public:
+ template< typename T, bool > static T* create( const InstantiationData& data );
+};
+
+inline
+bool Plugin::ignoreLocation( const Decl* decl ) const
+{
+ return ignoreLocation( decl->getLocation());
+}
+
+inline
+bool Plugin::ignoreLocation( const Stmt* stmt ) const
+{
+ // Invalid location can happen at least for ImplicitCastExpr of
+ // ImplicitParam 'self' in Objective C method declarations:
+ return stmt->getBeginLoc().isValid() && ignoreLocation( stmt->getBeginLoc());
+}
+
+inline bool Plugin::ignoreLocation(TypeLoc tloc) const
+{
+ return ignoreLocation(tloc.getBeginLoc());
+}
+
+template< typename T >
+Plugin* Plugin::createHelper( const InstantiationData& data )
+ {
+ return new T( data );
+}
+
+template< typename T >
+inline
+Plugin::Registration< T >::Registration( const char* optionName, bool byDefault )
+{
+ registerPlugin( &T::template createHelper< T >, optionName, T::isPPCallback, T::isSharedPlugin, byDefault );
+}
+
+inline
+RewritePlugin::RewriteOptions::RewriteOptions( RewriteOption option )
+ : flags( option )
+{
+ // Note that 'flags' stores also RemoveLineIfEmpty, it must be kept in sync with the base class.
+ if( flags & RewritePlugin::RemoveLineIfEmpty )
+ RemoveLineIfEmpty = true;
+}
+
+inline
+RewritePlugin::RewriteOption operator|( RewritePlugin::RewriteOption option1, RewritePlugin::RewriteOption option2 )
+{
+ return static_cast< RewritePlugin::RewriteOption >( int( option1 ) | int( option2 ));
+}
+
+template<typename Derived>
+class FilteringRewritePlugin : public RecursiveASTVisitor<Derived>, public RewritePlugin
+{
+public:
+ explicit FilteringRewritePlugin( const InstantiationData& data ) : RewritePlugin(data) {}
+
+ bool TraverseNamespaceDecl(NamespaceDecl * decl) {
+ if (ignoreLocation(decl->getBeginLoc()))
+ return true;
+ return RecursiveASTVisitor<Derived>::TraverseNamespaceDecl(decl);
+ }
+};
+
+void normalizeDotDotInFilePath(std::string&);
+
+// Same as pathname.startswith(prefix), except on Windows, where pathname and
+// prefix may also contain backslashes:
+bool hasPathnamePrefix(StringRef pathname, StringRef prefix);
+
+// Same as pathname == other, except on Windows, where pathname and other may
+// also contain backslashes:
+bool isSamePathname(StringRef pathname, StringRef other);
+
+// Check whether fullPathname is either SRCDIR/include/includePathname or
+// SDKDIR/include/includePathname:
+bool isSameUnoIncludePathname(StringRef fullPathname, StringRef includePathname);
+
+// It appears that, given a function declaration, there is no way to determine
+// the language linkage of the function's type, only of the function's name
+// (via FunctionDecl::isExternC); however, in a case like
+//
+// extern "C" { static void f(); }
+//
+// the function's name does not have C language linkage while the function's
+// type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
+// 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
+// "Language linkage of function type":
+bool hasCLanguageLinkageType(FunctionDecl const * decl);
+
+// Count the number of times the base class is present in the subclass hierarchy
+//
+int derivedFromCount(clang::QualType subclassType, clang::QualType baseclassType);
+int derivedFromCount(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
+
+// It looks like Clang wrongly implements DR 4
+// (<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#4>) and treats
+// a variable declared in an 'extern "..." {...}'-style linkage-specification as
+// if it contained the 'extern' specifier:
+bool hasExternalLinkage(VarDecl const * decl);
+
+bool isSmartPointerType(const Expr*);
+bool isSmartPointerType(clang::QualType);
+
+const Decl* getFunctionDeclContext(ASTContext& context, const Stmt* stmt);
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */