summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/pointertobool.cxx
blob: 6988c4f5d93b60e8f5b3c67e6b2584967508599b (plain)
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
/*
 * 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 the only conversions that are 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( CompilerInstance& compiler );
        void run();
        bool VisitImplicitCastExpr( const ImplicitCastExpr* expr );
    private:
        bool ignoreConversion( const Stmt* stmt );
    };

PointerToBool::PointerToBool( CompilerInstance& compiler )
    : Plugin( compiler )
    {
    }

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 = Lexer::getLocForEndOfToken( expr->getLocEnd(), 0, compiler.getSourceManager(), compiler.getLangOpts());
        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