summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/conststringvar.cxx
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2017-01-10 08:12:52 +0100
committerStephan Bergmann <sbergman@redhat.com>2017-01-10 08:12:52 +0100
commit0d2ac4afe9f583dee64a5dcebb93caf7d42d0891 (patch)
tree242c92766c5e449ec8452541552151e672638239 /compilerplugins/clang/conststringvar.cxx
parentd86f83b6d942fa18ffb1e74daff6032dea954dda (diff)
New loplugin:conststringvar
Change-Id: I16648b018ed0f69a085322cfb88481ee2a0c27ca
Diffstat (limited to 'compilerplugins/clang/conststringvar.cxx')
-rw-r--r--compilerplugins/clang/conststringvar.cxx136
1 files changed, 136 insertions, 0 deletions
diff --git a/compilerplugins/clang/conststringvar.cxx b/compilerplugins/clang/conststringvar.cxx
new file mode 100644
index 000000000000..58a94e559428
--- /dev/null
+++ b/compilerplugins/clang/conststringvar.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <set>
+#include <stack>
+
+#include "check.hxx"
+#include "plugin.hxx"
+
+// Find non-const vars of 'char const *' type initialized with a const expr,
+// that could likely be const (and will then probably trigger further
+// loplugin:stringconstant findings).
+
+namespace {
+
+// It looks like Clang wrongly implements DR 4
+// (<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#4>) and treats
+// a variable declared in an 'extern "..." {...}'-style linkage-specification as
+// if it contained the 'extern' specifier:
+bool hasExternalLinkage(VarDecl const * decl) {
+ if (decl->getLinkageAndVisibility().getLinkage() != ExternalLinkage) {
+ return false;
+ }
+ for (auto ctx = decl->getLexicalDeclContext();
+ ctx->getDeclKind() != Decl::TranslationUnit;
+ ctx = ctx->getLexicalParent())
+ {
+ if (auto ls = dyn_cast<LinkageSpecDecl>(ctx)) {
+ if (!ls->hasBraces()) {
+ return true;
+ }
+ if (auto prev = decl->getPreviousDecl()) {
+ return hasExternalLinkage(prev);
+ }
+ return !decl->isInAnonymousNamespace();
+ }
+ }
+ return true;
+}
+
+class ConstStringVar:
+ public RecursiveASTVisitor<ConstStringVar>, public loplugin::Plugin
+{
+public:
+ explicit ConstStringVar(InstantiationData const & data): Plugin(data) {}
+
+ void run() override {
+ if (compiler.getLangOpts().CPlusPlus) {
+ // clang::Expr::isCXX11ConstantExpr only works for C++
+ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+ for (auto v: vars_) {
+ report(
+ DiagnosticsEngine::Warning,
+ "variable is only used as rvalue, should be const",
+ v->getLocation())
+ << v->getSourceRange();
+ }
+ }
+ }
+
+ bool TraverseImplicitCastExpr(ImplicitCastExpr * expr) {
+ bool pushed = false;
+ if (expr->getCastKind() == CK_LValueToRValue) {
+ if (auto dr = dyn_cast<DeclRefExpr>(
+ expr->getSubExpr()->IgnoreParenImpCasts()))
+ {
+ if (auto vd = dyn_cast<VarDecl>(dr->getDecl())) {
+ if (vars_.find(vd->getCanonicalDecl()) != vars_.end()) {
+ casted_.push(dr);
+ pushed = true;
+ }
+ }
+ }
+ }
+ bool b = RecursiveASTVisitor::TraverseImplicitCastExpr(expr);
+ if (pushed) {
+ casted_.pop();
+ }
+ return b;
+ }
+
+ bool VisitVarDecl(VarDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+ if (decl != decl->getCanonicalDecl()) {
+ return true;
+ }
+ if (isa<ParmVarDecl>(decl) || hasExternalLinkage(decl)) {
+ return true;
+ }
+ if (!loplugin::TypeCheck(decl->getType()).NonConstVolatile().Pointer()
+ .Const().Char())
+ {
+ return true;
+ }
+ auto init = decl->getAnyInitializer();
+ if (init == nullptr) {
+ return true;
+ }
+ APValue v;
+ if (!init->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
+ return true;
+ }
+ vars_.insert(decl);
+ return true;
+ }
+
+ bool VisitDeclRefExpr(DeclRefExpr const * expr) {
+ if (!casted_.empty() && expr == casted_.top()) {
+ return true;
+ }
+ auto vd = dyn_cast<VarDecl>(expr->getDecl());
+ if (vd == nullptr) {
+ return true;
+ }
+ vars_.erase(vd->getCanonicalDecl());
+ return true;
+ }
+
+private:
+ std::set<VarDecl const *> vars_;
+ std::stack<DeclRefExpr const *> casted_;
+};
+
+loplugin::Plugin::Registration<ConstStringVar> X("conststringvar");
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */