summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/checkconfigmacros.cxx
blob: 8524d7b8edb6024a57c696de7b7072651deb84d0 (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
/*
 * 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/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( CompilerInstance& compiler );
        virtual void run() override;
#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 2
        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;
        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;
        virtual void Defined( const Token& macroToken, const MacroDirective* info ) override;
#endif
    private:
        void checkMacro( const Token& macroToken, SourceLocation location );
        std::set< string > configMacros;
    };

CheckConfigMacros::CheckConfigMacros( CompilerInstance& compiler )
    : Plugin( compiler )
    {
    compiler.getPreprocessor().addPPCallbacks( this );
    }

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

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

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

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

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

void CheckConfigMacros::checkMacro( const Token& macroToken, SourceLocation location )
    {
    if( configMacros.find( macroToken.getIdentifierInfo()->getName()) != configMacros.end())
        {
        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( "bodynotinblock" );

} // namespace