Skip to content
Closed
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
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseRangesCheck.cpp
UseSpanCheck.cpp
UseStartsEndsWithCheck.cpp
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseRangesCheck.h"
#include "UseSpanCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
Expand Down Expand Up @@ -80,6 +81,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
"modernize-use-integer-sign-comparison");
CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
CheckFactories.registerCheck<UseSpanCheck>(
"modernize-use-span");
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
"modernize-use-starts-ends-with");
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
Expand Down
142 changes: 142 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===--- UseSpanCheck.cpp - clang-tidy ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "UseSpanCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "../utils/IncludeInserter.h"
#include <string>

using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

namespace {
AST_MATCHER(QualType, isRefToVectorOrArray) {
if (!Node->isReferenceType())
return false;

QualType PointeeType = Node->getPointeeType();

const Type *UnqualifiedType = PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
if (!UnqualifiedType || !UnqualifiedType->isRecordType())
return false;

const CXXRecordDecl *Record = UnqualifiedType->getAsCXXRecordDecl();
if (!Record)
return false;

const std::string Name = Record->getQualifiedNameAsString();
return Name == "std::vector" || Name == "std::array";
}
} // namespace

UseSpanCheck::UseSpanCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(utils::IncludeSorter::IS_LLVM, areDiagsSelfContained()) {}

void UseSpanCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
}

void UseSpanCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
functionDecl(
forEachDescendant(
parmVarDecl(hasType(qualType(isRefToVectorOrArray())))
.bind("param"))),
this);
}

void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
if (!Param)
return;

QualType ParamType = Param->getType();
if (!ParamType->isReferenceType())
return;

// Get the pointee type (the vector/array type)
QualType PointeeType = ParamType->getPointeeType();
bool IsConst = PointeeType.isConstQualified();

const Type *UnqualifiedType = PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
if (!UnqualifiedType || !UnqualifiedType->isRecordType())
return;

const CXXRecordDecl *Record = UnqualifiedType->getAsCXXRecordDecl();
if (!Record)
return;

const std::string RecordName = Record->getQualifiedNameAsString();
if (RecordName != "std::vector" && RecordName != "std::array")
return;

// Check if it's a template specialization
if (!isa<ClassTemplateSpecializationDecl>(Record))
return;

// Get the template arguments
const auto *TemplateSpecRecord = cast<ClassTemplateSpecializationDecl>(Record);
const TemplateArgumentList &Args = TemplateSpecRecord->getTemplateArgs();
if (Args.size() < 1)
return;

// Get the element type from the first template argument
const TemplateArgument &Arg = Args[0];
if (Arg.getKind() != TemplateArgument::Type)
return;

QualType ElementType = Arg.getAsType();

// Get the source range for the parameter type
TypeSourceInfo *TSI = Param->getTypeSourceInfo();
TypeLoc TL = TSI->getTypeLoc();

// Get the source range for the entire type, including qualifiers
SourceRange TypeRange = TL.getSourceRange();

// Create the diagnostic
auto Diag = diag(Param->getBeginLoc(),
"parameter %0 is reference to %1; consider using std::span instead")
<< Param
<< (RecordName == "std::vector" ? "std::vector" : "std::array");

// Create the fix-it hint
std::string SpanType;
std::string ElementTypeWithConst = IsConst ? "const " + ElementType.getAsString() : ElementType.getAsString();

// For std::array, we should preserve the size in the span
if (RecordName == "std::array" && Args.size() >= 2) {
const TemplateArgument &SizeArg = Args[1];
if (SizeArg.getKind() == TemplateArgument::Integral) {
llvm::APSInt Size = SizeArg.getAsIntegral();
// Convert APSInt to string
SmallString<16> SizeStr;
Size.toString(SizeStr, 10);
SpanType = "std::span<" + ElementTypeWithConst + ", " + SizeStr.str().str() + ">";
} else {
SpanType = "std::span<" + ElementTypeWithConst + ">";
}
} else {
SpanType = "std::span<" + ElementTypeWithConst + ">";
}

// Create the replacement for the entire type including qualifiers
Diag << FixItHint::CreateReplacement(TypeRange, SpanType);

// Add the include for <span>
Diag << Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(Param->getBeginLoc()),
"<span>");
}

} // namespace clang::tidy::modernize
39 changes: 39 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseSpanCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===--- UseSpanCheck.h - clang-tidy ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANCHECK_H

#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"

namespace clang::tidy::modernize {

/// Suggests replacing const references to std::vector and std::array with
/// std::span.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-span.html
class UseSpanCheck : public ClangTidyCheck {
public:
UseSpanCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus20;
}

private:
utils::IncludeInserter Inserter;
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANCHECK_H
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ New checks
Finds potentially erroneous calls to ``reset`` method on smart pointers when
the pointee type also has a ``reset`` method.

- New :doc:`modernize-use-span
<clang-tidy/checks/modernize/use-span>` check.

Suggests replacing const references to std::vector and std::array with
std::span.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ Clang-Tidy Checks
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
:doc:`modernize-use-ranges <modernize/use-ranges>`, "Yes"
:doc:`modernize-use-span <modernize/use-span>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
Expand Down
48 changes: 48 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/modernize/use-span.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.. title:: clang-tidy - modernize-use-span

modernize-use-span
==================

This check suggests using ``std::span`` in function parameters when taking a
range of contiguous elements by reference. ``std::span`` can accept vectors,
arrays, and C-style arrays, so the function is no longer bound to a specific
container type.

This check implements `R.14<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ap>``
from the C++ Core Guidelines.

This check requires C++20 or later.

Examples
--------

.. code-block:: c++

// Before
void process(const std::vector<int>& vec) {
for (const auto& val : vec) {
// Process val
}
}

void analyze(const std::array<double, 5>& arr) {
for (const auto& val : arr) {
// Analyze val
}
}

// After
void process(std::span<const int> vec) {
for (const auto& val : vec) {
// Process val
}
}

void analyze(std::span<const double, 5> arr) {
for (const auto& val : arr) {
// Analyze val
}
}

The transformed code can now accept any contiguous container of the appropriate
element type and optionally specified length.
Loading
Loading