From 9263b101c39172cbcf04189c515bca80cb15f3aa Mon Sep 17 00:00:00 2001 From: Stephan Bergmann Date: Tue, 1 Jul 2014 17:38:02 +0200 Subject: Activate the "suspicious cast to sal_Bool" parts of loplugin:salbool Change-Id: I78a368ef2899b2462251b45a327fc7b1f31fe764 --- compilerplugins/clang/salbool.cxx | 546 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 546 insertions(+) create mode 100644 compilerplugins/clang/salbool.cxx (limited to 'compilerplugins/clang/salbool.cxx') diff --git a/compilerplugins/clang/salbool.cxx b/compilerplugins/clang/salbool.cxx new file mode 100644 index 000000000000..a3476be2d8de --- /dev/null +++ b/compilerplugins/clang/salbool.cxx @@ -0,0 +1,546 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include +#include +#include +#include +#include + +#include "clang/AST/Attr.h" + +#include "compat.hxx" +#include "plugin.hxx" + +namespace { + +bool isSalBool(QualType type) { + TypedefType const * t = type->getAs(); + return t != nullptr && t->getDecl()->getNameAsString() == "sal_Bool"; +} + +// 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. +// "Language linkage of function type": +bool hasCLanguageLinkageType(FunctionDecl const * decl) { + assert(decl != nullptr); + if (decl->isExternC()) { + return true; + } +#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3 + if (decl->isInExternCContext()) { + return true; + } +#else + if (decl->getCanonicalDecl()->getDeclContext()->isExternCContext()) { + return true; + } +#endif + return false; +} + +bool canOverrideTemplateArgumentMember(CXXMethodDecl const * decl) { + CXXRecordDecl const * r = dyn_cast(decl->getDeclContext()); + assert(r != nullptr); + for (auto b = r->bases_begin(); b != r->bases_end(); ++b) { + if (b->getType()->isTemplateTypeParmType()) { + return true; + } + } + return false; +} + +//TODO: current implementation is not at all general, just tests what we +// encounter in practice: +bool hasBoolOverload(FunctionDecl const * decl) { + if (isa(decl)) { + unsigned n = decl->getNumParams(); + CXXRecordDecl const * r = dyn_cast( + decl->getDeclContext()); + assert(r != nullptr); + for (auto m = r->method_begin(); m != r->method_end(); ++m) { + if (m->getDeclName() == decl->getDeclName() + && m->getNumParams() == n) + { + bool hasSB = false; + for (unsigned i = 0; i != n; ++i) { + QualType t1 { decl->getParamDecl(i)->getType() }; + bool isSB = isSalBool(t1); + bool isSBRef = !isSB && t1->isReferenceType() + && isSalBool(t1.getNonReferenceType()); + QualType t2 { m->getParamDecl(i)->getType() }; + if (!(isSB + ? t2->isBooleanType() + : isSBRef + ? (t2->isReferenceType() + && t2.getNonReferenceType()->isBooleanType()) + : t2 == t1)) + { + goto next; + } + hasSB |= isSB || isSBRef; + } + return hasSB; + // cheaply protect against the case where decl would have no + // sal_Bool parameters at all and would match itself + next:; + } + } + } + return false; +} + +class SalBool: + public RecursiveASTVisitor, public loplugin::RewritePlugin +{ +public: + explicit SalBool(InstantiationData const & data): + RewritePlugin(data), + fullMode_(std::getenv("loplugin:salbool") != nullptr) + {} + + virtual void run() override; + + bool VisitUnaryAddrOf(UnaryOperator const * op); + + bool VisitCStyleCastExpr(CStyleCastExpr * expr); + + bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr); + + bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr); + + bool WalkUpFromParmVarDecl(ParmVarDecl const * decl); + bool VisitParmVarDecl(ParmVarDecl const * decl); + + bool WalkUpFromVarDecl(VarDecl const * decl); + bool VisitVarDecl(VarDecl const * decl); + + bool WalkUpFromFieldDecl(FieldDecl const * decl); + bool VisitFieldDecl(FieldDecl const * decl); + + bool WalkUpFromFunctionDecl(FunctionDecl const * decl); + bool VisitFunctionDecl(FunctionDecl const * decl); + + bool VisitValueDecl(ValueDecl const * decl); + +private: + bool isInSpecialMainFile(SourceLocation spellingLocation) const; + + bool isMacroBodyExpansion(SourceLocation location) const; + + bool rewrite(SourceLocation location); + + bool fullMode_; + std::set varDecls_; +}; + +void SalBool::run() { + if (compiler.getLangOpts().CPlusPlus) { + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); + for (auto decl: varDecls_) { + SourceLocation loc { decl->getLocStart() }; + TypeSourceInfo * tsi = decl->getTypeSourceInfo(); + if (tsi != nullptr) { + SourceLocation l { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getBeginLoc()) }; + SourceLocation end { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getEndLoc()) }; + assert(l.isFileID() && end.isFileID()); + if (l == end + || compiler.getSourceManager().isBeforeInTranslationUnit( + l, end)) + { + for (;;) { + unsigned n = Lexer::MeasureTokenLength( + l, compiler.getSourceManager(), + compiler.getLangOpts()); + std::string s { + compiler.getSourceManager().getCharacterData(l), + n }; + if (s == "sal_Bool") { + loc = l; + break; + } + if (l == end) { + break; + } + l = l.getLocWithOffset(std::max(n, 1)); + } + } + } + if (fullMode_ && !rewrite(loc)) { + report( + DiagnosticsEngine::Warning, + "VarDecl, use \"bool\" instead of \"sal_Bool\"", loc) + << decl->getSourceRange(); + } + } + } +} + +bool SalBool::VisitUnaryAddrOf(UnaryOperator const * op) { + Expr const * e1 = op->getSubExpr()->IgnoreParenCasts(); + if (isSalBool(e1->getType())) { + DeclRefExpr const * e2 = dyn_cast(e1); + if (e2 != nullptr) { + VarDecl const * d = dyn_cast(e2->getDecl()); + if (d != nullptr) { + varDecls_.erase(d); + } + } + } + return true; +} + +bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) { + if (ignoreLocation(expr)) { + return true; + } + if (isSalBool(expr->getType())) { + SourceLocation loc { expr->getLocStart() }; + while (compiler.getSourceManager().isMacroArgExpansion(loc)) { + loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc); + } + if (isMacroBodyExpansion(loc)) { + StringRef name { Lexer::getImmediateMacroName( + loc, compiler.getSourceManager(), compiler.getLangOpts()) }; + if (name == "sal_False" || name == "sal_True") { + return true; + } + } + report( + DiagnosticsEngine::Warning, + "CStyleCastExpr, suspicious cast from %0 to %1", + expr->getLocStart()) + << expr->getSubExpr()->IgnoreParenImpCasts()->getType() + << expr->getType() << expr->getSourceRange(); + } + return true; +} + +bool SalBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) { + if (ignoreLocation(expr)) { + return true; + } + if (isSalBool(expr->getType())) { + report( + DiagnosticsEngine::Warning, + "CStyleCastExpr, suspicious cast from %0 to %1", + expr->getLocStart()) + << expr->getSubExpr()->IgnoreParenImpCasts()->getType() + << expr->getType() << expr->getSourceRange(); + } + return true; +} + +bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) { + if (ignoreLocation(expr)) { + return true; + } + if (isSalBool(expr->getType())) { + report( + DiagnosticsEngine::Warning, + "CStyleCastExpr, suspicious cast from %0 to %1", + expr->getLocStart()) + << expr->getSubExpr()->IgnoreParenImpCasts()->getType() + << expr->getType() << expr->getSourceRange(); + } + return true; +} + +bool SalBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) { + return VisitParmVarDecl(decl); +} + +bool SalBool::VisitParmVarDecl(ParmVarDecl const * decl) { + if (ignoreLocation(decl)) { + return true; + } + if (isSalBool(decl->getType().getNonReferenceType())) { + FunctionDecl const * f = dyn_cast(decl->getDeclContext()); + if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool ); + f = f->getCanonicalDecl(); + if (!(hasCLanguageLinkageType(f) + || (isInUnoIncludeFile( + compiler.getSourceManager().getSpellingLoc( + f->getNameInfo().getLoc())) + && (!f->isInlined() || f->hasAttr() + || decl->getType()->isReferenceType() + || hasBoolOverload(f))) + || f->isDeleted())) + { + CXXMethodDecl const * m = dyn_cast(f); + //XXX: canOverrideTemplateArgumentMember(m) does not require + // !m->isVirtual() + if ((m == nullptr || !m->isVirtual())) + { + SourceLocation loc { decl->getLocStart() }; + TypeSourceInfo * tsi = decl->getTypeSourceInfo(); + if (tsi != nullptr) { + SourceLocation l { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getBeginLoc()) }; + SourceLocation end { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getEndLoc()) }; + assert(l.isFileID() && end.isFileID()); + if (l == end + || (compiler.getSourceManager() + .isBeforeInTranslationUnit(l, end))) + { + for (;;) { + unsigned n = Lexer::MeasureTokenLength( + l, compiler.getSourceManager(), + compiler.getLangOpts()); + std::string s { + compiler.getSourceManager().getCharacterData(l), + n }; + if (s == "sal_Bool") { + loc = l; + break; + } + if (l == end) { + break; + } + l = l.getLocWithOffset(std::max(n, 1)); + } + } + } + // Only rewrite declarations in include files if a + // definition is also seen, to avoid compilation of a + // definition (in a main file only processed later) to fail + // with a "mismatch" error before the rewriter had a chance + // to act upon the definition (but use the heuristic of + // assuming pure virtual functions do not have definitions): + if (fullMode_ + && !((compat::isInMainFile( + compiler.getSourceManager(), + compiler.getSourceManager().getSpellingLoc( + dyn_cast( + decl->getDeclContext()) + ->getNameInfo().getLoc())) + || f->isDefined() || f->isPure()) + && rewrite(loc))) + { + report( + DiagnosticsEngine::Warning, + "ParmVarDecl, use \"bool\" instead of \"sal_Bool\"", + loc) + << decl->getSourceRange(); + } + } + } + } + } + return true; +} + +bool SalBool::WalkUpFromVarDecl(VarDecl const * decl) { + return VisitVarDecl(decl); +} + +bool SalBool::VisitVarDecl(VarDecl const * decl) { + if (ignoreLocation(decl)) { + return true; + } + if (!decl->isExternC() && isSalBool(decl->getType()) + && !isInSpecialMainFile( + compiler.getSourceManager().getSpellingLoc(decl->getLocStart()))) + { + varDecls_.insert(decl); + } + return true; +} + +bool SalBool::WalkUpFromFieldDecl(FieldDecl const * decl) { + return VisitFieldDecl(decl); +} + +bool SalBool::VisitFieldDecl(FieldDecl const * decl) { + if (ignoreLocation(decl)) { + return true; + } + if (isSalBool(decl->getType())) { + TagDecl const * td = dyn_cast(decl->getDeclContext()); + assert(td != nullptr); + if (!(((td->isStruct() || td->isUnion()) + && compat::isExternCContext(*td)) + || isInUnoIncludeFile( + compiler.getSourceManager().getSpellingLoc( + decl->getLocation())))) + { + SourceLocation loc { decl->getLocStart() }; + TypeSourceInfo * tsi = decl->getTypeSourceInfo(); + if (tsi != nullptr) { + SourceLocation l { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getBeginLoc()) }; + SourceLocation end { + compiler.getSourceManager().getExpansionLoc( + tsi->getTypeLoc().getEndLoc()) }; + assert(l.isFileID() && end.isFileID()); + if (l == end + || compiler.getSourceManager().isBeforeInTranslationUnit( + l, end)) + { + for (;;) { + unsigned n = Lexer::MeasureTokenLength( + l, compiler.getSourceManager(), + compiler.getLangOpts()); + std::string s { + compiler.getSourceManager().getCharacterData(l), + n }; + if (s == "sal_Bool") { + loc = l; + break; + } + if (l == end) { + break; + } + l = l.getLocWithOffset(std::max(n, 1)); + } + } + } + if (fullMode_ && !rewrite(loc)) { + report( + DiagnosticsEngine::Warning, + "FieldDecl, use \"bool\" instead of \"sal_Bool\"", loc) + << decl->getSourceRange(); + } + } + } + return true; +} + +bool SalBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) { + return VisitFunctionDecl(decl); +} + +bool SalBool::VisitFunctionDecl(FunctionDecl const * decl) { + if (ignoreLocation(decl)) { + return true; + } + if (isSalBool(compat::getReturnType(*decl).getNonReferenceType())) { + FunctionDecl const * f = decl->getCanonicalDecl(); + CXXMethodDecl const * m = dyn_cast(f); + //XXX: canOverrideTemplateArgumentMember(m) does not require + // !m->isVirtual() + if ((m == nullptr || !m->isVirtual() + || (m->size_overridden_methods() == 0 + && !canOverrideTemplateArgumentMember(m))) + && !(hasCLanguageLinkageType(f) + || (isInUnoIncludeFile( + compiler.getSourceManager().getSpellingLoc( + f->getNameInfo().getLoc())) + && (!f->isInlined() || f->hasAttr())))) + { + SourceLocation loc { decl->getLocStart() }; + SourceLocation l { compiler.getSourceManager().getExpansionLoc( + loc) }; + SourceLocation end { compiler.getSourceManager().getExpansionLoc( + decl->getNameInfo().getLoc()) }; + assert(l.isFileID() && end.isFileID()); + if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) { + while (l != end) { + unsigned n = Lexer::MeasureTokenLength( + l, compiler.getSourceManager(), compiler.getLangOpts()); + std::string s { + compiler.getSourceManager().getCharacterData(l), n }; + if (s == "sal_Bool") { + loc = l; + break; + } + l = l.getLocWithOffset(std::max(n, 1)); + } + } + // Only rewrite declarations in include files if a definition is + // also seen, to avoid compilation of a definition (in a main file + // only processed later) to fail with a "mismatch" error before the + // rewriter had a chance to act upon the definition (but use the + // heuristic of assuming pure virtual functions do not have + // definitions): + if (fullMode_ + && !((compat::isInMainFile( + compiler.getSourceManager(), + compiler.getSourceManager().getSpellingLoc( + decl->getNameInfo().getLoc())) + || f->isDefined() || f->isPure()) + && rewrite(loc))) + { + report( + DiagnosticsEngine::Warning, + "use \"bool\" instead of \"sal_Bool\" as return type", loc) + << decl->getSourceRange(); + } + } + } + return true; +} + +bool SalBool::VisitValueDecl(ValueDecl const * decl) { + if (ignoreLocation(decl)) { + return true; + } + if (fullMode_ && isSalBool(decl->getType()) + && !rewrite(decl->getLocStart())) + { + report( + DiagnosticsEngine::Warning, + "ValueDecl, use \"bool\" instead of \"sal_Bool\"", + decl->getLocStart()) + << decl->getSourceRange(); + } + return true; +} + +bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation) const { + return compat::isInMainFile(compiler.getSourceManager(), spellingLocation) + && (compiler.getSourceManager().getFilename(spellingLocation) + == SRCDIR "/cppu/qa/test_any.cxx"); +} + +bool SalBool::isMacroBodyExpansion(SourceLocation location) const { +#if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3 + return compiler.getSourceManager().isMacroBodyExpansion(location); +#else + return location.isMacroID() + && !compiler.getSourceManager().isMacroArgExpansion(location); +#endif +} + +bool SalBool::rewrite(SourceLocation location) { + if (rewriter != nullptr) { + //TODO: "::sal_Bool" -> "bool", not "::bool" + SourceLocation loc { compiler.getSourceManager().getExpansionLoc( + location) }; + unsigned n = Lexer::MeasureTokenLength( + loc, compiler.getSourceManager(), compiler.getLangOpts()); + if (std::string(compiler.getSourceManager().getCharacterData(loc), n) + == "sal_Bool") + { + return replaceText(loc, n, "bool"); + } + } + return false; +} + +loplugin::Plugin::Registration X("salbool", true); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit