/* -*- 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. * */ #ifndef LO_CLANG_SHARED_PLUGINS #include #include #include #include #include #include #include "plugin.hxx" /* */ namespace { class NamespaceIndentation : public loplugin::FilteringPlugin { public: explicit NamespaceIndentation(loplugin::InstantiationData const& data) : FilteringPlugin(data) { } virtual bool preRun() override { return true; } virtual void run() override { if (preRun()) TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } bool VisitNamespaceDecl(NamespaceDecl const*); private: std::string GetFullNamespace(const NamespaceDecl* nsDecl); }; void trim(std::string& str) { // right trim auto it1 = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale::classic()); }); str.erase(it1.base(), str.end()); // left trim auto it2 = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale::classic()); }); str.erase(str.begin(), it2); } bool NamespaceIndentation::VisitNamespaceDecl(NamespaceDecl const* nsDecl) { if (ignoreLocation(nsDecl)) return true; if (nsDecl->isAnonymousNamespace()) return true; if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(nsDecl->getLocation()))) return true; // right now, just fixing up the fallout from clang-tidy-modernize-namespaces, which // does not touch header files if (!compiler.getSourceManager().isInMainFile(nsDecl->getLocation())) return true; auto& SM = compiler.getSourceManager(); // if we have a combined ns (.e.g namespace aaa::bbb), this appears in the AST // as two nested namespace sharing the same source locations, so ignore the outer decls if (!nsDecl->decls_empty()) { auto child = dyn_cast_or_null(*nsDecl->decls_begin()); if (child) { bool invalid1 = false; bool invalid2 = false; unsigned line1 = SM.getPresumedLineNumber(compat::getBeginLoc(nsDecl), &invalid1); unsigned line2 = SM.getPresumedLineNumber(compat::getBeginLoc(child), &invalid2); if (line1 == line2) return true; } } // Truly hacky way to find the actual beginning of an xxx::yyy namespace declaration // if we are inside the yyy NameSpaceDecl of // namespace xxx::yyy // the beginLoc is just between the "xxx" and the "::" auto nsDeclBeginLoc = compat::getBeginLoc(nsDecl); bool foundMultiple = false; { constexpr int BACKSCAN = 32; auto beginLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(-BACKSCAN); auto endLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(3); const char* p1 = SM.getCharacterData(beginLoc); const char* p2 = SM.getCharacterData(endLoc); unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts()); if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048) return true; auto s = std::string(p1, p2 - p1); auto idx1 = s.rfind(" "); // find the space preceding the namespace token if (idx1 != std::string::npos) { auto namespaceToken = s.substr(idx1); if (namespaceToken.find("::") != std::string::npos) { auto idx = s.rfind("\n"); nsDeclBeginLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(idx - BACKSCAN + 1); foundMultiple = true; } } } // for now, I am only interested in fixing the fallout from clang-tidy-modernize-namespace, not // anything else if (!foundMultiple) return true; bool invalid1 = false; bool invalid2 = false; unsigned col1 = SM.getPresumedColumnNumber(nsDeclBeginLoc, &invalid1); unsigned col2 = SM.getPresumedColumnNumber(nsDecl->getRBraceLoc(), &invalid2); unsigned line1 = SM.getPresumedLineNumber(nsDeclBeginLoc, &invalid1); unsigned line2 = SM.getPresumedLineNumber(nsDecl->getRBraceLoc(), &invalid2); if (invalid1 || invalid2) return true; if (line1 == line2) // single line declaration return true; if (col1 != col2) report(DiagnosticsEngine::Warning, "statement right brace mis-aligned", nsDecl->getRBraceLoc()); // no easy way to get the position of the left brace auto endLoc = compat::getBeginLoc(nsDecl).getLocWithOffset(256); const char* p1 = SM.getCharacterData(SM.getExpansionLoc(compat::getBeginLoc(nsDecl))); const char* p2 = SM.getCharacterData(SM.getExpansionLoc(endLoc)); unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts()); if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048) return true; auto s = std::string(p1, p2 - p1 + n); auto idx1 = s.find("\n"); auto idx2 = s.find("{"); if (idx1 != std::string::npos && idx2 != std::string::npos) if (idx1 < idx2) { auto col3 = idx2 - idx1; if (col1 != col3) report(DiagnosticsEngine::Warning, "statement left brace mis-aligned", compat::getBeginLoc(nsDecl)); } // extract the comment following the end brace auto beginLoc = nsDecl->getRBraceLoc(); endLoc = beginLoc.getLocWithOffset(128); p1 = SM.getCharacterData(SM.getExpansionLoc(beginLoc)); p2 = SM.getCharacterData(SM.getExpansionLoc(endLoc)); n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts()); if (p2 < p1 || n > 128 || (p2 - p1 + n) > 2048) return true; s = std::string(p1, p2 - p1 + n); idx1 = s.find("//"); idx2 = s.find("\n"); if (idx1 != std::string::npos && idx2 != std::string::npos && idx1 < idx2) { idx1 += 2; s = s.substr(idx1, idx2 - idx1); trim(s); std::string fullNamespace = GetFullNamespace(nsDecl); if (!(s == fullNamespace || s == (fullNamespace + " namespace") || s == "namespace" || s == ("namespace " + fullNamespace) || s == ("namespace ::" + fullNamespace) || s == ("end " + fullNamespace) || s == "end namespace" || s == ("end namespace " + fullNamespace) || s == ("end " + fullNamespace + " namespace") || s == "end of namespace" || s == ("end of namespace " + fullNamespace) || s == ("end of namespace ::" + fullNamespace) || s == ("eof of namespace " + fullNamespace))) { report(DiagnosticsEngine::Warning, "incorrect comment at end of namespace %0", nsDecl->getRBraceLoc()) << fullNamespace; } } return true; } std::string NamespaceIndentation::GetFullNamespace(const NamespaceDecl* nsDecl) { std::vector names; auto ns = nsDecl; while (ns) { names.push_back(ns->getName()); ns = dyn_cast(ns->getParent()); } std::string fullNamespace; for (auto it = names.rbegin(); it != names.rend(); ++it) fullNamespace += "::" + it->str(); fullNamespace = fullNamespace.substr(2); return fullNamespace; } // leave this off by default, so as not to annoy people loplugin::Plugin::Registration namespaceindentation("namespaceindentation", false); } // namespace #endif // LO_CLANG_SHARED_PLUGINS /* vim:set shiftwidth=4 softtabstop=4 expandtab: */