Files
sousa-gecko/build/clang-plugin/FinalCycleCollectingIsupportsChecker.cpp
T
Keith Cirkel 2f1f3b0163 Bug 2045044 - Introduce clang plugin to check for using _FINAL macro variants for isupports r=dom-core-reviewers,firefox-static-analysis-reviewers,smaug,sergesanspaille
This change introduces a clang plugin which checks for uses of
`NS_DECL_CYCLE_COLLECTING_ISUPPORTS` in a `final` class. These should instead
use `NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL`.

Not using the `_FINAL` variant can slip in under code review, and still build,
but depending on how the unified builds work out might cause a build error. This
can result in someone making changes to e.g. moz.build having to do some wildly
unrelated follow up working cleaning these up before their build passes.

By introducing a clang plugin to catch these, we can prevent them entering into
tree, and hopefully have a happier and healthier codebase for it.

Differential Revision: https://phabricator.services.mozilla.com/D304799
2026-06-10 14:57:34 +00:00

62 lines
2.4 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FinalCycleCollectingIsupportsChecker.h"
#include "CustomMatchers.h"
#include "clang/Lex/Lexer.h"
void FinalCycleCollectingIsupportsChecker::registerMatchers(
MatchFinder *AstMatcher) {
// Match final classes in DOM paths that have an AddRef method marked
// `override` (not `final`) expanded from
// NS_DECL_CYCLE_COLLECTING_ISUPPORTS_META. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
// uses `override`; NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL uses `final`.
AstMatcher->addMatcher(
cxxRecordDecl(
isFinal(), isInPath("dom/html"),
has(cxxMethodDecl(hasName("AddRef"), isOverride(), unless(isFinal()),
isExpandedFromMacro(
"NS_DECL_CYCLE_COLLECTING_ISUPPORTS_META"))
.bind("addRef")))
.bind("decl"),
this);
}
void FinalCycleCollectingIsupportsChecker::check(
const MatchFinder::MatchResult &Result) {
const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
const auto *AddRef = Result.Nodes.getNodeAs<CXXMethodDecl>("addRef");
SourceManager &SM = Result.Context->getSourceManager();
SourceLocation AddRefLoc = AddRef->getLocation();
// Walk up the macro expansion stack to find the file-level location of
// the NS_DECL_CYCLE_COLLECTING_ISUPPORTS token.
SourceLocation MacroCallLoc = SM.getImmediateMacroCallerLoc(AddRefLoc);
while (MacroCallLoc.isMacroID()) {
MacroCallLoc = SM.getImmediateMacroCallerLoc(MacroCallLoc);
}
// Get the source range of the macro name token for the fixit.
CharSourceRange FixRange =
Lexer::makeFileCharRange(CharSourceRange::getTokenRange(MacroCallLoc), SM,
Result.Context->getLangOpts());
diag(D->getBeginLoc(),
"final class %0 uses NS_DECL_CYCLE_COLLECTING_ISUPPORTS; "
"use NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL instead",
DiagnosticIDs::Error)
<< D;
if (FixRange.isValid()) {
diag(MacroCallLoc,
"replace NS_DECL_CYCLE_COLLECTING_ISUPPORTS with "
"NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL",
DiagnosticIDs::Note)
<< FixItHint::CreateReplacement(
FixRange, "NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL");
}
}