Bug Summary

File:build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
Warning:line 158, column 9
Value stored to 'ThrownType' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ExceptionAnalyzer.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-15/lib/clang/15.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clang-tidy/utils -I /build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/clang-tools-extra/clang-tidy/utils -I tools/clang/tools/extra/clang-tidy -I /build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/clang/include -I tools/clang/include -I include -I /build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-15/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-04-20-140412-16051-1 -x c++ /build/llvm-toolchain-snapshot-15~++20220420111733+e13d2efed663/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
1//===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ExceptionAnalyzer.h"
10
11namespace clang {
12namespace tidy {
13namespace utils {
14
15void ExceptionAnalyzer::ExceptionInfo::registerException(
16 const Type *ExceptionType) {
17 assert(ExceptionType != nullptr && "Only valid types are accepted")(static_cast <bool> (ExceptionType != nullptr &&
"Only valid types are accepted") ? void (0) : __assert_fail (
"ExceptionType != nullptr && \"Only valid types are accepted\""
, "clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp",
17, __extension__ __PRETTY_FUNCTION__))
;
18 Behaviour = State::Throwing;
19 ThrownExceptions.insert(ExceptionType);
20}
21
22void ExceptionAnalyzer::ExceptionInfo::registerExceptions(
23 const Throwables &Exceptions) {
24 if (Exceptions.size() == 0)
25 return;
26 Behaviour = State::Throwing;
27 ThrownExceptions.insert(Exceptions.begin(), Exceptions.end());
28}
29
30ExceptionAnalyzer::ExceptionInfo &ExceptionAnalyzer::ExceptionInfo::merge(
31 const ExceptionAnalyzer::ExceptionInfo &Other) {
32 // Only the following two cases require an update to the local
33 // 'Behaviour'. If the local entity is already throwing there will be no
34 // change and if the other entity is throwing the merged entity will throw
35 // as well.
36 // If one of both entities is 'Unknown' and the other one does not throw
37 // the merged entity is 'Unknown' as well.
38 if (Other.Behaviour == State::Throwing)
39 Behaviour = State::Throwing;
40 else if (Other.Behaviour == State::Unknown && Behaviour == State::NotThrowing)
41 Behaviour = State::Unknown;
42
43 ContainsUnknown = ContainsUnknown || Other.ContainsUnknown;
44 ThrownExceptions.insert(Other.ThrownExceptions.begin(),
45 Other.ThrownExceptions.end());
46 return *this;
47}
48
49static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
50 const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
51 const auto *BaseClass = BaseType->getAsCXXRecordDecl();
52 if (!DerivedClass || !BaseClass)
53 return false;
54
55 return !DerivedClass->forallBases(
56 [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
57}
58
59bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(const Type *BaseClass) {
60 llvm::SmallVector<const Type *, 8> TypesToDelete;
61 for (const Type *T : ThrownExceptions) {
62 if (T == BaseClass || isBaseOf(T, BaseClass))
63 TypesToDelete.push_back(T);
64 }
65
66 for (const Type *T : TypesToDelete)
67 ThrownExceptions.erase(T);
68
69 reevaluateBehaviour();
70 return TypesToDelete.size() > 0;
71}
72
73ExceptionAnalyzer::ExceptionInfo &
74ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
75 const llvm::StringSet<> &IgnoredTypes, bool IgnoreBadAlloc) {
76 llvm::SmallVector<const Type *, 8> TypesToDelete;
77 // Note: Using a 'SmallSet' with 'llvm::remove_if()' is not possible.
78 // Therefore this slightly hacky implementation is required.
79 for (const Type *T : ThrownExceptions) {
80 if (const auto *TD = T->getAsTagDecl()) {
81 if (TD->getDeclName().isIdentifier()) {
82 if ((IgnoreBadAlloc &&
83 (TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
84 (IgnoredTypes.count(TD->getName()) > 0))
85 TypesToDelete.push_back(T);
86 }
87 }
88 }
89 for (const Type *T : TypesToDelete)
90 ThrownExceptions.erase(T);
91
92 reevaluateBehaviour();
93 return *this;
94}
95
96void ExceptionAnalyzer::ExceptionInfo::clear() {
97 Behaviour = State::NotThrowing;
98 ContainsUnknown = false;
99 ThrownExceptions.clear();
100}
101
102void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
103 if (ThrownExceptions.size() == 0)
104 if (ContainsUnknown)
105 Behaviour = State::Unknown;
106 else
107 Behaviour = State::NotThrowing;
108 else
109 Behaviour = State::Throwing;
110}
111
112ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
113 const FunctionDecl *Func,
114 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
115 if (CallStack.count(Func))
116 return ExceptionInfo::createNonThrowing();
117
118 if (const Stmt *Body = Func->getBody()) {
119 CallStack.insert(Func);
120 ExceptionInfo Result =
121 throwsException(Body, ExceptionInfo::Throwables(), CallStack);
122
123 // For a constructor, we also have to check the initializers.
124 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
125 for (const CXXCtorInitializer *Init : Ctor->inits()) {
126 ExceptionInfo Excs = throwsException(
127 Init->getInit(), ExceptionInfo::Throwables(), CallStack);
128 Result.merge(Excs);
129 }
130 }
131
132 CallStack.erase(Func);
133 return Result;
134 }
135
136 auto Result = ExceptionInfo::createUnknown();
137 if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
138 for (const QualType &Ex : FPT->exceptions())
139 Result.registerException(Ex.getTypePtr());
140 }
141 return Result;
142}
143
144/// Analyzes a single statement on it's throwing behaviour. This is in principle
145/// possible except some 'Unknown' functions are called.
146ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
147 const Stmt *St, const ExceptionInfo::Throwables &Caught,
148 llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
149 auto Results = ExceptionInfo::createNonThrowing();
150 if (!St)
151 return Results;
152
153 if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
154 if (const auto *ThrownExpr = Throw->getSubExpr()) {
155 const auto *ThrownType =
156 ThrownExpr->getType()->getUnqualifiedDesugaredType();
157 if (ThrownType->isReferenceType())
158 ThrownType = ThrownType->castAs<ReferenceType>()
Value stored to 'ThrownType' is never read
159 ->getPointeeType()
160 ->getUnqualifiedDesugaredType();
161 Results.registerException(
162 ThrownExpr->getType()->getUnqualifiedDesugaredType());
163 } else
164 // A rethrow of a caught exception happens which makes it possible
165 // to throw all exception that are caught in the 'catch' clause of
166 // the parent try-catch block.
167 Results.registerExceptions(Caught);
168 } else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
169 ExceptionInfo Uncaught =
170 throwsException(Try->getTryBlock(), Caught, CallStack);
171 for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
172 const CXXCatchStmt *Catch = Try->getHandler(I);
173
174 // Everything is catched through 'catch(...)'.
175 if (!Catch->getExceptionDecl()) {
176 ExceptionInfo Rethrown = throwsException(
177 Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
178 Results.merge(Rethrown);
179 Uncaught.clear();
180 } else {
181 const auto *CaughtType =
182 Catch->getCaughtType()->getUnqualifiedDesugaredType();
183 if (CaughtType->isReferenceType()) {
184 CaughtType = CaughtType->castAs<ReferenceType>()
185 ->getPointeeType()
186 ->getUnqualifiedDesugaredType();
187 }
188
189 // If the caught exception will catch multiple previously potential
190 // thrown types (because it's sensitive to inheritance) the throwing
191 // situation changes. First of all filter the exception types and
192 // analyze if the baseclass-exception is rethrown.
193 if (Uncaught.filterByCatch(CaughtType)) {
194 ExceptionInfo::Throwables CaughtExceptions;
195 CaughtExceptions.insert(CaughtType);
196 ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
197 CaughtExceptions, CallStack);
198 Results.merge(Rethrown);
199 }
200 }
201 }
202 Results.merge(Uncaught);
203 } else if (const auto *Call = dyn_cast<CallExpr>(St)) {
204 if (const FunctionDecl *Func = Call->getDirectCallee()) {
205 ExceptionInfo Excs = throwsException(Func, CallStack);
206 Results.merge(Excs);
207 }
208 } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
209 ExceptionInfo Excs =
210 throwsException(Construct->getConstructor(), CallStack);
211 Results.merge(Excs);
212 } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
213 ExceptionInfo Excs =
214 throwsException(DefaultInit->getExpr(), Caught, CallStack);
215 Results.merge(Excs);
216 } else {
217 for (const Stmt *Child : St->children()) {
218 ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
219 Results.merge(Excs);
220 }
221 }
222 return Results;
223}
224
225ExceptionAnalyzer::ExceptionInfo
226ExceptionAnalyzer::analyzeImpl(const FunctionDecl *Func) {
227 ExceptionInfo ExceptionList;
228
229 // Check if the function has already been analyzed and reuse that result.
230 if (FunctionCache.count(Func) == 0) {
231 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
232 ExceptionList = throwsException(Func, CallStack);
233
234 // Cache the result of the analysis. This is done prior to filtering
235 // because it is best to keep as much information as possible.
236 // The results here might be relevant to different analysis passes
237 // with different needs as well.
238 FunctionCache.insert(std::make_pair(Func, ExceptionList));
239 } else
240 ExceptionList = FunctionCache[Func];
241
242 return ExceptionList;
243}
244
245ExceptionAnalyzer::ExceptionInfo
246ExceptionAnalyzer::analyzeImpl(const Stmt *Stmt) {
247 llvm::SmallSet<const FunctionDecl *, 32> CallStack;
248 return throwsException(Stmt, ExceptionInfo::Throwables(), CallStack);
249}
250
251template <typename T>
252ExceptionAnalyzer::ExceptionInfo
253ExceptionAnalyzer::analyzeDispatch(const T *Node) {
254 ExceptionInfo ExceptionList = analyzeImpl(Node);
255
256 if (ExceptionList.getBehaviour() == State::NotThrowing ||
257 ExceptionList.getBehaviour() == State::Unknown)
258 return ExceptionList;
259
260 // Remove all ignored exceptions from the list of exceptions that can be
261 // thrown.
262 ExceptionList.filterIgnoredExceptions(IgnoredExceptions, IgnoreBadAlloc);
263
264 return ExceptionList;
265}
266
267ExceptionAnalyzer::ExceptionInfo
268ExceptionAnalyzer::analyze(const FunctionDecl *Func) {
269 return analyzeDispatch(Func);
270}
271
272ExceptionAnalyzer::ExceptionInfo
273ExceptionAnalyzer::analyze(const Stmt *Stmt) {
274 return analyzeDispatch(Stmt);
275}
276
277} // namespace utils
278} // namespace tidy
279
280} // namespace clang