Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-tidy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ add_subdirectory(altera)
add_subdirectory(boost)
add_subdirectory(bugprone)
add_subdirectory(cert)
add_subdirectory(chromium)
add_subdirectory(concurrency)
add_subdirectory(cppcoreguidelines)
add_subdirectory(darwin)
Expand Down Expand Up @@ -88,6 +89,7 @@ set(ALL_CLANG_TIDY_CHECKS
clangTidyDarwinModule
clangTidyFuchsiaModule
clangTidyGoogleModule
clangTidyChromiumModule
clangTidyHICPPModule
clangTidyLinuxKernelModule
clangTidyLLVMModule
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clang-tidy/ClangTidyForceLinker.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ extern volatile int GoogleModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination =
GoogleModuleAnchorSource;

// This anchor is used to force the linker to link the ChromiumModule.
extern volatile int ChromiumModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED ChromiumModuleAnchor = ChromiumModuleAnchorSource;


// This anchor is used to force the linker to link the HICPPModule.
extern volatile int HICPPModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED HICPPModuleAnchorDestination =
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/cert/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_clang_library(clangTidyCERTModule STATIC
clangTidyBugproneModule
clangTidyConcurrencyModule
clangTidyGoogleModule
clangTidyChromiumModule
clangTidyMiscModule
clangTidyPerformanceModule
clangTidyReadabilityModule
Expand Down
108 changes: 108 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/BlinkCastCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "BlinkCastCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/Path.h"

using namespace clang::ast_matchers;

namespace clang::tidy::chromium {

namespace {
bool isInBlinkNamespace(const Decl* D) {
const DeclContext* Context = D->getDeclContext();
while (Context) {
if (const auto* NS = dyn_cast<NamespaceDecl>(Context)) {
if (NS->getName() == "blink") {
return true;
}
}
Context = Context->getParent();
}
return false;
}

bool isInBlinkDirectory(const SourceLocation& Loc, const SourceManager& SM) {
if (Loc.isInvalid()) {
return false;
}

FileID FID = SM.getFileID(Loc);
const FileEntry* Entry = SM.getFileEntryForID(FID);
if (!Entry) {
return false;
}

StringRef Path = SM.getFilename(Loc);
return Path.contains("third_party/blink/") ||
Path.contains("third_party\\blink\\") ||
Path.contains("/blink/") ||
Path.contains("\\blink\\");
}

std::string getSourceText(const SourceRange& Range, const ASTContext& Context) {
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
CharSourceRange CharRange = CharSourceRange::getTokenRange(Range);
return Lexer::getSourceText(CharRange, SM, LangOpts).str();
}
} // namespace

void ChromiumBlinkCastCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxStaticCastExpr(
hasAncestor(functionDecl().bind("func"))
).bind("static_cast"),
this);
}

void ChromiumBlinkCastCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Cast = Result.Nodes.getNodeAs<CXXStaticCastExpr>("static_cast");
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
if (!Cast || !Func)
return;

bool inBlinkCode = isInBlinkNamespace(Func) ||
isInBlinkDirectory(Func->getLocation(), *Result.SourceManager);

if (!inBlinkCode)
return;

// Get the target type's CXXRecordDecl if it's a class type
const CXXRecordDecl* RecordDecl = nullptr;
if (const auto* PointeeType = Cast->getType()->getPointeeType().getTypePtr()) {
if (auto* RT = PointeeType->getAs<RecordType>()) {
RecordDecl = dyn_cast<CXXRecordDecl>(RT->getDecl());
}
}

if (!RecordDecl)
return;

bool IsPoly = RecordDecl->isPolymorphic();

std::string SubExprStr = getSourceText(Cast->getSubExpr()->getSourceRange(), *Result.Context);
std::string TypeStr = getSourceText(Cast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(), *Result.Context);

// Remove pointer star if present
if (TypeStr.back() == '*') {
TypeStr.pop_back();
}
// Trim any whitespace
TypeStr.erase(TypeStr.find_last_not_of(" \t") + 1);

// Use DynamicTo for polymorphic types, To for non-polymorphic
std::string Replacement = IsPoly ? "DynamicTo" : "To";
Replacement += "<" + TypeStr + ">(" + SubExprStr + ")";

diag(Cast->getBeginLoc(),
"static_cast in Blink should be replaced with %0")
<< (IsPoly ? "DynamicTo<T>" : "To<T>")
<< FixItHint::CreateReplacement(Cast->getSourceRange(), Replacement);
}

} // namespace clang::tidy::chromium
22 changes: 22 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/BlinkCastCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_BLINKCASTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_BLINKCASTCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::chromium {

/// Checks for static_cast usage in Blink code and suggests using To<T> or DynamicTo<T>.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/chromium/blink-cast.html
class ChromiumBlinkCastCheck : public ClangTidyCheck {
public:
ChromiumBlinkCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};

} // namespace clang::tidy::chromium

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_BLINKCASTCHECK_H
28 changes: 28 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
set(LLVM_LINK_COMPONENTS
FrontendOpenMP
Support
)

add_clang_library(clangTidyChromiumModule STATIC
TimeUnitCheck.cpp
InternalNamespaceCheck.cpp
BlinkCastCheck.cpp
ChromiumTidyModule.cpp
LINK_LIBS
clangTidy
clangTidyReadabilityModule
clangTidyUtils

DEPENDS
omp_gen
ClangDriverOptions
)

clang_target_link_libraries(clangTidyChromiumModule
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangLex
clangTooling
)
35 changes: 35 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/ChromiumTidyModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "BlinkCastCheck.h"
#include "InternalNamespaceCheck.h"
#include "TimeUnitCheck.h"

namespace clang::tidy::chromium {

class ChromiumModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ChromiumTimeUnitCheck>(
"chromium-time-unit");
CheckFactories.registerCheck<ChromiumInternalNamespaceCheck>(
"chromium-internal-namespace");
CheckFactories.registerCheck<ChromiumBlinkCastCheck>(
"chromium-blink-cast");
}
};

// Register the module
static clang::tidy::ClangTidyModuleRegistry::Add<ChromiumModule>
X("chromium", "Adds Chromium-specific checks.");

} // namespace clang::tidy::chromium

namespace clang {
namespace tidy {

// This anchor is used to force the linker to link the ChromiumModule.
volatile int ChromiumModuleAnchorSource = 0;

} // namespace tidy
} // namespace clang
102 changes: 102 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/InternalNamespaceCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "InternalNamespaceCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/Support/Path.h"
#include <string>

using namespace clang::ast_matchers;

namespace clang::tidy::chromium {

ChromiumInternalNamespaceCheck::ChromiumInternalNamespaceCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
TestSourcePath = Options.get("test_source_path", "");
}

void ChromiumInternalNamespaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "test_source_path", TestSourcePath);
}

void ChromiumInternalNamespaceCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
nestedNameSpecifierLoc(
loc(specifiesNamespace(hasName("internal"))))
.bind("internal_ns"),
this);
}

bool ChromiumInternalNamespaceCheck::isInComponentPath(StringRef FilePath,
StringRef ComponentName) {
SmallString<256> CanonicalPath(FilePath);
llvm::sys::path::native(CanonicalPath, llvm::sys::path::Style::posix);
StringRef Path(CanonicalPath);

// Normalize build directory paths
StringRef NormalizedPath = Path;
if (Path.contains("/out/") || Path.contains("/Debug/") || Path.contains("/Release/")) {
size_t GenPos = Path.find("/gen/");
if (GenPos != StringRef::npos) {
NormalizedPath = Path.substr(GenPos + 5); // Skip "/gen/"
}
}

// Check each path component
StringRef Remaining = NormalizedPath;
while (!Remaining.empty()) {
if (llvm::sys::path::filename(Remaining) == ComponentName) {
StringRef Parent = llvm::sys::path::parent_path(Remaining);
if (Parent.empty() || llvm::sys::path::filename(Parent) == "src" ||
llvm::sys::path::filename(Parent) == "components") {
return true;
}
}
Remaining = llvm::sys::path::parent_path(Remaining);
}

return false;
}

void ChromiumInternalNamespaceCheck::check(const MatchFinder::MatchResult &Result) {
const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("internal_ns");
if (!NNS)
return;

SourceLocation Loc = NNS->getLocalBeginLoc();
const SourceManager &SM = *Result.SourceManager;
StringRef FilePath = !TestSourcePath.empty() ? TestSourcePath : SM.getFilename(Loc);
if (FilePath.empty())
return;

std::string FullNamespace;
for (NestedNameSpecifier *Prefix = NNS->getNestedNameSpecifier();
Prefix;
Prefix = Prefix->getPrefix()) {
if (const auto *NS = Prefix->getAsNamespace()) {
if (!FullNamespace.empty()) {
FullNamespace = "::" + FullNamespace;
}
FullNamespace = NS->getName().str() + FullNamespace;
}
}

size_t InternalPos = FullNamespace.rfind("::internal");
if (InternalPos == std::string::npos)
return;

std::string ComponentName;
size_t LastSep = FullNamespace.rfind("::", InternalPos - 1);
if (LastSep == std::string::npos) {
ComponentName = FullNamespace.substr(0, InternalPos);
} else {
ComponentName = FullNamespace.substr(LastSep + 2, InternalPos - LastSep - 2);
}

if (!isInComponentPath(FilePath, ComponentName)) {
diag(Loc, "do not use %0::internal namespace from outside %0/ directory")
<< ComponentName;
}
}

} // namespace clang::tidy::chromium
22 changes: 22 additions & 0 deletions clang-tools-extra/clang-tidy/chromium/InternalNamespaceCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_INTERNAL_NAMESPACE_CHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_INTERNAL_NAMESPACE_CHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::chromium {

class ChromiumInternalNamespaceCheck : public ClangTidyCheck {
public:
ChromiumInternalNamespaceCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;

private:
bool isInComponentPath(llvm::StringRef FilePath, llvm::StringRef ComponentName);
std::string TestSourcePath;
};

} // namespace clang::tidy::chromium

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CHROMIUM_INTERNAL_NAMESPACE_CHECK_H
Loading