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 <iostream>
13 #include <unordered_map>
14 
15 #include "plugin.hxx"
16 
17 #include <clang/Frontend/CompilerInstance.h>
18 #include <clang/Frontend/FrontendActions.h>
19 #include <clang/Tooling/CommonOptionsParser.h>
20 #include <clang/Tooling/Refactoring.h>
21 #include <llvm/Support/Signals.h>
22 
23 /// Finds duplicated preprocessor defines, which generally indicate that some definition
24 /// needs to be centralised somewhere.
25 
26 namespace loplugin
27 {
28 struct Entry
29 {
30     clang::SourceLocation m_aLoc;
31 };
32 
33 class DuplicateDefines : public clang::PPCallbacks, public Plugin
34 {
35 public:
36     explicit DuplicateDefines(const InstantiationData& data);
37     virtual void run() override;
38     void MacroDefined(const Token& MacroNameTok, const MacroDirective* MD) override;
39     void MacroUndefined(const Token& MacroNameTok, const MacroDefinition& MD,
40                         const MacroDirective* Undef) override;
41     enum
42     {
43         isPPCallback = true
44     };
45 
46 private:
47     clang::Preprocessor& m_rPP;
48     std::unordered_map<std::string, Entry> m_aDefMap;
49 };
50 
DuplicateDefines(const InstantiationData & data)51 DuplicateDefines::DuplicateDefines(const InstantiationData& data)
52     : Plugin(data)
53     , m_rPP(compiler.getPreprocessor())
54 {
55     compiler.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(this));
56 }
57 
run()58 void DuplicateDefines::run()
59 {
60     // nothing, only check preprocessor usage
61 }
62 
MacroDefined(const Token & rMacroNameTok,const MacroDirective *)63 void DuplicateDefines::MacroDefined(const Token& rMacroNameTok, const MacroDirective*)
64 {
65     auto aLoc = rMacroNameTok.getLocation();
66     if (ignoreLocation(aLoc))
67         return;
68 
69     std::string aMacroName = m_rPP.getSpelling(rMacroNameTok);
70 
71     // some testing macro
72     if (aMacroName == "RTL_STRING_CONST_FUNCTION")
73         return;
74     if (aMacroName == "rtl")
75         return;
76     // we replicate this macro in all the .hrc files
77     if (aMacroName == "NC_")
78         return;
79     // TODO no obvious fix for these
80     if (aMacroName == "FID_SEARCH_NOW" || aMacroName == "FID_SVX_START" || aMacroName == "FN_PARAM")
81         return;
82 
83     if (!m_aDefMap.emplace(aMacroName, Entry{ aLoc }).second)
84     {
85         report(DiagnosticsEngine::Warning, "duplicate defines", aLoc);
86         report(DiagnosticsEngine::Note, "previous define", m_aDefMap[aMacroName].m_aLoc);
87         return;
88     }
89 }
90 
MacroUndefined(const Token & rMacroNameTok,const MacroDefinition &,const MacroDirective *)91 void DuplicateDefines::MacroUndefined(const Token& rMacroNameTok, const MacroDefinition& /*MD*/,
92                                       const MacroDirective* /*Undef*/)
93 {
94     auto aLoc = rMacroNameTok.getLocation();
95     if (ignoreLocation(aLoc))
96         return;
97 
98     std::string aMacroName = m_rPP.getSpelling(rMacroNameTok);
99     m_aDefMap.erase(aMacroName);
100 }
101 
102 static Plugin::Registration<DuplicateDefines> X("duplicatedefines", false);
103 }
104 
105 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
106