1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* -*- 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.
 *
 */

/*
This is a compile check.

Warns about 'auto' declarations becoming rtl::OUStringConcat, such as
auto str = "string" + OUString::number( 10 );
The type of the expression is rtl::OUStringConcat and those refer to temporaries
and so their lifecycle should not extend the lifecycle of those temporaries.
*/

#ifndef LO_CLANG_SHARED_PLUGINS

#include "plugin.hxx"
#include "check.hxx"

namespace loplugin
{

class StringConcatAuto
    : public FilteringPlugin< StringConcatAuto >
    {
    public:
        StringConcatAuto( const InstantiationData& data );
        virtual void run() override;
        bool shouldVisitTemplateInstantiations () const { return true; }
        bool VisitVarDecl( const VarDecl* decl );
        bool VisitFunctionDecl( const FunctionDecl* decl );
    private:
        enum class Check { Var, Return };
        bool checkDecl( const DeclaratorDecl* decl, const QualType type, const SourceRange& range, Check check );
    };

StringConcatAuto::StringConcatAuto( const InstantiationData& data )
    : FilteringPlugin( data )
    {
    }

void StringConcatAuto::run()
    {
    TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
    }

bool StringConcatAuto::VisitVarDecl( const VarDecl* decl )
    {
    return checkDecl( decl, decl->getType(),
        decl->getTypeSourceInfo()
            ? decl->getTypeSourceInfo()->getTypeLoc().getSourceRange()
            : decl->getSourceRange(),
        Check::Var );
    }

bool StringConcatAuto::VisitFunctionDecl( const FunctionDecl* decl )
    {
    return checkDecl( decl, decl->getReturnType(), decl->getReturnTypeSourceRange(), Check::Return );
    }

bool StringConcatAuto::checkDecl( const DeclaratorDecl* decl, QualType type, const SourceRange& range, Check check )
    {
    if( ignoreLocation( decl ))
        return true;
    if( isa< ParmVarDecl >( decl )) // parameters should be fine, temporaries should exist during the call
        return true;
    std::string fileName = getFilenameOfLocation(
        compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))).str();
    loplugin::normalizeDotDotInFilePath(fileName);
    if (loplugin::isSamePathname(fileName, SRCDIR "/include/rtl/string.hxx")
        || loplugin::isSamePathname(fileName, SRCDIR "/include/rtl/ustring.hxx")
        || loplugin::isSamePathname(fileName, SRCDIR "/include/rtl/strbuf.hxx")
        || loplugin::isSamePathname(fileName, SRCDIR "/include/rtl/ustrbuf.hxx")
        || loplugin::isSamePathname(fileName, SRCDIR "/include/rtl/stringconcat.hxx"))
        return true;
    auto const tc = loplugin::TypeCheck( type.getNonReferenceType().getCanonicalType());
    const char* typeString = nullptr;
    if( tc.Struct("OUStringConcat").Namespace("rtl").GlobalNamespace())
        typeString = "OUString";
    else if( tc.Struct("OStringConcat").Namespace("rtl").GlobalNamespace())
        typeString = "OString";
    else if( tc.Struct("OUStringNumber").Namespace("rtl").GlobalNamespace())
        typeString = "OUString";
    else if( tc.Struct("OStringNumber").Namespace("rtl").GlobalNamespace())
        typeString = "OString";
    else
        return true;
    report( DiagnosticsEngine::Warning,
        check == Check::Var
            ? "creating a variable of type %0 will make it reference temporaries"
            : "returning a variable of type %0 will make it reference temporaries",
        decl->getLocation())
        << type;
    report( DiagnosticsEngine::Note,
        "use %0 instead",
        range.getBegin())
        << typeString
        << FixItHint::CreateReplacement( range, typeString );
    return true;
    }

static Plugin::Registration< StringConcatAuto > stringconcatauto( "stringconcatauto" );

} // namespace

#endif // LO_CLANG_SHARED_PLUGINS

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */