1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  
     | 
    
      /* -*- 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.
 *
 */
#include "plugin.hxx"
#include <clang/AST/ASTContext.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Lex/Lexer.h>
namespace loplugin
{
/*
This is a compile check.
Check for pointer-to-bool conversions (that are sadly implicit) which are unwanted
and potentially mistakes.
So far the only places that are checked are passing arguments to functions, as those
could easily choose a different overload.
The original idea was that to follow the explicit bool feature from C++11, where
the only conversions that would be considered safe are in conditions (which
in turn means also in ||, && and ! operators) and places where it's considered
unlikely for it to be a problem (or rather, less of a problem
than explicitly avoiding the warning in the code). The code for this is currently
commented out (there are a couple of places such as 'bool foo = returns_pointer();'
that would need modification), possibly enable those later.
*/
class PointerToBool
    : public RecursiveASTVisitor< PointerToBool >
    , public Plugin
    {
    public:
        explicit PointerToBool( const InstantiationData& data );
        void run();
        bool VisitImplicitCastExpr( const ImplicitCastExpr* expr );
    private:
        bool ignoreConversion( const Stmt* stmt );
    };
PointerToBool::PointerToBool( const InstantiationData& data )
    : Plugin( data )
    {
    }
void PointerToBool::run()
    {
    TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
    }
bool PointerToBool::VisitImplicitCastExpr( const ImplicitCastExpr* expr )
    {
    if( ignoreLocation( expr ))
        return true;
    // Warning about CK_MemberPointerToBoolean would mean warning about
    // cases there the 'safe bool' idiom is used, so give that such
    // a conversion is otherwise unlikely anyway, it's probably better
    // not to warn here at all (at least as long as the 'explicit bool'
    // from C++11 is not in use).
    if( expr->getCastKind() == CK_PointerToBoolean )
        {
        if( ignoreConversion( expr ))
            return true;
        report( DiagnosticsEngine::Warning,
            "pointer %0 implicitly converted to bool", expr->getLocStart())
            << expr->getSubExpr()->getType() << expr->getSourceRange();
        SourceLocation endOfExpression = locationAfterToken( expr->getLocEnd());
        report( DiagnosticsEngine::Note,
            "explicitly compare to null pointer to silence this warning", endOfExpression )
            << FixItHint::CreateInsertion( endOfExpression, " != NULL" );
        }
    return true;
    }
bool PointerToBool::ignoreConversion( const Stmt* stmt )
    {
#if 1 // less strict version
    const Stmt* parent = parentStmt( stmt );
    if( parent == NULL )
        return true;
    switch( parent->getStmtClass())
        {
        case Stmt::ConditionalOperatorClass:
            if( stmt == cast< ConditionalOperator >( parent )->getCond())
                return true;
            break;
        case Stmt::BinaryOperatorClass:
            {
            const BinaryOperator* binary = cast< BinaryOperator >( parent );
            if(( binary->getOpcode() == BO_LAnd || binary->getOpcode() == BO_LOr )
                && ( stmt == binary->getLHS() || stmt == binary->getRHS()))
                {
                return true;
                }
            break;
            }
        case Stmt::UnaryOperatorClass:
            {
            const UnaryOperator* unary = cast< UnaryOperator >( parent );
            if( unary->getOpcode() == UO_LNot && stmt == unary->getSubExpr())
                return true;
            break;
            }
        default:
            if( const ExplicitCastExpr* castexpr = dyn_cast< ExplicitCastExpr >( parent ))
                if( castexpr->getTypeAsWritten()->isBooleanType() && stmt == castexpr->getSubExpr())
                    return true;
            if( dyn_cast< CallExpr >( parent ))
                return false; // The only place where it's not ignored.
            break;
        }
    return ignoreConversion( parent );
#else // more strict version
    // Warn only if the expression is not used in a conditional context.
    const Stmt* parent = parentStmt( stmt );
    if( parent == NULL ) // Should not happen inside a function, but can happen inside
        return false;    // ctor initializer list.
    switch( parent->getStmtClass())
        {
        case Stmt::IfStmtClass:
            return ( stmt == cast< IfStmt >( parent )->getCond());
        case Stmt::WhileStmtClass:
            return ( stmt == cast< WhileStmt >( parent )->getCond());
        case Stmt::DoStmtClass:
            return ( stmt == cast< DoStmt >( parent )->getCond());
        case Stmt::ForStmtClass:
            return ( stmt == cast< ForStmt >( parent )->getCond());
        case Stmt::ConditionalOperatorClass:
            return ( stmt == cast< ConditionalOperator >( parent )->getCond());
        case Stmt::BinaryOperatorClass:
            {
            const BinaryOperator* binary = cast< BinaryOperator >( parent );
            return (( binary->getOpcode() == BO_LAnd || binary->getOpcode() == BO_LOr )
                && ( stmt == binary->getLHS() || stmt == binary->getRHS()));
            }
        case Stmt::UnaryOperatorClass:
            {
            const UnaryOperator* unary = cast< UnaryOperator >( parent );
            return ( unary->getOpcode() == UO_LNot && stmt == unary->getSubExpr());
            }
        case Stmt::ExprWithCleanupsClass: // Often happens inside if() condition.
            return isInConditionalContext( parent );
        default:
            if( const ExplicitCastExpr* castexpr = dyn_cast< ExplicitCastExpr >( parent ))
                if( castexpr->getTypeAsWritten()->isBooleanType() && stmt == castexpr->getSubExpr())
                    return true;
            break;
        }
    return false;
#endif
    }
static Plugin::Registration< PointerToBool > X( "pointertobool" );
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
 
     |