mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1114999 - Part 1: Create an analysis for disallowing calling AddRef and Release on the return value of functions; r=jrmuizel
This commit is contained in:
parent
5688d549a4
commit
b0661f5ce2
@ -74,12 +74,18 @@ private:
|
|||||||
virtual void run(const MatchFinder::MatchResult &Result);
|
virtual void run(const MatchFinder::MatchResult &Result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NoAddRefReleaseOnReturnChecker : public MatchFinder::MatchCallback {
|
||||||
|
public:
|
||||||
|
virtual void run(const MatchFinder::MatchResult &Result);
|
||||||
|
};
|
||||||
|
|
||||||
ScopeChecker stackClassChecker;
|
ScopeChecker stackClassChecker;
|
||||||
ScopeChecker globalClassChecker;
|
ScopeChecker globalClassChecker;
|
||||||
NonHeapClassChecker nonheapClassChecker;
|
NonHeapClassChecker nonheapClassChecker;
|
||||||
ArithmeticArgChecker arithmeticArgChecker;
|
ArithmeticArgChecker arithmeticArgChecker;
|
||||||
TrivialCtorDtorChecker trivialCtorDtorChecker;
|
TrivialCtorDtorChecker trivialCtorDtorChecker;
|
||||||
NaNExprChecker nanExprChecker;
|
NaNExprChecker nanExprChecker;
|
||||||
|
NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
|
||||||
MatchFinder astMatcher;
|
MatchFinder astMatcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -389,6 +395,12 @@ AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
|
|||||||
return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
|
return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This matcher will match any function declaration that is marked to prohibit
|
||||||
|
/// calling AddRef or Release on its return value.
|
||||||
|
AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
|
||||||
|
return MozChecker::hasCustomAnnotation(&Node, "moz_no_addref_release_on_return");
|
||||||
|
}
|
||||||
|
|
||||||
/// This matcher will match all arithmetic binary operators.
|
/// This matcher will match all arithmetic binary operators.
|
||||||
AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
|
AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
|
||||||
BinaryOperatorKind opcode = Node.getOpcode();
|
BinaryOperatorKind opcode = Node.getOpcode();
|
||||||
@ -458,6 +470,17 @@ AST_MATCHER(BinaryOperator, isInSkScalarDotH) {
|
|||||||
return llvm::sys::path::rbegin(FileName)->equals("SkScalar.h");
|
return llvm::sys::path::rbegin(FileName)->equals("SkScalar.h");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This matcher will match all accesses to AddRef or Release methods.
|
||||||
|
AST_MATCHER(MemberExpr, isAddRefOrRelease) {
|
||||||
|
ValueDecl *Member = Node.getMemberDecl();
|
||||||
|
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
|
||||||
|
if (Method) {
|
||||||
|
std::string Name = Method->getNameAsString();
|
||||||
|
return Name == "AddRef" || Name == "Release";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,6 +571,12 @@ DiagnosticsMatcher::DiagnosticsMatcher()
|
|||||||
unless(anyOf(isInSystemHeader(), isInSkScalarDotH()))
|
unless(anyOf(isInSystemHeader(), isInSkScalarDotH()))
|
||||||
)).bind("node"),
|
)).bind("node"),
|
||||||
&nanExprChecker);
|
&nanExprChecker);
|
||||||
|
|
||||||
|
astMatcher.addMatcher(callExpr(callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
|
||||||
|
hasParent(memberExpr(isAddRefOrRelease(),
|
||||||
|
hasParent(callExpr())).bind("member")
|
||||||
|
)).bind("node"),
|
||||||
|
&noAddRefReleaseOnReturnChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagnosticsMatcher::ScopeChecker::run(
|
void DiagnosticsMatcher::ScopeChecker::run(
|
||||||
@ -733,6 +762,19 @@ void DiagnosticsMatcher::NaNExprChecker::run(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiagnosticsMatcher::NoAddRefReleaseOnReturnChecker::run(
|
||||||
|
const MatchFinder::MatchResult &Result) {
|
||||||
|
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||||
|
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||||
|
DiagnosticIDs::Error, "%1 cannot be called on the return value of %0");
|
||||||
|
const Stmt *node = Result.Nodes.getNodeAs<Stmt>("node");
|
||||||
|
const FunctionDecl *func = Result.Nodes.getNodeAs<FunctionDecl>("func");
|
||||||
|
const MemberExpr *member = Result.Nodes.getNodeAs<MemberExpr>("member");
|
||||||
|
const CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(member->getMemberDecl());
|
||||||
|
|
||||||
|
Diag.Report(node->getLocStart(), errorID) << func << method;
|
||||||
|
}
|
||||||
|
|
||||||
class MozCheckAction : public PluginASTAction {
|
class MozCheckAction : public PluginASTAction {
|
||||||
public:
|
public:
|
||||||
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {
|
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
# Build without any warning flags, and with clang verify flag for a
|
# Build without any warning flags, and with clang verify flag for a
|
||||||
# syntax-only build (no codegen).
|
# syntax-only build (no codegen), without a limit on the number of errors.
|
||||||
OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify
|
OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0
|
||||||
OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify
|
OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
||||||
|
65
build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp
Normal file
65
build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#define MOZ_NO_ADDREF_RELEASE_ON_RETURN __attribute__((annotate("moz_no_addref_release_on_return")))
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
void AddRef();
|
||||||
|
void Release();
|
||||||
|
void foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct X {
|
||||||
|
T* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
T& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
T h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct SP {
|
||||||
|
T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
};
|
||||||
|
|
||||||
|
Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
|
||||||
|
|
||||||
|
void test() {
|
||||||
|
S s;
|
||||||
|
s.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||||
|
s.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||||
|
s.f()->foo();
|
||||||
|
s.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}}
|
||||||
|
s.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}}
|
||||||
|
s.g().foo();
|
||||||
|
s.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||||
|
s.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||||
|
s.h().foo();
|
||||||
|
X<Test> x;
|
||||||
|
x.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||||
|
x.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||||
|
x.f()->foo();
|
||||||
|
x.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}}
|
||||||
|
x.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}}
|
||||||
|
x.g().foo();
|
||||||
|
x.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||||
|
x.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||||
|
x.h().foo();
|
||||||
|
SP<Test> sp;
|
||||||
|
sp->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}}
|
||||||
|
sp->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}}
|
||||||
|
sp->foo();
|
||||||
|
f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}}
|
||||||
|
f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}}
|
||||||
|
f()->foo();
|
||||||
|
g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}}
|
||||||
|
g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}}
|
||||||
|
g().foo();
|
||||||
|
h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}}
|
||||||
|
h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}}
|
||||||
|
h().foo();
|
||||||
|
}
|
@ -11,6 +11,7 @@ SOURCES += [
|
|||||||
'TestMustOverride.cpp',
|
'TestMustOverride.cpp',
|
||||||
'TestNANTestingExpr.cpp',
|
'TestNANTestingExpr.cpp',
|
||||||
'TestNANTestingExprC.c',
|
'TestNANTestingExprC.c',
|
||||||
|
'TestNoAddRefReleaseOnReturn.cpp',
|
||||||
'TestNoArithmeticExprInArgument.cpp',
|
'TestNoArithmeticExprInArgument.cpp',
|
||||||
'TestNonHeapClass.cpp',
|
'TestNonHeapClass.cpp',
|
||||||
'TestStackClass.cpp',
|
'TestStackClass.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user