Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//== DynamicTypeChecker.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
// This checker looks for cases where the dynamic type of an object is unrelated
10
// to its static type. The type information utilized by this check is collected
11
// by the DynamicTypePropagation checker. This check does not report any type
12
// error for ObjC Generic types, in order to avoid duplicate erros from the
13
// ObjC Generics checker. This checker is not supposed to modify the program
14
// state, it is just the observer of the type information provided by other
15
// checkers.
16
//
17
//===----------------------------------------------------------------------===//
18
19
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21
#include "clang/StaticAnalyzer/Core/Checker.h"
22
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
25
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
27
28
using namespace clang;
29
using namespace ento;
30
31
namespace {
32
class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
33
  mutable std::unique_ptr<BugType> BT;
34
15
  void initBugType() const {
35
15
    if (!BT)
36
3
      BT.reset(
37
3
          new BugType(this, "Dynamic and static type mismatch", "Type Error"));
38
15
  }
39
40
  class DynamicTypeBugVisitor : public BugReporterVisitor {
41
  public:
42
15
    DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
43
44
15
    void Profile(llvm::FoldingSetNodeID &ID) const override {
45
15
      static int X = 0;
46
15
      ID.AddPointer(&X);
47
15
      ID.AddPointer(Reg);
48
15
    }
49
50
    PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
51
                                     BugReporterContext &BRC,
52
                                     PathSensitiveBugReport &BR) override;
53
54
  private:
55
    // The tracked region.
56
    const MemRegion *Reg;
57
  };
58
59
  void reportTypeError(QualType DynamicType, QualType StaticType,
60
                       const MemRegion *Reg, const Stmt *ReportedNode,
61
                       CheckerContext &C) const;
62
63
public:
64
  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
65
};
66
}
67
68
void DynamicTypeChecker::reportTypeError(QualType DynamicType,
69
                                         QualType StaticType,
70
                                         const MemRegion *Reg,
71
                                         const Stmt *ReportedNode,
72
15
                                         CheckerContext &C) const {
73
15
  initBugType();
74
15
  SmallString<192> Buf;
75
15
  llvm::raw_svector_ostream OS(Buf);
76
15
  OS << "Object has a dynamic type '";
77
15
  QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
78
15
                  llvm::Twine());
79
15
  OS << "' which is incompatible with static type '";
80
15
  QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81
15
                  llvm::Twine());
82
15
  OS << "'";
83
15
  auto R = std::make_unique<PathSensitiveBugReport>(
84
15
      *BT, OS.str(), C.generateNonFatalErrorNode());
85
15
  R->markInteresting(Reg);
86
15
  R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
87
15
  R->addRange(ReportedNode->getSourceRange());
88
15
  C.emitReport(std::move(R));
89
15
}
90
91
PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
92
707
    const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
93
707
  ProgramStateRef State = N->getState();
94
707
  ProgramStateRef StatePrev = N->getFirstPred()->getState();
95
96
707
  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
97
707
  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
98
707
  if (!TrackedType.isValid())
99
0
    return nullptr;
100
101
707
  if (TrackedTypePrev.isValid() &&
102
707
      TrackedTypePrev.getType() == TrackedType.getType())
103
695
    return nullptr;
104
105
  // Retrieve the associated statement.
106
12
  const Stmt *S = N->getStmtForDiagnostics();
107
12
  if (!S)
108
0
    return nullptr;
109
110
12
  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
111
112
12
  SmallString<256> Buf;
113
12
  llvm::raw_svector_ostream OS(Buf);
114
12
  OS << "Type '";
115
12
  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
116
12
                  LangOpts, llvm::Twine());
117
12
  OS << "' is inferred from ";
118
119
12
  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
120
0
    OS << "explicit cast (from '";
121
0
    QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
122
0
                    Qualifiers(), OS, LangOpts, llvm::Twine());
123
0
    OS << "' to '";
124
0
    QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
125
0
                    LangOpts, llvm::Twine());
126
0
    OS << "')";
127
12
  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
128
0
    OS << "implicit cast (from '";
129
0
    QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
130
0
                    Qualifiers(), OS, LangOpts, llvm::Twine());
131
0
    OS << "' to '";
132
0
    QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
133
0
                    LangOpts, llvm::Twine());
134
0
    OS << "')";
135
12
  } else {
136
12
    OS << "this context";
137
12
  }
138
139
  // Generate the extra diagnostic.
140
12
  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
141
12
                             N->getLocationContext());
142
12
  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
143
12
}
144
145
666
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
146
666
  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
147
666
  if (!Decl)
148
197
    return false;
149
150
469
  return Decl->getDefinition();
151
666
}
152
153
// TODO: consider checking explicit casts?
154
void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
155
8.50k
                                       CheckerContext &C) const {
156
  // TODO: C++ support.
157
8.50k
  if (CE->getCastKind() != CK_BitCast)
158
7.96k
    return;
159
160
544
  const MemRegion *Region = C.getSVal(CE).getAsRegion();
161
544
  if (!Region)
162
48
    return;
163
164
496
  ProgramStateRef State = C.getState();
165
496
  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
166
167
496
  if (!DynTypeInfo.isValid())
168
0
    return;
169
170
496
  QualType DynType = DynTypeInfo.getType();
171
496
  QualType StaticType = CE->getType();
172
173
496
  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
174
496
  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
175
176
496
  if (!DynObjCType || 
!StaticObjCType364
)
177
144
    return;
178
179
352
  if (!hasDefinition(DynObjCType) || 
!hasDefinition(StaticObjCType)314
)
180
198
    return;
181
182
154
  ASTContext &ASTCtxt = C.getASTContext();
183
184
  // Strip kindeofness to correctly detect subtyping relationships.
185
154
  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
186
154
  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
187
188
  // Specialized objects are handled by the generics checker.
189
154
  if (StaticObjCType->isSpecialized())
190
94
    return;
191
192
60
  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
193
43
    return;
194
195
17
  if (DynTypeInfo.canBeASubClass() &&
196
17
      ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
197
2
    return;
198
199
15
  reportTypeError(DynType, StaticType, Region, CE, C);
200
15
}
201
202
77
void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
203
77
  mgr.registerChecker<DynamicTypeChecker>();
204
77
}
205
206
154
bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
207
154
  return true;
208
154
}