xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //=======- ForwardDeclChecker.cpp --------------------------------*- C++ -*-==//
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 "ASTUtils.h"
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclCXX.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
15 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
16 #include "clang/Basic/SourceLocation.h"
17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "llvm/ADT/DenseSet.h"
22 #include "llvm/Support/SaveAndRestore.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 
29 class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30   BugType Bug;
31   mutable BugReporter *BR = nullptr;
32   mutable RetainTypeChecker RTC;
33   mutable llvm::DenseSet<const Type *> SystemTypes;
34 
35 public:
ForwardDeclChecker()36   ForwardDeclChecker()
37       : Bug(this, "Forward declared member or local variable or parameter",
38             "WebKit coding guidelines") {}
39 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const40   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
41                     BugReporter &BRArg) const {
42     BR = &BRArg;
43 
44     // The calls to checkAST* from AnalysisConsumer don't
45     // visit template instantiations or lambda classes. We
46     // want to visit those, so we make our own RecursiveASTVisitor.
47     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
48       using Base = RecursiveASTVisitor<LocalVisitor>;
49 
50       const ForwardDeclChecker *Checker;
51       Decl *DeclWithIssue{nullptr};
52 
53       explicit LocalVisitor(const ForwardDeclChecker *Checker)
54           : Checker(Checker) {
55         assert(Checker);
56       }
57 
58       bool shouldVisitTemplateInstantiations() const { return true; }
59       bool shouldVisitImplicitCode() const { return false; }
60 
61       bool VisitTypedefDecl(TypedefDecl *TD) {
62         Checker->visitTypedef(TD);
63         return true;
64       }
65 
66       bool VisitRecordDecl(const RecordDecl *RD) {
67         Checker->visitRecordDecl(RD, DeclWithIssue);
68         return true;
69       }
70 
71       bool TraverseDecl(Decl *D) {
72         llvm::SaveAndRestore SavedDecl(DeclWithIssue);
73         if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
74           DeclWithIssue = D;
75         return Base::TraverseDecl(D);
76       }
77 
78       bool VisitVarDecl(VarDecl *V) {
79         if (V->isLocalVarDecl())
80           Checker->visitVarDecl(V, DeclWithIssue);
81         return true;
82       }
83 
84       bool VisitCallExpr(const CallExpr *CE) {
85         Checker->visitCallExpr(CE, DeclWithIssue);
86         return true;
87       }
88 
89       bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
90         Checker->visitConstructExpr(CE, DeclWithIssue);
91         return true;
92       }
93 
94       bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
95         Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
96         return true;
97       }
98     };
99 
100     LocalVisitor visitor(this);
101     RTC.visitTranslationUnitDecl(TUD);
102     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
103   }
104 
visitTypedef(const TypedefDecl * TD) const105   void visitTypedef(const TypedefDecl *TD) const {
106     RTC.visitTypedef(TD);
107     auto QT = TD->getUnderlyingType().getCanonicalType();
108     assert(BR && "expected nonnull BugReporter");
109     if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {
110       if (auto *Type = QT.getTypePtrOrNull())
111         SystemTypes.insert(Type);
112     }
113   }
114 
isUnknownType(QualType QT) const115   bool isUnknownType(QualType QT) const {
116     auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();
117     if (!CanonicalType)
118       return false;
119     auto PointeeQT = CanonicalType->getPointeeType();
120     auto *PointeeType = PointeeQT.getTypePtrOrNull();
121     if (!PointeeType)
122       return false;
123     auto *R = PointeeType->getAsCXXRecordDecl();
124     if (!R) // Forward declaration of a Objective-C interface is safe.
125       return false;
126     auto Name = R->getName();
127     if (R->hasDefinition())
128       return false;
129     // Find a definition amongst template declarations.
130     if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(R)) {
131       if (auto *S = Specialization->getSpecializedTemplate()) {
132         for (S = S->getMostRecentDecl(); S; S = S->getPreviousDecl()) {
133           if (S->isThisDeclarationADefinition())
134             return false;
135         }
136       }
137     }
138     return !RTC.isUnretained(QT) && !SystemTypes.contains(CanonicalType) &&
139            !SystemTypes.contains(PointeeType) && !Name.starts_with("Opaque") &&
140            Name != "_NSZone";
141   }
142 
visitRecordDecl(const RecordDecl * RD,const Decl * DeclWithIssue) const143   void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
144     if (!RD->isThisDeclarationADefinition())
145       return;
146 
147     if (RD->isImplicit() || RD->isLambda())
148       return;
149 
150     const auto RDLocation = RD->getLocation();
151     if (!RDLocation.isValid())
152       return;
153 
154     const auto Kind = RD->getTagKind();
155     if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
156       return;
157 
158     assert(BR && "expected nonnull BugReporter");
159     if (BR->getSourceManager().isInSystemHeader(RDLocation))
160       return;
161 
162     // Ref-counted smartpointers actually have raw-pointer to uncounted type as
163     // a member but we trust them to handle it correctly.
164     auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
165     if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtr(R))
166       return;
167 
168     for (auto *Member : RD->fields()) {
169       auto QT = Member->getType();
170       if (isUnknownType(QT)) {
171         SmallString<100> Buf;
172         llvm::raw_svector_ostream Os(Buf);
173 
174         const std::string TypeName = QT.getAsString();
175         Os << "Member variable ";
176         printQuotedName(Os, Member);
177         Os << " uses a forward declared type '" << TypeName << "'";
178 
179         const SourceLocation SrcLocToReport = Member->getBeginLoc();
180         PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
181         auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
182         Report->addRange(Member->getSourceRange());
183         Report->setDeclWithIssue(DeclWithIssue);
184         BR->emitReport(std::move(Report));
185       }
186     }
187   }
188 
visitVarDecl(const VarDecl * V,const Decl * DeclWithIssue) const189   void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {
190     assert(BR && "expected nonnull BugReporter");
191     if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))
192       return;
193 
194     auto QT = V->getType();
195     if (!isUnknownType(QT))
196       return;
197 
198     SmallString<100> Buf;
199     llvm::raw_svector_ostream Os(Buf);
200     Os << "Local variable ";
201     printQuotedQualifiedName(Os, V);
202 
203     reportBug(V->getBeginLoc(), V->getSourceRange(), DeclWithIssue, Os.str(),
204               QT);
205   }
206 
visitCallExpr(const CallExpr * CE,const Decl * DeclWithIssue) const207   void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
208     assert(BR && "expected nonnull BugReporter");
209     if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
210       return;
211 
212     if (auto *F = CE->getDirectCallee()) {
213       // Skip the first argument for overloaded member operators (e. g. lambda
214       // or std::function call operator).
215       unsigned ArgIdx =
216           isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
217 
218       for (auto P = F->param_begin();
219            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
220         visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
221     }
222   }
223 
visitConstructExpr(const CXXConstructExpr * CE,const Decl * DeclWithIssue) const224   void visitConstructExpr(const CXXConstructExpr *CE,
225                           const Decl *DeclWithIssue) const {
226     assert(BR && "expected nonnull BugReporter");
227     if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
228       return;
229 
230     if (auto *F = CE->getConstructor()) {
231       // Skip the first argument for overloaded member operators (e. g. lambda
232       // or std::function call operator).
233       unsigned ArgIdx =
234           isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
235 
236       for (auto P = F->param_begin();
237            P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
238         visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
239     }
240   }
241 
visitObjCMessageExpr(const ObjCMessageExpr * E,const Decl * DeclWithIssue) const242   void visitObjCMessageExpr(const ObjCMessageExpr *E,
243                             const Decl *DeclWithIssue) const {
244     assert(BR && "expected nonnull BugReporter");
245     if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
246       return;
247 
248     if (auto *Receiver = E->getInstanceReceiver()) {
249       Receiver = Receiver->IgnoreParenCasts();
250       if (isUnknownType(E->getReceiverType()))
251         reportUnknownRecieverType(Receiver, DeclWithIssue);
252     }
253 
254     auto *MethodDecl = E->getMethodDecl();
255     if (!MethodDecl)
256       return;
257 
258     auto ArgCount = E->getNumArgs();
259     for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
260       visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);
261   }
262 
visitCallArg(const Expr * Arg,const ParmVarDecl * Param,const Decl * DeclWithIssue) const263   void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
264                     const Decl *DeclWithIssue) const {
265     auto *ArgExpr = Arg->IgnoreParenCasts();
266     if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) {
267       auto *InnerCallee = InnerCE->getDirectCallee();
268       if (InnerCallee && InnerCallee->isInStdNamespace() &&
269           safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
270         ArgExpr = InnerCE->getArg(0);
271         if (ArgExpr)
272           ArgExpr = ArgExpr->IgnoreParenCasts();
273       }
274     }
275     if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
276         isa<CXXDefaultArgExpr>(ArgExpr))
277       return;
278     if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
279       if (auto *ValDecl = DRE->getDecl()) {
280         if (isa<ParmVarDecl>(ValDecl))
281           return;
282       }
283     }
284 
285     QualType ArgType = Param->getType();
286     if (!isUnknownType(ArgType))
287       return;
288 
289     reportUnknownArgType(Arg, Param, DeclWithIssue);
290   }
291 
reportUnknownArgType(const Expr * CA,const ParmVarDecl * Param,const Decl * DeclWithIssue) const292   void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,
293                             const Decl *DeclWithIssue) const {
294     assert(CA);
295 
296     SmallString<100> Buf;
297     llvm::raw_svector_ostream Os(Buf);
298 
299     const std::string paramName = safeGetName(Param);
300     Os << "Call argument";
301     if (!paramName.empty()) {
302       Os << " for parameter ";
303       printQuotedQualifiedName(Os, Param);
304     }
305 
306     reportBug(CA->getExprLoc(), CA->getSourceRange(), DeclWithIssue, Os.str(),
307               Param->getType());
308   }
309 
reportUnknownRecieverType(const Expr * Receiver,const Decl * DeclWithIssue) const310   void reportUnknownRecieverType(const Expr *Receiver,
311                                  const Decl *DeclWithIssue) const {
312     assert(Receiver);
313     reportBug(Receiver->getExprLoc(), Receiver->getSourceRange(), DeclWithIssue,
314               "Receiver", Receiver->getType());
315   }
316 
reportBug(const SourceLocation & SrcLoc,const SourceRange & SrcRange,const Decl * DeclWithIssue,const StringRef & Description,QualType Type) const317   void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,
318                  const Decl *DeclWithIssue, const StringRef &Description,
319                  QualType Type) const {
320     SmallString<100> Buf;
321     llvm::raw_svector_ostream Os(Buf);
322 
323     const std::string TypeName = Type.getAsString();
324     Os << Description << " uses a forward declared type '" << TypeName << "'";
325 
326     assert(BR && "expected nonnull BugReporter");
327     PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());
328     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
329     Report->addRange(SrcRange);
330     Report->setDeclWithIssue(DeclWithIssue);
331     BR->emitReport(std::move(Report));
332   }
333 };
334 
335 } // namespace
336 
registerForwardDeclChecker(CheckerManager & Mgr)337 void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
338   Mgr.registerChecker<ForwardDeclChecker>();
339 }
340 
shouldRegisterForwardDeclChecker(const CheckerManager &)341 bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
342   return true;
343 }
344