summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/store/rtlconstasciimacro.cxx
blob: 482c9ade4201f441f83340e8b44a073fd439abd1 (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
/* -*- 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.
 *
 */

/*
This is a rewriter.

Remove uses of the macro RTL_CONSTASCII_USTRINGPARAM. One run is for one
specific use (see below), modify source to remove other uses.
*/

#include "plugin.hxx"

#include <clang/Lex/Preprocessor.h>

namespace loplugin
{

class RtlConstAsciiMacro
    : public RecursiveASTVisitor< RtlConstAsciiMacro >
    , public PPCallbacks
    , public RewritePlugin
    {
    public:
        explicit RtlConstAsciiMacro( const InstantiationData& data );
        virtual void run() override;
        bool VisitCXXConstructExpr( CXXConstructExpr* expr );
        bool VisitCXXTemporaryObjectExpr( CXXTemporaryObjectExpr* expr );
        bool VisitStringLiteral( const StringLiteral* literal );
#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
        virtual void MacroExpands( const Token& macro, const MacroInfo* info, SourceRange range ) override;
#else
        virtual void MacroExpands( const Token& macro, const MacroDirective* directive,
            SourceRange range, const MacroArgs* args ) override;
#endif
        enum { isPPCallback = true };
    private:
        map< SourceLocation, SourceLocation > expansions; // start location -> end location
        bool searchingForString;
        bool suitableString;
    };

RtlConstAsciiMacro::RtlConstAsciiMacro( const InstantiationData& data )
    : RewritePlugin( data )
    , searchingForString( false )
    {
    compiler.getPreprocessor().addPPCallbacks( this );
    }

void RtlConstAsciiMacro::run()
    {
    TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
    }

#if __clang_major__ < 3 || __clang_major__ == 3 && __clang_minor__ < 3
void RtlConstAsciiMacro::MacroExpands( const Token& macro, const MacroInfo*, SourceRange range )
#else
void RtlConstAsciiMacro::MacroExpands( const Token& macro, const MacroDirective*,
    SourceRange range, const MacroArgs* )
#endif
    {
    if( macro.getIdentifierInfo()->getName() != "RTL_CONSTASCII_USTRINGPARAM" )
        return;
    expansions[ range.getBegin() ] = range.getEnd();
    }

/* Remove use with the following ctor:
    OUString( const sal_Char * value, sal_Int32 length,
              rtl_TextEncoding encoding,
              sal_uInt32 convertFlags = OSTRING_TO_OUSTRING_CVTFLAGS )
   This means searching for CXXConstructExpr.
   For removal when used with functions it should check e.g. for CallExpr.
*/
bool RtlConstAsciiMacro::VisitCXXConstructExpr( CXXConstructExpr* expr )
    {
    if( ignoreLocation( expr ))
        return true;
    if( expr->getNumArgs() != 4 )
        return true;
    // The last argument should be the default one when the macro is used.
    if( dyn_cast< CXXDefaultArgExpr >( expr->getArg( 3 )) == NULL )
        return true;
    if( expr->getConstructor()->getQualifiedNameAsString() != "rtl::OUString::OUString" )
        return true;
    const SourceManager& src = compiler.getSourceManager();
    SourceLocation start = src.getExpansionLoc( expr->getArg( 0 )->getLocStart());
    // Macro fills in the first 3 arguments, so they must all come from the same expansion.
    if( start != src.getExpansionLoc( expr->getArg( 2 )->getLocEnd()))
        return true;
    if( expansions.find( start ) == expansions.end())
        return true;
    SourceLocation end = expansions[ start ];
    // Remove the location, since sometimes the same code may be processed more than once
    // (e.g. non-trivial default arguments).
    expansions.erase( start );
    // Check if the string argument to the macro is suitable.
    searchingForString = true;
    suitableString = false;
    TraverseStmt( expr->getArg( 0 ));
    searchingForString = false;
    if( !suitableString )
        return true;
    // Seach for '(' (don't just remove a given length to handle possible whitespace).
    const char* text = compiler.getSourceManager().getCharacterData( start );
    const char* pos = text;
    while( *pos != '(' )
        ++pos;
    ++pos;
    if( text[ -1 ] == ' ' && *pos == ' ' )
        ++pos; // do not leave two spaces
    removeText( start, pos - text, RemoveLineIfEmpty );
    const char* textend = compiler.getSourceManager().getCharacterData( end );
    if( textend[ -1 ] == ' ' && textend[ 1 ] == ' ' )
        removeText( end, 2, RemoveLineIfEmpty ); // Remove ') '.
    else
        removeText( end, 1, RemoveLineIfEmpty ); // Remove ')'.
    return true;
    }

bool RtlConstAsciiMacro::VisitCXXTemporaryObjectExpr( CXXTemporaryObjectExpr* expr )
    {
    return VisitCXXConstructExpr( expr );
    }

bool RtlConstAsciiMacro::VisitStringLiteral( const StringLiteral* literal )
    {
    if( !searchingForString )
        return true;
    if( suitableString ) // two string literals?
        {
        report( DiagnosticsEngine::Warning, "cannot analyze RTL_CONSTASCII_USTRINGPARAM (plugin needs fixing)" )
            << literal->getSourceRange();
        return true;
        }
    if( !literal->isAscii()) // ignore
        return true;
    if( !literal->containsNonAsciiOrNull())
        suitableString = true;
    return true;
    }

static Plugin::Registration< RtlConstAsciiMacro > X( "rtlconstasciimacro" );

} // namespace

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