Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 1 | // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| 2 | // Exceptions. See /LICENSE for license information. |
| 3 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | |
| 5 | #include "nullability_verification/pointer_nullability_diagnosis.h" |
| 6 | |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 7 | #include <optional> |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 8 | #include <string> |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 9 | |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 10 | #include "nullability_verification/pointer_nullability.h" |
| 11 | #include "nullability_verification/pointer_nullability_matchers.h" |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 12 | #include "clang/AST/ASTContext.h" |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 13 | #include "clang/AST/DeclCXX.h" |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 14 | #include "clang/AST/Expr.h" |
Wei Yi Tee | 1bf1116 | 2022-08-26 03:16:53 -0700 | [diff] [blame] | 15 | #include "clang/AST/ExprCXX.h" |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 16 | #include "clang/AST/Stmt.h" |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 17 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
Wei Yi Tee | 217eb5f | 2022-09-15 03:18:28 -0700 | [diff] [blame] | 18 | #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 19 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 20 | #include "clang/Basic/Specifiers.h" |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 21 | |
| 22 | namespace clang { |
| 23 | namespace tidy { |
| 24 | namespace nullability { |
| 25 | |
| 26 | using ast_matchers::MatchFinder; |
Wei Yi Tee | 217eb5f | 2022-09-15 03:18:28 -0700 | [diff] [blame] | 27 | using dataflow::CFGMatchSwitchBuilder; |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 28 | using dataflow::Environment; |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 29 | using dataflow::TransferStateForDiagnostics; |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 30 | |
| 31 | namespace { |
| 32 | |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 33 | // Returns true if `Expr` is uninterpreted or known to be nullable. |
| 34 | bool isNullableOrUntracked(const Expr* E, const Environment& Env) { |
| 35 | auto* ActualVal = getPointerValueFromExpr(E, Env); |
Dani Ferreira Franco Moura | 58f7c2f | 2023-01-12 09:31:30 -0800 | [diff] [blame] | 36 | if (ActualVal == nullptr) { |
| 37 | llvm::dbgs() |
| 38 | << "The dataflow analysis framework does not model a PointerValue for " |
| 39 | "the following Expr, and thus its dereference is marked as unsafe:"; |
| 40 | E->dump(); |
| 41 | } |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 42 | return !ActualVal || isNullable(*ActualVal, Env); |
| 43 | } |
| 44 | |
| 45 | // Returns true if an uninterpreted or nullable `Expr` was assigned to a |
| 46 | // construct with a non-null `DeclaredType`. |
| 47 | bool isIncompatibleAssignment(QualType DeclaredType, const Expr* E, |
| 48 | const Environment& Env, ASTContext& Ctx) { |
| 49 | assert(DeclaredType->isAnyPointerType()); |
| 50 | return getNullabilityKind(DeclaredType, Ctx) == NullabilityKind::NonNull && |
| 51 | isNullableOrUntracked(E, Env); |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 52 | } |
| 53 | |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 54 | std::optional<CFGElement> diagnoseDereference( |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 55 | const UnaryOperator* UnaryOp, const MatchFinder::MatchResult&, |
| 56 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
| 57 | if (isNullableOrUntracked(UnaryOp->getSubExpr(), State.Env)) { |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 58 | return std::optional<CFGElement>(CFGStmt(UnaryOp)); |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 59 | } |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 60 | return std::nullopt; |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 61 | } |
| 62 | |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 63 | std::optional<CFGElement> diagnoseArrow( |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 64 | const MemberExpr* MemberExpr, const MatchFinder::MatchResult& Result, |
| 65 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
| 66 | if (isNullableOrUntracked(MemberExpr->getBase(), State.Env)) { |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 67 | return std::optional<CFGElement>(CFGStmt(MemberExpr)); |
Wei Yi Tee | 9c16161 | 2022-08-19 14:20:02 -0700 | [diff] [blame] | 68 | } |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 69 | return std::nullopt; |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 70 | } |
| 71 | |
Wei Yi Tee | 1bf1116 | 2022-08-26 03:16:53 -0700 | [diff] [blame] | 72 | bool isIncompatibleArgumentList(ArrayRef<QualType> ParamTypes, |
| 73 | ArrayRef<const Expr*> Args, |
| 74 | const Environment& Env, ASTContext& Ctx) { |
| 75 | assert(ParamTypes.size() == Args.size()); |
| 76 | for (unsigned int I = 0; I < Args.size(); ++I) { |
| 77 | auto ParamType = ParamTypes[I].getNonReferenceType(); |
| 78 | if (!ParamType->isAnyPointerType()) { |
| 79 | continue; |
| 80 | } |
| 81 | if (isIncompatibleAssignment(ParamType, Args[I], Env, Ctx)) { |
| 82 | return true; |
| 83 | } |
| 84 | } |
| 85 | return false; |
| 86 | } |
| 87 | |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 88 | NullabilityKind parseNullabilityKind(StringRef EnumName) { |
| 89 | return llvm::StringSwitch<NullabilityKind>(EnumName) |
| 90 | .Case("NK_nonnull", NullabilityKind::NonNull) |
| 91 | .Case("NK_nullable", NullabilityKind::Nullable) |
| 92 | .Case("NK_unspecified", NullabilityKind::Unspecified) |
| 93 | .Default(NullabilityKind::Unspecified); |
| 94 | } |
| 95 | |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 96 | /// Evaluates the `__assert_nullability` call by comparing the expected |
| 97 | /// nullability to the nullability computed by the dataflow analysis. |
| 98 | /// |
| 99 | /// If the function being diagnosed is called `__assert_nullability`, we assume |
| 100 | /// it is a call of the shape __assert_nullability<a, b, c, ...>(p), where `p` |
| 101 | /// is an expression that contains pointers and a, b, c ... represent each of |
| 102 | /// the NullabilityKinds in `p`'s expected nullability. An expression's |
| 103 | /// nullability can be expressed as a vector of NullabilityKinds, where each |
| 104 | /// vector element corresponds to one of the pointers contained in the |
| 105 | /// expression. |
| 106 | /// |
| 107 | /// For example: |
| 108 | /// \code |
| 109 | /// enum NullabilityKind { |
| 110 | /// NK_nonnull, |
| 111 | /// NK_nullable, |
| 112 | /// NK_unspecified, |
| 113 | /// }; |
| 114 | /// |
| 115 | /// template<NullabilityKind ...NK, typename T> |
| 116 | /// void __assert_nullability(T&); |
| 117 | /// |
| 118 | /// template<typename T0, typename T1> |
| 119 | /// struct Struct2Arg { |
| 120 | /// T0 arg0; |
| 121 | /// T1 arg1; |
| 122 | /// }; |
| 123 | /// |
| 124 | /// void target(Struct2Arg<int *, int * _Nullable> p) { |
| 125 | /// __assert_nullability<NK_unspecified, NK_nullable>(p); |
| 126 | /// } |
| 127 | /// \endcode |
| 128 | bool diagnoseAssertNullabilityCall( |
| 129 | const CallExpr* CE, |
| 130 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State, |
| 131 | ASTContext& Ctx) { |
| 132 | auto* DRE = cast<DeclRefExpr>(CE->getCallee()->IgnoreImpCasts()); |
| 133 | |
| 134 | // Extract the expected nullability from the template parameter pack. |
| 135 | std::vector<NullabilityKind> Expected; |
| 136 | for (auto P : DRE->template_arguments()) { |
| 137 | if (P.getArgument().getKind() == TemplateArgument::Expression) { |
| 138 | if (auto* EnumDRE = dyn_cast<DeclRefExpr>(P.getSourceExpression())) { |
| 139 | Expected.push_back(parseNullabilityKind(EnumDRE->getDecl()->getName())); |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | // Compare the nullability computed by nullability analysis with the |
| 145 | // expected one. |
| 146 | const Expr* GivenExpr = CE->getArg(0); |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 147 | std::optional<ArrayRef<NullabilityKind>> MaybeComputed = |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 148 | State.Lattice.getExprNullability(GivenExpr); |
| 149 | if (!MaybeComputed.has_value()) { |
| 150 | llvm::dbgs() |
| 151 | << "Could not evaluate __assert_nullability. Could not find the " |
| 152 | "nullability of the argument expression: "; |
| 153 | CE->dump(); |
| 154 | return false; |
| 155 | } |
| 156 | if (MaybeComputed->vec() == Expected) return true; |
| 157 | // The computed and expected nullabilities differ. Print both to aid |
| 158 | // debugging. |
| 159 | llvm::dbgs() << "__assert_nullability failed at location: "; |
| 160 | CE->getExprLoc().print(llvm::dbgs(), Ctx.getSourceManager()); |
| 161 | llvm::dbgs() << "\nExpression:\n"; |
| 162 | GivenExpr->dump(); |
| 163 | llvm::dbgs() << "Expected nullability: "; |
| 164 | llvm::dbgs() << nullabilityToString(Expected) << "\n"; |
| 165 | llvm::dbgs() << "Computed nullability: "; |
| 166 | llvm::dbgs() << nullabilityToString(*MaybeComputed) << "\n"; |
| 167 | return false; |
| 168 | } |
| 169 | |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 170 | // TODO(b/233582219): Handle call expressions whose callee is not a decl (e.g. |
| 171 | // a function returned from another function), or when the callee cannot be |
| 172 | // interpreted as a function type (e.g. a pointer to a function pointer). |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 173 | std::optional<CFGElement> diagnoseCallExpr( |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 174 | const CallExpr* CE, const MatchFinder::MatchResult& Result, |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 175 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 176 | auto* Callee = CE->getCalleeDecl(); |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 177 | if (!Callee) return std::nullopt; |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 178 | |
| 179 | auto* CalleeType = Callee->getFunctionType(); |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 180 | if (!CalleeType) return std::nullopt; |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 181 | |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 182 | if (auto* FD = Callee->getAsFunction()) { |
| 183 | if (FD->getDeclName().isIdentifier() && |
| 184 | FD->getName() == "__assert_nullability" && |
| 185 | !diagnoseAssertNullabilityCall(CE, State, *Result.Context)) { |
| 186 | // TODO: Handle __assert_nullability failures differently from regular |
| 187 | // diagnostic ([[unsafe]]) failures. |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 188 | return std::optional<CFGElement>(CFGStmt(CE)); |
Dani Ferreira Franco Moura | f9dae20 | 2023-01-09 10:15:18 -0800 | [diff] [blame] | 189 | } |
| 190 | } |
| 191 | |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 192 | auto ParamTypes = CalleeType->getAs<FunctionProtoType>()->getParamTypes(); |
Wei Yi Tee | 1bf1116 | 2022-08-26 03:16:53 -0700 | [diff] [blame] | 193 | ArrayRef<const Expr*> Args(CE->getArgs(), CE->getNumArgs()); |
| 194 | if (isa<CXXOperatorCallExpr>(CE)) { |
| 195 | // The first argument of an operator call expression is the operand which |
| 196 | // does not appear in the list of parameter types. |
| 197 | Args = Args.drop_front(); |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 198 | } |
| 199 | |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 200 | return isIncompatibleArgumentList(ParamTypes, Args, State.Env, |
| 201 | *Result.Context) |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 202 | ? std::optional<CFGElement>(CFGStmt(CE)) |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 203 | : std::nullopt; |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 204 | } |
| 205 | |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 206 | std::optional<CFGElement> diagnoseConstructExpr( |
Wei Yi Tee | afbca01 | 2022-09-19 11:54:28 -0700 | [diff] [blame] | 207 | const CXXConstructExpr* CE, const MatchFinder::MatchResult& Result, |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 208 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
Wei Yi Tee | afbca01 | 2022-09-19 11:54:28 -0700 | [diff] [blame] | 209 | auto ConstructorParamTypes = CE->getConstructor() |
| 210 | ->getType() |
| 211 | ->getAs<FunctionProtoType>() |
| 212 | ->getParamTypes(); |
| 213 | ArrayRef<const Expr*> ConstructorArgs(CE->getArgs(), CE->getNumArgs()); |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 214 | return isIncompatibleArgumentList(ConstructorParamTypes, ConstructorArgs, |
| 215 | State.Env, *Result.Context) |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 216 | ? std::optional<CFGElement>(CFGStmt(CE)) |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 217 | : std::nullopt; |
Wei Yi Tee | afbca01 | 2022-09-19 11:54:28 -0700 | [diff] [blame] | 218 | } |
| 219 | |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 220 | std::optional<CFGElement> diagnoseReturn( |
Wei Yi Tee | ed480d6 | 2022-08-26 03:19:54 -0700 | [diff] [blame] | 221 | const ReturnStmt* RS, const MatchFinder::MatchResult& Result, |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 222 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
| 223 | auto ReturnType = cast<FunctionDecl>(State.Env.getDeclCtx())->getReturnType(); |
Dani Ferreira Franco Moura | b88f19f | 2023-01-26 04:47:09 -0800 | [diff] [blame] | 224 | |
| 225 | // TODO: Handle non-pointer return types. |
| 226 | if (!ReturnType->isPointerType()) { |
| 227 | return std::nullopt; |
| 228 | } |
Wei Yi Tee | ed480d6 | 2022-08-26 03:19:54 -0700 | [diff] [blame] | 229 | |
| 230 | auto* ReturnExpr = RS->getRetValue(); |
| 231 | assert(ReturnExpr->getType()->isPointerType()); |
| 232 | |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 233 | return isIncompatibleAssignment(ReturnType, ReturnExpr, State.Env, |
| 234 | *Result.Context) |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 235 | ? std::optional<CFGElement>(CFGStmt(RS)) |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 236 | : std::nullopt; |
Wei Yi Tee | ed480d6 | 2022-08-26 03:19:54 -0700 | [diff] [blame] | 237 | } |
| 238 | |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 239 | std::optional<CFGElement> diagnoseMemberInitializer( |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 240 | const CXXCtorInitializer* CI, const MatchFinder::MatchResult& Result, |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 241 | const TransferStateForDiagnostics<PointerNullabilityLattice>& State) { |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 242 | assert(CI->isAnyMemberInitializer()); |
| 243 | auto MemberType = CI->getAnyMember()->getType(); |
| 244 | if (!MemberType->isAnyPointerType()) { |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 245 | return std::nullopt; |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 246 | } |
| 247 | auto MemberInitExpr = CI->getInit(); |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 248 | return isIncompatibleAssignment(MemberType, MemberInitExpr, State.Env, |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 249 | *Result.Context) |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 250 | ? std::optional<CFGElement>(CFGInitializer(CI)) |
Googler | 59d7fc0 | 2022-12-16 09:45:39 -0800 | [diff] [blame] | 251 | : std::nullopt; |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 252 | } |
| 253 | |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 254 | auto buildDiagnoser() { |
Dani Ferreira Franco Moura | 4a73ca7 | 2022-12-14 10:23:56 -0800 | [diff] [blame] | 255 | return CFGMatchSwitchBuilder<const dataflow::TransferStateForDiagnostics< |
| 256 | PointerNullabilityLattice>, |
Googler | bad7052 | 2023-01-31 04:37:38 -0800 | [diff] [blame] | 257 | std::optional<CFGElement>>() |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 258 | // (*) |
Wei Yi Tee | 217eb5f | 2022-09-15 03:18:28 -0700 | [diff] [blame] | 259 | .CaseOfCFGStmt<UnaryOperator>(isPointerDereference(), diagnoseDereference) |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 260 | // (->) |
Wei Yi Tee | 217eb5f | 2022-09-15 03:18:28 -0700 | [diff] [blame] | 261 | .CaseOfCFGStmt<MemberExpr>(isPointerArrow(), diagnoseArrow) |
Wei Yi Tee | 036efdf | 2022-08-19 14:16:26 -0700 | [diff] [blame] | 262 | // Check compatibility of parameter assignments |
Wei Yi Tee | 217eb5f | 2022-09-15 03:18:28 -0700 | [diff] [blame] | 263 | .CaseOfCFGStmt<CallExpr>(isCallExpr(), diagnoseCallExpr) |
| 264 | .CaseOfCFGStmt<ReturnStmt>(isPointerReturn(), diagnoseReturn) |
Wei Yi Tee | afbca01 | 2022-09-19 11:54:28 -0700 | [diff] [blame] | 265 | .CaseOfCFGStmt<CXXConstructExpr>(isConstructExpr(), diagnoseConstructExpr) |
Wei Yi Tee | 566e2df | 2022-09-19 11:59:03 -0700 | [diff] [blame] | 266 | .CaseOfCFGInit<CXXCtorInitializer>(isCtorMemberInitializer(), |
| 267 | diagnoseMemberInitializer) |
Wei Yi Tee | 8b58e19 | 2022-08-02 10:15:40 -0700 | [diff] [blame] | 268 | .Build(); |
| 269 | } |
| 270 | |
| 271 | } // namespace |
| 272 | |
| 273 | PointerNullabilityDiagnoser::PointerNullabilityDiagnoser() |
| 274 | : Diagnoser(buildDiagnoser()) {} |
| 275 | |
| 276 | } // namespace nullability |
| 277 | } // namespace tidy |
Wei Yi Tee | 8374c8f | 2022-08-11 01:18:41 -0700 | [diff] [blame] | 278 | } // namespace clang |