diff --git a/config/static-checking-config.mk b/config/static-checking-config.mk index 061f8daccbf3..ecdeb91a4eb0 100644 --- a/config/static-checking-config.mk +++ b/config/static-checking-config.mk @@ -5,6 +5,7 @@ DEHYDRA_SCRIPT = $(topsrcdir)/config/static-checking.js DEHYDRA_MODULES = \ $(topsrcdir)/xpcom/analysis/final.js \ + $(topsrcdir)/xpcom/analysis/override.js \ $(topsrcdir)/xpcom/analysis/must-override.js \ $(NULL) diff --git a/config/static-checking.js b/config/static-checking.js index f9961a3fce56..13ba4555fa11 100644 --- a/config/static-checking.js +++ b/config/static-checking.js @@ -62,6 +62,12 @@ function signaturesMatch(m1, m2) { if (m1.shortName != m2.shortName) return false; + + if (m1.isVirtual != m2.isVirtual) + return false; + + if (m1.isStatic != m2.isStatic) + return false; let p1 = m1.type.parameters; let p2 = m2.type.parameters; diff --git a/xpcom/analysis/override.js b/xpcom/analysis/override.js new file mode 100644 index 000000000000..21201f946427 --- /dev/null +++ b/xpcom/analysis/override.js @@ -0,0 +1,42 @@ +/** + * NS_OVERRIDE may be marked on class methods which are intended to override + * a method in a base class. If the method is removed or altered in the base + * class, the compiler will force all subclass overrides to be modified. + */ + +/** + * Generate all the base classes recursively of class `c`. + */ +function all_bases(c) +{ + for each (let b in c.bases) { + yield b.type; + for (let bb in all_bases(b.type)) + yield bb; + } +} + +function process_decl(d) +{ + if (!hasAttribute(d, 'NS_override')) + return; + + if (!d.memberOf || !d.isFunction) { + error("%s is marked NS_OVERRIDE but is not a class function.".format(d.name), d.loc); + return; + } + + if (d.isStatic) { + error("Marking NS_OVERRIDE on static function %s is meaningless.".format(d.name), d.loc); + return; + } + + for (let base in all_bases(d.memberOf)) { + for each (let m in base.members) { + if (m.shortName == d.shortName && signaturesMatch(m, d)) + return; + } + } + + error("NS_OVERRIDE function %s does not override a base class method with the same name and signature".format(d.name), d.loc); +} diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index c736658bbb82..7febe0b73de5 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -505,11 +505,13 @@ typedef PRUint32 nsrefcnt; # define NS_INPARAM __attribute__((user("NS_inparam"))) # define NS_OUTPARAM __attribute__((user("NS_outparam"))) # define NS_INOUTPARAM __attribute__((user("NS_inoutparam"))) +# define NS_OVERRIDE __attribute__((user("NS_override"))) #else # define NS_SCRIPTABLE # define NS_INPARAM # define NS_OUTPARAM # define NS_INOUTPARAM +# define NS_OVERRIDE #endif #endif /* nscore_h___ */ diff --git a/xpcom/tests/static-checker/Makefile.in b/xpcom/tests/static-checker/Makefile.in index 89ba23557208..05690b3bf1ea 100644 --- a/xpcom/tests/static-checker/Makefile.in +++ b/xpcom/tests/static-checker/Makefile.in @@ -124,10 +124,22 @@ MUST_OVERRIDE_FAILURE_TESTCASES = \ OverrideFail4.cpp \ $(NULL) +OVERRIDE_PASS_TESTCASES = \ + override-pass.cpp \ + $(NULL) + +OVERRIDE_FAILURE_TESTCASES = \ + override-global.cpp \ + override-signature.cpp \ + override-static.cpp \ + override-virtual.cpp \ + $(NULL) + STATIC_FAILURE_TESTCASES = \ $(FINAL_FAILURE_TESTCASES) \ $(FLOW_FAILURE_TESTCASES) \ $(MUST_OVERRIDE_FAILURE_TESTCASES) \ + $(OVERRIDE_FAILURE_TESTCASES) \ $(NULL) STATIC_WARNING_TESTCASES = \ @@ -141,6 +153,7 @@ STATIC_PASS_TESTCASES = \ $(STACK_PASS_TESTCASES) \ $(FLOW_PASS_TESTCASES) \ $(MUST_OVERRIDE_PASS_TESTCASES) \ + $(OVERRIDE_PASS_TESTCASES) \ $(NULL) diff --git a/xpcom/tests/static-checker/override-global.cpp b/xpcom/tests/static-checker/override-global.cpp new file mode 100644 index 000000000000..e79d70c3f101 --- /dev/null +++ b/xpcom/tests/static-checker/override-global.cpp @@ -0,0 +1 @@ +__attribute__((user("NS_override"))) int m(); diff --git a/xpcom/tests/static-checker/override-pass.cpp b/xpcom/tests/static-checker/override-pass.cpp new file mode 100644 index 000000000000..d5ac1e169897 --- /dev/null +++ b/xpcom/tests/static-checker/override-pass.cpp @@ -0,0 +1,15 @@ +class A +{ + int a(int, char*); + void c(char); +}; + +class B : A +{ + __attribute__((user("NS_override"))) int a(int, char*); +}; + +class C : B +{ + __attribute__((user("NS_override"))) void c(char); +}; diff --git a/xpcom/tests/static-checker/override-signature.cpp b/xpcom/tests/static-checker/override-signature.cpp new file mode 100644 index 000000000000..ec2c17aa63bc --- /dev/null +++ b/xpcom/tests/static-checker/override-signature.cpp @@ -0,0 +1,9 @@ +class A +{ + int m(int); +}; + +class B : A +{ + __attribute__((user("NS_override"))) int m(void*); +}; diff --git a/xpcom/tests/static-checker/override-static.cpp b/xpcom/tests/static-checker/override-static.cpp new file mode 100644 index 000000000000..b56bd7fd3e6f --- /dev/null +++ b/xpcom/tests/static-checker/override-static.cpp @@ -0,0 +1,9 @@ +class A +{ + static int m(); +}; + +class B : A +{ + __attribute__((user("NS_override"))) static int m(); +}; diff --git a/xpcom/tests/static-checker/override-virtual.cpp b/xpcom/tests/static-checker/override-virtual.cpp new file mode 100644 index 000000000000..020bfce47b97 --- /dev/null +++ b/xpcom/tests/static-checker/override-virtual.cpp @@ -0,0 +1,9 @@ +class A +{ + int m(); +}; + +class B : A +{ + __attribute__((user("NS_override"))) virtual int m(); +};