nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / namespaceindentation.cxx
blob95182197cbd5bb3705368188955b7450e4daa8df
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 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
11 #ifndef LO_CLANG_SHARED_PLUGINS
13 #include <cassert>
14 #include <string>
15 #include <iostream>
16 #include <locale>
17 #include <fstream>
18 #include <set>
19 #include "plugin.hxx"
24 namespace
26 class NamespaceIndentation : public loplugin::FilteringPlugin<NamespaceIndentation>
28 public:
29 explicit NamespaceIndentation(loplugin::InstantiationData const& data)
30 : FilteringPlugin(data)
34 virtual bool preRun() override { return true; }
36 virtual void run() override
38 if (preRun())
39 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
42 bool VisitNamespaceDecl(NamespaceDecl const*);
44 private:
45 std::string GetFullNamespace(const NamespaceDecl* nsDecl);
48 void trim(std::string& str)
50 // right trim
51 auto it1 = std::find_if(str.rbegin(), str.rend(), [](char ch) {
52 return !std::isspace<char>(ch, std::locale::classic());
53 });
54 str.erase(it1.base(), str.end());
55 // left trim
56 auto it2 = std::find_if(str.begin(), str.end(), [](char ch) {
57 return !std::isspace<char>(ch, std::locale::classic());
58 });
59 str.erase(str.begin(), it2);
62 bool NamespaceIndentation::VisitNamespaceDecl(NamespaceDecl const* nsDecl)
64 if (ignoreLocation(nsDecl))
65 return true;
66 if (nsDecl->isAnonymousNamespace())
67 return true;
68 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(nsDecl->getLocation())))
69 return true;
71 // right now, just fixing up the fallout from clang-tidy-modernize-namespaces, which
72 // does not touch header files
73 if (!compiler.getSourceManager().isInMainFile(nsDecl->getLocation()))
74 return true;
76 auto& SM = compiler.getSourceManager();
78 // if we have a combined ns (.e.g namespace aaa::bbb), this appears in the AST
79 // as two nested namespace sharing the same source locations, so ignore the outer decls
80 if (!nsDecl->decls_empty())
82 auto child = dyn_cast_or_null<NamespaceDecl>(*nsDecl->decls_begin());
83 if (child)
85 bool invalid1 = false;
86 bool invalid2 = false;
87 unsigned line1 = SM.getPresumedLineNumber(compat::getBeginLoc(nsDecl), &invalid1);
88 unsigned line2 = SM.getPresumedLineNumber(compat::getBeginLoc(child), &invalid2);
89 if (line1 == line2)
90 return true;
94 // Truly hacky way to find the actual beginning of an xxx::yyy namespace declaration
95 // if we are inside the yyy NameSpaceDecl of
96 // namespace xxx::yyy
97 // the beginLoc is just between the "xxx" and the "::"
98 auto nsDeclBeginLoc = compat::getBeginLoc(nsDecl);
99 bool foundMultiple = false;
101 constexpr int BACKSCAN = 32;
102 auto beginLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(-BACKSCAN);
103 auto endLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(3);
104 const char* p1 = SM.getCharacterData(beginLoc);
105 const char* p2 = SM.getCharacterData(endLoc);
106 unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
107 if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048)
108 return true;
109 auto s = std::string(p1, p2 - p1);
110 auto idx1 = s.rfind(" "); // find the space preceding the namespace token
111 if (idx1 != std::string::npos)
113 auto namespaceToken = s.substr(idx1);
114 if (namespaceToken.find("::") != std::string::npos)
116 auto idx = s.rfind("\n");
117 nsDeclBeginLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(idx - BACKSCAN + 1);
118 foundMultiple = true;
123 // for now, I am only interested in fixing the fallout from clang-tidy-modernize-namespace, not
124 // anything else
125 if (!foundMultiple)
126 return true;
128 bool invalid1 = false;
129 bool invalid2 = false;
130 unsigned col1 = SM.getPresumedColumnNumber(nsDeclBeginLoc, &invalid1);
131 unsigned col2 = SM.getPresumedColumnNumber(nsDecl->getRBraceLoc(), &invalid2);
132 unsigned line1 = SM.getPresumedLineNumber(nsDeclBeginLoc, &invalid1);
133 unsigned line2 = SM.getPresumedLineNumber(nsDecl->getRBraceLoc(), &invalid2);
134 if (invalid1 || invalid2)
135 return true;
136 if (line1 == line2) // single line declaration
137 return true;
138 if (col1 != col2)
139 report(DiagnosticsEngine::Warning, "statement right brace mis-aligned",
140 nsDecl->getRBraceLoc());
142 // no easy way to get the position of the left brace
143 auto endLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(256);
144 const char* p1 = SM.getCharacterData(SM.getExpansionLoc(compat::getBeginLoc(nsDecl)));
145 const char* p2 = SM.getCharacterData(SM.getExpansionLoc(endLoc));
146 unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
147 if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048)
148 return true;
149 auto s = std::string(p1, p2 - p1 + n);
150 auto idx1 = s.find("\n");
151 auto idx2 = s.find("{");
152 if (idx1 != std::string::npos && idx2 != std::string::npos)
153 if (idx1 < idx2)
155 auto col3 = idx2 - idx1;
156 if (col1 != col3)
157 report(DiagnosticsEngine::Warning, "statement left brace mis-aligned",
158 compat::getBeginLoc(nsDecl));
161 // extract the comment following the end brace
162 auto beginLoc = nsDecl->getRBraceLoc();
163 endLoc = beginLoc.getLocWithOffset(128);
164 p1 = SM.getCharacterData(SM.getExpansionLoc(beginLoc));
165 p2 = SM.getCharacterData(SM.getExpansionLoc(endLoc));
166 n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
167 if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048)
168 return true;
169 s = std::string(p1, p2 - p1 + n);
170 idx1 = s.find("//");
171 idx2 = s.find("\n");
172 if (idx1 != std::string::npos && idx2 != std::string::npos && idx1 < idx2)
174 idx1 += 2;
175 s = s.substr(idx1, idx2 - idx1);
176 trim(s);
177 std::string fullNamespace = GetFullNamespace(nsDecl);
178 if (!(s == fullNamespace || s == (fullNamespace + " namespace") || s == "namespace"
179 || s == ("namespace " + fullNamespace) || s == ("namespace ::" + fullNamespace)
180 || s == ("end " + fullNamespace) || s == "end namespace"
181 || s == ("end namespace " + fullNamespace)
182 || s == ("end " + fullNamespace + " namespace") || s == "end of namespace"
183 || s == ("end of namespace " + fullNamespace)
184 || s == ("end of namespace ::" + fullNamespace)
185 || s == ("eof of namespace " + fullNamespace)))
187 report(DiagnosticsEngine::Warning, "incorrect comment at end of namespace %0",
188 nsDecl->getRBraceLoc())
189 << fullNamespace;
193 return true;
196 std::string NamespaceIndentation::GetFullNamespace(const NamespaceDecl* nsDecl)
198 std::vector<llvm::StringRef> names;
199 auto ns = nsDecl;
200 while (ns)
202 names.push_back(ns->getName());
203 ns = dyn_cast<NamespaceDecl>(ns->getParent());
205 std::string fullNamespace;
206 for (auto it = names.rbegin(); it != names.rend(); ++it)
207 fullNamespace += "::" + it->str();
208 fullNamespace = fullNamespace.substr(2);
209 return fullNamespace;
212 // leave this off by default, so as not to annoy people
213 loplugin::Plugin::Registration<NamespaceIndentation> namespaceindentation("namespaceindentation",
214 false);
216 } // namespace
218 #endif // LO_CLANG_SHARED_PLUGINS
220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */