1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
15 Expr
const * stripCtor(Expr
const * expr
) {
17 auto const e1
= dyn_cast
<CXXFunctionalCastExpr
>(e0
);
19 e0
= e1
->getSubExpr()->IgnoreParenImpCasts();
21 auto const e2
= dyn_cast
<CXXBindTemporaryExpr
>(e0
);
25 auto const e3
= dyn_cast
<CXXConstructExpr
>(
26 e2
->getSubExpr()->IgnoreParenImpCasts());
30 auto qt
= loplugin::DeclCheck(e3
->getConstructor());
31 if (!((qt
.MemberFunction().Class("OString").Namespace("rtl")
33 || (qt
.MemberFunction().Class("OUString").Namespace("rtl")
38 if (e3
->getNumArgs() != 2) {
41 return e3
->getArg(0)->IgnoreParenImpCasts();
45 public RecursiveASTVisitor
<StringConcat
>, public loplugin::Plugin
48 explicit StringConcat(loplugin::InstantiationData
const & data
):
52 { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
54 bool VisitCallExpr(CallExpr
const * expr
);
57 bool isStringLiteral(Expr
const * expr
);
60 bool StringConcat::VisitCallExpr(CallExpr
const * expr
) {
61 if (ignoreLocation(expr
)) {
64 FunctionDecl
const * fdecl
= expr
->getDirectCallee();
65 if (fdecl
== nullptr) {
68 OverloadedOperatorKind oo
= fdecl
->getOverloadedOperator();
69 if ((oo
!= OverloadedOperatorKind::OO_Plus
70 && oo
!= OverloadedOperatorKind::OO_LessLess
)
71 || fdecl
->getNumParams() != 2 || expr
->getNumArgs() != 2
72 || !isStringLiteral(expr
->getArg(1)->IgnoreParenImpCasts()))
76 SourceLocation leftLoc
;
77 auto const leftExpr
= expr
->getArg(0)->IgnoreParenImpCasts();
78 if (isStringLiteral(leftExpr
)) {
79 leftLoc
= leftExpr
->getLocStart();
81 CallExpr
const * left
= dyn_cast
<CallExpr
>(leftExpr
);
82 if (left
== nullptr) {
85 FunctionDecl
const * ldecl
= left
->getDirectCallee();
86 if (ldecl
== nullptr) {
89 OverloadedOperatorKind loo
= ldecl
->getOverloadedOperator();
90 if ((loo
!= OverloadedOperatorKind::OO_Plus
91 && loo
!= OverloadedOperatorKind::OO_LessLess
)
92 || ldecl
->getNumParams() != 2 || left
->getNumArgs() != 2
93 || !isStringLiteral(left
->getArg(1)->IgnoreParenImpCasts()))
97 leftLoc
= left
->getArg(1)->getLocStart();
100 compiler
.getSourceManager().getFilename(
101 compiler
.getSourceManager().getSpellingLoc(expr
->getLocStart())) };
102 if (loplugin::isSamePathname(
103 name
, SRCDIR
"/sal/qa/rtl/strings/test_ostring_concat.cxx")
104 || loplugin::isSamePathname(
105 name
, SRCDIR
"/sal/qa/rtl/strings/test_oustring_concat.cxx"))
109 CXXOperatorCallExpr
const * op
= dyn_cast
<CXXOperatorCallExpr
>(expr
);
111 DiagnosticsEngine::Warning
,
112 "replace '%0' between string literals with juxtaposition",
113 op
== nullptr ? expr
->getExprLoc() : op
->getOperatorLoc())
114 << (oo
== OverloadedOperatorKind::OO_Plus
? "+" : "<<")
115 << SourceRange(leftLoc
, expr
->getArg(1)->getLocEnd());
119 bool StringConcat::isStringLiteral(Expr
const * expr
) {
120 expr
= stripCtor(expr
);
121 if (!isa
<clang::StringLiteral
>(expr
)) {
124 // OSL_THIS_FUNC may be defined as "" in include/osl/diagnose.h, so don't
125 // warn about expressions like 'SAL_INFO(..., OSL_THIS_FUNC << ":")' or
126 // 'OUString(OSL_THIS_FUNC) + ":"':
127 auto loc
= expr
->getLocStart();
128 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
129 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
131 return !compiler
.getSourceManager().isMacroBodyExpansion(loc
)
132 || (Lexer::getImmediateMacroName(
133 loc
, compiler
.getSourceManager(), compiler
.getLangOpts())
137 loplugin::Plugin::Registration
<StringConcat
> X("stringconcat");
141 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */