| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| //===- bolt/Core/GDBIndex.cpp - GDB Index support ------------------------===// | ||
| // | ||
| // 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 "bolt/Core/GDBIndex.h" | ||
|
|
||
| using namespace llvm::bolt; | ||
| using namespace llvm::support::endian; | ||
|
|
||
| void GDBIndex::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) { | ||
| std::lock_guard<std::mutex> Lock(GDBIndexMutex); | ||
| if (!BC.getGdbIndexSection()) | ||
| return; | ||
| GDBIndexTUEntryVector.emplace_back(Entry); | ||
| } | ||
|
|
||
| void GDBIndex::updateGdbIndexSection( | ||
| const CUOffsetMap &CUMap, const uint32_t NumCUs, | ||
| DebugARangesSectionWriter &ARangesSectionWriter) { | ||
| if (!BC.getGdbIndexSection()) | ||
| return; | ||
|
|
||
| // See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html | ||
| // for .gdb_index section format. | ||
|
|
||
| StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents(); | ||
|
|
||
| const char *Data = GdbIndexContents.data(); | ||
|
|
||
| // Parse the header. | ||
| const uint32_t Version = read32le(Data); | ||
| if (Version != 7 && Version != 8) { | ||
| errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n"; | ||
| exit(1); | ||
| } | ||
|
|
||
| // Some .gdb_index generators use file offsets while others use section | ||
| // offsets. Hence we can only rely on offsets relative to each other, | ||
| // and ignore their absolute values. | ||
| const uint32_t CUListOffset = read32le(Data + 4); | ||
| const uint32_t CUTypesOffset = read32le(Data + 8); | ||
| const uint32_t AddressTableOffset = read32le(Data + 12); | ||
| const uint32_t SymbolTableOffset = read32le(Data + 16); | ||
| const uint32_t ConstantPoolOffset = read32le(Data + 20); | ||
| Data += 24; | ||
|
|
||
| // Map CUs offsets to indices and verify existing index table. | ||
| std::map<uint32_t, uint32_t> OffsetToIndexMap; | ||
| const uint32_t CUListSize = CUTypesOffset - CUListOffset; | ||
| const uint32_t TUListSize = AddressTableOffset - CUTypesOffset; | ||
| const unsigned NUmCUsEncoded = CUListSize / 16; | ||
| unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion(); | ||
| unsigned NumDWARF5TUs = | ||
| getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits(); | ||
| bool SkipTypeUnits = false; | ||
| // For DWARF5 Types are in .debug_info. | ||
| // LLD doesn't generate Types CU List, and in CU list offset | ||
| // only includes CUs. | ||
| // GDB 11+ includes only CUs in CU list and generates Types | ||
| // list. | ||
| // GDB 9 includes CUs and TUs in CU list and generates TYpes | ||
| // list. The NumCUs is CUs + TUs, so need to modify the check. | ||
| // For split-dwarf | ||
| // GDB-11, DWARF5: TU units from dwo are not included. | ||
| // GDB-11, DWARF4: TU units from dwo are included. | ||
| if (MaxDWARFVersion >= 5) | ||
| SkipTypeUnits = !TUListSize ? true | ||
| : ((NUmCUsEncoded + NumDWARF5TUs) == | ||
| BC.DwCtx->getNumCompileUnits()); | ||
|
|
||
| if (!((CUListSize == NumCUs * 16) || | ||
| (CUListSize == (NumCUs + NumDWARF5TUs) * 16))) { | ||
| errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n"; | ||
| exit(1); | ||
| } | ||
| DenseSet<uint64_t> OriginalOffsets; | ||
| for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits(); | ||
| Index < Units; ++Index) { | ||
| const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index); | ||
| if (SkipTypeUnits && CU->isTypeUnit()) | ||
| continue; | ||
| const uint64_t Offset = read64le(Data); | ||
| Data += 16; | ||
| if (CU->getOffset() != Offset) { | ||
| errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n"; | ||
| exit(1); | ||
| } | ||
|
|
||
| OriginalOffsets.insert(Offset); | ||
| OffsetToIndexMap[Offset] = Index; | ||
| } | ||
|
|
||
| // Ignore old address table. | ||
| const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset; | ||
| // Move Data to the beginning of symbol table. | ||
| Data += SymbolTableOffset - CUTypesOffset; | ||
|
|
||
| // Calculate the size of the new address table. | ||
| uint32_t NewAddressTableSize = 0; | ||
| for (const auto &CURangesPair : ARangesSectionWriter.getCUAddressRanges()) { | ||
| const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second; | ||
| NewAddressTableSize += Ranges.size() * 20; | ||
| } | ||
|
|
||
| // Difference between old and new table (and section) sizes. | ||
| // Could be negative. | ||
| int32_t Delta = NewAddressTableSize - OldAddressTableSize; | ||
|
|
||
| size_t NewGdbIndexSize = GdbIndexContents.size() + Delta; | ||
|
|
||
| // Free'd by ExecutableFileMemoryManager. | ||
| auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize]; | ||
| uint8_t *Buffer = NewGdbIndexContents; | ||
|
|
||
| write32le(Buffer, Version); | ||
| write32le(Buffer + 4, CUListOffset); | ||
| write32le(Buffer + 8, CUTypesOffset); | ||
| write32le(Buffer + 12, AddressTableOffset); | ||
| write32le(Buffer + 16, SymbolTableOffset + Delta); | ||
| write32le(Buffer + 20, ConstantPoolOffset + Delta); | ||
| Buffer += 24; | ||
|
|
||
| using MapEntry = std::pair<uint32_t, CUInfo>; | ||
| std::vector<MapEntry> CUVector(CUMap.begin(), CUMap.end()); | ||
| // Need to sort since we write out all of TUs in .debug_info before CUs. | ||
| std::sort(CUVector.begin(), CUVector.end(), | ||
| [](const MapEntry &E1, const MapEntry &E2) -> bool { | ||
| return E1.second.Offset < E2.second.Offset; | ||
| }); | ||
| // Writing out CU List <Offset, Size> | ||
| for (auto &CUInfo : CUVector) { | ||
| // Skipping TU for DWARF5 when they are not included in CU list. | ||
| if (!OriginalOffsets.count(CUInfo.first)) | ||
| continue; | ||
| write64le(Buffer, CUInfo.second.Offset); | ||
| // Length encoded in CU doesn't contain first 4 bytes that encode length. | ||
| write64le(Buffer + 8, CUInfo.second.Length + 4); | ||
| Buffer += 16; | ||
| } | ||
|
|
||
| // Rewrite TU CU List, since abbrevs can be different. | ||
| // Entry example: | ||
| // 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature = | ||
| // 0x418503b8111e9a7b Spec says " triplet, the first value is the CU offset, | ||
| // the second value is the type offset in the CU, and the third value is the | ||
| // type signature" Looking at what is being generated by gdb-add-index. The | ||
| // first entry is TU offset, second entry is offset from it, and third entry | ||
| // is the type signature. | ||
| if (TUListSize) | ||
| for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) { | ||
| write64le(Buffer, Entry.UnitOffset); | ||
| write64le(Buffer + 8, Entry.TypeDIERelativeOffset); | ||
| write64le(Buffer + 16, Entry.TypeHash); | ||
| Buffer += sizeof(GDBIndexTUEntry); | ||
| } | ||
|
|
||
| // Generate new address table. | ||
| for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair : | ||
| ARangesSectionWriter.getCUAddressRanges()) { | ||
| const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first]; | ||
| const DebugAddressRangesVector &Ranges = CURangesPair.second; | ||
| for (const DebugAddressRange &Range : Ranges) { | ||
| write64le(Buffer, Range.LowPC); | ||
| write64le(Buffer + 8, Range.HighPC); | ||
| write32le(Buffer + 16, CUIndex); | ||
| Buffer += 20; | ||
| } | ||
| } | ||
|
|
||
| const size_t TrailingSize = | ||
| GdbIndexContents.data() + GdbIndexContents.size() - Data; | ||
| assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize && | ||
| "size calculation error"); | ||
|
|
||
| // Copy over the rest of the original data. | ||
| memcpy(Buffer, Data, TrailingSize); | ||
|
|
||
| // Register the new section. | ||
| BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents, | ||
| NewGdbIndexSize); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| //===--- UseInternalLinkageCheck.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 "UseInternalLinkageCheck.h" | ||
| #include "../utils/FileExtensionsUtils.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
| #include "clang/ASTMatchers/ASTMatchers.h" | ||
| #include "clang/ASTMatchers/ASTMatchersMacros.h" | ||
| #include "clang/Basic/SourceLocation.h" | ||
| #include "clang/Basic/Specifiers.h" | ||
| #include "llvm/ADT/STLExtras.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::misc { | ||
|
|
||
| namespace { | ||
|
|
||
| AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); } | ||
|
|
||
| static bool isInMainFile(SourceLocation L, SourceManager &SM, | ||
| const FileExtensionsSet &HeaderFileExtensions) { | ||
| for (;;) { | ||
| if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions)) | ||
| return false; | ||
| if (SM.isInMainFile(L)) | ||
| return true; | ||
| // not in header file but not in main file | ||
| L = SM.getIncludeLoc(SM.getFileID(L)); | ||
| if (L.isValid()) | ||
| continue; | ||
| // Conservative about the unknown | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet, | ||
| HeaderFileExtensions) { | ||
| return llvm::all_of(Node.redecls(), [&](const Decl *D) { | ||
| return isInMainFile(D->getLocation(), | ||
| Finder->getASTContext().getSourceManager(), | ||
| HeaderFileExtensions); | ||
| }); | ||
| } | ||
|
|
||
| AST_POLYMORPHIC_MATCHER(isExternStorageClass, | ||
| AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
| VarDecl)) { | ||
| return Node.getStorageClass() == SC_Extern; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) { | ||
| auto Common = | ||
| allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions), | ||
| unless(anyOf( | ||
| // 1. internal linkage | ||
| isStaticStorageClass(), isInAnonymousNamespace(), | ||
| // 2. explicit external linkage | ||
| isExternStorageClass(), isExternC(), | ||
| // 3. template | ||
| isExplicitTemplateSpecialization(), | ||
| // 4. friend | ||
| hasAncestor(friendDecl())))); | ||
| Finder->addMatcher( | ||
| functionDecl(Common, unless(cxxMethodDecl()), unless(isMain())) | ||
| .bind("fn"), | ||
| this); | ||
| Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this); | ||
| } | ||
|
|
||
| static constexpr StringRef Message = | ||
| "%0 %1 can be made static or moved into an anonymous namespace " | ||
| "to enforce internal linkage"; | ||
|
|
||
| void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) { | ||
| if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) { | ||
| diag(FD->getLocation(), Message) << "function" << FD; | ||
| return; | ||
| } | ||
| if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) { | ||
| diag(VD->getLocation(), Message) << "variable" << VD; | ||
| return; | ||
| } | ||
| llvm_unreachable(""); | ||
| } | ||
|
|
||
| } // namespace clang::tidy::misc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===--- UseInternalLinkageCheck.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_MISC_USEINTERNALLINKAGECHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::misc { | ||
|
|
||
| /// Detects variables and functions that can be marked as static or moved into | ||
| /// an anonymous namespace to enforce internal linkage. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-internal-linkage.html | ||
| class UseInternalLinkageCheck : public ClangTidyCheck { | ||
| public: | ||
| UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context), | ||
| HeaderFileExtensions(Context->getHeaderFileExtensions()) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
| return TK_IgnoreUnlessSpelledInSource; | ||
| } | ||
|
|
||
| private: | ||
| FileExtensionsSet HeaderFileExtensions; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::misc | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| .. title:: clang-tidy - clang-analyzer-cplusplus.ArrayDelete | ||
| .. meta:: | ||
| :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete | ||
|
|
||
| clang-analyzer-cplusplus.ArrayDelete | ||
| ==================================== | ||
|
|
||
| Reports destructions of arrays of polymorphic objects that are destructed as | ||
| their base class. | ||
|
|
||
| The `clang-analyzer-cplusplus.ArrayDelete` check is an alias, please see | ||
| `Clang Static Analyzer Available Checkers | ||
| <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete>`_ | ||
| for more information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| .. title:: clang-tidy - clang-analyzer-security.SetgidSetuidOrder | ||
|
|
||
| clang-analyzer-security.SetgidSetuidOrder | ||
| ========================================= | ||
|
|
||
| Warn on possible reversed order of 'setgid(getgid()))' and 'setuid(getuid())' | ||
| (CERT: POS36-C). | ||
|
|
||
| The clang-analyzer-security.SetgidSetuidOrder check is an alias of | ||
| Clang Static Analyzer security.SetgidSetuidOrder. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| .. title:: clang-tidy - clang-analyzer-unix.Stream | ||
| .. meta:: | ||
| :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream | ||
|
|
||
| clang-analyzer-unix.Stream | ||
| ========================== | ||
|
|
||
| Check stream handling functions. | ||
|
|
||
| The `clang-analyzer-unix.Stream` check is an alias, please see | ||
| `Clang Static Analyzer Available Checkers | ||
| <https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream>`_ | ||
| for more information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| .. title:: clang-tidy - misc-use-internal-linkage | ||
|
|
||
| misc-use-internal-linkage | ||
| ========================= | ||
|
|
||
| Detects variables and functions that can be marked as static or moved into | ||
| an anonymous namespace to enforce internal linkage. | ||
|
|
||
| Static functions and variables are scoped to a single file. Marking functions | ||
| and variables as static helps to better remove dead code. In addition, it gives | ||
| the compiler more information and allows for more aggressive optimizations. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int v1; // can be marked as static | ||
|
|
||
| void fn1(); // can be marked as static | ||
|
|
||
| namespace { | ||
| // already in anonymous namespace | ||
| int v2; | ||
| void fn2(); | ||
| } | ||
| // already declared as extern | ||
| extern int v2; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- -config="{CheckOptions: {bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: true, bugprone-sizeof-expression.WarnOnSizeOfPointer: true}}" -- | ||
|
|
||
| class C { | ||
| int size() { return sizeof(this); } | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(this)' | ||
| }; | ||
|
|
||
| #define LEN 8 | ||
|
|
||
| int X; | ||
| extern int A[10]; | ||
| extern short B[10]; | ||
|
|
||
| #pragma pack(1) | ||
| struct S { char a, b, c; }; | ||
|
|
||
| enum E { E_VALUE = 0 }; | ||
| enum class EC { VALUE = 0 }; | ||
|
|
||
| bool AsBool() { return false; } | ||
| int AsInt() { return 0; } | ||
| E AsEnum() { return E_VALUE; } | ||
| EC AsEnumClass() { return EC::VALUE; } | ||
| S AsStruct() { return {}; } | ||
|
|
||
| struct M { | ||
| int AsInt() { return 0; } | ||
| E AsEnum() { return E_VALUE; } | ||
| S AsStruct() { return {}; } | ||
| }; | ||
|
|
||
| int Test1(const char* ptr) { | ||
| int sum = 0; | ||
| sum += sizeof(LEN); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)' | ||
| sum += sizeof(LEN + 1); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)' | ||
| sum += sizeof(sum, LEN); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)' | ||
| sum += sizeof(AsBool()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(AsInt()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(AsEnum()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(AsEnumClass()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(M{}.AsInt()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(M{}.AsEnum()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer | ||
| sum += sizeof(sizeof(X)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(LEN + sizeof(X)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(LEN + LEN + sizeof(X)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(LEN + (LEN + sizeof(X))); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(LEN + -sizeof(X)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(LEN + - + -sizeof(X)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' | ||
| sum += sizeof(char) / sizeof(char); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' | ||
| sum += sizeof(A) / sizeof(S); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator | ||
| sum += sizeof(char) / sizeof(int); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator | ||
| sum += sizeof(char) / sizeof(A); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator | ||
| sum += sizeof(B[0]) / sizeof(A); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator | ||
| sum += sizeof(ptr) / sizeof(char); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(ptr) / sizeof(ptr[0]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(ptr) / sizeof(char*); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(ptr) / sizeof(void*); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(ptr) / sizeof(const void volatile*); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(ptr) / sizeof(char); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(int) * sizeof(char); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication | ||
| sum += sizeof(ptr) * sizeof(ptr[0]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication | ||
| sum += sizeof(int) * (2 * sizeof(char)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication | ||
| sum += (2 * sizeof(char)) * sizeof(int); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious 'sizeof' by 'sizeof' multiplication | ||
| if (sizeof(A) < 0x100000) sum += 42; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant | ||
| if (sizeof(A) <= 0xFFFFFFFEU) sum += 42; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant | ||
| return sum; | ||
| } | ||
|
|
||
| int Test5() { | ||
| typedef int Array10[10]; | ||
| typedef C ArrayC[10]; | ||
|
|
||
| struct MyStruct { | ||
| Array10 arr; | ||
| Array10* ptr; | ||
| }; | ||
| typedef const MyStruct TMyStruct; | ||
| typedef const MyStruct *PMyStruct; | ||
| typedef TMyStruct *PMyStruct2; | ||
|
|
||
| static TMyStruct kGlocalMyStruct = {}; | ||
| static TMyStruct volatile * kGlocalMyStructPtr = &kGlocalMyStruct; | ||
|
|
||
| MyStruct S; | ||
| PMyStruct PS; | ||
| PMyStruct2 PS2; | ||
| Array10 A10; | ||
| C *PtrArray[10]; | ||
| C *PC; | ||
|
|
||
| char *PChar; | ||
| int *PInt, **PPInt; | ||
| MyStruct **PPMyStruct; | ||
|
|
||
| int sum = 0; | ||
| sum += sizeof(&S.arr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(&kGlocalMyStruct.arr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(&kGlocalMyStructPtr->arr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(S.arr + 0); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(+ S.arr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof((int*)S.arr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
|
|
||
| sum += sizeof(S.ptr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(kGlocalMyStruct.ptr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(kGlocalMyStructPtr->ptr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
|
|
||
| sum += sizeof(&kGlocalMyStruct); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(&S); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(MyStruct*); | ||
| sum += sizeof(PMyStruct); | ||
| sum += sizeof(PS); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(PS2); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(&A10); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(PtrArray) / sizeof(PtrArray[1]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(A10) / sizeof(PtrArray[0]); | ||
| sum += sizeof(PC) / sizeof(PtrArray[0]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' | ||
| sum += sizeof(ArrayC) / sizeof(PtrArray[0]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator | ||
|
|
||
| sum += sizeof(PChar); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(PInt); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(PPInt); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(PPMyStruct); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
|
|
||
| return sum; | ||
| } | ||
|
|
||
| void some_generic_function(const void *arg, int argsize); | ||
| int *IntP, **IntPP; | ||
| C *ClassP, **ClassPP; | ||
|
|
||
| void GenericFunctionTest() { | ||
| // The `sizeof(pointer)` checks ignore situations where the pointer is | ||
| // produced by dereferencing a pointer-to-pointer, because this is unlikely | ||
| // to be an accident and can appear in legitimate code that tries to call | ||
| // a generic function which emulates dynamic typing within C. | ||
| some_generic_function(IntPP, sizeof(*IntPP)); | ||
| some_generic_function(ClassPP, sizeof(*ClassPP)); | ||
| // Using `...[0]` instead of the dereference operator is another common | ||
| // variant, which is also widespread in the idiomatic array-size calculation: | ||
| // `sizeof(array) / sizeof(array[0])`. | ||
| some_generic_function(IntPP, sizeof(IntPP[0])); | ||
| some_generic_function(ClassPP, sizeof(ClassPP[0])); | ||
| // FIXME: There is a third common pattern where the generic function is | ||
| // called with `&Variable` and `sizeof(Variable)`. Right now these are | ||
| // reported by the `sizeof(pointer)` checks, but this causes some false | ||
| // positives, so it would be good to create an exception for them. | ||
| some_generic_function(&IntPP, sizeof(IntP)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| some_generic_function(&ClassPP, sizeof(ClassP)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| } | ||
|
|
||
| int ValidExpressions() { | ||
| int A[] = {1, 2, 3, 4}; | ||
| static const char str[] = "hello"; | ||
| static const char* ptr[] { "aaa", "bbb", "ccc" }; | ||
| typedef C *CA10[10]; | ||
| C *PtrArray[10]; | ||
| CA10 PtrArray1; | ||
|
|
||
| int sum = 0; | ||
| if (sizeof(A) < 10) | ||
| sum += sizeof(A); | ||
| sum += sizeof(int); | ||
| sum += sizeof(AsStruct()); | ||
| sum += sizeof(M{}.AsStruct()); | ||
| sum += sizeof(A[sizeof(A) / sizeof(int)]); | ||
| // Here the outer sizeof is reported, but the inner ones are accepted: | ||
| sum += sizeof(&A[sizeof(A) / sizeof(int)]); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer | ||
| sum += sizeof(sizeof(0)); // Special case: sizeof size_t. | ||
| sum += sizeof(void*); | ||
| sum += sizeof(void const *); | ||
| sum += sizeof(void const *) / 4; | ||
| sum += sizeof(str); | ||
| sum += sizeof(str) / sizeof(char); | ||
| sum += sizeof(str) / sizeof(str[0]); | ||
| sum += sizeof(ptr) / sizeof(ptr[0]); | ||
| sum += sizeof(ptr) / sizeof(*(ptr)); | ||
| sum += sizeof(PtrArray) / sizeof(PtrArray[0]); | ||
| // Canonical type of PtrArray1 is same as PtrArray. | ||
| sum = sizeof(PtrArray) / sizeof(PtrArray1[0]); | ||
| // There is no warning for 'sizeof(T*)/sizeof(Q)' case. | ||
| sum += sizeof(PtrArray) / sizeof(A[0]); | ||
| return sum; | ||
| } |