1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * Based on LLVM/Clang.
6 *
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
9 *
10 */
11
12 #include <cassert>
13 #include <iostream>
14 #include <unordered_map>
15
16 #include "plugin.hxx"
17
18 #include <clang/Frontend/CompilerInstance.h>
19 #include <clang/Frontend/FrontendActions.h>
20 #include <clang/Tooling/CommonOptionsParser.h>
21 #include <clang/Tooling/Refactoring.h>
22 #include <llvm/Support/Signals.h>
23
24 /// Finds duplicated preprocessor defines, which generally indicate that some definition
25 /// needs to be centralised somewhere.
26
27 namespace
28 {
29 struct Entry
30 {
31 clang::SourceLocation m_aLoc;
32 };
33
34 class DuplicateDefines : public clang::PPCallbacks, public loplugin::Plugin
35 {
36 public:
37 explicit DuplicateDefines(const loplugin::InstantiationData& data);
38 virtual void run() override;
39 void MacroDefined(const Token& MacroNameTok, const MacroDirective* MD) override;
40 void MacroUndefined(const Token& MacroNameTok, const MacroDefinition& MD,
41 const MacroDirective* Undef) override;
42 enum
43 {
44 isPPCallback = true
45 };
46
47 private:
48 clang::Preprocessor& m_rPP;
49 std::unordered_map<std::string, Entry> m_aDefMap;
50 };
51
DuplicateDefines(const loplugin::InstantiationData & data)52 DuplicateDefines::DuplicateDefines(const loplugin::InstantiationData& data)
53 : Plugin(data)
54 , m_rPP(compiler.getPreprocessor())
55 {
56 compiler.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(this));
57 }
58
run()59 void DuplicateDefines::run()
60 {
61 // nothing, only check preprocessor usage
62 }
63
MacroDefined(const Token & rMacroNameTok,const MacroDirective *)64 void DuplicateDefines::MacroDefined(const Token& rMacroNameTok, const MacroDirective*)
65 {
66 auto aLoc = rMacroNameTok.getLocation();
67 if (ignoreLocation(aLoc))
68 return;
69
70 std::string aMacroName = m_rPP.getSpelling(rMacroNameTok);
71
72 // some testing macro
73 if (aMacroName == "RTL_STRING_CONST_FUNCTION")
74 return;
75 if (aMacroName == "rtl")
76 return;
77 // we replicate these macros in all the .hrc files
78 if (aMacroName == "NC_" || aMacroName == "NNC_")
79 return;
80 // We define this prior to including <windows.h>:
81 if (aMacroName == "WIN32_LEAN_AND_MEAN")
82 {
83 return;
84 }
85 // TODO no obvious fix for these
86 if (aMacroName == "FID_SEARCH_NOW" || aMacroName == "FID_SVX_START" || aMacroName == "FN_PARAM")
87 return;
88 // ignore for now, requires adding too many includes to sw/
89 if (aMacroName == "MM50")
90 return;
91
92 // ignore for now, we have the same define in svx and sw, but I can't remove one of them because
93 // they reference strings in different resource bundles
94 if (aMacroName == "STR_UNDO_COL_DELETE" || aMacroName == "STR_UNDO_ROW_DELETE"
95 || aMacroName == "STR_TABLE_NUMFORMAT" || aMacroName == "STR_DELETE")
96 return;
97
98 if (m_aDefMap.emplace(aMacroName, Entry{ aLoc }).second)
99 {
100 return;
101 }
102
103 // Happens e.g. with macros defined in include/premac.h, which is intended to be included
104 // (without include guards) multiple times:
105 auto const other = m_aDefMap[aMacroName].m_aLoc;
106 assert(aLoc == compiler.getSourceManager().getSpellingLoc(aLoc));
107 assert(other == compiler.getSourceManager().getSpellingLoc(other));
108 if ((compiler.getSourceManager().getFilename(aLoc)
109 == compiler.getSourceManager().getFilename(other))
110 && (compiler.getSourceManager().getSpellingLineNumber(aLoc)
111 == compiler.getSourceManager().getSpellingLineNumber(other))
112 && (compiler.getSourceManager().getSpellingColumnNumber(aLoc)
113 == compiler.getSourceManager().getSpellingColumnNumber(other)))
114 {
115 return;
116 }
117
118 report(DiagnosticsEngine::Warning, "duplicate defines", aLoc);
119 report(DiagnosticsEngine::Note, "previous define", other);
120 }
121
MacroUndefined(const Token & rMacroNameTok,const MacroDefinition &,const MacroDirective *)122 void DuplicateDefines::MacroUndefined(const Token& rMacroNameTok, const MacroDefinition& /*MD*/,
123 const MacroDirective* /*Undef*/)
124 {
125 auto aLoc = rMacroNameTok.getLocation();
126 if (ignoreLocation(aLoc))
127 return;
128
129 std::string aMacroName = m_rPP.getSpelling(rMacroNameTok);
130 m_aDefMap.erase(aMacroName);
131 }
132
133 loplugin::Plugin::Registration<DuplicateDefines> X("duplicatedefines", true);
134 }
135
136 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
137