/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
Line | Count | Source |
1 | | //===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===// |
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 file defines a CheckObjCInstMethSignature, a flow-insensitive check |
10 | | // that determines if an Objective-C class interface incorrectly redefines |
11 | | // the method signature in a subclass. |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | | #include "clang/Analysis/PathDiagnostic.h" |
17 | | #include "clang/AST/ASTContext.h" |
18 | | #include "clang/AST/DeclObjC.h" |
19 | | #include "clang/AST/Type.h" |
20 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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 | 6 | ASTContext &C) { |
30 | | |
31 | | // Right now don't compare the compatibility of pointers. That involves |
32 | | // looking at subtyping relationships. FIXME: Future patch. |
33 | 6 | if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()3 ) |
34 | 2 | return true; |
35 | | |
36 | 4 | return C.typesAreCompatible(Derived, Ancestor); |
37 | 6 | } |
38 | | |
39 | | static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, |
40 | | const ObjCMethodDecl *MethAncestor, |
41 | | BugReporter &BR, ASTContext &Ctx, |
42 | | const ObjCImplementationDecl *ID, |
43 | 6 | const CheckerBase *Checker) { |
44 | | |
45 | 6 | QualType ResDerived = MethDerived->getReturnType(); |
46 | 6 | QualType ResAncestor = MethAncestor->getReturnType(); |
47 | | |
48 | 6 | if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { |
49 | 2 | std::string sbuf; |
50 | 2 | llvm::raw_string_ostream os(sbuf); |
51 | | |
52 | 2 | os << "The Objective-C class '" |
53 | 2 | << *MethDerived->getClassInterface() |
54 | 2 | << "', which is derived from class '" |
55 | 2 | << *MethAncestor->getClassInterface() |
56 | 2 | << "', defines the instance method '"; |
57 | 2 | MethDerived->getSelector().print(os); |
58 | 2 | os << "' whose return type is '" << ResDerived |
59 | 2 | << "'. A method with the same name (same selector) is also defined in " |
60 | 2 | "class '" |
61 | 2 | << *MethAncestor->getClassInterface() << "' and has a return type of '" |
62 | 2 | << ResAncestor |
63 | 2 | << "'. These two types are incompatible, and may result in undefined " |
64 | 2 | "behavior for clients of these classes."; |
65 | | |
66 | 2 | PathDiagnosticLocation MethDLoc = |
67 | 2 | PathDiagnosticLocation::createBegin(MethDerived, |
68 | 2 | BR.getSourceManager()); |
69 | | |
70 | 2 | BR.EmitBasicReport( |
71 | 2 | MethDerived, Checker, "Incompatible instance method return type", |
72 | 2 | categories::CoreFoundationObjectiveC, os.str(), MethDLoc); |
73 | 2 | } |
74 | 6 | } |
75 | | |
76 | | static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, |
77 | | BugReporter &BR, |
78 | 15 | const CheckerBase *Checker) { |
79 | | |
80 | 15 | const ObjCInterfaceDecl *D = ID->getClassInterface(); |
81 | 15 | const ObjCInterfaceDecl *C = D->getSuperClass(); |
82 | | |
83 | 15 | if (!C) |
84 | 2 | return; |
85 | | |
86 | 13 | ASTContext &Ctx = BR.getContext(); |
87 | | |
88 | | // Build a DenseMap of the methods for quick querying. |
89 | 13 | typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; |
90 | 13 | MapTy IMeths; |
91 | 13 | unsigned NumMethods = 0; |
92 | | |
93 | 29 | for (auto *M : ID->instance_methods()) { |
94 | 29 | IMeths[M->getSelector()] = M; |
95 | 29 | ++NumMethods; |
96 | 29 | } |
97 | | |
98 | | // Now recurse the class hierarchy chain looking for methods with the |
99 | | // same signatures. |
100 | 28 | while (C && NumMethods17 ) { |
101 | 62 | for (const auto *M : C->instance_methods()) { |
102 | 62 | Selector S = M->getSelector(); |
103 | | |
104 | 62 | MapTy::iterator MI = IMeths.find(S); |
105 | | |
106 | 62 | if (MI == IMeths.end() || MI->second == nullptr6 ) |
107 | 56 | continue; |
108 | | |
109 | 6 | --NumMethods; |
110 | 6 | ObjCMethodDecl *MethDerived = MI->second; |
111 | 6 | MI->second = nullptr; |
112 | | |
113 | 6 | CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); |
114 | 6 | } |
115 | | |
116 | 15 | C = C->getSuperClass(); |
117 | 15 | } |
118 | 13 | } |
119 | | |
120 | | //===----------------------------------------------------------------------===// |
121 | | // ObjCMethSigsChecker |
122 | | //===----------------------------------------------------------------------===// |
123 | | |
124 | | namespace { |
125 | | class ObjCMethSigsChecker : public Checker< |
126 | | check::ASTDecl<ObjCImplementationDecl> > { |
127 | | public: |
128 | | void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, |
129 | 15 | BugReporter &BR) const { |
130 | 15 | CheckObjCInstMethSignature(D, BR, this); |
131 | 15 | } |
132 | | }; |
133 | | } |
134 | | |
135 | 49 | void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { |
136 | 49 | mgr.registerChecker<ObjCMethSigsChecker>(); |
137 | 49 | } |
138 | | |
139 | 98 | bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) { |
140 | 98 | return true; |
141 | 98 | } |