summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/.gitignore1
-rw-r--r--compilerplugins/Makefile23
-rw-r--r--compilerplugins/Makefile-clang.mk73
-rw-r--r--compilerplugins/Makefile.mk32
-rw-r--r--compilerplugins/README46
-rw-r--r--compilerplugins/clang/bodynotinblock.cxx138
-rw-r--r--compilerplugins/clang/bodynotinblock.hxx35
-rw-r--r--compilerplugins/clang/compileplugin.cxx100
-rw-r--r--compilerplugins/clang/compileplugin.hxx47
-rw-r--r--compilerplugins/clang/unusedvariablecheck.cxx102
-rw-r--r--compilerplugins/clang/unusedvariablecheck.hxx31
11 files changed, 628 insertions, 0 deletions
diff --git a/compilerplugins/.gitignore b/compilerplugins/.gitignore
new file mode 100644
index 000000000000..b672fdeaf35b
--- /dev/null
+++ b/compilerplugins/.gitignore
@@ -0,0 +1 @@
+obj
diff --git a/compilerplugins/Makefile b/compilerplugins/Makefile
new file mode 100644
index 000000000000..4281c12da996
--- /dev/null
+++ b/compilerplugins/Makefile
@@ -0,0 +1,23 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+ifeq ($(SOLARENV),)
+ifeq ($(gb_Side),)
+gb_Side:=host
+endif
+include $(dir $(realpath $(lastword $(MAKEFILE_LIST))))../config_$(gb_Side).mk
+endif
+
+include $(SRCDIR)/compilerplugins/Makefile.mk
+
+all: build
+build: compilerplugins
+clean: compilerplugins-clean
+
+# vim: set noet sw=4 ts=4:
diff --git a/compilerplugins/Makefile-clang.mk b/compilerplugins/Makefile-clang.mk
new file mode 100644
index 000000000000..e9192fe4a1ac
--- /dev/null
+++ b/compilerplugins/Makefile-clang.mk
@@ -0,0 +1,73 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# Make sure variables in this Makefile do not conflict with other variables (e.g. from gbuild).
+
+# The list of source files.
+CLANGSRC=compileplugin.cxx \
+ bodynotinblock.cxx \
+ unusedvariablecheck.cxx \
+
+
+# You may occassionally want to override some of these
+
+# Compile flags ('make CLANGCXXFLAGS=-g' if you need to debug the plugin)
+CLANGCXXFLAGS=-O2 -Wall -g
+# The prefix where Clang resides, override to where Clang resides if using a source build.
+CLANGDIR=/usr
+# The build directory (different from CLANGDIR if using a Clang out-of-source build)
+CLANGBUILD=/usr
+
+# The uninteresting rest.
+
+# Clang headers require these.
+CLANGDEFS=-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fno-rtti
+# All include locations needed.
+CLANGINCLUDES=-I$(CLANGDIR)/include -I$(CLANGDIR)/tools/clang/include -I$(CLANGBUILD)/include -I$(CLANGBUILD)/tools/clang/include
+
+# Clang/LLVM libraries are intentionally not linked in, they are usually built as static libraries, which means the resulting
+# plugin would be big (even though the clang binary already includes it all) and it'd be necessary to explicitly specify
+# also all the dependency libraries.
+
+CLANGINDIR=$(SRCDIR)/compilerplugins/clang
+# Cannot use $(WORKDIR), the plugin should survive even 'make clean', otherwise the rebuilt
+# plugin will cause cache misses with ccache.
+CLANGOUTDIR=$(SRCDIR)/compilerplugins/obj
+
+compilerplugins: $(CLANGOUTDIR) $(CLANGOUTDIR)/compileplugin.so
+
+compilerplugins-clean:
+ rm -rf $(CLANGOUTDIR)
+
+$(CLANGOUTDIR):
+ mkdir -p $(CLANGOUTDIR)
+
+CLANGOBJS=
+
+define clangbuildsrc
+$(3): $(2) $(SRCDIR)/compilerplugins/Makefile-clang.mk $(CLANGOUTDIR)/clang-timestamp
+ @echo [build CXX] $(subst $(SRCDIR)/,,$(2))
+ $(CXX) $(CLANGCXXFLAGS) $(CLANGDEFS) $(CLANGINCLUDES) $(2) -fPIC -c -o $(3) -MMD -MT $(3) -MP -MF $(CLANGOUTDIR)/$(1).d
+
+-include $(CLANGOUTDIR)/$(1).d
+
+$(CLANGOUTDIR)/compileplugin.so: $(3)
+$(CLANGOUTDIR)/compileplugin.so: CLANGOBJS += $(3)
+endef
+
+$(foreach src, $(CLANGSRC), $(eval $(call clangbuildsrc,$(src),$(CLANGINDIR)/$(src),$(CLANGOUTDIR)/$(src:.cxx=.o))))
+
+$(CLANGOUTDIR)/compileplugin.so: $(CLANGOBJS)
+ @echo [build LNK] $(subst $(SRCDIR)/,,$@)
+ $(CXX) -shared $(CLANGOBJS) -o $@
+
+# Clang most probably doesn't maintain binary compatibility, so rebuild when clang changes.
+$(CLANGOUTDIR)/clang-timestamp: $(CLANGBUILD)/bin/clang
+ touch $@ -r $^
+
+# vim: set noet sw=4 ts=4:
diff --git a/compilerplugins/Makefile.mk b/compilerplugins/Makefile.mk
new file mode 100644
index 000000000000..c3b5290c9b2d
--- /dev/null
+++ b/compilerplugins/Makefile.mk
@@ -0,0 +1,32 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+.PHONY: compilerplugins compilerplugins-clean
+
+ifeq ($(COMPILER_PLUGINS),)
+
+# no support
+
+compilerplugins:
+compilerplugins-clean:
+compilerplugins.clean:
+
+else
+
+ifeq ($(COM_GCC_IS_CLANG),TRUE)
+
+include $(SRCDIR)/compilerplugins/Makefile-clang.mk
+
+compilerplugins.clean: compilerplugins-clean
+
+endif
+
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/compilerplugins/README b/compilerplugins/README
new file mode 100644
index 000000000000..50c7505dd72e
--- /dev/null
+++ b/compilerplugins/README
@@ -0,0 +1,46 @@
+Compiler plugins.
+
+== Overview ==
+
+This directory contains code for compiler plugins. These are used to perform
+additional actions during compilation (such as additional warnings) and
+also to perform mass code refactoring.
+
+Currently only the Clang compiler is supported (http://clang.llvm.org).
+
+== Usage ==
+
+Compiler plugins are enabled automatically by --enable-dbgutil if Clang headers
+are found or explicitly using --enable-compiler-plugins.
+
+
+== Functionality ==
+
+=== Compile plugin ===
+
+The compile plugin is used during normal compilation to perform additional checks.
+All warnings and errors are marked '[loplugin]' in the message.
+
+==== Unused variable check ====
+
+- unused parameter 'foo' [loplugin]
+- unused variable 'foo' [loplugin]
+
+Additional check for unused variables.
+
+==== Body of if/while/for not in {} ====
+
+- statement aligned as second statement in if/while/for body but not in a statement block [loplugin]
+
+Warn about the following construct:
+
+ if( a != 0 )
+ b = 2;
+ c = 3;
+
+Here either both statements should be inside {} or the second statement in indented wrong.
+
+
+== Code documentation / howtos ==
+
+TBD
diff --git a/compilerplugins/clang/bodynotinblock.cxx b/compilerplugins/clang/bodynotinblock.cxx
new file mode 100644
index 000000000000..f13eb9392357
--- /dev/null
+++ b/compilerplugins/clang/bodynotinblock.cxx
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bodynotinblock.hxx"
+
+#include <clang/Basic/SourceManager.h>
+
+namespace loplugin
+{
+
+/*
+Check for two statements that are both indented to look like a body of if/while/for
+but are not inside a compound statement and thus the second one is unrelated.
+*/
+
+BodyNotInBlock::BodyNotInBlock( ASTContext& context )
+ : Plugin( context )
+ {
+ }
+
+void BodyNotInBlock::run()
+ {
+ TraverseDecl( context.getTranslationUnitDecl());
+ }
+
+bool BodyNotInBlock::VisitFunctionDecl( FunctionDecl* declaration )
+ {
+ if( ignoreLocation( declaration ))
+ return true;
+ if( !declaration->doesThisDeclarationHaveABody())
+ return true;
+ StmtParents parents;
+ traverseStatement( declaration->getBody(), parents );
+ return true;
+ }
+
+void BodyNotInBlock::traverseStatement( const Stmt* stmt, StmtParents& parents )
+ {
+ parents.push_back( stmt );
+ for( ConstStmtIterator it = stmt->child_begin();
+ it != stmt->child_end();
+ ++it )
+ {
+ if( *it != NULL ) // some children can be apparently NULL
+ {
+ traverseStatement( *it, parents ); // substatements first
+ parents.push_back( *it );
+ if( const IfStmt* ifstmt = dyn_cast< IfStmt >( *it ))
+ {
+ checkBody( ifstmt->getThen(), parents, 0, ifstmt->getElse() != NULL );
+ checkBody( ifstmt->getElse(), parents, 0 );
+ }
+ else if( const WhileStmt* whilestmt = dyn_cast< WhileStmt >( *it ))
+ checkBody( whilestmt->getBody(), parents, 1 );
+ else if( const ForStmt* forstmt = dyn_cast< ForStmt >( *it ))
+ checkBody( forstmt->getBody(), parents, 2 );
+ else if( const CXXForRangeStmt* forstmt = dyn_cast< CXXForRangeStmt >( *it ))
+ checkBody( forstmt->getBody(), parents, 2 );
+ parents.pop_back();
+ }
+ }
+ assert( parents.back() == stmt );
+ parents.pop_back();
+ }
+
+void BodyNotInBlock::checkBody( const Stmt* body, const StmtParents& parents, int stmtType, bool dontGoUp )
+ {
+ if( body == NULL || parents.size() < 2 )
+ return;
+ // TODO: If the if/while/for comes from a macro expansion, ignore it completely for
+ // now. The code below could assume everything is in the same place (and thus also column)
+ // and give a false warning. Moreover some macros are rather lousily written and would
+ // result in poor formatting. To be evaluated later, maybe this could be handled
+ // including macro expansion.
+ if( parents.back()->getLocStart().isMacroID())
+ return;
+ if( dyn_cast< CompoundStmt >( body ))
+ return; // if body is a compound statement, then it is in {}
+ // Find the next statement (in source position) after 'body'.
+ for( int parent_pos = parents.size() - 2; // start from grandparent
+ parent_pos >= 0;
+ --parent_pos )
+ {
+ for( ConstStmtIterator it = parents[ parent_pos ]->child_begin();
+ it != parents[ parent_pos ]->child_end();
+ )
+ {
+ if( *it == parents[ parent_pos + 1 ] ) // found grand(grand...)parent
+ {
+ // get next statement after our (grand...)parent
+ ++it;
+ while( it != parents[ parent_pos ]->child_end() && *it == NULL )
+ ++it; // skip empty ones (missing 'else' bodies for example)
+ if( it != parents[ parent_pos ]->child_end())
+ {
+ bool invalid1, invalid2;
+ unsigned bodyColumn = context.getSourceManager()
+ .getPresumedColumnNumber( body->getLocStart(), &invalid1 );
+ unsigned nextStatementColumn = context.getSourceManager()
+ .getPresumedColumnNumber( (*it)->getLocStart(), &invalid2 );
+ if( invalid1 || invalid2 )
+ return;
+ if( bodyColumn == nextStatementColumn )
+ {
+ report( DiagnosticsEngine::Warning,
+ "statement aligned as second statement in %select{if|while|for}0 body but not in a statement block [loplugin]",
+ (*it)->getLocStart()) << stmtType;
+ report( DiagnosticsEngine::Note,
+ "%select{if|while|for}0 body statement is here [loplugin]",
+ body->getLocStart()) << stmtType;
+ }
+ return;
+ }
+ // else we need to go higher to find the next statement
+ }
+ else
+ ++it;
+ }
+ // If going up would mean leaving a {} block, stop, because the } should
+ // make it visible the two statements are not in the same body.
+ if( dyn_cast< CompoundStmt >( parents[ parent_pos ] ))
+ return;
+ // If the body to be checked is a body of an if statement that has also
+ // an else part, don't go up, the else is after the body and should make
+ // it clear the body does not continue there.
+ if( dontGoUp )
+ return;
+ }
+ }
+
+} // namespace
diff --git a/compilerplugins/clang/bodynotinblock.hxx b/compilerplugins/clang/bodynotinblock.hxx
new file mode 100644
index 000000000000..a2c47e6683cd
--- /dev/null
+++ b/compilerplugins/clang/bodynotinblock.hxx
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef BODYNOTINBLOCK_H
+#define BODYNOTINBLOCK_H
+
+#include "compileplugin.hxx"
+
+namespace loplugin
+{
+
+class BodyNotInBlock
+ : public RecursiveASTVisitor< BodyNotInBlock >
+ , public Plugin
+ {
+ public:
+ explicit BodyNotInBlock( ASTContext& context );
+ void run();
+ bool VisitFunctionDecl( FunctionDecl* declaration );
+ private:
+ typedef std::vector< const Stmt* > StmtParents;
+ void traverseStatement( const Stmt* stmt, StmtParents& parents );
+ void checkBody( const Stmt* body, const StmtParents& parents, int stmtType, bool dontGoUp = false );
+ };
+
+} // namespace
+
+#endif // BODYNOTINBLOCK_H
diff --git a/compilerplugins/clang/compileplugin.cxx b/compilerplugins/clang/compileplugin.cxx
new file mode 100644
index 000000000000..a61a3bab66a4
--- /dev/null
+++ b/compilerplugins/clang/compileplugin.cxx
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ *
+ */
+
+#include "compileplugin.hxx"
+
+#include <clang/AST/ASTConsumer.h>
+#include <clang/AST/ASTContext.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Frontend/FrontendAction.h>
+#include <clang/Frontend/FrontendPluginRegistry.h>
+#include <clang/Rewrite/Rewriter.h>
+
+#include "bodynotinblock.hxx"
+#include "unusedvariablecheck.hxx"
+
+using namespace clang;
+
+namespace loplugin
+{
+
+Plugin::Plugin( ASTContext& context )
+ : context( context )
+ {
+ }
+
+DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc )
+ {
+ DiagnosticsEngine& diag = context.getDiagnostics();
+#if 0
+ // Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason.
+ if( level == DiagnosticsEngine::Warning && diag.getWarningsAsErrors())
+ level = DiagnosticsEngine::Error;
+ if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal())
+ level = DiagnosticsEngine::Fatal;
+#endif
+ return diag.Report( loc, diag.getCustomDiagID( level, message ));
+ }
+
+bool Plugin::ignoreLocation( SourceLocation loc )
+ {
+ return context.getSourceManager().isInSystemHeader( context.getSourceManager().getExpansionLoc( loc ));
+ }
+
+/**
+ Class that manages all LO modules.
+*/
+class PluginHandler
+ : public ASTConsumer
+ {
+ public:
+ explicit PluginHandler( ASTContext& context )
+ : rewriter( context.getSourceManager(), context.getLangOpts())
+ , bodyNotInBlock( context )
+ , unusedVariableCheck( context )
+ {
+ }
+ virtual void HandleTranslationUnit( ASTContext& context )
+ {
+ if( context.getDiagnostics().hasErrorOccurred())
+ return;
+ bodyNotInBlock.run();
+ unusedVariableCheck.run();
+ // TODO also LO header files? or a subdir?
+ if( const RewriteBuffer* buf = rewriter.getRewriteBufferFor( context.getSourceManager().getMainFileID()))
+ buf->write( llvm::outs());
+ // TODO else write out the original file?
+ }
+ private:
+ Rewriter rewriter;
+ BodyNotInBlock bodyNotInBlock;
+ UnusedVariableCheck unusedVariableCheck;
+ };
+
+/**
+ The Clang plugin class, just forwards to PluginHandler.
+*/
+class LibreOfficeAction
+ : public PluginASTAction
+ {
+ public:
+ virtual ASTConsumer* CreateASTConsumer( CompilerInstance& Compiler, StringRef InFile )
+ {
+ return new PluginHandler( Compiler.getASTContext());
+ }
+ virtual bool ParseArgs( const CompilerInstance& CI, const std::vector< std::string >& args )
+ {
+ return true;
+ }
+ };
+
+} // namespace
+
+static FrontendPluginRegistry::Add< loplugin::LibreOfficeAction > X( "loplugin", "LibreOffice compile check plugin" );
diff --git a/compilerplugins/clang/compileplugin.hxx b/compilerplugins/clang/compileplugin.hxx
new file mode 100644
index 000000000000..6c854d1e0179
--- /dev/null
+++ b/compilerplugins/clang/compileplugin.hxx
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef COMPILEPLUGIN_H
+#define COMPILEPLUGIN_H
+
+#include <clang/AST/RecursiveASTVisitor.h>
+
+using namespace clang;
+
+namespace loplugin
+{
+
+class Plugin
+ {
+ public:
+ explicit Plugin( ASTContext& context );
+ protected:
+ DiagnosticBuilder report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc );
+ bool ignoreLocation( SourceLocation loc );
+ bool ignoreLocation( const Decl* decl );
+ bool ignoreLocation( const Stmt* stmt );
+ ASTContext& context;
+ };
+
+inline
+bool Plugin::ignoreLocation( const Decl* decl )
+ {
+ return ignoreLocation( decl->getLocation());
+ }
+
+inline
+bool Plugin::ignoreLocation( const Stmt* stmt )
+ {
+ return ignoreLocation( stmt->getLocStart());
+ }
+
+} // namespace
+
+#endif // COMPILEPLUGIN_H
diff --git a/compilerplugins/clang/unusedvariablecheck.cxx b/compilerplugins/clang/unusedvariablecheck.cxx
new file mode 100644
index 000000000000..3d9ca3de0513
--- /dev/null
+++ b/compilerplugins/clang/unusedvariablecheck.cxx
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ *
+ */
+
+#include "unusedvariablecheck.hxx"
+
+#include <clang/Basic/SourceManager.h>
+
+namespace loplugin
+{
+
+/*
+Check for unused classes where the compiler cannot decide (e.g. because of
+non-trivial or extern ctors) if a variable is unused if only its ctor/dtor
+are called and nothing else. For example std::vector is a class where
+the ctor may call further functions, but an unused std::string variable
+does nothing. On the other hand, std::auto_ptr instances are used
+for their dtors and so are not unused even if not otherwise accessed.
+
+Classes which are safe to be warned about need to be marked using
+SAL_WARN_UNUSED (see e.g. OUString). For external classes such as std::vector
+that cannot be edited there is a manual list below.
+*/
+
+UnusedVariableCheck::UnusedVariableCheck( ASTContext& context )
+ : Plugin( context )
+ {
+ }
+
+void UnusedVariableCheck::run()
+ {
+ TraverseDecl( context.getTranslationUnitDecl());
+ }
+
+bool UnusedVariableCheck::VisitNamedDecl( NamedDecl* declaration )
+ {
+ if( ignoreLocation( declaration ))
+ return true;
+ if( !isa< VarDecl >( declaration ))
+ return true;
+ const VarDecl* var = cast< VarDecl >( declaration );
+ if( var->isReferenced() || var->isUsed())
+ return true;
+ if( var->isDefinedOutsideFunctionOrMethod())
+ return true;
+ if( CXXRecordDecl* type = var->getType()->getAsCXXRecordDecl())
+ {
+ bool warn_unused = false;
+ if( type->hasAttrs())
+ {
+ // Clang currently has no support for custom attributes, but
+ // the annotate attribute comes close, so check for __attribute__((annotate("lo_warn_unused")))
+ for( specific_attr_iterator<AnnotateAttr> i = type->specific_attr_begin<AnnotateAttr>(),
+ e = type->specific_attr_end<AnnotateAttr>();
+ i != e;
+ ++i )
+ {
+ if( (*i)->getAnnotation() == "lo_warn_unused" )
+ {
+ warn_unused = true;
+ break;
+ }
+ }
+ }
+ if( !warn_unused )
+ {
+ std::string n = type->getQualifiedNameAsString();
+ // Check some common non-LO types.
+ if( n == "std::string" || n == "std::basic_string"
+ || n == "std::list" || n == "std::__debug::list"
+ || n == "std::vector" || n == "std::__debug::vector" )
+ warn_unused = true;
+ }
+ if( warn_unused )
+ {
+ if( const ParmVarDecl* param = dyn_cast< ParmVarDecl >( var ))
+ {
+ if( !param->getDeclName())
+ return true; // unnamed parameter -> unused
+ // If this declaration does not have a body, then the parameter is indeed not used,
+ // so ignore.
+ if( const FunctionDecl* func = dyn_cast_or_null< FunctionDecl >( param->getParentFunctionOrMethod()))
+ if( !func->doesThisDeclarationHaveABody())
+ return true;
+ report( DiagnosticsEngine::Warning, "unused parameter %0 [loplugin]",
+ var->getLocation()) << var->getDeclName();
+ }
+ else
+ report( DiagnosticsEngine::Warning, "unused variable %0 [loplugin]",
+ var->getLocation()) << var->getDeclName();
+ }
+ }
+ return true;
+ }
+
+} // namespace
diff --git a/compilerplugins/clang/unusedvariablecheck.hxx b/compilerplugins/clang/unusedvariablecheck.hxx
new file mode 100644
index 000000000000..21e0eabd03c0
--- /dev/null
+++ b/compilerplugins/clang/unusedvariablecheck.hxx
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef UNUSEDVARIABLECHECK_H
+#define UNUSEDVARIABLECHECK_H
+
+#include "compileplugin.hxx"
+
+namespace loplugin
+{
+
+class UnusedVariableCheck
+ : public RecursiveASTVisitor< UnusedVariableCheck >
+ , public Plugin
+ {
+ public:
+ explicit UnusedVariableCheck( ASTContext& context );
+ void run();
+ bool VisitNamedDecl( NamedDecl* declaration );
+ };
+
+} // namespace
+
+#endif // UNUSEDVARIABLECHECK_H