summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorNoel Grandin <noel@peralex.com>2013-05-02 10:36:43 +0200
committerNoel Grandin <noelgrandin@gmail.com>2013-05-22 10:44:29 +0000
commit6a043e9c0acff20e1618ca8ec15c21d5d0fd0d37 (patch)
tree2746468845d6f1159e3759ee2cf7a620fca15b6e /compilerplugins
parent697a007c61b9cabceb9767fad87cd5822b300452 (diff)
Use the new type-checking Reference constructor to reduce code noise
Also create a Clang compiler plugin to detect such cases. Change-Id: I61ad1a1d6b1c017eeb51f226d2dde0e9bb7f1752 Reviewed-on: https://gerrit.libreoffice.org/4001 Tested-by: LibreOffice gerrit bot <gerrit@libreoffice.org> Reviewed-by: Noel Grandin <noelgrandin@gmail.com> Tested-by: Noel Grandin <noelgrandin@gmail.com>
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/clang/store/referencecasting.cxx194
-rw-r--r--compilerplugins/clang/store/referencecasting.hxx31
2 files changed, 225 insertions, 0 deletions
diff --git a/compilerplugins/clang/store/referencecasting.cxx b/compilerplugins/clang/store/referencecasting.cxx
new file mode 100644
index 000000000000..c05c233cc337
--- /dev/null
+++ b/compilerplugins/clang/store/referencecasting.cxx
@@ -0,0 +1,194 @@
+/*
+ * 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( compiler.getSourceManager().getFilename( 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
diff --git a/compilerplugins/clang/store/referencecasting.hxx b/compilerplugins/clang/store/referencecasting.hxx
new file mode 100644
index 000000000000..732e32de7b75
--- /dev/null
+++ b/compilerplugins/clang/store/referencecasting.hxx
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef REFERENCECASTING_H
+#define REFERENCECASTING_H
+
+#include "plugin.hxx"
+
+namespace loplugin
+{
+
+class ReferenceCasting
+ : public RecursiveASTVisitor< ReferenceCasting >
+ , public Plugin
+ {
+ public:
+ explicit ReferenceCasting( CompilerInstance& compiler );
+ virtual void run();
+ bool VisitCXXConstructExpr( CXXConstructExpr* cce );
+ };
+
+} // namespace
+
+#endif // REFERENCECASTING_H