[clang-tools-extra] Enforce SL.con.3: Add check to replace operator[] with at() (PR #90043)
https://github.com/sebwolf-de updated https://github.com/llvm/llvm-project/pull/90043 >From 8eb5863305e8f9a1311a540faf35f24fc6f55c6c Mon Sep 17 00:00:00 2001 From: Sebastian Wolf Date: Wed, 17 Apr 2024 16:16:35 +0200 Subject: [PATCH 1/3] Enforce SL.con.3: Add check to replace operator[] with at() on std containers --- .../AvoidBoundsErrorsCheck.cpp| 81 +++ .../AvoidBoundsErrorsCheck.h | 32 .../cppcoreguidelines/CMakeLists.txt | 1 + .../CppCoreGuidelinesTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 5 ++ .../cppcoreguidelines/avoid-bounds-errors.rst | 20 + .../docs/clang-tidy/checks/list.rst | 1 + .../cppcoreguidelines/avoid-bounds-errors.cpp | 66 +++ 8 files changed, 209 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-bounds-errors.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-bounds-errors.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp new file mode 100644 index 0..524c21b5bdb81 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp @@ -0,0 +1,81 @@ +//===--- AvoidBoundsErrorsCheck.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 "AvoidBoundsErrorsCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +#include +using namespace clang::ast_matchers; + +namespace clang::tidy::cppcoreguidelines { + +bool isApplicable(const QualType ) { + const auto TypeStr = Type.getAsString(); + bool Result = false; + // Only check for containers in the std namespace + if (TypeStr.find("std::vector") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::array") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::deque") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::map") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::unordered_map") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::flat_map") != std::string::npos) { +Result = true; + } + // TODO Add std::span with C++26 + return Result; +} + +void AvoidBoundsErrorsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(cxxMethodDecl(hasName("operator[]")).bind("f"))) + .bind("x"), + this); +} + +void AvoidBoundsErrorsCheck::check(const MatchFinder::MatchResult ) { + const ASTContext = *Result.Context; + const SourceManager = Context.getSourceManager(); + const auto *MatchedExpr = Result.Nodes.getNodeAs("x"); + const auto *MatchedFunction = Result.Nodes.getNodeAs("f"); + const auto Type = MatchedFunction->getThisType(); + if (!isApplicable(Type)) { +return; + } + + // Get original code. + const SourceLocation b(MatchedExpr->getBeginLoc()); + const SourceLocation e(MatchedExpr->getEndLoc()); + const std::string OriginalCode = + Lexer::getSourceText(CharSourceRange::getTokenRange(b, e), Source, + getLangOpts()) + .str(); + const auto Range = SourceRange(b, e); + + // Build replacement. + std::string NewCode = OriginalCode; + const auto BeginOpen = NewCode.find("["); + NewCode.replace(BeginOpen, 1, ".at("); + const auto BeginClose = NewCode.find("]"); + NewCode.replace(BeginClose, 1, ")"); + + diag(MatchedExpr->getBeginLoc(), "Do not use operator[], use at() instead.") + << FixItHint::CreateReplacement(Range, NewCode); +} + +} // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h new file mode 100644 index 0..f915729cd7bbe --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h @@ -0,0 +1,32 @@ +//===--- AvoidBoundsErrorsCheck.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
[clang-tools-extra] Enforce SL.con.3: Add check to replace operator[] with at() (PR #90043)
https://github.com/sebwolf-de created https://github.com/llvm/llvm-project/pull/90043 The string based test to find out whether the check is applicable on the class is not ideal, but I did not find a more elegant way, yet. If there is more clang-query magic available, that I'm not aware of, I'm happy to adapt that. >From 8eb5863305e8f9a1311a540faf35f24fc6f55c6c Mon Sep 17 00:00:00 2001 From: Sebastian Wolf Date: Wed, 17 Apr 2024 16:16:35 +0200 Subject: [PATCH] Enforce SL.con.3: Add check to replace operator[] with at() on std containers --- .../AvoidBoundsErrorsCheck.cpp| 81 +++ .../AvoidBoundsErrorsCheck.h | 32 .../cppcoreguidelines/CMakeLists.txt | 1 + .../CppCoreGuidelinesTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 5 ++ .../cppcoreguidelines/avoid-bounds-errors.rst | 20 + .../docs/clang-tidy/checks/list.rst | 1 + .../cppcoreguidelines/avoid-bounds-errors.cpp | 66 +++ 8 files changed, 209 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-bounds-errors.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-bounds-errors.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp new file mode 100644 index 00..524c21b5bdb818 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.cpp @@ -0,0 +1,81 @@ +//===--- AvoidBoundsErrorsCheck.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 "AvoidBoundsErrorsCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +#include +using namespace clang::ast_matchers; + +namespace clang::tidy::cppcoreguidelines { + +bool isApplicable(const QualType ) { + const auto TypeStr = Type.getAsString(); + bool Result = false; + // Only check for containers in the std namespace + if (TypeStr.find("std::vector") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::array") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::deque") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::map") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::unordered_map") != std::string::npos) { +Result = true; + } + if (TypeStr.find("std::flat_map") != std::string::npos) { +Result = true; + } + // TODO Add std::span with C++26 + return Result; +} + +void AvoidBoundsErrorsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(cxxMethodDecl(hasName("operator[]")).bind("f"))) + .bind("x"), + this); +} + +void AvoidBoundsErrorsCheck::check(const MatchFinder::MatchResult ) { + const ASTContext = *Result.Context; + const SourceManager = Context.getSourceManager(); + const auto *MatchedExpr = Result.Nodes.getNodeAs("x"); + const auto *MatchedFunction = Result.Nodes.getNodeAs("f"); + const auto Type = MatchedFunction->getThisType(); + if (!isApplicable(Type)) { +return; + } + + // Get original code. + const SourceLocation b(MatchedExpr->getBeginLoc()); + const SourceLocation e(MatchedExpr->getEndLoc()); + const std::string OriginalCode = + Lexer::getSourceText(CharSourceRange::getTokenRange(b, e), Source, + getLangOpts()) + .str(); + const auto Range = SourceRange(b, e); + + // Build replacement. + std::string NewCode = OriginalCode; + const auto BeginOpen = NewCode.find("["); + NewCode.replace(BeginOpen, 1, ".at("); + const auto BeginClose = NewCode.find("]"); + NewCode.replace(BeginClose, 1, ")"); + + diag(MatchedExpr->getBeginLoc(), "Do not use operator[], use at() instead.") + << FixItHint::CreateReplacement(Range, NewCode); +} + +} // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h new file mode 100644 index 00..f915729cd7bbee --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidBoundsErrorsCheck.h @@ -0,0 +1,32 @@ +//===--- AvoidBoundsErrorsCheck.h - clang-tidy --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See