summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/store/referencecasting.cxx
blob: 4466fb66e122b36201acbe2671cc4f7a6bee36a1 (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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/* -*- 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 "referencecasting.hxx"

#include <clang/AST/Attr.h>
#include <iostream>

namespace loplugin
{

/*
This is a compile-time checker.

Check for cases where we have
 - two IDL interfaces A and B,
 - B extends A
 - we are converting a Reference<B> to a Reference<A>

Note that it generates the occasional false positive.

Also, it makes clang3.2 crash on about 4 files in the LO codebase.
I have logged a bug here:
  http://llvm.org/bugs/show_bug.cgi?id=15902

*/
ReferenceCasting::ReferenceCasting( CompilerInstance& compiler )
    : Plugin( compiler )
{
}

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

// This:
// static void example_method()
// {
//     css::uno::Reference<B> b;
//     css::uno::Reference<A>(b, css::uno::UNO_QUERY);
// }
// Compiles to this AST:
// (CompoundStmt 0x205d430 </noel-extra1/libo-clang/compilerplugins/clang/noel1.cxx:17:1, line:20:1>
//   (DeclStmt 0x20580a8 <line:18:5, col:32>
//     (0x20530e0 "css::uno::Reference<B> refB =
//       (CXXConstructExpr 0x2058078 <col:28> 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>''void(void)')"))
//   (DeclStmt 0x205d418 <line:19:5, col:59>
//     (0x2058310 "css::uno::Reference<A> refA =
//       (CXXConstructExpr 0x205d3d8 <col:28, col:58> 'css::uno::Reference<A>':'class com::sun::star::uno::Reference<class A>''void (const class com::sun::star::uno::BaseReference &, enum com::sun::star::uno::UnoReference_Query)'
//         (ImplicitCastExpr 0x205d3c0 <col:33> 'const class com::sun::star::uno::BaseReference' lvalue <NoOp>
//           (ImplicitCastExpr 0x205d3a0 <col:33> 'class com::sun::star::uno::BaseReference' lvalue <DerivedToBase (BaseReference)>
//             (DeclRefExpr 0x20582a0 <col:33> 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>' lvalue Var 0x20530e0 'refB' 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>')))
//         (DeclRefExpr 0x2058398 <col:39, col:49> 'enum com::sun::star::uno::UnoReference_Query' EnumConstant 0x1831de0 'UNO_QUERY' 'enum com::sun::star::uno::UnoReference_Query'))")))
//
//
// This:
// static void example_method1(css::uno::Reference<A>)
// {
// }
// static void example_method2()
// {
//     css::uno::Reference<B> refB;
//     example_method1(css::uno::Reference<A>(refB, css::uno::UNO_QUERY));
// }
//  Compiles to this AST:
// static void example_method1(css::uno::Reference<A>) (CompoundStmt 0x2a74ee8 </noel-extra1/libo-clang/compilerplugins/clang/noel1.cxx:17:1, line:18:1>)
// static void example_method2() (CompoundStmt 0x2a7a650 </noel-extra1/libo-clang/compilerplugins/clang/noel1.cxx:21:1, line:24:1>
//  (DeclStmt 0x2a7a1a8 <line:22:5, col:32>
//    (0x2a751e0 "css::uno::Reference<B> refB =
//      (CXXConstructExpr 0x2a7a178 <col:28> 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>''void(void)')"))
//  (ExprWithCleanups 0x2a7a638 <line:23:5, col:70> 'void'
//    (CallExpr 0x2a7a570 <col:5, col:70> 'void'
//      (ImplicitCastExpr 0x2a7a558 <col:5> 'void (*)(css::uno::Reference<A>)' <FunctionToPointerDecay>
//        (DeclRefExpr 0x2a7a4d8 <col:5> 'void (css::uno::Reference<A>)' lvalue Function 0x2a6ff00 'example_method1' 'void (css::uno::Reference<A>)'))
//      (CXXBindTemporaryExpr 0x2a7a618 <col:21, col:69> 'css::uno::Reference<A>':'class com::sun::star::uno::Reference<class A>' (CXXTemporary 0x2a7a610)
//        (CXXConstructExpr 0x2a7a5d8 <col:21, col:69> 'css::uno::Reference<A>':'class com::sun::star::uno::Reference<class A>''void (const Reference<class A> &)' elidable
//          (MaterializeTemporaryExpr 0x2a7a5c0 <col:21, col:69> 'const Reference<class A>':'const class com::sun::star::uno::Reference<class A>' lvalue
//            (ImplicitCastExpr 0x2a7a5a8 <col:21, col:69> 'const Reference<class A>':'const class com::sun::star::uno::Reference<class A>' <NoOp>
//              (CXXBindTemporaryExpr 0x2a7a4b8 <col:21, col:69> 'css::uno::Reference<A>':'class com::sun::star::uno::Reference<class A>' (CXXTemporary 0x2a7a4b0)
//                (CXXTemporaryObjectExpr 0x2a7a460 <col:21, col:69> 'css::uno::Reference<A>':'class com::sun::star::uno::Reference<class A>''void (const class com::sun::star::uno::BaseReference &, enum com::sun::star::uno::UnoReference_Query)'
//                  (ImplicitCastExpr 0x2a7a448 <col:44> 'const class com::sun::star::uno::BaseReference' lvalue <NoOp>
//                    (ImplicitCastExpr 0x2a7a428 <col:44> 'class com::sun::star::uno::BaseReference' lvalue <DerivedToBase (BaseReference)>
//                      (DeclRefExpr 0x2a7a340 <col:44> 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>' lvalue Var 0x2a751e0 'refB' 'css::uno::Reference<B>':'class com::sun::star::uno::Reference<class B>')))
//                  (DeclRefExpr 0x2a7a398 <col:50, col:60> 'enum com::sun::star::uno::UnoReference_Query' EnumConstant 0x224ee20 'UNO_QUERY' 'enum com::sun::star::uno::UnoReference_Query'))))))))))

static const Type* extractTemplateType(Expr* cce);

bool ReferenceCasting::VisitCXXConstructExpr( CXXConstructExpr* cce )
{
    // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
    if( StringRef(compiler.getSourceManager().getPresumedLoc( cce->getSourceRange().getBegin() )).find( "Reference.h" ) != StringRef::npos )
        return true;

    // look for calls to the Reference<T>(x,UNO_something) constructor
    if( cce->getConstructor()->getNameInfo().getName().getAsString() != "Reference" )
        return true;

    if( cce->getNumArgs() != 2 )
        return true;

    // extract the type parameter passed to the template
    const Type * templateParamType = extractTemplateType(cce);
    if ( !templateParamType )
        return true;

    // extract the type of the first parameter passed to the constructor
    Expr* constructorArg0 = cce->getArg(0);
    if( !constructorArg0 )
       return true;

    // ignore the Reference(XInterface*,...) constructor
    if( constructorArg0->getType()->isPointerType() )
       return true;

    // drill down the expression tree till we hit the bottom
    DeclRefExpr* constructorSubArg2;
    Expr* constructorArg0SubExpr = constructorArg0;
    for(;;)
    {
        // if we've hit the member expression we want, break
        constructorSubArg2 = dyn_cast<DeclRefExpr>( constructorArg0SubExpr );
        if( constructorSubArg2 )
            break;
        CastExpr* tmp1 = dyn_cast<CastExpr>( constructorArg0SubExpr );
        if( tmp1 ) {
            constructorArg0SubExpr = tmp1->getSubExpr();
            continue;
        }
        MaterializeTemporaryExpr* tmp2 = dyn_cast<MaterializeTemporaryExpr>( constructorArg0SubExpr );
        if( tmp2 ) {
            constructorArg0SubExpr = tmp2->GetTemporaryExpr();
            continue;
        }
        CXXBindTemporaryExpr* tmp3 = dyn_cast<CXXBindTemporaryExpr>( constructorArg0SubExpr );
        if( tmp3 ) {
            constructorArg0SubExpr = tmp3->getSubExpr();
            continue;
        }
        CXXTemporaryObjectExpr* tmp4 = dyn_cast<CXXTemporaryObjectExpr>( constructorArg0SubExpr );
        if( tmp4 ) {
            constructorArg0SubExpr = tmp4->getArg(0);
            continue;
        }
        return true;
    }

    const Type * tmp3 = extractTemplateType( constructorSubArg2 );
    if ( !tmp3 )
        return true;

    const RecordType* templateParamRT = dyn_cast<RecordType>( templateParamType );
    const RecordType* constructorArgRT = dyn_cast<RecordType>( tmp3 );
    if( !templateParamRT || !constructorArgRT )
       return true;

    CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>( templateParamRT->getDecl() );
    CXXRecordDecl* constructorArgRD = dyn_cast<CXXRecordDecl>( constructorArgRT->getDecl() );

    if (constructorArgRD->Equals(templateParamRD) || constructorArgRD->isDerivedFrom(templateParamRD))
        report( DiagnosticsEngine::Warning,
                "the source reference is already a subtype of the destination reference",
                cce->getLocStart()) // and the exact position where the message should point
                    << cce->getSourceRange(); // and the full return statement to highlight (optional)

    return true;
}

static const Type* extractTemplateType(Expr* cce)
{
    QualType cceQT = cce->getType();
    const Type* cceType = cceQT.getTypePtr();
    const TemplateSpecializationType* cceTST = dyn_cast<TemplateSpecializationType>( cceType );
    if( !cceTST )
        return NULL;
    if( cceTST->getNumArgs() != 1 )
        return NULL;
    const TemplateArgument & cceTA = cceTST->getArg(0);
    QualType templateParamQT = cceTA.getAsType();
    return templateParamQT.getTypePtr();
}

static Plugin::Registration< ReferenceCasting > X( "referencecasting" );

} // namespace

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