cid#1555694 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / compilerplugins / clang / finalclasses.cxx
blob2d19dd67d70332353f9863d3b18902d72e98c4c3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <cassert>
11 #include <set>
12 #include <string>
13 #include <iostream>
14 #include "config_clang.h"
15 #include "compat.hxx"
16 #include "plugin.hxx"
17 #include <fstream>
19 /**
20 Look for classes that are final i.e. nothing extends them, and have either
21 (a) protected fields or members.
23 (b) virtual members
25 In the case of (a), those members/fields can be made private.
26 In the case of (b), making the class final means the compiler can devirtualise
27 some method calls
29 The process goes something like this:
30 $ make check
31 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='finalclasses' check
32 $ ./compilerplugins/clang/finalclasses.py
36 namespace {
38 // try to limit the voluminous output a little
39 static std::set<std::string> inheritedFromSet;
40 static std::map<std::string,std::string> definitionMap; // className -> filename
42 class FinalClasses:
43 public RecursiveASTVisitor<FinalClasses>, public loplugin::Plugin
45 public:
46 explicit FinalClasses(loplugin::InstantiationData const & data):
47 Plugin(data) {}
49 virtual void run() override
51 handler.enableTreeWideAnalysisMode();
53 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
55 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
56 // writing to the same logfile
57 std::string output;
58 for (const std::string & s : inheritedFromSet)
59 output += "inherited-from:\t" + s + "\n";
60 for (const auto & s : definitionMap)
61 output += "definition:\t" + s.first + "\t" + s.second + "\n";
62 std::ofstream myfile;
63 myfile.open( WORKDIR "/loplugin.finalclasses.log", std::ios::app | std::ios::out);
64 myfile << output;
65 myfile.close();
68 bool shouldVisitTemplateInstantiations () const { return true; }
70 bool shouldVisitImplicitCode() const { return true; }
72 bool VisitCXXRecordDecl( const CXXRecordDecl* decl);
73 private:
74 void checkBase(QualType qt);
77 bool ignoreClass(StringRef s)
79 // ignore stuff in the standard library, and UNO stuff we can't touch.
80 if (compat::starts_with(s, "rtl::") || compat::starts_with(s, "sal::")
81 || compat::starts_with(s, "com::sun::") || compat::starts_with(s, "std::")
82 || compat::starts_with(s, "boost::")
83 || s == "OString" || s == "OUString" || s == "bad_alloc")
85 return true;
87 return false;
90 bool FinalClasses::VisitCXXRecordDecl(const CXXRecordDecl* decl)
92 if (ignoreLocation(decl))
93 return true;
94 if (!decl->hasDefinition())
95 return true;
97 for (auto it = decl->bases_begin(); it != decl->bases_end(); ++it)
99 const CXXBaseSpecifier spec = *it;
100 checkBase(spec.getType());
102 for (auto it = decl->vbases_begin(); it != decl->vbases_end(); ++it)
104 const CXXBaseSpecifier spec = *it;
105 checkBase(spec.getType());
108 if (decl->hasAttr<FinalAttr>())
109 return true;
110 bool bFoundVirtual = false;
111 bool bFoundProtected = false;
112 for (auto it = decl->method_begin(); it != decl->method_end(); ++it) {
113 auto i = *it;
114 // ignore methods that are overriding base-class methods, making them private
115 // isn't useful
116 if ( !i->hasAttr<OverrideAttr>() && i->getAccess() == AS_protected )
117 bFoundProtected = true;
118 if ( i->isVirtual() )
119 bFoundVirtual = true;
122 if (!bFoundProtected)
124 for (auto it = decl->field_begin(); it != decl->field_end(); ++it) {
125 auto i = *it;
126 if ( i->getAccess() == AS_protected ) {
127 bFoundProtected = true;
128 break;
132 if (!bFoundProtected && !bFoundVirtual)
133 return true;
135 std::string s = decl->getQualifiedNameAsString();
136 if (ignoreClass(s))
137 return true;
139 SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(decl->getBeginLoc());
140 auto const filename = getFilenameOfLocation(spellingLocation);
141 auto sourceLocation = filename.substr(strlen(SRCDIR)).str() + ":"
142 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(spellingLocation));
143 definitionMap.insert( std::pair<std::string,std::string>(s, sourceLocation) );
144 return true;
147 void FinalClasses::checkBase(QualType baseType)
149 // need to look through typedefs, hence the getUnqualifiedDesugaredType
150 baseType = baseType.getDesugaredType(compiler.getASTContext());
151 std::string x;
152 // so that we get just the template name, excluding the template parameters
153 if (baseType->isRecordType())
154 x = baseType->getAsCXXRecordDecl()->getQualifiedNameAsString();
155 else if (auto templateType = baseType->getAs<TemplateSpecializationType>())
156 x = templateType->getTemplateName().getAsTemplateDecl()->getQualifiedNameAsString();
157 else
158 x = baseType.getAsString();
159 inheritedFromSet.insert( x );
162 loplugin::Plugin::Registration< FinalClasses > X("finalclasses", false);
166 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */