1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | #include "clang/AST/ASTContext.h" |
17 | #include "clang/AST/DeclObjC.h" |
18 | #include "clang/AST/Type.h" |
19 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
20 | #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" |
21 | #include "clang/StaticAnalyzer/Core/Checker.h" |
22 | #include "llvm/ADT/DenseMap.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | static bool AreTypesCompatible(QualType Derived, QualType Ancestor, |
29 | ASTContext &C) { |
30 | |
31 | |
32 | |
33 | if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) |
34 | return true; |
35 | |
36 | return C.typesAreCompatible(Derived, Ancestor); |
37 | } |
38 | |
39 | static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, |
40 | const ObjCMethodDecl *MethAncestor, |
41 | BugReporter &BR, ASTContext &Ctx, |
42 | const ObjCImplementationDecl *ID, |
43 | const CheckerBase *Checker) { |
44 | |
45 | QualType ResDerived = MethDerived->getReturnType(); |
46 | QualType ResAncestor = MethAncestor->getReturnType(); |
47 | |
48 | if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { |
49 | std::string sbuf; |
50 | llvm::raw_string_ostream os(sbuf); |
51 | |
52 | os << "The Objective-C class '" |
53 | << *MethDerived->getClassInterface() |
54 | << "', which is derived from class '" |
55 | << *MethAncestor->getClassInterface() |
56 | << "', defines the instance method '"; |
57 | MethDerived->getSelector().print(os); |
58 | os << "' whose return type is '" |
59 | << ResDerived.getAsString() |
60 | << "'. A method with the same name (same selector) is also defined in " |
61 | "class '" |
62 | << *MethAncestor->getClassInterface() |
63 | << "' and has a return type of '" |
64 | << ResAncestor.getAsString() |
65 | << "'. These two types are incompatible, and may result in undefined " |
66 | "behavior for clients of these classes."; |
67 | |
68 | PathDiagnosticLocation MethDLoc = |
69 | PathDiagnosticLocation::createBegin(MethDerived, |
70 | BR.getSourceManager()); |
71 | |
72 | BR.EmitBasicReport( |
73 | MethDerived, Checker, "Incompatible instance method return type", |
74 | categories::CoreFoundationObjectiveC, os.str(), MethDLoc); |
75 | } |
76 | } |
77 | |
78 | static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, |
79 | BugReporter &BR, |
80 | const CheckerBase *Checker) { |
81 | |
82 | const ObjCInterfaceDecl *D = ID->getClassInterface(); |
83 | const ObjCInterfaceDecl *C = D->getSuperClass(); |
84 | |
85 | if (!C) |
86 | return; |
87 | |
88 | ASTContext &Ctx = BR.getContext(); |
89 | |
90 | |
91 | typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; |
92 | MapTy IMeths; |
93 | unsigned NumMethods = 0; |
94 | |
95 | for (auto *M : ID->instance_methods()) { |
96 | IMeths[M->getSelector()] = M; |
97 | ++NumMethods; |
98 | } |
99 | |
100 | |
101 | |
102 | while (C && NumMethods) { |
103 | for (const auto *M : C->instance_methods()) { |
104 | Selector S = M->getSelector(); |
105 | |
106 | MapTy::iterator MI = IMeths.find(S); |
107 | |
108 | if (MI == IMeths.end() || MI->second == nullptr) |
109 | continue; |
110 | |
111 | --NumMethods; |
112 | ObjCMethodDecl *MethDerived = MI->second; |
113 | MI->second = nullptr; |
114 | |
115 | CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); |
116 | } |
117 | |
118 | C = C->getSuperClass(); |
119 | } |
120 | } |
121 | |
122 | |
123 | |
124 | |
125 | |
126 | namespace { |
127 | class ObjCMethSigsChecker : public Checker< |
128 | check::ASTDecl<ObjCImplementationDecl> > { |
129 | public: |
130 | void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, |
131 | BugReporter &BR) const { |
132 | CheckObjCInstMethSignature(D, BR, this); |
133 | } |
134 | }; |
135 | } |
136 | |
137 | void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { |
138 | mgr.registerChecker<ObjCMethSigsChecker>(); |
139 | } |
140 | |
141 | bool ento::shouldRegisterObjCMethSigsChecker(const LangOptions &LO) { |
142 | return true; |
143 | } |
144 | |