1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 // Find constant character array variables that are either
11 // (a) passed into O[U]String constructors
12 // (b) assigned to O[U]String
13 // and are declared using macro names
14 // and should thus be turned into O[U]StringLiteral variables.
24 class StringLiteralDefine final
: public loplugin::FilteringPlugin
<StringLiteralDefine
>
27 explicit StringLiteralDefine(loplugin::InstantiationData
const& data
)
28 : FilteringPlugin(data
)
32 bool TraverseInitListExpr(InitListExpr
* expr
, DataRecursionQueue
* queue
= nullptr)
34 return WalkUpFromInitListExpr(expr
)
35 && TraverseSynOrSemInitListExpr(
36 expr
->isSemanticForm() ? expr
: expr
->getSemanticForm(), queue
);
39 bool VisitCXXConstructExpr(CXXConstructExpr
const* expr
)
41 if (ignoreLocation(expr
))
43 loplugin::TypeCheck
const tc(expr
->getType());
44 if (!(tc
.Class("OString").Namespace("rtl").GlobalNamespace()
45 || tc
.Class("OUString").Namespace("rtl").GlobalNamespace()))
49 auto const ctor
= expr
->getConstructor();
50 if (ctor
->getNumParams() != 2)
53 const Expr
* arg0
= expr
->getArg(0)->IgnoreParenImpCasts();
54 auto const e1
= dyn_cast
<clang::StringLiteral
>(arg0
);
57 auto argLoc
= compat::getBeginLoc(arg0
);
58 // check if the arg is a macro
59 auto macroLoc
= compiler
.getSourceManager().getSpellingLoc(argLoc
);
60 if (argLoc
== macroLoc
)
62 // check if it is the right kind of macro (not particularly reliable checks)
63 if (!macroLoc
.isValid() || !compiler
.getSourceManager().isInMainFile(macroLoc
)
64 || compiler
.getSourceManager().isInSystemHeader(macroLoc
)
65 // not sure when these became available
66 #if CLANG_VERSION >= 130000
67 || compiler
.getSourceManager().isWrittenInBuiltinFile(macroLoc
)
68 || compiler
.getSourceManager().isWrittenInScratchSpace(macroLoc
)
69 || compiler
.getSourceManager().isWrittenInCommandLineFile(macroLoc
)
71 || isInUnoIncludeFile(macroLoc
))
73 StringRef fileName
= getFilenameOfLocation(macroLoc
);
74 StringRef name
{ Lexer::getImmediateMacroName(
75 compat::getBeginLoc(arg0
), compiler
.getSourceManager(), compiler
.getLangOpts()) };
76 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/config_host/"))
78 // used in both OUString and OString context
79 if (name
== "FM_COL_LISTBOX" || name
== "HID_RELATIONDIALOG_LEFTFIELDCELL"
80 || name
== "OOO_HELP_INDEX" || name
== "IMP_PNG" || name
.startswith("MNI_ACTION_"))
82 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/svx/source/stbctrls/pszctrl.cxx"))
84 // used as a prefix and/or concatenated with other strings
85 if (name
.startswith("UNO_JAVA_JFW") || name
== "SETNODE_BINDINGS" || name
== "PATHDELIMITER"
86 || name
== "SETNODE_ALLFILEFORMATS" || name
== "SETNODE_DISABLED"
87 || name
== "XMLNS_DIALOGS_PREFIX" || name
== "XMLNS_LIBRARY_PREFIX"
88 || name
== "XMLNS_SCRIPT_PREFIX" || name
== "XMLNS_TOOLBAR" || name
== "XMLNS_XLINK"
89 || name
== "XMLNS_XLINK_PREFIX")
91 if (loplugin::hasPathnamePrefix(fileName
,
92 SRCDIR
"/stoc/source/security/access_controller.cxx")
93 && (name
== "SERVICE_NAME" || name
== "USER_CREDS"))
95 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/stoc/source/security/file_policy.cxx")
96 && name
== "IMPL_NAME")
98 if (loplugin::hasPathnamePrefix(fileName
,
99 SRCDIR
"/desktop/source/migration/services/jvmfwk.cxx")
100 && name
== "IMPL_NAME")
102 if (loplugin::hasPathnamePrefix(
103 fileName
, SRCDIR
"/xmlsecurity/source/xmlsec/xmldocumentwrapper_xmlsecimpl.cxx")
104 && name
== "STRXMLNS")
106 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/sw/source/ui/fldui/fldvar.cxx")
107 && name
== "USER_DATA_VERSION_1")
109 // not sure how to exclude the case where the whole block is in a macro
110 // (vs. what I am looking for - regular code with a macro name as the argument)
111 if (name
== "assert" || name
== "SAL_INFO" || name
== "DECLIMPL_SERVICEINFO_DERIVED"
112 || name
== "OSL_VERIFY" || name
== "OSL_ENSURE" || name
== "DECL_PROP_2"
113 || name
== "DECL_PROP_3" || name
== "DECL_PROP_1" || name
== "DECL_DEP_PROP_2"
114 || name
== "DECL_DEP_PROP_3" || name
== "CALL_ELEMENT_HANDLER_AND_CARE_FOR_EXCEPTIONS"
115 || name
== "IMPLEMENT_SERVICE_INFO" || name
== "SQL_GET_REFERENCES"
116 || name
== "SFX_IMPL_OBJECTFACTORY" || name
== "IMPLEMENT_SERVICE_INFO1"
117 || name
== "IMPLEMENT_SERVICE_INFO2" || name
== "IMPLEMENT_SERVICE_INFO3"
118 || name
== "IMPLEMENT_SERVICE_INFO_IMPLNAME" || name
== "SC_SIMPLE_SERVICE_INFO"
119 || name
== "SC_SIMPLE_SERVICE_INFO_COMPAT" || name
== "OUT_COMMENT"
120 || name
== "LOCALE_EN" || name
== "LOCALE" || name
== "VBAFONTBASE_PROPNAME"
121 || name
== "VBAHELPER_IMPL_XHELPERINTERFACE" || name
== "IMPRESS_MAP_ENTRIES"
122 || name
== "DRAW_MAP_ENTRIES" || name
== "DRAW_PAGE_NOTES_PROPERTIES"
123 || name
== "COMMON_FLDTYP_PROPERTIES" || name
== "GRAPHIC_PAGE_PROPERTIES"
124 || name
== "makeDelay" || name
== "makeEvent" || name
== "OOO_IMPORTER"
125 || name
== "DBG_ASSERT" || name
.startswith("CPPUNIT_ASSERT"))
127 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
128 "/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx")
129 && name
== "DEFAULT_SIZE")
131 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/filter/source/t602/t602filter.cxx"))
133 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/hwpfilter/source/formula.cxx"))
135 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/hwpfilter/source/hwpreader.cxx"))
137 if (loplugin::hasPathnamePrefix(fileName
, SRCDIR
"/filter/source/svg/svgexport.cxx")
138 && name
== "NSPREFIX")
141 if (!reported_
.insert(macroLoc
).second
)
144 report(DiagnosticsEngine::Warning
,
145 "change macro '%0' to 'constexpr "
146 "%select{OStringLiteral|OUStringLiteral}1'",
148 << name
<< (tc
.Class("OString").Namespace("rtl").GlobalNamespace() ? 0 : 1);
149 report(DiagnosticsEngine::Note
, "macro used here", compat::getBeginLoc(arg0
))
150 << arg0
->getSourceRange();
154 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
161 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
165 std::set
<SourceLocation
> reported_
;
168 // Off by default because it needs some hand-holding
169 static loplugin::Plugin::Registration
<StringLiteralDefine
> reg("stringliteraldefine", false);
172 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */