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