-
Notifications
You must be signed in to change notification settings - Fork 15.3k
Add modernize-use-span linter check #140001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This linter check recommends using std::span over const references to std::array or std::vector.
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp clang-tools-extra/clang-tidy/modernize/UseSpanCheck.h clang-tools-extra/test/clang-tidy/checkers/modernize/use-span.cpp clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cppView the diff from clang-format here.diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 1fba227a7..c07febf2e 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -81,8 +81,7 @@ public:
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
"modernize-use-integer-sign-comparison");
CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
- CheckFactories.registerCheck<UseSpanCheck>(
- "modernize-use-span");
+ CheckFactories.registerCheck<UseSpanCheck>("modernize-use-span");
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
"modernize-use-starts-ends-with");
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp
index 9a9f3c3fd..cc1d167d6 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanCheck.cpp
@@ -7,10 +7,10 @@
//===----------------------------------------------------------------------===//
#include "UseSpanCheck.h"
+#include "../utils/IncludeInserter.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;
@@ -24,7 +24,8 @@ AST_MATCHER(QualType, isRefToVectorOrArray) {
QualType PointeeType = Node->getPointeeType();
- const Type *UnqualifiedType = PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
+ const Type *UnqualifiedType =
+ PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
if (!UnqualifiedType || !UnqualifiedType->isRecordType())
return false;
@@ -41,18 +42,17 @@ UseSpanCheck::UseSpanCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(utils::IncludeSorter::IS_LLVM, areDiagsSelfContained()) {}
-void UseSpanCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+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);
+ Finder->addMatcher(functionDecl(forEachDescendant(
+ parmVarDecl(hasType(qualType(isRefToVectorOrArray())))
+ .bind("param"))),
+ this);
}
void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
@@ -68,7 +68,8 @@ void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
QualType PointeeType = ParamType->getPointeeType();
bool IsConst = PointeeType.isConstQualified();
- const Type *UnqualifiedType = PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
+ const Type *UnqualifiedType =
+ PointeeType.getTypePtr()->getUnqualifiedDesugaredType();
if (!UnqualifiedType || !UnqualifiedType->isRecordType())
return;
@@ -85,7 +86,8 @@ void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
return;
// Get the template arguments
- const auto *TemplateSpecRecord = cast<ClassTemplateSpecializationDecl>(Record);
+ const auto *TemplateSpecRecord =
+ cast<ClassTemplateSpecializationDecl>(Record);
const TemplateArgumentList &Args = TemplateSpecRecord->getTemplateArgs();
if (Args.size() < 1)
return;
@@ -105,14 +107,16 @@ void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
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");
+ 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();
+ 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) {
@@ -122,7 +126,8 @@ void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
// Convert APSInt to string
SmallString<16> SizeStr;
Size.toString(SizeStr, 10);
- SpanType = "std::span<" + ElementTypeWithConst + ", " + SizeStr.str().str() + ">";
+ SpanType = "std::span<" + ElementTypeWithConst + ", " +
+ SizeStr.str().str() + ">";
} else {
SpanType = "std::span<" + ElementTypeWithConst + ">";
}
@@ -135,8 +140,7 @@ void UseSpanCheck::check(const MatchFinder::MatchResult &Result) {
// Add the include for <span>
Diag << Inserter.createIncludeInsertion(
- Result.SourceManager->getFileID(Param->getBeginLoc()),
- "<span>");
+ Result.SourceManager->getFileID(Param->getBeginLoc()), "<span>");
}
} // namespace clang::tidy::modernize
|
|
|
|
Another capability gap is that a span doesn't seem to have bounds-checking until C++26 and I'm not sure if the bounds-checking works for |
|
Thinking more, this is not a good check at this time. |
|
I came across it because of #156058. I think the conversion can already be useful now, but restrictions could be imposed.
I guess you mean cases like this: // before
void print(const std::vector<int>& values) {
for (int v : values) std::cout << v << " ";
}
// after
void print(std::span<const int> values) {
for (int v : values) std::cout << v << " ";
}
print({1, 2, 3, 4});Could the test be made configurable for this purpose? Option 1) By default, it only reports methods if they are within a compilation unit and thus more visible for the check. In our tests, for example, we often have vectors with enum values that could be stored in a And in principle: You can always disable any check if you don't like it ;-)
Isn't that only relevant if an |
|
@SunBlack I'm still in favour of the check but I didn't see myself working on it until C++26 as it won't be usable for many people. I would prefer Option 2 if I was still working on this. In the example you gave, the initializer list should be in a constexpr std::array to avoid using magic numbers.
This is a footgun, because I believe |
This linter check recommends using
std::spanin function parameters over C-arrays or references tostd::vectorandstd::array. This is because astd::spancan take in arguments of C-arrays, vectors, arrays, or any contiguous data structure. The C++ Core Guidelines recommend using spans to pass non-owning views of contiguous memory.The check triggers as long as contiguous data structure is used in a way that would be supported by
std::span. For instance, if a function takes a vector, callingpush_backor using the vector in another function call that needs a vector would prevent this check from triggeringThe applied fixit recommends either a compile-time length defined
std::span(in case of replacing a std::array) or a runtime defined length (in case of replacing std::vector).TODO:
const std::vector<int>&toconst std::span<const int>when it should bestd::span<const int>, becausestd::spandoesn't propagateconst. I have no idea how to do this, so I would appreciate advice.gsl::spanor custom span wrappers?std::span" logic