diff options
-rw-r--r-- | compilerplugins/Makefile-clang.mk | 2 | ||||
-rw-r--r-- | compilerplugins/README | 7 | ||||
-rw-r--r-- | compilerplugins/clang/compileplugin.cxx | 5 | ||||
-rw-r--r-- | compilerplugins/clang/unusedvariablecheck.cxx | 112 | ||||
-rw-r--r-- | compilerplugins/clang/unusedvariablecheck.hxx | 35 | ||||
-rw-r--r-- | sal/inc/sal/types.h | 20 |
6 files changed, 180 insertions, 1 deletions
diff --git a/compilerplugins/Makefile-clang.mk b/compilerplugins/Makefile-clang.mk index 1797e424ddb1..9b37fb9939c7 100644 --- a/compilerplugins/Makefile-clang.mk +++ b/compilerplugins/Makefile-clang.mk @@ -9,7 +9,7 @@ # Make sure variables in this Makefile do not conflict with other variables (e.g. from gbuild). # The list of source files. -CLANGSRC=compileplugin.cxx +CLANGSRC=compileplugin.cxx unusedvariablecheck.cxx # You may occassionally want to override some of these diff --git a/compilerplugins/README b/compilerplugins/README index 98ac70c85ef5..2430d40fb72e 100644 --- a/compilerplugins/README +++ b/compilerplugins/README @@ -21,6 +21,13 @@ are found or explicitly using --enable-compiler-plugins. 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. + == Code documentation / howtos == diff --git a/compilerplugins/clang/compileplugin.cxx b/compilerplugins/clang/compileplugin.cxx index fb2632e3d2cd..2b1647560117 100644 --- a/compilerplugins/clang/compileplugin.cxx +++ b/compilerplugins/clang/compileplugin.cxx @@ -17,6 +17,8 @@ #include <clang/Frontend/FrontendPluginRegistry.h> #include <clang/Rewrite/Rewriter.h> +#include "unusedvariablecheck.hxx" + using namespace clang; namespace loplugin @@ -31,12 +33,14 @@ class PluginHandler public: explicit PluginHandler( ASTContext& context ) : rewriter( context.getSourceManager(), context.getLangOpts()) + , unusedVariableCheck( context ) { } virtual void HandleTranslationUnit( ASTContext& context ) { if( context.getDiagnostics().hasErrorOccurred()) return; + unusedVariableCheck.run(); // TODO also LO header files? or a subdir? if( const RewriteBuffer* buf = rewriter.getRewriteBufferFor( context.getSourceManager().getMainFileID())) buf->write( llvm::outs()); @@ -44,6 +48,7 @@ class PluginHandler } private: Rewriter rewriter; + UnusedVariableCheck unusedVariableCheck; }; /** diff --git a/compilerplugins/clang/unusedvariablecheck.cxx b/compilerplugins/clang/unusedvariablecheck.cxx new file mode 100644 index 000000000000..029e637b578c --- /dev/null +++ b/compilerplugins/clang/unusedvariablecheck.cxx @@ -0,0 +1,112 @@ +/* + * 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> + +using namespace clang; + +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 ) + : context( context ) + { + } + +void UnusedVariableCheck::run() + { + TraverseDecl( context.getTranslationUnitDecl()); + } + +DiagnosticBuilder UnusedVariableCheck::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) + { + // Do some mappings (e.g. for -Werror) that clang does not do for custom messages for some reason. + DiagnosticsEngine& diag = context.getDiagnostics(); + if( level == DiagnosticsEngine::Warning && diag.getWarningsAsErrors()) + level = DiagnosticsEngine::Error; + if( level == DiagnosticsEngine::Error && diag.getErrorsAsFatal()) + level = DiagnosticsEngine::Fatal; + return diag.Report( loc, diag.getCustomDiagID( level, message )); + } + +bool UnusedVariableCheck::VisitNamedDecl( NamedDecl* declaration ) + { + // TODO also LO header files? or a subdir? + if( !context.getSourceManager().isFromMainFile( declaration->getLocStart())) + return true; + if( !isa< VarDecl >( declaration )) + return true; + const VarDecl* var = cast< VarDecl >( declaration ); + if( var->isReferenced() || var->isUsed()) + 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 this declaration does not have a body, then the parameter is indeed not used, + // so ignore. + if( const FunctionDecl* func = dyn_cast< FunctionDecl >( param->getParentFunctionOrMethod())) + if( !func->doesThisDeclarationHaveABody()) + return true; + report( DiagnosticsEngine::Warning, "unused parameter %0 [loplugin]", + var->getLocStart()) << var->getDeclName(); + } + else + report( DiagnosticsEngine::Warning, "unused variable %0 [loplugin]", + var->getLocStart()) << var->getDeclName(); + } + } + return true; + } + +} // namespace diff --git a/compilerplugins/clang/unusedvariablecheck.hxx b/compilerplugins/clang/unusedvariablecheck.hxx new file mode 100644 index 000000000000..c49532a86414 --- /dev/null +++ b/compilerplugins/clang/unusedvariablecheck.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 UNUSEDVARIABLECHECK_H +#define UNUSEDVARIABLECHECK_H + +#include <clang/AST/RecursiveASTVisitor.h> + +using namespace clang; + +namespace loplugin +{ + +class UnusedVariableCheck + : public RecursiveASTVisitor< UnusedVariableCheck > + { + public: + explicit UnusedVariableCheck( ASTContext& context ); + void run(); + bool VisitNamedDecl( NamedDecl* declaration ); + private: + DiagnosticBuilder report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ); + ASTContext& context; + }; + +} // namespace + +#endif // UNUSEDVARIABLECHECK_H diff --git a/sal/inc/sal/types.h b/sal/inc/sal/types.h index f97ecc701438..0baf3d19cb1c 100644 --- a/sal/inc/sal/types.h +++ b/sal/inc/sal/types.h @@ -489,6 +489,26 @@ template< typename T1, typename T2 > inline T1 static_int_cast(T2 n) { #endif #endif +/** + + Annotate classes where a compiler should warn if an instance is unused. + + The compiler cannot warn about unused instances if they have non-trivial + or external constructors or destructors. Classes marked with SAL_WARN_UNUSED + will be warned about. + + Currently implemented by a Clang compiler plugin. + + @since LibreOffice 3.7 + +*/ + +#if defined __clang__ +#define SAL_WARN_UNUSED __attribute__((annotate("lo_warn_unused"))) +#else +#define SAL_WARN_UNUSED +#endif + #endif /*_SAL_TYPES_H_ */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |