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 #ifndef LO_CLANG_SHARED_PLUGINS
13 
14 #include "check.hxx"
15 #include "plugin.hxx"
16 #include <clang/Lex/Lexer.h>
17 #include <fstream>
18 #include <set>
19 
20 namespace loplugin
21 {
22 /*
23 This is a compile check.
24 
25 Check that DBG_UNHANDLED_EXCEPTION is always the first statement in a catch block, otherwise
26 it does not work properly.
27 */
28 
29 class DbgUnhandledException : public loplugin::FilteringPlugin<DbgUnhandledException>
30 {
31 public:
32     explicit DbgUnhandledException(InstantiationData const& data);
33     virtual void run() override;
34     bool VisitCallExpr(CallExpr const* call);
35     bool TraverseCXXCatchStmt(CXXCatchStmt*);
36     bool PreTraverseCXXCatchStmt(CXXCatchStmt*);
37     bool PostTraverseCXXCatchStmt(CXXCatchStmt*, bool traverseOk);
38 
39 private:
40     std::stack<CXXCatchStmt const*> currCatchStmt;
41 };
42 
DbgUnhandledException(const InstantiationData & data)43 DbgUnhandledException::DbgUnhandledException(const InstantiationData& data)
44     : FilteringPlugin(data)
45 {
46 }
47 
run()48 void DbgUnhandledException::run()
49 {
50     TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
51 }
52 
PreTraverseCXXCatchStmt(CXXCatchStmt * catchStmt)53 bool DbgUnhandledException::PreTraverseCXXCatchStmt(CXXCatchStmt* catchStmt)
54 {
55     currCatchStmt.push(catchStmt);
56     return true;
57 }
58 
PostTraverseCXXCatchStmt(CXXCatchStmt * catchStmt,bool)59 bool DbgUnhandledException::PostTraverseCXXCatchStmt(CXXCatchStmt* catchStmt, bool)
60 {
61     assert(currCatchStmt.top() == catchStmt);
62     (void)catchStmt;
63     currCatchStmt.pop();
64     return true;
65 }
66 
TraverseCXXCatchStmt(CXXCatchStmt * catchStmt)67 bool DbgUnhandledException::TraverseCXXCatchStmt(CXXCatchStmt* catchStmt)
68 {
69     if (!PreTraverseCXXCatchStmt(catchStmt))
70         return false;
71     bool ret = RecursiveASTVisitor::TraverseCXXCatchStmt(catchStmt);
72     if (!PostTraverseCXXCatchStmt(catchStmt, ret))
73         return false;
74     return ret;
75 }
76 
VisitCallExpr(const CallExpr * call)77 bool DbgUnhandledException::VisitCallExpr(const CallExpr* call)
78 {
79     if (ignoreLocation(call))
80         return true;
81     const FunctionDecl* func = call->getDirectCallee();
82     if (!func)
83         return true;
84 
85     if (!func->getIdentifier() || func->getName() != "DbgUnhandledException")
86         return true;
87 
88     if (currCatchStmt.empty())
89     {
90         report(DiagnosticsEngine::Warning, "DBG_UNHANDLED_EXCEPTION outside catch block",
91                compat::getBeginLoc(call));
92         return true;
93     }
94     auto catchBlock = dyn_cast<CompoundStmt>(currCatchStmt.top()->getHandlerBlock());
95     if (!catchBlock)
96     {
97         report(DiagnosticsEngine::Warning,
98                "something wrong with DBG_UNHANDLED_EXCEPTION, no CompoundStmt?",
99                compat::getBeginLoc(call));
100         return true;
101     }
102     if (catchBlock->size() < 1)
103     {
104         report(DiagnosticsEngine::Warning,
105                "something wrong with DBG_UNHANDLED_EXCEPTION, CompoundStmt size == 0?",
106                compat::getBeginLoc(call));
107         return true;
108     }
109 
110     Stmt const* firstStmt = *catchBlock->body_begin();
111     if (auto exprWithCleanups = dyn_cast<ExprWithCleanups>(firstStmt))
112         firstStmt = exprWithCleanups->getSubExpr();
113     if (firstStmt != call)
114     {
115         report(DiagnosticsEngine::Warning,
116                "DBG_UNHANDLED_EXCEPTION must be first statement in catch block",
117                compat::getBeginLoc(call));
118     }
119     return true;
120 }
121 
122 static Plugin::Registration<DbgUnhandledException> dbgunhandledexception("dbgunhandledexception");
123 
124 } // namespace
125 
126 #endif // LO_CLANG_SHARED_PLUGINS
127 
128 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
129