summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/checkconfigmacros.cxx
blob: cde3a42f8dba1e12a5e65e0fe1d81969e762761d (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
/* -*- 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 "compat.hxx"
#include "plugin.hxx"

#include <clang/Lex/Preprocessor.h>

namespace loplugin
{

/*
This is a compile check.

Feature macros from config_XXX.h headers are always #defined (to 1 or 0 in case of yes/no
settings). It is a mistake to use #ifdef/#ifndef/defined to check them.

Using 1/0 instead of defined/undefined avoids undetected problems when e.g. the necessary
#include of the config_XXX.h file is missing.
*/

class CheckConfigMacros
    : public PPCallbacks
    , public Plugin
    {
    public:
        explicit CheckConfigMacros( const InstantiationData& data );
        virtual void run() override;
#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
        virtual void MacroDefined( const Token& macroToken, const MacroInfo* info ) override;
        virtual void MacroUndefined( const Token& macroToken, const MacroInfo* info ) override;
        virtual void Ifdef( SourceLocation location, const Token& macroToken ) override;
        virtual void Ifndef( SourceLocation location, const Token& macroToken ) override;
        virtual void Defined( const Token& macroToken ) override;
#else
        virtual void MacroDefined( const Token& macroToken, const MacroDirective* info ) override;
#if __clang_major__ == 3 && __clang_minor__ < 7
        virtual void MacroUndefined( const Token& macroToken, const MacroDirective* info ) override;
        virtual void Ifdef( SourceLocation location, const Token& macroToken, const MacroDirective* info ) override;
        virtual void Ifndef( SourceLocation location, const Token& macroToken, const MacroDirective* info ) override;
#else
        virtual void MacroUndefined( const Token& macroToken, const MacroDefinition& info ) override;
        virtual void Ifdef( SourceLocation location, const Token& macroToken, const MacroDefinition& info ) override;
        virtual void Ifndef( SourceLocation location, const Token& macroToken, const MacroDefinition& info ) override;
#endif
#if __clang_major__ == 3 && __clang_minor__ < 4
        virtual void Defined( const Token& macroToken, const MacroDirective* info ) override;
#elif __clang_major__ == 3 && __clang_minor__ < 7
        virtual void Defined( const Token& macroToken, const MacroDirective* info, SourceRange Range ) override;
#else
        virtual void Defined( const Token& macroToken, const MacroDefinition& info, SourceRange Range ) override;
#endif
#endif
        enum { isPPCallback = true };
    private:
        void checkMacro( const Token& macroToken, SourceLocation location );
        std::set< string > configMacros;
    };

CheckConfigMacros::CheckConfigMacros( const InstantiationData& data )
    : Plugin( data )
    {
    compat::addPPCallbacks(compiler.getPreprocessor(), this);
    }

void CheckConfigMacros::run()
    {
    // nothing, only check preprocessor usage
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void CheckConfigMacros::MacroDefined( const Token& macroToken, const MacroInfo* info )
    {
    SourceLocation location = info->getDefinitionLoc();
#else
void CheckConfigMacros::MacroDefined( const Token& macroToken, const MacroDirective* info )
    {
    SourceLocation location = info->getLocation();
#endif
    const char* filename = compiler.getSourceManager().getPresumedLoc( location ).getFilename();
    if( filename != NULL
        && ( strncmp( filename, BUILDDIR "/config_host/", strlen( BUILDDIR "/config_host/" )) == 0
            || strncmp( filename, BUILDDIR "/config_build/", strlen( BUILDDIR "/config_build/" )) == 0 ))
        {
//        fprintf(stderr,"DEF: %s %s\n", macroToken.getIdentifierInfo()->getName().data(), filename );
        configMacros.insert( macroToken.getIdentifierInfo()->getName());
        }
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void CheckConfigMacros::MacroUndefined( const Token& macroToken, const MacroInfo* )
#elif __clang_major__ == 3 && __clang_minor__ < 7
void CheckConfigMacros::MacroUndefined( const Token& macroToken, const MacroDirective* )
#else
void CheckConfigMacros::MacroUndefined( const Token& macroToken, const MacroDefinition& )
#endif
    {
    configMacros.erase( macroToken.getIdentifierInfo()->getName());
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void CheckConfigMacros::Ifdef( SourceLocation location, const Token& macroToken )
#elif __clang_major__ == 3 && __clang_minor__ < 7
void CheckConfigMacros::Ifdef( SourceLocation location, const Token& macroToken, const MacroDirective* )
#else
void CheckConfigMacros::Ifdef( SourceLocation location, const Token& macroToken, const MacroDefinition& )
#endif
    {
    checkMacro( macroToken, location );
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void CheckConfigMacros::Ifndef( SourceLocation location, const Token& macroToken )
#elif __clang_major__ == 3 && __clang_minor__ < 7
void CheckConfigMacros::Ifndef( SourceLocation location, const Token& macroToken, const MacroDirective* )
#else
void CheckConfigMacros::Ifndef( SourceLocation location, const Token& macroToken, const MacroDefinition& )
#endif
    {
    checkMacro( macroToken, location );
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void CheckConfigMacros::Defined( const Token& macroToken )
#elif __clang_major__ == 3 && __clang_minor__ < 4
void CheckConfigMacros::Defined( const Token& macroToken, const MacroDirective* )
#elif __clang_major__ == 3 && __clang_minor__ < 7
void CheckConfigMacros::Defined( const Token& macroToken, const MacroDirective* , SourceRange )
#else
void CheckConfigMacros::Defined( const Token& macroToken, const MacroDefinition& , SourceRange )
#endif
    {
    checkMacro( macroToken, macroToken.getLocation());
    }

void CheckConfigMacros::checkMacro( const Token& macroToken, SourceLocation location )
    {
    if( configMacros.find( macroToken.getIdentifierInfo()->getName()) != configMacros.end())
        {
        const char* filename = compiler.getSourceManager().getPresumedLoc( location ).getFilename();
        if( filename == NULL
            || strncmp( filename, SRCDIR "/include/LibreOfficeKit/", strlen( SRCDIR "/include/LibreOfficeKit/" )) != 0 )
            {
            report( DiagnosticsEngine::Error, "checking whether a config macro %0 is defined",
                location ) << macroToken.getIdentifierInfo()->getName();
            report( DiagnosticsEngine::Note, "use #if instead of #ifdef/#ifndef/defined", location );
            }
        }
    }

static Plugin::Registration< CheckConfigMacros > X( "checkconfigmacros" );

} // namespace

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */