Skip to content

Commit f8f9c9e

Browse files
authored
Merge branch 'main' into mlir-llvm-module-asm
2 parents 7a20b31 + 740758a commit f8f9c9e

File tree

532 files changed

+19512
-5428
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

532 files changed

+19512
-5428
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "IncorrectRoundingsCheck.h"
3939
#include "InfiniteLoopCheck.h"
4040
#include "IntegerDivisionCheck.h"
41+
#include "InvalidEnumDefaultInitializationCheck.h"
4142
#include "LambdaFunctionNameCheck.h"
4243
#include "MacroParenthesesCheck.h"
4344
#include "MacroRepeatedSideEffectsCheck.h"
@@ -165,6 +166,8 @@ class BugproneModule : public ClangTidyModule {
165166
CheckFactories.registerCheck<InfiniteLoopCheck>("bugprone-infinite-loop");
166167
CheckFactories.registerCheck<IntegerDivisionCheck>(
167168
"bugprone-integer-division");
169+
CheckFactories.registerCheck<InvalidEnumDefaultInitializationCheck>(
170+
"bugprone-invalid-enum-default-initialization");
168171
CheckFactories.registerCheck<LambdaFunctionNameCheck>(
169172
"bugprone-lambda-function-name");
170173
CheckFactories.registerCheck<MacroParenthesesCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_clang_library(clangTidyBugproneModule STATIC
3030
InaccurateEraseCheck.cpp
3131
IncorrectEnableIfCheck.cpp
3232
IncorrectEnableSharedFromThisCheck.cpp
33+
InvalidEnumDefaultInitializationCheck.cpp
3334
UnintendedCharOstreamOutputCheck.cpp
3435
ReturnConstRefFromParameterCheck.cpp
3536
SuspiciousStringviewDataUsageCheck.cpp
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//===--- InvalidEnumDefaultInitializationCheck.cpp - clang-tidy -----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "InvalidEnumDefaultInitializationCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/TypeVisitor.h"
12+
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include <algorithm>
14+
15+
using namespace clang::ast_matchers;
16+
17+
namespace clang::tidy::bugprone {
18+
19+
namespace {
20+
21+
bool isCompleteAndHasNoZeroValue(const EnumDecl *D) {
22+
const EnumDecl *Definition = D->getDefinition();
23+
return Definition && Definition->isComplete() &&
24+
!Definition->enumerators().empty() &&
25+
std::none_of(Definition->enumerator_begin(),
26+
Definition->enumerator_end(),
27+
[](const EnumConstantDecl *Value) {
28+
return Value->getInitVal().isZero();
29+
});
30+
}
31+
32+
AST_MATCHER(EnumDecl, isCompleteAndHasNoZeroValue) {
33+
return isCompleteAndHasNoZeroValue(&Node);
34+
}
35+
36+
// Find an initialization which initializes the value (if it has enum type) to a
37+
// default zero value.
38+
AST_MATCHER(Expr, isEmptyInit) {
39+
if (isa<CXXScalarValueInitExpr, ImplicitValueInitExpr>(&Node))
40+
return true;
41+
if (const auto *Init = dyn_cast<InitListExpr>(&Node)) {
42+
if (Init->getNumInits() == 0)
43+
return true;
44+
}
45+
return false;
46+
}
47+
48+
AST_MATCHER(InitListExpr, hasArrayFiller) { return Node.hasArrayFiller(); }
49+
50+
// Check if any type has a "child" type that is an enum without zero value.
51+
// The "child" type can be an array element type or member type of a record
52+
// type (or a recursive combination of these). In this case, if the "root" type
53+
// is statically initialized, the enum component is initialized to zero.
54+
class FindEnumMember : public TypeVisitor<FindEnumMember, bool> {
55+
public:
56+
const EnumType *FoundEnum = nullptr;
57+
58+
bool VisitType(const Type *T) {
59+
const Type *DesT = T->getUnqualifiedDesugaredType();
60+
if (DesT != T)
61+
return Visit(DesT);
62+
return false;
63+
}
64+
bool VisitArrayType(const ArrayType *T) {
65+
return Visit(T->getElementType().getTypePtr());
66+
}
67+
bool VisitConstantArrayType(const ConstantArrayType *T) {
68+
return Visit(T->getElementType().getTypePtr());
69+
}
70+
bool VisitEnumType(const EnumType *T) {
71+
if (isCompleteAndHasNoZeroValue(T->getDecl())) {
72+
FoundEnum = T;
73+
return true;
74+
}
75+
return false;
76+
}
77+
bool VisitRecordType(const RecordType *T) {
78+
const RecordDecl *RD = T->getDecl();
79+
if (RD->isUnion())
80+
return false;
81+
auto VisitField = [this](const FieldDecl *F) {
82+
return Visit(F->getType().getTypePtr());
83+
};
84+
return llvm::any_of(RD->fields(), VisitField);
85+
}
86+
};
87+
88+
} // namespace
89+
90+
InvalidEnumDefaultInitializationCheck::InvalidEnumDefaultInitializationCheck(
91+
StringRef Name, ClangTidyContext *Context)
92+
: ClangTidyCheck(Name, Context) {}
93+
94+
void InvalidEnumDefaultInitializationCheck::registerMatchers(
95+
MatchFinder *Finder) {
96+
auto EnumWithoutZeroValue = enumType(
97+
hasDeclaration(enumDecl(isCompleteAndHasNoZeroValue()).bind("enum")));
98+
auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
99+
anyOf(EnumWithoutZeroValue,
100+
arrayType(hasElementType(qualType(
101+
hasUnqualifiedDesugaredType(EnumWithoutZeroValue)))))));
102+
Finder->addMatcher(
103+
expr(isEmptyInit(), hasType(EnumOrArrayOfEnum)).bind("expr"), this);
104+
105+
// Array initialization can contain an "array filler" for the (syntactically)
106+
// unspecified elements. This expression is not found by AST matchers and can
107+
// have any type (the array's element type). This is an implicitly generated
108+
// initialization, so if the type contains somewhere an enum without zero
109+
// enumerator, the zero initialization applies here. We search this array
110+
// element type for the specific enum type manually when this matcher matches.
111+
Finder->addMatcher(initListExpr(hasArrayFiller()).bind("array_filler_expr"),
112+
this);
113+
}
114+
115+
void InvalidEnumDefaultInitializationCheck::check(
116+
const MatchFinder::MatchResult &Result) {
117+
const auto *InitExpr = Result.Nodes.getNodeAs<Expr>("expr");
118+
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum");
119+
if (!InitExpr) {
120+
const auto *InitList =
121+
Result.Nodes.getNodeAs<InitListExpr>("array_filler_expr");
122+
// Initialization of omitted array elements with array filler was found.
123+
// Check the type for enum without zero value.
124+
// FIXME: In this way only one enum-typed value is found, not all of these.
125+
FindEnumMember Finder;
126+
if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))
127+
return;
128+
InitExpr = InitList;
129+
Enum = Finder.FoundEnum->getDecl();
130+
}
131+
132+
if (!InitExpr || !Enum)
133+
return;
134+
135+
ASTContext &ACtx = Enum->getASTContext();
136+
SourceLocation Loc = InitExpr->getExprLoc();
137+
if (Loc.isInvalid()) {
138+
if (isa<ImplicitValueInitExpr, InitListExpr>(InitExpr)) {
139+
DynTypedNodeList Parents = ACtx.getParents(*InitExpr);
140+
if (Parents.empty())
141+
return;
142+
143+
if (const auto *Ctor = Parents[0].get<CXXConstructorDecl>()) {
144+
// Try to find member initializer with the found expression and get the
145+
// source ___location from it.
146+
CXXCtorInitializer *const *CtorInit = std::find_if(
147+
Ctor->init_begin(), Ctor->init_end(),
148+
[InitExpr](const CXXCtorInitializer *Init) {
149+
return Init->isMemberInitializer() && Init->getInit() == InitExpr;
150+
});
151+
if (!CtorInit)
152+
return;
153+
Loc = (*CtorInit)->getLParenLoc();
154+
} else if (const auto *InitList = Parents[0].get<InitListExpr>()) {
155+
// The expression may be implicitly generated for an initialization.
156+
// Search for a parent initialization list with valid source ___location.
157+
while (InitList->getExprLoc().isInvalid()) {
158+
DynTypedNodeList Parents = ACtx.getParents(*InitList);
159+
if (Parents.empty())
160+
return;
161+
InitList = Parents[0].get<InitListExpr>();
162+
if (!InitList)
163+
return;
164+
}
165+
Loc = InitList->getExprLoc();
166+
}
167+
}
168+
// If still not found a source ___location, omit the warning.
169+
// Ideally all such cases (if they exist) should be handled to make the
170+
// check more precise.
171+
if (Loc.isInvalid())
172+
return;
173+
}
174+
diag(Loc, "enum value of type %0 initialized with invalid value of 0, "
175+
"enum doesn't have a zero-value enumerator")
176+
<< Enum;
177+
diag(Enum->getLocation(), "enum is defined here", DiagnosticIDs::Note);
178+
}
179+
180+
} // namespace clang::tidy::bugprone
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===--- InvalidEnumDefaultInitializationCheck.h - clang-tidy -*- C++ -*---===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INVALIDENUMDEFAULTINITIALIZATIONCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INVALIDENUMDEFAULTINITIALIZATIONCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Detects default initialization (to 0) of variables with `enum` type where
17+
/// the enum has no enumerator with value of 0.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/invalid-enum-default-initialization.html
21+
class InvalidEnumDefaultInitializationCheck : public ClangTidyCheck {
22+
public:
23+
InvalidEnumDefaultInitializationCheck(StringRef Name,
24+
ClangTidyContext *Context);
25+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
26+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
};
28+
29+
} // namespace clang::tidy::bugprone
30+
31+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INVALIDENUMDEFAULTINITIALIZATIONCHECK_H

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,6 +3267,56 @@ TEST(SignatureHelpTest, VariadicType) {
32673267
}
32683268
}
32693269

3270+
TEST(SignatureHelpTest, SkipExplicitObjectParameter) {
3271+
Annotations Code(R"cpp(
3272+
struct A {
3273+
void foo(this auto&& self, int arg);
3274+
void bar(this A self, int arg);
3275+
};
3276+
int main() {
3277+
A a {};
3278+
a.foo($c1^);
3279+
(&A::bar)($c2^);
3280+
(&A::foo)($c3^);
3281+
}
3282+
)cpp");
3283+
3284+
auto TU = TestTU::withCode(Code.code());
3285+
TU.ExtraArgs = {"-std=c++23"};
3286+
3287+
MockFS FS;
3288+
auto Inputs = TU.inputs(FS);
3289+
3290+
auto Preamble = TU.preamble();
3291+
ASSERT_TRUE(Preamble);
3292+
3293+
{
3294+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c1"),
3295+
*Preamble, Inputs, MarkupKind::PlainText);
3296+
3297+
EXPECT_EQ(1U, Result.signatures.size());
3298+
3299+
EXPECT_THAT(Result.signatures[0], AllOf(sig("foo([[int arg]]) -> void")));
3300+
}
3301+
{
3302+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c2"),
3303+
*Preamble, Inputs, MarkupKind::PlainText);
3304+
3305+
EXPECT_EQ(1U, Result.signatures.size());
3306+
3307+
EXPECT_THAT(Result.signatures[0], AllOf(sig("([[A]], [[int]]) -> void")));
3308+
}
3309+
{
3310+
// TODO: llvm/llvm-project/146649
3311+
const auto Result = signatureHelp(testPath(TU.Filename), Code.point("c3"),
3312+
*Preamble, Inputs, MarkupKind::PlainText);
3313+
// TODO: We expect 1 signature here, with this signature
3314+
EXPECT_EQ(0U, Result.signatures.size());
3315+
// EXPECT_THAT(Result.signatures[0], AllOf(sig("([[auto&&]], [[int]]) ->
3316+
// void")));
3317+
}
3318+
}
3319+
32703320
TEST(CompletionTest, IncludedCompletionKinds) {
32713321
Annotations Test(R"cpp(#include "^)cpp");
32723322
auto TU = TestTU::withCode(Test.code());
@@ -4369,14 +4419,24 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
43694419
Annotations Code(R"cpp(
43704420
struct A {
43714421
void foo(this auto&& self, int arg);
4422+
void bar(this A self, int arg);
43724423
};
43734424
43744425
int main() {
43754426
A a {};
4376-
a.^
4427+
a.$c1^;
4428+
(&A::fo$c2^;
4429+
(&A::ba$c3^;
43774430
}
43784431
)cpp");
43794432

4433+
// TODO: llvm/llvm-project/146649
4434+
// This is incorrect behavior. Correct Result should be a variant of,
4435+
// c2: signature = (auto&& self, int arg)
4436+
// snippet = (${1: auto&& self}, ${2: int arg})
4437+
// c3: signature = (A self, int arg)
4438+
// snippet = (${1: A self}, ${2: int arg})
4439+
43804440
auto TU = TestTU::withCode(Code.code());
43814441
TU.ExtraArgs = {"-std=c++23"};
43824442

@@ -4387,12 +4447,31 @@ TEST(CompletionTest, SkipExplicitObjectParameter) {
43874447

43884448
MockFS FS;
43894449
auto Inputs = TU.inputs(FS);
4390-
auto Result = codeComplete(testPath(TU.Filename), Code.point(),
4391-
Preamble.get(), Inputs, Opts);
4392-
4393-
EXPECT_THAT(Result.Completions,
4394-
ElementsAre(AllOf(named("foo"), signature("(int arg)"),
4395-
snippetSuffix("(${1:int arg})"))));
4450+
{
4451+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"),
4452+
Preamble.get(), Inputs, Opts);
4453+
4454+
EXPECT_THAT(Result.Completions,
4455+
UnorderedElementsAre(AllOf(named("foo"), signature("(int arg)"),
4456+
snippetSuffix("(${1:int arg})")),
4457+
AllOf(named("bar"), signature("(int arg)"),
4458+
snippetSuffix("(${1:int arg})"))));
4459+
}
4460+
{
4461+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"),
4462+
Preamble.get(), Inputs, Opts);
4463+
EXPECT_THAT(
4464+
Result.Completions,
4465+
ElementsAre(AllOf(named("foo"), signature("<class self:auto>(int arg)"),
4466+
snippetSuffix("<${1:class self:auto}>"))));
4467+
}
4468+
{
4469+
auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"),
4470+
Preamble.get(), Inputs, Opts);
4471+
EXPECT_THAT(Result.Completions,
4472+
ElementsAre(AllOf(named("bar"), signature("(int arg)"),
4473+
snippetSuffix(""))));
4474+
}
43964475
}
43974476
} // namespace
43984477
} // namespace clangd

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ Improvements to clang-tidy
108108
New checks
109109
^^^^^^^^^^
110110

111+
- New :doc:`bugprone-invalid-enum-default-initialization
112+
<clang-tidy/checks/bugprone/invalid-enum-default-initialization>` check.
113+
114+
Detects default initialization (to 0) of variables with ``enum`` type where
115+
the enum has no enumerator with value of 0.
116+
111117
- New :doc:`llvm-mlir-op-builder
112118
<clang-tidy/checks/llvm/use-new-mlir-op-builder>` check.
113119

0 commit comments

Comments
 (0)