diff options
Diffstat (limited to 'clang-tools-extra')
268 files changed, 7034 insertions, 1557 deletions
diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp index 3e367ab..471ca45 100644 --- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp @@ -31,24 +31,9 @@ llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) { return Splitted; } -SourceLocation startLocationForType(TypeLoc TLoc) { - // For elaborated types (e.g. `struct a::A`) we want the portion after the - // `struct` but including the namespace qualifier, `a::`. - if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) { - NestedNameSpecifierLoc NestedNameSpecifier = - TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); - if (NestedNameSpecifier.getNestedNameSpecifier()) - return NestedNameSpecifier.getBeginLoc(); - TLoc = TLoc.getNextTypeLoc(); - } - return TLoc.getBeginLoc(); -} - SourceLocation endLocationForType(TypeLoc TLoc) { - // Dig past any namespace or keyword qualifications. - while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || - TLoc.getTypeLocClass() == TypeLoc::Qualified) - TLoc = TLoc.getNextTypeLoc(); + if (auto QTL = TLoc.getAs<QualifiedTypeLoc>()) + TLoc = QTL.getUnqualifiedLoc(); // The location for template specializations (e.g. Foo<int>) includes the // templated types in its location range. We want to restrict this to just @@ -550,8 +535,8 @@ void ChangeNamespaceTool::run( Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( "nested_specifier_loc")) { SourceLocation Start = Specifier->getBeginLoc(); - SourceLocation End = endLocationForType(Specifier->getTypeLoc()); - fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); + SourceLocation End = endLocationForType(Specifier->castAsTypeLoc()); + fixTypeLoc(Result, Start, End, Specifier->castAsTypeLoc()); } else if (const auto *BaseInitializer = Result.Nodes.getNodeAs<CXXCtorInitializer>( "base_initializer")) { @@ -562,19 +547,16 @@ void ChangeNamespaceTool::run( // filtered by matchers in some cases, e.g. the type is templated. We should // handle the record type qualifier instead. TypeLoc Loc = *TLoc; - while (Loc.getTypeLocClass() == TypeLoc::Qualified) - Loc = Loc.getNextTypeLoc(); - if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { - NestedNameSpecifierLoc NestedNameSpecifier = - Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); - // FIXME: avoid changing injected class names. - if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) { - const Type *SpecifierType = NNS->getAsType(); - if (SpecifierType && SpecifierType->isRecordType()) - return; - } - } - fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); + if (auto QTL = Loc.getAs<QualifiedTypeLoc>()) + Loc = QTL.getUnqualifiedLoc(); + // FIXME: avoid changing injected class names. + if (NestedNameSpecifier NestedNameSpecifier = + Loc.getPrefix().getNestedNameSpecifier(); + NestedNameSpecifier.getKind() == NestedNameSpecifier::Kind::Type && + NestedNameSpecifier.getAsType()->isRecordType()) + return; + fixTypeLoc(Result, Loc.getNonElaboratedBeginLoc(), endLocationForType(Loc), + Loc); } else if (const auto *VarRef = Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) { const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl"); @@ -588,10 +570,9 @@ void ChangeNamespaceTool::run( } else if (const auto *EnumConstRef = Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) { // Do not rename the reference if it is already scoped by the EnumDecl name. - if (EnumConstRef->hasQualifier() && - EnumConstRef->getQualifier()->getKind() == - NestedNameSpecifier::SpecifierKind::TypeSpec && - EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) + if (NestedNameSpecifier Qualifier = EnumConstRef->getQualifier(); + Qualifier.getKind() == NestedNameSpecifier::Kind::Type && + Qualifier.getAsType()->isEnumeralType()) return; const auto *EnumConstDecl = Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl"); diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp index a64cb5e..1ab40aa 100644 --- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp @@ -144,17 +144,22 @@ Error MustacheHTMLGenerator::generateDocs( } else return JSONGenerator.takeError(); } + SmallString<128> JSONPath; + sys::path::native(RootDir.str() + "/json", JSONPath); StringMap<json::Value> JSONFileMap; { llvm::TimeTraceScope TS("Iterate JSON files"); std::error_code EC; - sys::fs::directory_iterator JSONIter(RootDir, EC); + sys::fs::directory_iterator JSONIter(JSONPath, EC); std::vector<json::Value> JSONFiles; JSONFiles.reserve(Infos.size()); if (EC) return createStringError("Failed to create directory iterator."); + SmallString<128> HTMLDirPath(RootDir.str() + "/html/"); + if (auto EC = sys::fs::create_directories(HTMLDirPath)) + return createFileError(HTMLDirPath, EC); while (JSONIter != sys::fs::directory_iterator()) { if (EC) return createFileError("Failed to iterate: " + JSONIter->path(), EC); @@ -177,14 +182,15 @@ Error MustacheHTMLGenerator::generateDocs( return Parsed.takeError(); std::error_code FileErr; - SmallString<16> HTMLPath(Path.begin(), Path.end()); - sys::path::replace_extension(HTMLPath, "html"); - raw_fd_ostream InfoOS(HTMLPath, FileErr, sys::fs::OF_None); + SmallString<128> HTMLFilePath(HTMLDirPath); + sys::path::append(HTMLFilePath, sys::path::filename(Path)); + sys::path::replace_extension(HTMLFilePath, "html"); + raw_fd_ostream InfoOS(HTMLFilePath, FileErr, sys::fs::OF_None); if (FileErr) return createFileOpenError(Path, FileErr); - if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLPath), - HTMLPath, InfoOS, CDCtx)) + if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLFilePath), + HTMLFilePath, InfoOS, CDCtx)) return Err; JSONIter.increment(EC); } diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp index 599b381..26794a5 100644 --- a/clang-tools-extra/clang-doc/JSONGenerator.cpp +++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp @@ -600,7 +600,9 @@ Error JSONGenerator::generateDocs( Info *Info = Group.getValue().get(); SmallString<128> Path; - sys::path::native(RootDir, Path); + auto RootDirStr = RootDir.str() + "/json"; + StringRef JSONDir = StringRef(RootDirStr); + sys::path::native(JSONDir, Path); if (!CreatedDirs.contains(Path)) { if (std::error_code Err = sys::fs::create_directories(Path); Err != std::error_code()) diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index de73f68..dd7cd0b 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -778,7 +778,9 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, Mangler->mangleCXXVTable(CXXD, MangledStream); else MangledStream << D->getNameAsString(); - if (MangledName.size() > 255) + // A 250 length limit was chosen since 255 is a common limit across + // different filesystems, with a 5 character buffer for file extensions. + if (MangledName.size() > 250) // File creation fails if the mangled name is too long, so default to the // USR. We should look for a better check since filesystems differ in // maximum filename length @@ -901,9 +903,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, if (!D->isThisDeclarationADefinition()) return; for (const CXXBaseSpecifier &B : D->bases()) { - if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { - if (const CXXRecordDecl *Base = - cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) { + if (const auto *Base = B.getType()->getAsCXXRecordDecl()) { + if (Base->isCompleteDefinition()) { // Initialized without USR and name, this will be set in the following // if-else stmt. BaseRecordInfo BI( diff --git a/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp b/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp index bb48883..1f30d27 100644 --- a/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp +++ b/clang-tools-extra/clang-include-fixer/find-all-symbols/FindAllSymbols.cpp @@ -216,8 +216,7 @@ void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) { // Uses of most types: just look at what the typeLoc refers to. MatchFinder->addMatcher( typeLoc(isExpansionInMainFile(), - loc(qualType(allOf(unless(elaboratedType()), - hasDeclaration(Types.bind("use")))))), + loc(qualType(hasDeclaration(Types.bind("use"))))), this); // Uses of typedefs: these are often transparent to hasDeclaration, so we need // to handle them explicitly. diff --git a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt index 2fdeb65d..dec0287 100644 --- a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt +++ b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangReorderFields STATIC + Designator.cpp ReorderFieldsAction.cpp DEPENDS diff --git a/clang-tools-extra/clang-reorder-fields/Designator.cpp b/clang-tools-extra/clang-reorder-fields/Designator.cpp new file mode 100644 index 0000000..d36a080 --- /dev/null +++ b/clang-tools-extra/clang-reorder-fields/Designator.cpp @@ -0,0 +1,219 @@ +//===-- tools/extra/clang-reorder-fields/utils/Designator.cpp ---*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the definition of the Designator and Designators utility +/// classes. +/// +//===----------------------------------------------------------------------===// + +#include "Designator.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace reorder_fields { + +void Designator::advanceToNextField() { + assert(!isFinished() && "Iterator is already finished"); + switch (Tag) { + case STRUCT: + if (StructIt.Record->isUnion()) { + // Union always finishes on first increment. + StructIt.Field = StructIt.Record->field_end(); + Type = QualType(); + break; + } + ++StructIt.Field; + if (StructIt.Field != StructIt.Record->field_end()) { + Type = StructIt.Field->getType(); + } else { + Type = QualType(); + } + break; + case ARRAY: + ++ArrayIt.Index; + break; + case ARRAY_RANGE: + ArrayIt.Index = ArrayRangeIt.End + 1; + ArrayIt.Size = ArrayRangeIt.Size; + Tag = ARRAY; + break; + } +} + +bool Designator::isFinished() { + switch (Tag) { + case STRUCT: + return StructIt.Field == StructIt.Record->field_end(); + case ARRAY: + return ArrayIt.Index == ArrayIt.Size; + case ARRAY_RANGE: + return ArrayRangeIt.End == ArrayRangeIt.Size; + } + return false; +} + +Designators::Designators(const Expr *Init, const InitListExpr *ILE, + const ASTContext *Context) + : Context(Context) { + if (ILE->getType()->isArrayType()) { + const ConstantArrayType *CAT = + Context->getAsConstantArrayType(ILE->getType()); + // Only constant size arrays are supported. + if (!CAT) { + DesignatorList.clear(); + return; + } + DesignatorList.push_back( + {CAT->getElementType(), 0, CAT->getSize().getZExtValue()}); + } else { + const RecordDecl *DesignatorRD = ILE->getType()->getAsRecordDecl(); + DesignatorList.push_back({DesignatorRD->field_begin()->getType(), + DesignatorRD->field_begin(), DesignatorRD}); + } + + // If the designator list is empty at this point, then there must be excess + // elements in the initializer list. They are not currently supported. + if (DesignatorList.empty()) + return; + + if (!enterImplicitInitLists(Init)) + DesignatorList.clear(); +} + +Designators::Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE, + const ASTContext *Context) + : Context(Context) { + for (const auto &D : DIE->designators()) { + if (D.isFieldDesignator()) { + RecordDecl *DesignatorRecord = D.getFieldDecl()->getParent(); + for (auto FieldIt = DesignatorRecord->field_begin(); + FieldIt != DesignatorRecord->field_end(); ++FieldIt) { + if (*FieldIt == D.getFieldDecl()) { + DesignatorList.push_back( + {FieldIt->getType(), FieldIt, DesignatorRecord}); + break; + } + } + } else { + const QualType CurrentType = DesignatorList.empty() + ? ILE->getType() + : DesignatorList.back().getType(); + const ConstantArrayType *CAT = + Context->getAsConstantArrayType(CurrentType); + if (!CAT) { + // Non-constant-sized arrays are not supported. + DesignatorList.clear(); + return; + } + if (D.isArrayDesignator()) { + DesignatorList.push_back({CAT->getElementType(), + DIE->getArrayIndex(D) + ->EvaluateKnownConstInt(*Context) + .getZExtValue(), + CAT->getSize().getZExtValue()}); + } else if (D.isArrayRangeDesignator()) { + DesignatorList.push_back({CAT->getElementType(), + DIE->getArrayRangeStart(D) + ->EvaluateKnownConstInt(*Context) + .getZExtValue(), + DIE->getArrayRangeEnd(D) + ->EvaluateKnownConstInt(*Context) + .getZExtValue(), + CAT->getSize().getZExtValue()}); + } else { + llvm_unreachable("Unexpected designator kind"); + } + } + } +} + +bool Designators::advanceToNextField(const Expr *Init) { + // Remove all designators that refer to the last field of a struct or final + // element of the array. + while (!DesignatorList.empty()) { + auto &CurrentDesignator = DesignatorList.back(); + CurrentDesignator.advanceToNextField(); + if (CurrentDesignator.isFinished()) { + DesignatorList.pop_back(); + continue; + } + break; + } + + // If the designator list is empty at this point, then there must be excess + // elements in the initializer list. They are not currently supported. + if (DesignatorList.empty()) + return false; + + if (!enterImplicitInitLists(Init)) { + DesignatorList.clear(); + return false; + } + + return true; +} + +bool Designators::enterImplicitInitLists(const Expr *Init) { + // Check for missing braces by comparing the type of the last designator and + // type of Init. + while (true) { + const QualType T = DesignatorList.back().getType(); + // If the types match, there are no missing braces. + if (Init->getType() == T) + break; + + // If the current type is a struct, then get its first field. + if (T->isRecordType()) { + DesignatorList.push_back({T->getAsRecordDecl()->field_begin()->getType(), + T->getAsRecordDecl()->field_begin(), + T->getAsRecordDecl()}); + continue; + } + // If the current type is an array, then get its first element. + if (T->isArrayType()) { + DesignatorList.push_back( + {Context->getAsArrayType(T)->getElementType(), 0, + Context->getAsConstantArrayType(T)->getSize().getZExtValue()}); + continue; + } + + // The initializer doesn't match the expected type. The initializer list is + // invalid. + return false; + } + + return true; +} + +std::string Designators::toString() const { + if (DesignatorList.empty()) + return ""; + std::string Designator; + llvm::raw_string_ostream OS(Designator); + for (auto &I : DesignatorList) { + switch (I.getTag()) { + case Designator::STRUCT: + OS << '.' << I.getStructIter()->getName(); + break; + case Designator::ARRAY: + OS << '[' << I.getArrayIndex() << ']'; + break; + case Designator::ARRAY_RANGE: + OS << '[' << I.getArrayRangeStart() << "..." << I.getArrayRangeEnd() + << ']'; + } + } + OS << " = "; + return Designator; +} + +} // namespace reorder_fields +} // namespace clang diff --git a/clang-tools-extra/clang-reorder-fields/Designator.h b/clang-tools-extra/clang-reorder-fields/Designator.h new file mode 100644 index 0000000..859be28 --- /dev/null +++ b/clang-tools-extra/clang-reorder-fields/Designator.h @@ -0,0 +1,164 @@ +//===-- tools/extra/clang-reorder-fields/utils/Designator.h -----*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declarations of the Designator and Designators +/// utility classes. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" + +namespace clang { +namespace reorder_fields { + +/// Represents a part of a designation in a C99/C++20 designated initializer. It +/// is a tagged union of different kinds of designators: struct, array and array +/// range. Holds enough information to be able to advance to the next field and +/// to know when all fields have been iterated through. +class Designator { +public: + enum Kind { STRUCT, ARRAY, ARRAY_RANGE }; + + Designator(const QualType Type, RecordDecl::field_iterator Field, + const RecordDecl *RD) + : Tag(STRUCT), Type(Type), StructIt({Field, RD}) {} + + Designator(const QualType Type, uint64_t Idx, uint64_t Size) + : Tag(ARRAY), Type(Type), ArrayIt({Idx, Size}) {} + + Designator(const QualType Type, uint64_t Start, uint64_t End, uint64_t Size) + : Tag(ARRAY_RANGE), Type(Type), ArrayRangeIt({Start, End, Size}) {} + + /// Moves the iterator to the next element. + void advanceToNextField(); + + /// Checks if the iterator has iterated through all elements. + bool isFinished(); + + Kind getTag() const { return Tag; } + QualType getType() const { return Type; } + + const RecordDecl::field_iterator getStructIter() const { + assert(Tag == STRUCT && "Must be a field designator"); + return StructIt.Field; + } + + const RecordDecl *getStructDecl() const { + assert(Tag == STRUCT && "Must be a field designator"); + return StructIt.Record; + } + + uint64_t getArrayIndex() const { + assert(Tag == ARRAY && "Must be an array designator"); + return ArrayIt.Index; + } + + uint64_t getArrayRangeStart() const { + assert(Tag == ARRAY_RANGE && "Must be an array range designator"); + return ArrayRangeIt.Start; + } + + uint64_t getArrayRangeEnd() const { + assert(Tag == ARRAY_RANGE && "Must be an array range designator"); + return ArrayRangeIt.End; + } + + uint64_t getArraySize() const { + assert((Tag == ARRAY || Tag == ARRAY_RANGE) && + "Must be an array or range designator"); + if (Tag == ARRAY) + return ArrayIt.Size; + return ArrayRangeIt.Size; + } + +private: + /// Type of the designator. + Kind Tag; + + /// Type of the designated entry. For arrays this is the type of the element. + QualType Type; + + /// Field designator has the iterator to the field and the record the field + /// is declared in. + struct StructIter { + RecordDecl::field_iterator Field; + const RecordDecl *Record; + }; + + /// Array designator has an index and size of the array. + struct ArrayIter { + uint64_t Index; + uint64_t Size; + }; + + /// Array range designator has a start and end index and size of the array. + struct ArrayRangeIter { + uint64_t Start; + uint64_t End; + uint64_t Size; + }; + + union { + StructIter StructIt; + ArrayIter ArrayIt; + ArrayRangeIter ArrayRangeIt; + }; +}; + +/// List of designators. +class Designators { +public: + /// Initialize to the first member of the struct/array. Enters implicit + /// initializer lists until a type that matches Init is found. + Designators(const Expr *Init, const InitListExpr *ILE, + const ASTContext *Context); + + /// Initialize to the designators of the given expression. + Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE, + const ASTContext *Context); + + /// Return whether this designator list is valid. + bool isValid() const { return !DesignatorList.empty(); } + + /// Moves the designators to the next initializer in the struct/array. If the + /// type of next initializer doesn't match the expected type then there are + /// omitted braces and we add new designators to reflect that. + bool advanceToNextField(const Expr *Init); + + /// Gets a string representation from a list of designators. This string will + /// be inserted before an initializer expression to make it designated. + std::string toString() const; + + size_t size() const { return DesignatorList.size(); } + + SmallVector<Designator>::const_iterator begin() const { + return DesignatorList.begin(); + } + SmallVector<Designator>::const_iterator end() const { + return DesignatorList.end(); + } + +private: + /// Enters any implicit initializer lists until a type that matches the given + /// expression is found. + bool enterImplicitInitLists(const Expr *Init); + + const ASTContext *Context; + SmallVector<Designator, 1> DesignatorList; +}; + +} // namespace reorder_fields +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H diff --git a/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp b/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp index ada9122..affa276 100644 --- a/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp +++ b/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ReorderFieldsAction.h" +#include "Designator.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -25,6 +26,7 @@ #include "clang/Tooling/Refactoring.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/ErrorHandling.h" #include <string> namespace clang { @@ -162,7 +164,91 @@ getNewFieldsOrder(const RecordDecl *Definition, return NewFieldsOrder; } +struct ReorderedStruct { +public: + ReorderedStruct(const RecordDecl *Decl, ArrayRef<unsigned> NewFieldsOrder) + : Definition(Decl), NewFieldsOrder(NewFieldsOrder), + NewFieldsPositions(NewFieldsOrder.size()) { + for (unsigned I = 0; I < NewFieldsPositions.size(); ++I) + NewFieldsPositions[NewFieldsOrder[I]] = I; + } + + /// Compares compatible designators according to the new struct order. + /// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0 + /// if they are equal. + bool operator()(const Designator &Lhs, const Designator &Rhs) const; + + /// Compares compatible designator lists according to the new struct order. + /// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0 + /// if they are equal. + bool operator()(const Designators &Lhs, const Designators &Rhs) const; + + const RecordDecl *Definition; + ArrayRef<unsigned> NewFieldsOrder; + SmallVector<unsigned, 4> NewFieldsPositions; +}; + +bool ReorderedStruct::operator()(const Designator &Lhs, + const Designator &Rhs) const { + switch (Lhs.getTag()) { + case Designator::STRUCT: + assert(Rhs.getTag() == Designator::STRUCT && "Incompatible designators"); + assert(Lhs.getStructDecl() == Rhs.getStructDecl() && + "Incompatible structs"); + // Use the new layout for reordered struct. + if (Definition == Lhs.getStructDecl()) { + return NewFieldsPositions[Lhs.getStructIter()->getFieldIndex()] < + NewFieldsPositions[Rhs.getStructIter()->getFieldIndex()]; + } + return Lhs.getStructIter()->getFieldIndex() < + Rhs.getStructIter()->getFieldIndex(); + case Designator::ARRAY: + case Designator::ARRAY_RANGE: + // Array designators can be compared to array range designators. + assert((Rhs.getTag() == Designator::ARRAY || + Rhs.getTag() == Designator::ARRAY_RANGE) && + "Incompatible designators"); + size_t LhsIdx = Lhs.getTag() == Designator::ARRAY + ? Lhs.getArrayIndex() + : Lhs.getArrayRangeStart(); + size_t RhsIdx = Rhs.getTag() == Designator::ARRAY + ? Rhs.getArrayIndex() + : Rhs.getArrayRangeStart(); + return LhsIdx < RhsIdx; + } + llvm_unreachable("Invalid designator tag"); +} + +bool ReorderedStruct::operator()(const Designators &Lhs, + const Designators &Rhs) const { + return std::lexicographical_compare(Lhs.begin(), Lhs.end(), Rhs.begin(), + Rhs.end(), *this); +} + // FIXME: error-handling +/// Replaces a range of source code by the specified text. +static void +addReplacement(SourceRange Old, StringRef New, const ASTContext &Context, + std::map<std::string, tooling::Replacements> &Replacements) { + tooling::Replacement R(Context.getSourceManager(), + CharSourceRange::getTokenRange(Old), New, + Context.getLangOpts()); + consumeError(Replacements[std::string(R.getFilePath())].add(R)); +} + +/// Replaces one range of source code by another and adds a prefix. +static void +addReplacement(SourceRange Old, SourceRange New, StringRef Prefix, + const ASTContext &Context, + std::map<std::string, tooling::Replacements> &Replacements) { + std::string NewText = + (Prefix + Lexer::getSourceText(CharSourceRange::getTokenRange(New), + Context.getSourceManager(), + Context.getLangOpts())) + .str(); + addReplacement(Old, NewText, Context, Replacements); +} + /// Replaces one range of source code by another. static void addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context, @@ -174,10 +260,7 @@ addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context, StringRef NewText = Lexer::getSourceText(CharSourceRange::getTokenRange(New), Context.getSourceManager(), Context.getLangOpts()); - tooling::Replacement R(Context.getSourceManager(), - CharSourceRange::getTokenRange(Old), NewText, - Context.getLangOpts()); - consumeError(Replacements[std::string(R.getFilePath())].add(R)); + addReplacement(Old, NewText.str(), Context, Replacements); } /// Find all member fields used in the given init-list initializer expr @@ -279,33 +362,33 @@ static SourceRange getFullFieldSourceRange(const FieldDecl &Field, /// different accesses (public/protected/private) is not supported. /// \returns true on success. static bool reorderFieldsInDefinition( - const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder, - const ASTContext &Context, + const ReorderedStruct &RS, const ASTContext &Context, std::map<std::string, tooling::Replacements> &Replacements) { - assert(Definition && "Definition is null"); + assert(RS.Definition && "Definition is null"); SmallVector<const FieldDecl *, 10> Fields; - for (const auto *Field : Definition->fields()) + for (const auto *Field : RS.Definition->fields()) Fields.push_back(Field); // Check that the permutation of the fields doesn't change the accesses - for (const auto *Field : Definition->fields()) { + for (const auto *Field : RS.Definition->fields()) { const auto FieldIndex = Field->getFieldIndex(); - if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) { + if (Field->getAccess() != + Fields[RS.NewFieldsOrder[FieldIndex]]->getAccess()) { llvm::errs() << "Currently reordering of fields with different accesses " "is not supported\n"; return false; } } - for (const auto *Field : Definition->fields()) { + for (const auto *Field : RS.Definition->fields()) { const auto FieldIndex = Field->getFieldIndex(); - if (FieldIndex == NewFieldsOrder[FieldIndex]) + if (FieldIndex == RS.NewFieldsOrder[FieldIndex]) continue; - addReplacement( - getFullFieldSourceRange(*Field, Context), - getFullFieldSourceRange(*Fields[NewFieldsOrder[FieldIndex]], Context), - Context, Replacements); + addReplacement(getFullFieldSourceRange(*Field, Context), + getFullFieldSourceRange( + *Fields[RS.NewFieldsOrder[FieldIndex]], Context), + Context, Replacements); } return true; } @@ -316,7 +399,7 @@ static bool reorderFieldsInDefinition( /// fields. Thus, we need to ensure that we reorder just the initializers that /// are present. static void reorderFieldsInConstructor( - const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder, + const CXXConstructorDecl *CtorDecl, const ReorderedStruct &RS, ASTContext &Context, std::map<std::string, tooling::Replacements> &Replacements) { assert(CtorDecl && "Constructor declaration is null"); @@ -328,10 +411,6 @@ static void reorderFieldsInConstructor( // Thus this assert needs to be after the previous checks. assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition"); - SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size()); - for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i) - NewFieldsPositions[NewFieldsOrder[i]] = i; - SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder; SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder; for (const auto *Initializer : CtorDecl->inits()) { @@ -342,8 +421,8 @@ static void reorderFieldsInConstructor( const FieldDecl *ThisM = Initializer->getMember(); const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context); for (const FieldDecl *UM : UsedMembers) { - if (NewFieldsPositions[UM->getFieldIndex()] > - NewFieldsPositions[ThisM->getFieldIndex()]) { + if (RS.NewFieldsPositions[UM->getFieldIndex()] > + RS.NewFieldsPositions[ThisM->getFieldIndex()]) { DiagnosticsEngine &DiagEngine = Context.getDiagnostics(); auto Description = ("reordering field " + UM->getName() + " after " + ThisM->getName() + " makes " + UM->getName() + @@ -361,8 +440,8 @@ static void reorderFieldsInConstructor( auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS, const CXXCtorInitializer *RHS) { assert(LHS && RHS); - return NewFieldsPositions[LHS->getMember()->getFieldIndex()] < - NewFieldsPositions[RHS->getMember()->getFieldIndex()]; + return RS.NewFieldsPositions[LHS->getMember()->getFieldIndex()] < + RS.NewFieldsPositions[RHS->getMember()->getFieldIndex()]; }; llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition); assert(OldWrittenInitializersOrder.size() == @@ -374,35 +453,188 @@ static void reorderFieldsInConstructor( Replacements); } +/// Replacement for broken InitListExpr::isExplicit function. +/// FIXME: Remove when InitListExpr::isExplicit is fixed. +static bool isImplicitILE(const InitListExpr *ILE, const ASTContext &Context) { + // The ILE is implicit if either: + // - The left brace loc of the ILE matches the start of first init expression + // (for non designated decls) + // - The right brace loc of the ILE matches the end of first init expression + // (for designated decls) + // The first init expression should be taken from the syntactic form, but + // since the ILE could be implicit, there might not be a syntactic form. + // For that reason we have to check against all init expressions. + for (const Expr *Init : ILE->inits()) { + if (ILE->getLBraceLoc() == Init->getBeginLoc() || + ILE->getRBraceLoc() == Init->getEndLoc()) + return true; + } + return false; +} + +/// Finds the semantic form of the first explicit ancestor of the given +/// initializer list including itself. +static const InitListExpr *getExplicitILE(const InitListExpr *ILE, + ASTContext &Context) { + if (!isImplicitILE(ILE, Context)) + return ILE; + const InitListExpr *TopLevelILE = ILE; + DynTypedNodeList Parents = Context.getParents(*TopLevelILE); + while (!Parents.empty() && Parents.begin()->get<InitListExpr>()) { + TopLevelILE = Parents.begin()->get<InitListExpr>(); + Parents = Context.getParents(*TopLevelILE); + if (!isImplicitILE(TopLevelILE, Context)) + break; + } + if (!TopLevelILE->isSemanticForm()) { + return TopLevelILE->getSemanticForm(); + } + return TopLevelILE; +} + +static void reportError(const Twine &Message, SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isValid()) { + llvm::errs() << SM.getFilename(Loc) << ":" << SM.getPresumedLineNumber(Loc) + << ":" << SM.getPresumedColumnNumber(Loc) << ": "; + } + llvm::errs() << Message; +} + /// Reorders initializers in the brace initialization of an aggregate. /// /// At the moment partial initialization is not supported. /// \returns true on success static bool reorderFieldsInInitListExpr( - const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder, - const ASTContext &Context, + const InitListExpr *InitListEx, const ReorderedStruct &RS, + ASTContext &Context, std::map<std::string, tooling::Replacements> &Replacements) { assert(InitListEx && "Init list expression is null"); - // We care only about InitListExprs which originate from source code. - // Implicit InitListExprs are created by the semantic analyzer. - if (!InitListEx->isExplicit()) + // Only process semantic forms of initializer lists. + if (!InitListEx->isSemanticForm()) { return true; - // The method InitListExpr::getSyntacticForm may return nullptr indicating - // that the current initializer list also serves as its syntactic form. - if (const auto *SyntacticForm = InitListEx->getSyntacticForm()) - InitListEx = SyntacticForm; + } + // If there are no initializers we do not need to change anything. if (!InitListEx->getNumInits()) return true; - if (InitListEx->getNumInits() != NewFieldsOrder.size()) { - llvm::errs() << "Currently only full initialization is supported\n"; - return false; + + // We care only about InitListExprs which originate from source code. + // Implicit InitListExprs are created by the semantic analyzer. + // We find the first parent InitListExpr that exists in source code and + // process it. This is necessary because of designated initializer lists and + // possible omitted braces. + InitListEx = getExplicitILE(InitListEx, Context); + + // Find if there are any designated initializations or implicit values. If all + // initializers are present and none have designators then just reorder them + // normally. Otherwise, designators are added to all initializers and they are + // sorted in the new order. + bool HasImplicitInit = false; + bool HasDesignatedInit = false; + // The method InitListExpr::getSyntacticForm may return nullptr indicating + // that the current initializer list also serves as its syntactic form. + const InitListExpr *SyntacticInitListEx = InitListEx; + if (const InitListExpr *SynILE = InitListEx->getSyntacticForm()) { + // Do not rewrite zero initializers. This check is only valid for syntactic + // forms. + if (SynILE->isIdiomaticZeroInitializer(Context.getLangOpts())) + return true; + + HasImplicitInit = InitListEx->getNumInits() != SynILE->getNumInits(); + HasDesignatedInit = llvm::any_of(SynILE->inits(), [](const Expr *Init) { + return isa<DesignatedInitExpr>(Init); + }); + + SyntacticInitListEx = SynILE; + } else { + // If there is no syntactic form, there can be no designators. Instead, + // there might be implicit values. + HasImplicitInit = + (RS.NewFieldsOrder.size() != InitListEx->getNumInits()) || + llvm::any_of(InitListEx->inits(), [&Context](const Expr *Init) { + return isa<ImplicitValueInitExpr>(Init) || + (isa<InitListExpr>(Init) && + isImplicitILE(dyn_cast<InitListExpr>(Init), Context)); + }); + } + + if (HasImplicitInit || HasDesignatedInit) { + // Designators are only supported from C++20. + if (!HasDesignatedInit && Context.getLangOpts().CPlusPlus && + !Context.getLangOpts().CPlusPlus20) { + reportError( + "Only full initialization without implicit values is supported\n", + InitListEx->getBeginLoc(), Context.getSourceManager()); + return false; + } + + // Handle case when some fields are designated. Some fields can be + // missing. Insert any missing designators and reorder the expressions + // according to the new order. + std::optional<Designators> CurrentDesignators; + // Remember each initializer expression along with its designators. They are + // sorted later to determine the correct order. + std::vector<std::pair<Designators, const Expr *>> Rewrites; + for (const Expr *Init : SyntacticInitListEx->inits()) { + if (const auto *DIE = dyn_cast_or_null<DesignatedInitExpr>(Init)) { + CurrentDesignators.emplace(DIE, SyntacticInitListEx, &Context); + if (!CurrentDesignators->isValid()) { + reportError("Unsupported initializer list\n", DIE->getBeginLoc(), + Context.getSourceManager()); + return false; + } + + // Use the child of the DesignatedInitExpr. This way designators are + // always replaced. + Rewrites.emplace_back(*CurrentDesignators, DIE->getInit()); + } else { + // If designators are not initialized then initialize to the first + // field, otherwise move the next field. + if (!CurrentDesignators) { + CurrentDesignators.emplace(Init, SyntacticInitListEx, &Context); + if (!CurrentDesignators->isValid()) { + reportError("Unsupported initializer list\n", + InitListEx->getBeginLoc(), Context.getSourceManager()); + return false; + } + } else if (!CurrentDesignators->advanceToNextField(Init)) { + reportError("Unsupported initializer list\n", + InitListEx->getBeginLoc(), Context.getSourceManager()); + return false; + } + + // Do not rewrite implicit values. They just had to be processed to + // find the correct designator. + if (!isa<ImplicitValueInitExpr>(Init)) + Rewrites.emplace_back(*CurrentDesignators, Init); + } + } + + // Sort the designators according to the new order. + llvm::stable_sort(Rewrites, [&RS](const auto &Lhs, const auto &Rhs) { + return RS(Lhs.first, Rhs.first); + }); + + for (unsigned i = 0, e = Rewrites.size(); i < e; ++i) { + addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(), + Rewrites[i].second->getSourceRange(), + Rewrites[i].first.toString(), Context, Replacements); + } + } else { + // Handle excess initializers by leaving them unchanged. + assert(SyntacticInitListEx->getNumInits() >= InitListEx->getNumInits()); + + // All field initializers are present and none have designators. They can be + // reordered normally. + for (unsigned i = 0, e = RS.NewFieldsOrder.size(); i < e; ++i) { + if (i != RS.NewFieldsOrder[i]) + addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(), + SyntacticInitListEx->getInit(RS.NewFieldsOrder[i]) + ->getSourceRange(), + Context, Replacements); + } } - for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i) - if (i != NewFieldsOrder[i]) - addReplacement(InitListEx->getInit(i)->getSourceRange(), - InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), - Context, Replacements); return true; } @@ -432,7 +664,9 @@ public: getNewFieldsOrder(RD, DesiredFieldsOrder); if (NewFieldsOrder.empty()) return; - if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements)) + ReorderedStruct RS{RD, NewFieldsOrder}; + + if (!reorderFieldsInDefinition(RS, Context, Replacements)) return; // CXXRD will be nullptr if C code (not C++) is being processed. @@ -440,24 +674,25 @@ public: if (CXXRD) for (const auto *C : CXXRD->ctors()) if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition())) - reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D), - NewFieldsOrder, Context, Replacements); + reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D), RS, + Context, Replacements); // We only need to reorder init list expressions for // plain C structs or C++ aggregate types. // For other types the order of constructor parameters is used, // which we don't change at the moment. // Now (v0) partial initialization is not supported. - if (!CXXRD || CXXRD->isAggregate()) + if (!CXXRD || CXXRD->isAggregate()) { for (auto Result : match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"), Context)) if (!reorderFieldsInInitListExpr( - Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder, - Context, Replacements)) { + Result.getNodeAs<InitListExpr>("initListExpr"), RS, Context, + Replacements)) { Replacements.clear(); return; } + } } }; } // end anonymous namespace diff --git a/clang-tools-extra/clang-tidy/.clang-format b/clang-tools-extra/clang-tidy/.clang-format new file mode 100644 index 0000000..d18cf7c --- /dev/null +++ b/clang-tools-extra/clang-tidy/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +QualifierAlignment: Left diff --git a/clang-tools-extra/clang-tidy/.clang-tidy b/clang-tools-extra/clang-tidy/.clang-tidy index 22a4bd7..0e33364 100644 --- a/clang-tools-extra/clang-tidy/.clang-tidy +++ b/clang-tools-extra/clang-tidy/.clang-tidy @@ -32,7 +32,6 @@ Checks: > -readability-magic-numbers, -readability-named-parameter, -readability-qualified-auto, - -readability-redundant-declaration, -readability-simplify-boolean-expr, -readability-static-definition-in-anonymous-namespace, -readability-suspicious-call-argument, diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 4ae2864..2064c78 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -424,6 +424,10 @@ ClangTidyASTConsumerFactory::createASTConsumer( FinderOptions.CheckProfiling.emplace(Profiling->Records); } + // Avoid processing system headers, unless the user explicitly requests it + if (!Context.getOptions().SystemHeaders.value_or(false)) + FinderOptions.IgnoreSystemHeaders = true; + std::unique_ptr<ast_matchers::MatchFinder> Finder( new ast_matchers::MatchFinder(std::move(FinderOptions))); @@ -540,7 +544,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, ArrayRef<std::string> InputFiles, llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS, bool ApplyAnyFix, bool EnableCheckProfile, - llvm::StringRef StoreCheckProfile) { + llvm::StringRef StoreCheckProfile, bool Quiet) { ClangTool Tool(Compilations, InputFiles, std::make_shared<PCHContainerOperations>(), BaseFS); @@ -577,8 +581,9 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, class ActionFactory : public FrontendActionFactory { public: ActionFactory(ClangTidyContext &Context, - IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS) - : ConsumerFactory(Context, std::move(BaseFS)) {} + IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS, + bool Quiet) + : ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {} std::unique_ptr<FrontendAction> create() override { return std::make_unique<Action>(&ConsumerFactory); } @@ -589,6 +594,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, DiagnosticConsumer *DiagConsumer) override { // Explicitly ask to define __clang_analyzer__ macro. Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true; + if (Quiet) + Invocation->getDiagnosticOpts().ShowCarets = false; return FrontendActionFactory::runInvocation( Invocation, Files, PCHContainerOps, DiagConsumer); } @@ -607,9 +614,10 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, }; ClangTidyASTConsumerFactory ConsumerFactory; + bool Quiet; }; - ActionFactory Factory(Context, std::move(BaseFS)); + ActionFactory Factory(Context, std::move(BaseFS), Quiet); Tool.run(&Factory); return DiagConsumer.take(); } diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h index 454261b..d37d68e 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.h +++ b/clang-tools-extra/clang-tidy/ClangTidy.h @@ -94,7 +94,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, ArrayRef<std::string> InputFiles, llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS, bool ApplyAnyFix, bool EnableCheckProfile = false, - llvm::StringRef StoreCheckProfile = StringRef()); + llvm::StringRef StoreCheckProfile = StringRef(), + bool Quiet = false); /// Controls what kind of fixes clang-tidy is allowed to apply. enum FixBehaviour { diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index f9d7597..fac6e04 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -533,7 +533,8 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) { Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index)); break; case clang::DiagnosticsEngine::ak_nestednamespec: - Builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(Index)); + Builder << NestedNameSpecifier::getFromVoidPointer( + reinterpret_cast<void *>(Info.getRawArg(Index))); break; case clang::DiagnosticsEngine::ak_declcontext: Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index)); diff --git a/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h index 7f6b652..ac71f34 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationAdditionCheck.h @@ -22,6 +22,9 @@ class DurationAdditionCheck : public ClangTidyCheck { public: DurationAdditionCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h index d759e1d..65ab7a3 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h @@ -22,6 +22,9 @@ class DurationComparisonCheck : public ClangTidyCheck { public: DurationComparisonCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h index fea9f70..a898ba0 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationConversionCastCheck.h @@ -22,6 +22,9 @@ class DurationConversionCastCheck : public ClangTidyCheck { public: DurationConversionCastCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h index 6394b3f..e7c3985 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.h @@ -24,6 +24,9 @@ class DurationFactoryFloatCheck : public ClangTidyCheck { public: DurationFactoryFloatCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h index 40ffb30..f5f088c 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.h @@ -24,6 +24,9 @@ class DurationFactoryScaleCheck : public ClangTidyCheck { public: DurationFactoryScaleCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h index 17f7853..c865f2f 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationSubtractionCheck.h @@ -22,6 +22,9 @@ class DurationSubtractionCheck : public ClangTidyCheck { public: DurationSubtractionCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h index aa25c5f..fc9cf23 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/DurationUnnecessaryConversionCheck.h @@ -22,6 +22,9 @@ class DurationUnnecessaryConversionCheck : public ClangTidyCheck { public: DurationUnnecessaryConversionCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h b/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h index 0c647087..bf22977 100644 --- a/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.h @@ -22,6 +22,9 @@ class TimeComparisonCheck : public ClangTidyCheck { public: TimeComparisonCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h index c947f6b..9e2ec1c 100644 --- a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.h @@ -22,6 +22,9 @@ class TimeSubtractionCheck : public ClangTidyCheck { public: TimeSubtractionCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; diff --git a/clang-tools-extra/clang-tidy/add_new_check.py b/clang-tools-extra/clang-tidy/add_new_check.py index e366f10..2b51a1d 100755 --- a/clang-tools-extra/clang-tidy/add_new_check.py +++ b/clang-tools-extra/clang-tidy/add_new_check.py @@ -89,13 +89,9 @@ def write_header( + check_name_camel.upper() + "_H" ) - f.write("//===--- ") - f.write(os.path.basename(filename)) - f.write(" - clang-tidy ") - f.write("-" * max(0, 42 - len(os.path.basename(filename)))) - f.write("*- C++ -*-===//") f.write( """ +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -145,13 +141,9 @@ def write_implementation( filename = os.path.join(module_path, check_name_camel) + ".cpp" print("Creating %s..." % filename) with io.open(filename, "w", encoding="utf8", newline="\n") as f: - f.write("//===--- ") - f.write(os.path.basename(filename)) - f.write(" - clang-tidy ") - f.write("-" * max(0, 51 - len(os.path.basename(filename)))) - f.write("-===//") f.write( """ +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp index 1bfe384..f188ae5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CapturingThisInMemberVariableCheck.cpp @@ -44,11 +44,11 @@ AST_MATCHER(CXXRecordDecl, correctHandleCaptureThisLambda) { if (Node.hasSimpleMoveAssignment()) return false; - for (CXXConstructorDecl const *C : Node.ctors()) { + for (const CXXConstructorDecl *C : Node.ctors()) { if (C->isCopyOrMoveConstructor() && C->isDefaulted() && !C->isDeleted()) return false; } - for (CXXMethodDecl const *M : Node.methods()) { + for (const CXXMethodDecl *M : Node.methods()) { if (M->isCopyAssignmentOperator()) llvm::errs() << M->isDeleted() << "\n"; if (M->isCopyAssignmentOperator() && M->isDefaulted() && !M->isDeleted()) diff --git a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp index 6565fa3..0625468 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp @@ -43,7 +43,8 @@ static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP, return false; } - return FriendType->getType()->getAsCXXRecordDecl() == Derived; + return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(), + Derived); }); } @@ -55,7 +56,8 @@ getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP, CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) { ++Idx; return Arg.getKind() == TemplateArgument::Type && - Arg.getAsType()->getAsCXXRecordDecl() == Derived; + declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(), + Derived); }); return AnyOf ? CRTP->getSpecializedTemplate() diff --git a/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h b/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h index 3044304..981e9b5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.h @@ -13,14 +13,16 @@ namespace clang::tidy::bugprone { -/// Detect dangling references in value handlers like -/// std::experimental::string_view. +/// Detect dangling references in value handlers like std::string_view. /// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/dangling-handle.html class DanglingHandleCheck : public ClangTidyCheck { public: DanglingHandleCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; diff --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp index a179d4bf..3c718f1 100644 --- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp @@ -577,7 +577,7 @@ approximateImplicitConversion(const TheCheck &Check, QualType LType, ImplicitConversionModellingMode ImplicitMode); static inline bool isUselessSugar(const Type *T) { - return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(T); + return isa<AttributedType, DecayedType, ParenType>(T); } namespace { @@ -997,7 +997,7 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From, WorkType = QualType{ToBuiltin, FastQualifiersToApply}; } - const auto *FromEnum = WorkType->getAs<EnumType>(); + const auto *FromEnum = WorkType->getAsCanonical<EnumType>(); const auto *ToEnum = To->getAs<EnumType>(); if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) { // Unscoped enumerations (or enumerations in C) convert to numerics. @@ -1040,7 +1040,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From, const auto *ToRecord = To->getAsCXXRecordDecl(); if (isDerivedToBase(FromRecord, ToRecord)) { LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n"); - WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply}; + WorkType = QualType{ + ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(), + FastQualifiersToApply}; } if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) { @@ -1072,9 +1074,9 @@ approximateStandardConversionSequence(const TheCheck &Check, QualType From, WorkType = To; } - if (WorkType == To) { + if (Ctx.hasSameType(WorkType, To)) { LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n"); - return {WorkType}; + return {Ctx.getCommonSugaredType(WorkType, To)}; } LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n"); @@ -1219,7 +1221,7 @@ tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD, if (std::optional<UserDefinedConversionSelector::PreparedConversion> SelectedConversion = ConversionSet()) { - QualType RecordType{RD->getTypeForDecl(), 0}; + CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD); ConversionSequence Result{RecordType, ToType}; // The conversion from the operator call's return type to ToType was @@ -1270,7 +1272,7 @@ tryConvertingConstructors(const TheCheck &Check, QualType FromType, if (std::optional<UserDefinedConversionSelector::PreparedConversion> SelectedConversion = ConversionSet()) { - QualType RecordType{RD->getTypeForDecl(), 0}; + CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD); ConversionSequence Result{FromType, RecordType}; Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard; @@ -1573,6 +1575,10 @@ template <typename T, std::size_t N = SmallDataStructureSize> using ParamToSmallSetMap = llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>; +template <typename T, std::size_t N = SmallDataStructureSize> +using ParamToSmallPtrSetMap = + llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>; + /// Returns whether the sets mapped to the two elements in the map have at /// least one element in common. template <typename MapTy, typename ElemTy> @@ -1697,7 +1703,7 @@ public: /// Implements the heuristic that marks two parameters related if the same /// member is accessed (referred to) inside the current function's body. class AccessedSameMemberOf { - ParamToSmallSetMap<const Decl *> AccessedMembers; + ParamToSmallPtrSetMap<const Decl *> AccessedMembers; public: void setup(const FunctionDecl *FD) { diff --git a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h index af8c1e5..435c440 100644 --- a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h @@ -26,6 +26,9 @@ class FoldInitTypeCheck : public ClangTidyCheck { public: FoldInitTypeCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp index 75ef628..070ed04 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp @@ -69,10 +69,9 @@ void ForwardDeclarationNamespaceCheck::check( // struct B { friend A; }; // \endcode // `A` will not be marked as "referenced" in the AST. - if (const TypeSourceInfo *Tsi = Decl->getFriendType()) { - QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context); - FriendTypes.insert(Desugared.getTypePtr()); - } + if (const TypeSourceInfo *Tsi = Decl->getFriendType()) + FriendTypes.insert( + Tsi->getType()->getCanonicalTypeUnqualified().getTypePtr()); } } @@ -119,7 +118,9 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() { if (CurDecl->hasDefinition() || CurDecl->isReferenced()) { continue; // Skip forward declarations that are used/referenced. } - if (FriendTypes.contains(CurDecl->getTypeForDecl())) { + if (FriendTypes.contains(CurDecl->getASTContext() + .getCanonicalTagType(CurDecl) + ->getTypePtr())) { continue; // Skip forward declarations referenced as friend. } if (CurDecl->getLocation().isMacroID() || diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp index 00e8f7e..10b747e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp @@ -33,21 +33,17 @@ AST_MATCHER(QualType, isEnableIf) { BaseType = BaseType->getPointeeType().getTypePtr(); } // Case: type parameter dependent (enable_if<is_integral<T>>). - if (const auto *Dependent = BaseType->getAs<DependentNameType>()) { - BaseType = Dependent->getQualifier()->getAsType(); - } + if (const auto *Dependent = BaseType->getAs<DependentNameType>()) + BaseType = Dependent->getQualifier().getAsType(); if (!BaseType) return false; if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) return true; // Case: enable_if_t< >. - if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) { - if (const auto *Q = Elaborated->getQualifier()) - if (const auto *Qualifier = Q->getAsType()) { - if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) { - return true; // Case: enable_if< >::type. - } - } - } + if (const auto *TT = BaseType->getAs<TypedefType>()) + if (NestedNameSpecifier Q = TT->getQualifier(); + Q.getKind() == NestedNameSpecifier::Kind::Type) + if (CheckTemplate(Q.getAsType()->getAs<TemplateSpecializationType>())) + return true; // Case: enable_if< >::type. return false; } AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, diff --git a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp index 75f1107..07cd90d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/IncorrectEnableIfCheck.cpp @@ -32,13 +32,10 @@ AST_MATCHER_P(TemplateTypeParmDecl, hasUnnamedDefaultArgument, void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( templateTypeParmDecl( - hasUnnamedDefaultArgument( - elaboratedTypeLoc( - hasNamedTypeLoc(templateSpecializationTypeLoc( - loc(qualType(hasDeclaration(namedDecl( - hasName("::std::enable_if")))))) - .bind("enable_if_specialization"))) - .bind("elaborated"))) + hasUnnamedDefaultArgument(templateSpecializationTypeLoc( + loc(qualType(hasDeclaration(namedDecl( + hasName("::std::enable_if")))))) + .bind("enable_if_specialization"))) .bind("enable_if"), this); } @@ -46,13 +43,11 @@ void IncorrectEnableIfCheck::registerMatchers(MatchFinder *Finder) { void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) { const auto *EnableIf = Result.Nodes.getNodeAs<TemplateTypeParmDecl>("enable_if"); - const auto *ElaboratedLoc = - Result.Nodes.getNodeAs<ElaboratedTypeLoc>("elaborated"); const auto *EnableIfSpecializationLoc = Result.Nodes.getNodeAs<TemplateSpecializationTypeLoc>( "enable_if_specialization"); - if (!EnableIf || !ElaboratedLoc || !EnableIfSpecializationLoc) + if (!EnableIf || !EnableIfSpecializationLoc) return; const SourceManager &SM = *Result.SourceManager; @@ -62,8 +57,10 @@ void IncorrectEnableIfCheck::check(const MatchFinder::MatchResult &Result) { auto Diag = diag(EnableIf->getBeginLoc(), "incorrect std::enable_if usage detected; use " "'typename std::enable_if<...>::type'"); + // FIXME: This should handle the enable_if specialization already having an + // elaborated keyword. if (!getLangOpts().CPlusPlus20) { - Diag << FixItHint::CreateInsertion(ElaboratedLoc->getBeginLoc(), + Diag << FixItHint::CreateInsertion(EnableIfSpecializationLoc->getBeginLoc(), "typename "); } Diag << FixItHint::CreateInsertion(RAngleLoc.getLocWithOffset(1), "::type"); diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp index 4b495e3..cda9c4e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp @@ -188,7 +188,7 @@ static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx, /// \return true iff all `CallExprs` visited have callees; false otherwise /// indicating there is an unresolved indirect call. static bool populateCallees(const Stmt *StmtNode, - llvm::SmallSet<const Decl *, 16> &Callees) { + llvm::SmallPtrSet<const Decl *, 16> &Callees) { if (const auto *Call = dyn_cast<CallExpr>(StmtNode)) { const Decl *Callee = Call->getDirectCallee(); @@ -212,7 +212,7 @@ static bool populateCallees(const Stmt *StmtNode, /// returns true iff `SCC` contains `Func` and its' function set overlaps with /// `Callees` static bool overlap(ArrayRef<CallGraphNode *> SCC, - const llvm::SmallSet<const Decl *, 16> &Callees, + const llvm::SmallPtrSet<const Decl *, 16> &Callees, const Decl *Func) { bool ContainsFunc = false, Overlap = false; @@ -264,7 +264,7 @@ static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond, if (!hasStaticLocalVariable(Cond)) return false; - llvm::SmallSet<const Decl *, 16> CalleesInLoop; + llvm::SmallPtrSet<const Decl *, 16> CalleesInLoop; if (!populateCallees(LoopStmt, CalleesInLoop)) { // If there are unresolved indirect calls, we assume there could diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp index f903e63..7d92ef3 100644 --- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp @@ -67,15 +67,15 @@ public: return Visit(T->getElementType().getTypePtr()); } bool VisitEnumType(const EnumType *T) { - if (isCompleteAndHasNoZeroValue(T->getDecl())) { + if (isCompleteAndHasNoZeroValue(T->getOriginalDecl())) { FoundEnum = T; return true; } return false; } bool VisitRecordType(const RecordType *T) { - const RecordDecl *RD = T->getDecl(); - if (RD->isUnion()) + const RecordDecl *RD = T->getOriginalDecl()->getDefinition(); + if (!RD || RD->isUnion()) return false; auto VisitField = [this](const FieldDecl *F) { return Visit(F->getType().getTypePtr()); @@ -125,7 +125,7 @@ void InvalidEnumDefaultInitializationCheck::check( if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr())) return; InitExpr = InitList; - Enum = Finder.FoundEnum->getDecl(); + Enum = Finder.FoundEnum->getOriginalDecl(); } if (!InitExpr || !Enum) diff --git a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h index dab64f74..04ba359 100644 --- a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.h @@ -32,6 +32,9 @@ public: using SourceRangeSet = std::set<SourceRange, SourceRangeLessThan>; LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus11; + } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; diff --git a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp index bfa2ab5..5dc988d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp @@ -39,24 +39,31 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee, // std::move(). This will hopefully prevent erroneous replacements if the // code does unusual things (e.g. create an alias for std::move() in // another namespace). - NestedNameSpecifier *NNS = Callee->getQualifier(); - if (!NNS) { + NestedNameSpecifier NNS = Callee->getQualifier(); + switch (NNS.getKind()) { + case NestedNameSpecifier::Kind::Null: // Called as "move" (i.e. presumably the code had a "using std::move;"). // We still conservatively put a "std::" in front of the forward because // we don't know whether the code also had a "using std::forward;". Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName); - } else if (const NamespaceBaseDecl *Namespace = NNS->getAsNamespace()) { + break; + case NestedNameSpecifier::Kind::Namespace: { + auto [Namespace, Prefix] = NNS.getAsNamespaceAndPrefix(); if (Namespace->getName() == "std") { - if (!NNS->getPrefix()) { + if (!Prefix) { // Called as "std::move". Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName); - } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) { + } else if (Prefix.getKind() == NestedNameSpecifier::Kind::Global) { // Called as "::std::move". Diag << FixItHint::CreateReplacement(CallRange, "::std::" + ForwardName); } } + break; + } + default: + return; } } } diff --git a/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h index ef5f9f1..5ec78be 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h @@ -31,7 +31,7 @@ public: } private: - bool const EnableInC; + const bool EnableInC; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp index b68888c..6344b4b 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MultipleNewInOneExpressionCheck.cpp @@ -15,14 +15,12 @@ using namespace clang::ast_matchers; namespace clang::tidy::bugprone { -namespace { - // Determine if the result of an expression is "stored" in some way. // It is true if the value is stored into a variable or used as initialization // or passed to a function or constructor. // For this use case compound assignments are not counted as a "store" (the 'E' // expression should have pointer type). -bool isExprValueStored(const Expr *E, ASTContext &C) { +static bool isExprValueStored(const Expr *E, ASTContext &C) { E = E->IgnoreParenCasts(); // Get first non-paren, non-cast parent. ParentMapContext &PMap = C.getParentMapContext(); @@ -49,6 +47,8 @@ bool isExprValueStored(const Expr *E, ASTContext &C) { return isa<CallExpr, CXXConstructExpr>(ParentE); } +namespace { + AST_MATCHER_P(CXXTryStmt, hasHandlerFor, ast_matchers::internal::Matcher<QualType>, InnerMatcher) { for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) { diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h index b044219..888d29f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.h @@ -23,6 +23,9 @@ namespace clang::tidy::bugprone { class OptionalValueConversionCheck : public ClangTidyCheck { public: OptionalValueConversionCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; diff --git a/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h b/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h index f552965..293069f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/ParentVirtualCallCheck.h @@ -21,6 +21,9 @@ class ParentVirtualCallCheck : public ClangTidyCheck { public: ParentVirtualCallCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h b/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h index 7227c3d..f50ce99 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofContainerCheck.h @@ -22,6 +22,9 @@ class SizeofContainerCheck : public ClangTidyCheck { public: SizeofContainerCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp index 88e048e6..8da6227 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -425,7 +425,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { "suspicious usage of 'sizeof(array)/sizeof(...)';" " denominator differs from the size of array elements") << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); - } else if (NumTy && DenomTy && NumTy == DenomTy && + } else if (NumTy && DenomTy && Ctx.hasSameType(NumTy, DenomTy) && !NumTy->isDependentType()) { // Dependent type should not be compared. diag(E->getOperatorLoc(), @@ -434,7 +434,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (!WarnOnSizeOfPointer) { // When 'WarnOnSizeOfPointer' is enabled, these messages become redundant: - if (PointedTy && DenomTy && PointedTy == DenomTy) { + if (PointedTy && DenomTy && Ctx.hasSameType(PointedTy, DenomTy)) { diag(E->getOperatorLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer " "is divided by size of pointed type") @@ -463,7 +463,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { const auto *SizeOfExpr = Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-mul-expr"); - if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { + if (Ctx.hasSameType(LPtrTy, RPtrTy) && + Ctx.hasSameType(LPtrTy, SizeofArgTy)) { diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " "pointer arithmetic") << SizeOfExpr->getSourceRange() << E->getOperatorLoc() @@ -477,7 +478,8 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { const auto *SizeOfExpr = Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-div-expr"); - if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { + if (Ctx.hasSameType(LPtrTy, RPtrTy) && + Ctx.hasSameType(LPtrTy, SizeofArgTy)) { diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " "pointer arithmetic") << SizeOfExpr->getSourceRange() << E->getOperatorLoc() diff --git a/clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.h b/clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.h index e9f1a6a..7fcc4b6 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/SmartPtrArrayMismatchCheck.h @@ -40,7 +40,7 @@ protected: static const char PointerTypeN[]; private: - StringRef const SmartPointerName; + const StringRef SmartPointerName; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp index 81c38d0..5b1b28d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp @@ -14,10 +14,8 @@ using namespace clang::ast_matchers; namespace clang::tidy::bugprone { -namespace { - -bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx, - const StringLiteral *Lit) { +static bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx, + const StringLiteral *Lit) { // String literals surrounded by parentheses are assumed to be on purpose. // i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] }; @@ -58,6 +56,8 @@ bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx, return false; } +namespace { + AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned, MaxConcatenatedTokens) { return Node.getNumConcatenated() > 1 && diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp index ddbb14e..02f4421 100644 --- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp @@ -169,15 +169,8 @@ void TaggedUnionMemberCountCheck::check( if (!Root || !UnionField || !TagField) return; - const auto *UnionDef = - UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl(); - const auto *EnumDef = llvm::dyn_cast<EnumDecl>( - TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl()); - - assert(UnionDef && "UnionDef is missing!"); - assert(EnumDef && "EnumDef is missing!"); - if (!UnionDef || !EnumDef) - return; + const auto *UnionDef = UnionField->getType()->castAsRecordDecl(); + const auto *EnumDef = TagField->getType()->castAsEnumDecl(); const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields()); auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef); diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp index ac49415..3b6969a 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp @@ -33,7 +33,7 @@ AST_MATCHER(VarDecl, isReferenced) { return Node.isReferenced(); } AST_MATCHER(VarDecl, explicitMarkUnused) { // Implementations should not emit a warning that a name-independent // declaration is used or unused. - LangOptions const &LangOpts = Finder->getASTContext().getLangOpts(); + const LangOptions &LangOpts = Finder->getASTContext().getLangOpts(); return Node.hasAttr<UnusedAttr>() || (LangOpts.CPlusPlus26 && Node.isPlaceholderVar(LangOpts)); } diff --git a/clang-tools-extra/clang-tidy/cert/DefaultOperatorNewAlignmentCheck.cpp b/clang-tools-extra/clang-tidy/cert/DefaultOperatorNewAlignmentCheck.cpp index 2a0d0ad..2c2248a 100644 --- a/clang-tools-extra/clang-tidy/cert/DefaultOperatorNewAlignmentCheck.cpp +++ b/clang-tools-extra/clang-tidy/cert/DefaultOperatorNewAlignmentCheck.cpp @@ -31,7 +31,7 @@ void DefaultOperatorNewAlignmentCheck::check( return; const TagDecl *D = T->getAsTagDecl(); // Alignment can not be obtained for undefined type. - if (!D || !D->getDefinition() || !D->isCompleteDefinition()) + if (!D || !D->isCompleteDefinition()) return; ASTContext &Context = D->getASTContext(); diff --git a/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp b/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp index 3b59d23..95536bb 100644 --- a/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp +++ b/clang-tools-extra/clang-tidy/cert/StrToNumCheck.cpp @@ -46,7 +46,9 @@ enum class ConversionKind { ToLongDouble }; -ConversionKind classifyConversionFunc(const FunctionDecl *FD) { +} // namespace + +static ConversionKind classifyConversionFunc(const FunctionDecl *FD) { return llvm::StringSwitch<ConversionKind>(FD->getName()) .Cases("atoi", "atol", ConversionKind::ToInt) .Case("atoll", ConversionKind::ToLongInt) @@ -54,8 +56,8 @@ ConversionKind classifyConversionFunc(const FunctionDecl *FD) { .Default(ConversionKind::None); } -ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO, - const TargetInfo &TI) { +static ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO, + const TargetInfo &TI) { // Scan the format string for the first problematic format specifier, then // report that as the conversion type. This will miss additional conversion // specifiers, but that is acceptable behavior. @@ -128,7 +130,7 @@ ConversionKind classifyFormatString(StringRef Fmt, const LangOptions &LO, return H.get(); } -StringRef classifyConversionType(ConversionKind K) { +static StringRef classifyConversionType(ConversionKind K) { switch (K) { case ConversionKind::None: llvm_unreachable("Unexpected conversion kind"); @@ -148,7 +150,7 @@ StringRef classifyConversionType(ConversionKind K) { llvm_unreachable("Unknown conversion kind"); } -StringRef classifyReplacement(ConversionKind K) { +static StringRef classifyReplacement(ConversionKind K) { switch (K) { case ConversionKind::None: llvm_unreachable("Unexpected conversion kind"); @@ -173,7 +175,6 @@ StringRef classifyReplacement(ConversionKind K) { } llvm_unreachable("Unknown conversion kind"); } -} // unnamed namespace void StrToNumCheck::check(const MatchFinder::MatchResult &Result) { const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr"); diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp index f615976..dd913c9 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp @@ -14,73 +14,73 @@ using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { -static bool isCopyConstructible(CXXRecordDecl const &Node) { +static bool isCopyConstructible(const CXXRecordDecl &Node) { if (Node.needsOverloadResolutionForCopyConstructor() && Node.needsImplicitCopyConstructor()) { // unresolved - for (CXXBaseSpecifier const &BS : Node.bases()) { - CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); + for (const CXXBaseSpecifier &BS : Node.bases()) { + const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl(); if (BRD != nullptr && !isCopyConstructible(*BRD)) return false; } } if (Node.hasSimpleCopyConstructor()) return true; - for (CXXConstructorDecl const *Ctor : Node.ctors()) + for (const CXXConstructorDecl *Ctor : Node.ctors()) if (Ctor->isCopyConstructor()) return !Ctor->isDeleted(); return false; } -static bool isMoveConstructible(CXXRecordDecl const &Node) { +static bool isMoveConstructible(const CXXRecordDecl &Node) { if (Node.needsOverloadResolutionForMoveConstructor() && Node.needsImplicitMoveConstructor()) { // unresolved - for (CXXBaseSpecifier const &BS : Node.bases()) { - CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); + for (const CXXBaseSpecifier &BS : Node.bases()) { + const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl(); if (BRD != nullptr && !isMoveConstructible(*BRD)) return false; } } if (Node.hasSimpleMoveConstructor()) return true; - for (CXXConstructorDecl const *Ctor : Node.ctors()) + for (const CXXConstructorDecl *Ctor : Node.ctors()) if (Ctor->isMoveConstructor()) return !Ctor->isDeleted(); return false; } -static bool isCopyAssignable(CXXRecordDecl const &Node) { +static bool isCopyAssignable(const CXXRecordDecl &Node) { if (Node.needsOverloadResolutionForCopyAssignment() && Node.needsImplicitCopyAssignment()) { // unresolved - for (CXXBaseSpecifier const &BS : Node.bases()) { - CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); + for (const CXXBaseSpecifier &BS : Node.bases()) { + const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl(); if (BRD != nullptr && !isCopyAssignable(*BRD)) return false; } } if (Node.hasSimpleCopyAssignment()) return true; - for (CXXMethodDecl const *Method : Node.methods()) + for (const CXXMethodDecl *Method : Node.methods()) if (Method->isCopyAssignmentOperator()) return !Method->isDeleted(); return false; } -static bool isMoveAssignable(CXXRecordDecl const &Node) { +static bool isMoveAssignable(const CXXRecordDecl &Node) { if (Node.needsOverloadResolutionForMoveAssignment() && Node.needsImplicitMoveAssignment()) { // unresolved - for (CXXBaseSpecifier const &BS : Node.bases()) { - CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); + for (const CXXBaseSpecifier &BS : Node.bases()) { + const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl(); if (BRD != nullptr && !isMoveAssignable(*BRD)) return false; } } if (Node.hasSimpleMoveAssignment()) return true; - for (CXXMethodDecl const *Method : Node.methods()) + for (const CXXMethodDecl *Method : Node.methods()) if (Method->isMoveAssignmentOperator()) return !Method->isDeleted(); return false; diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt index 2fb4d7f..0abb000 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule STATIC OwningMemoryCheck.cpp PreferMemberInitializerCheck.cpp ProBoundsArrayToPointerDecayCheck.cpp + ProBoundsAvoidUncheckedContainerAccess.cpp ProBoundsConstantArrayIndexCheck.cpp ProBoundsPointerArithmeticCheck.cpp ProTypeConstCastCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp index 4b3b7bf..cc1ae15 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -36,6 +36,7 @@ #include "OwningMemoryCheck.h" #include "PreferMemberInitializerCheck.h" #include "ProBoundsArrayToPointerDecayCheck.h" +#include "ProBoundsAvoidUncheckedContainerAccess.h" #include "ProBoundsConstantArrayIndexCheck.h" #include "ProBoundsPointerArithmeticCheck.h" #include "ProTypeConstCastCheck.h" @@ -107,6 +108,8 @@ public: "cppcoreguidelines-prefer-member-initializer"); CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>( "cppcoreguidelines-pro-bounds-array-to-pointer-decay"); + CheckFactories.registerCheck<ProBoundsAvoidUncheckedContainerAccess>( + "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access"); CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>( "cppcoreguidelines-pro-bounds-constant-array-index"); CheckFactories.registerCheck<ProBoundsPointerArithmeticCheck>( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp index 82d1cf1..75da6de 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp @@ -45,7 +45,7 @@ AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) { QualType ParamType = Node.getType().getNonPackExpansionType()->getPointeeType(); - const auto *TemplateType = ParamType->getAs<TemplateTypeParmType>(); + const auto *TemplateType = ParamType->getAsCanonical<TemplateTypeParmType>(); if (!TemplateType) return false; diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp index ca29317..29470b1 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp @@ -23,9 +23,9 @@ void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { } void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) { - auto LockType = elaboratedType(namesType(templateSpecializationType( + auto LockType = templateSpecializationType( hasDeclaration(namedDecl(matchers::matchesAnyListedName( - utils::options::parseStringList(LockGuards))))))); + utils::options::parseStringList(LockGuards))))); StatementMatcher Lock = declStmt(has(varDecl(hasType(LockType)).bind("lock-decl"))) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp index 593a4f8..79cd4bb 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp @@ -191,6 +191,9 @@ void PreferMemberInitializerCheck::check( if (!AssignmentToMember) continue; const FieldDecl *Field = AssignmentToMember->Field; + // Skip if the field is inherited from a base class. + if (Field->getParent() != Class) + continue; const Expr *InitValue = AssignmentToMember->Init; updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields); if (!canAdvanceAssignment(AssignedFields[Field])) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp new file mode 100644 index 0000000..35f432e --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.cpp @@ -0,0 +1,262 @@ +//===--- ProBoundsAvoidUncheckedContainerAccess.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 "ProBoundsAvoidUncheckedContainerAccess.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringRef.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::cppcoreguidelines { + +static constexpr llvm::StringRef DefaultExclusionStr = + "::std::map;::std::unordered_map;::std::flat_map"; + +ProBoundsAvoidUncheckedContainerAccess::ProBoundsAvoidUncheckedContainerAccess( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + ExcludedClasses(utils::options::parseStringList( + Options.get("ExcludeClasses", DefaultExclusionStr))), + FixMode(Options.get("FixMode", None)), + FixFunction(Options.get("FixFunction", "gsl::at")), + FixFunctionEmptyArgs(Options.get("FixFunctionEmptyArgs", FixFunction)) {} + +void ProBoundsAvoidUncheckedContainerAccess::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ExcludeClasses", + utils::options::serializeStringList(ExcludedClasses)); + Options.store(Opts, "FixMode", FixMode); + Options.store(Opts, "FixFunction", FixFunction); + Options.store(Opts, "FixFunctionEmptyArgs", FixFunctionEmptyArgs); +} + +// TODO: if at() is defined in another class in the class hierarchy of the class +// that defines the operator[] we matched on, findAlternative() will not detect +// it. +static const CXXMethodDecl * +findAlternativeAt(const CXXMethodDecl *MatchedOperator) { + const CXXRecordDecl *Parent = MatchedOperator->getParent(); + const QualType SubscriptThisObjType = + MatchedOperator->getFunctionObjectParameterReferenceType(); + + for (const CXXMethodDecl *Method : Parent->methods()) { + // Require 'Method' to be as accessible as 'MatchedOperator' or more + if (MatchedOperator->getAccess() < Method->getAccess()) + continue; + + if (MatchedOperator->isConst() != Method->isConst()) + continue; + + const QualType AtThisObjType = + Method->getFunctionObjectParameterReferenceType(); + if (SubscriptThisObjType != AtThisObjType) + continue; + + if (!Method->getNameInfo().getName().isIdentifier() || + Method->getName() != "at") + continue; + + const bool SameReturnType = + Method->getReturnType() == MatchedOperator->getReturnType(); + if (!SameReturnType) + continue; + + const bool SameNumberOfArguments = + Method->getNumParams() == MatchedOperator->getNumParams(); + if (!SameNumberOfArguments) + continue; + + for (unsigned ArgInd = 0; ArgInd < Method->getNumParams(); ArgInd++) { + const bool SameArgType = + Method->parameters()[ArgInd]->getOriginalType() == + MatchedOperator->parameters()[ArgInd]->getOriginalType(); + if (!SameArgType) + continue; + } + + return Method; + } + return nullptr; +} + +void ProBoundsAvoidUncheckedContainerAccess::registerMatchers( + MatchFinder *Finder) { + Finder->addMatcher( + mapAnyOf(cxxOperatorCallExpr, cxxMemberCallExpr) + .with(callee( + cxxMethodDecl( + hasOverloadedOperatorName("[]"), + anyOf(parameterCountIs(0), parameterCountIs(1)), + unless(matchers::matchesAnyListedName(ExcludedClasses))) + .bind("operator"))) + .bind("caller"), + this); +} + +void ProBoundsAvoidUncheckedContainerAccess::check( + const MatchFinder::MatchResult &Result) { + + const auto *MatchedExpr = Result.Nodes.getNodeAs<CallExpr>("caller"); + + if (FixMode == None) { + diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]', consider bounds-safe alternatives") + << MatchedExpr->getCallee()->getSourceRange(); + return; + } + + if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(MatchedExpr)) { + // Case: a[i] + const auto LeftBracket = SourceRange(OCE->getCallee()->getBeginLoc(), + OCE->getCallee()->getBeginLoc()); + const auto RightBracket = + SourceRange(OCE->getOperatorLoc(), OCE->getOperatorLoc()); + + if (FixMode == At) { + // Case: a[i] => a.at(i) + const auto *MatchedOperator = + Result.Nodes.getNodeAs<CXXMethodDecl>("operator"); + const CXXMethodDecl *Alternative = findAlternativeAt(MatchedOperator); + + if (!Alternative) { + diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]', consider " + "bounds-safe alternatives") + << MatchedExpr->getCallee()->getSourceRange(); + return; + } + + diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]', consider " + "bounds-safe alternative 'at()'") + << MatchedExpr->getCallee()->getSourceRange() + << FixItHint::CreateReplacement(LeftBracket, ".at(") + << FixItHint::CreateReplacement(RightBracket, ")"); + + diag(Alternative->getBeginLoc(), "viable 'at()' is defined here", + DiagnosticIDs::Note) + << Alternative->getNameInfo().getSourceRange(); + + } else if (FixMode == Function) { + // Case: a[i] => f(a, i) + // + // Since C++23, the subscript operator may also be called without an + // argument, which makes the following distinction necessary + const bool EmptySubscript = + MatchedExpr->getDirectCallee()->getNumParams() == 0; + + if (EmptySubscript) { + auto D = diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]'%select{, use safe " + "function '%1() instead|}0") + << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str() + << MatchedExpr->getCallee()->getSourceRange(); + if (!FixFunctionEmptyArgs.empty()) { + D << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(), + FixFunctionEmptyArgs.str() + "(") + << FixItHint::CreateRemoval(LeftBracket) + << FixItHint::CreateReplacement(RightBracket, ")"); + } + } else { + diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]', use safe function '%0()' instead") + << FixFunction.str() << MatchedExpr->getCallee()->getSourceRange() + << FixItHint::CreateInsertion(OCE->getArg(0)->getBeginLoc(), + FixFunction.str() + "(") + << FixItHint::CreateReplacement(LeftBracket, ", ") + << FixItHint::CreateReplacement(RightBracket, ")"); + } + } + } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(MatchedExpr)) { + // Case: a.operator[](i) or a->operator[](i) + const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee()); + + if (FixMode == At) { + // Cases: a.operator[](i) => a.at(i) and a->operator[](i) => a->at(i) + + const auto *MatchedOperator = + Result.Nodes.getNodeAs<CXXMethodDecl>("operator"); + + const CXXMethodDecl *Alternative = findAlternativeAt(MatchedOperator); + if (!Alternative) { + diag(Callee->getBeginLoc(), "possibly unsafe 'operator[]', consider " + "bounds-safe alternative 'at()'") + << Callee->getSourceRange(); + return; + } + diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]', consider " + "bounds-safe alternative 'at()'") + << FixItHint::CreateReplacement( + SourceRange(Callee->getMemberLoc(), Callee->getEndLoc()), + "at"); + + diag(Alternative->getBeginLoc(), "viable 'at()' defined here", + DiagnosticIDs::Note) + << Alternative->getNameInfo().getSourceRange(); + + } else if (FixMode == Function) { + // Cases: a.operator[](i) => f(a, i) and a->operator[](i) => f(*a, i) + const auto *Callee = dyn_cast<MemberExpr>(MCE->getCallee()); + + const bool EmptySubscript = + MCE->getMethodDecl()->getNumNonObjectParams() == 0; + + std::string BeginInsertion = + (EmptySubscript ? FixFunctionEmptyArgs.str() : FixFunction.str()) + + "("; + + if (Callee->isArrow()) + BeginInsertion += "*"; + + // Since C++23, the subscript operator may also be called without an + // argument, which makes the following distinction necessary + if (EmptySubscript) { + auto D = diag(MatchedExpr->getCallee()->getBeginLoc(), + "possibly unsafe 'operator[]'%select{, use safe " + "function '%1()' instead|}0") + << FixFunctionEmptyArgs.empty() << FixFunctionEmptyArgs.str() + << Callee->getSourceRange(); + + if (!FixFunctionEmptyArgs.empty()) { + D << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(), + BeginInsertion) + << FixItHint::CreateRemoval( + SourceRange(Callee->getOperatorLoc(), + MCE->getRParenLoc().getLocWithOffset(-1))); + } + } else { + diag(Callee->getBeginLoc(), + "possibly unsafe 'operator[]', use safe function '%0()' instead") + << FixFunction.str() << Callee->getSourceRange() + << FixItHint::CreateInsertion(MatchedExpr->getBeginLoc(), + BeginInsertion) + << FixItHint::CreateReplacement( + SourceRange( + Callee->getOperatorLoc(), + MCE->getArg(0)->getBeginLoc().getLocWithOffset(-1)), + ", "); + } + } + } +} + +} // namespace clang::tidy::cppcoreguidelines + +namespace clang::tidy { +using P = cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess; + +llvm::ArrayRef<std::pair<P::FixModes, StringRef>> +OptionEnumMapping<P::FixModes>::getEnumMapping() { + static constexpr std::pair<P::FixModes, StringRef> Mapping[] = { + {P::None, "none"}, {P::At, "at"}, {P::Function, "function"}}; + return {Mapping}; +} +} // namespace clang::tidy diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h new file mode 100644 index 0000000..cfd52d6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsAvoidUncheckedContainerAccess.h @@ -0,0 +1,56 @@ +//===--- ProBoundsAvoidUncheckedContainerAccess.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_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::cppcoreguidelines { + +/// Flags calls to operator[] in STL containers and suggests replacing it with +/// safe alternatives. +/// +/// See +/// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#slcon3-avoid-bounds-errors +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.html +class ProBoundsAvoidUncheckedContainerAccess : public ClangTidyCheck { +public: + ProBoundsAvoidUncheckedContainerAccess(StringRef Name, + ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + + enum FixModes { None, At, Function }; + +private: + // A list of class names that are excluded from the warning + std::vector<llvm::StringRef> ExcludedClasses; + // Setting which fix to suggest + FixModes FixMode; + llvm::StringRef FixFunction; + llvm::StringRef FixFunctionEmptyArgs; +}; +} // namespace clang::tidy::cppcoreguidelines + +namespace clang::tidy { +template <> +struct OptionEnumMapping< + cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess::FixModes> { + static ArrayRef<std::pair< + cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess::FixModes, + StringRef>> + getEnumMapping(); +}; +} // namespace clang::tidy +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_AVOID_UNCHECKED_CONTAINER_ACCESS_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp index 9ac7b9e..51995c5 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp @@ -14,6 +14,18 @@ using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { +ProBoundsPointerArithmeticCheck::ProBoundsPointerArithmeticCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowIncrementDecrementOperators( + Options.get("AllowIncrementDecrementOperators", false)) {} + +void ProBoundsPointerArithmeticCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowIncrementDecrementOperators", + AllowIncrementDecrementOperators); +} + void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) { const auto AllPointerTypes = anyOf(hasType(hasUnqualifiedDesugaredType(pointerType())), @@ -30,13 +42,14 @@ void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) { this); // Flag all operators ++, -- that result in a pointer - Finder->addMatcher( - unaryOperator(hasAnyOperatorName("++", "--"), - hasType(hasUnqualifiedDesugaredType(pointerType())), - unless(hasUnaryOperand( - ignoringImpCasts(declRefExpr(to(isImplicit())))))) - .bind("expr"), - this); + if (!AllowIncrementDecrementOperators) + Finder->addMatcher( + unaryOperator(hasAnyOperatorName("++", "--"), + hasType(hasUnqualifiedDesugaredType(pointerType())), + unless(hasUnaryOperand( + ignoringImpCasts(declRefExpr(to(isImplicit())))))) + .bind("expr"), + this); // Array subscript on a pointer (not an array) is also pointer arithmetic Finder->addMatcher( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h index 3466c72..785f754 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h @@ -21,13 +21,16 @@ namespace clang::tidy::cppcoreguidelines { /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.html class ProBoundsPointerArithmeticCheck : public ClangTidyCheck { public: - ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context); bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const bool AllowIncrementDecrementOperators; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index b413b12..e6e79f0 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -189,8 +189,8 @@ struct InitializerInsertion { // Convenience utility to get a RecordDecl from a QualType. const RecordDecl *getCanonicalRecordDecl(const QualType &Type) { - if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>()) - return RT->getDecl(); + if (const auto *RT = Type->getAsCanonical<RecordType>()) + return RT->getOriginalDecl(); return nullptr; } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp index 7675439..6508bfd5 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.cpp @@ -90,9 +90,8 @@ void SlicingCheck::diagnoseSlicedOverriddenMethods( } // Recursively process bases. for (const auto &Base : DerivedDecl.bases()) { - if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) { - if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>( - BaseRecordType->getDecl()->getDefinition())) + if (const auto *BaseRecord = Base.getType()->getAsCXXRecordDecl()) { + if (BaseRecord->isCompleteDefinition()) diagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl); } } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h index 02bfaf1..317547f 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SlicingCheck.h @@ -22,6 +22,9 @@ class SlicingCheck : public ClangTidyCheck { public: SlicingCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; diff --git a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h index 49d7820..120dc90 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h +++ b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsCallsCheck.h @@ -21,6 +21,9 @@ class DefaultArgumentsCallsCheck : public ClangTidyCheck { public: DefaultArgumentsCallsCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h index d03d0b7..da73fa4 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h +++ b/clang-tools-extra/clang-tidy/fuchsia/DefaultArgumentsDeclarationsCheck.h @@ -21,6 +21,9 @@ class DefaultArgumentsDeclarationsCheck : public ClangTidyCheck { public: DefaultArgumentsDeclarationsCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp index 80ff97a..4382f9d 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp +++ b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp @@ -39,7 +39,7 @@ bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node, bool &IsInterface) const { assert(Node->getIdentifier()); StringRef Name = Node->getIdentifier()->getName(); - llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name); + auto Pair = InterfaceMap.find(Name); if (Pair == InterfaceMap.end()) return false; IsInterface = Pair->second; @@ -71,13 +71,10 @@ bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) { for (const auto &I : Node->bases()) { if (I.isVirtual()) continue; - const auto *Ty = I.getType()->getAs<RecordType>(); - if (!Ty) + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base) continue; - const RecordDecl *D = Ty->getDecl()->getDefinition(); - if (!D) - continue; - const auto *Base = cast<CXXRecordDecl>(D); + assert(Base->isCompleteDefinition()); if (!isInterface(Base)) { addNodeToInterfaceMap(Node, false); return false; @@ -103,10 +100,10 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { for (const auto &I : D->bases()) { if (I.isVirtual()) continue; - const auto *Ty = I.getType()->getAs<RecordType>(); - if (!Ty) + const auto *Base = I.getType()->getAsCXXRecordDecl(); + if (!Base) continue; - const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); + assert(Base->isCompleteDefinition()); if (!isInterface(Base)) NumConcrete++; } @@ -114,10 +111,10 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { // Check virtual bases to see if there is more than one concrete // non-virtual base. for (const auto &V : D->vbases()) { - const auto *Ty = V.getType()->getAs<RecordType>(); - if (!Ty) + const auto *Base = V.getType()->getAsCXXRecordDecl(); + if (!Base) continue; - const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); + assert(Base->isCompleteDefinition()); if (!isInterface(Base)) NumConcrete++; } diff --git a/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h b/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h index 007f410..d26349d 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h +++ b/clang-tools-extra/clang-tidy/fuchsia/OverloadedOperatorCheck.h @@ -21,6 +21,9 @@ class OverloadedOperatorCheck : public ClangTidyCheck { public: OverloadedOperatorCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h b/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h index 426d89d..1bdf19f 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h +++ b/clang-tools-extra/clang-tidy/fuchsia/VirtualInheritanceCheck.h @@ -21,6 +21,9 @@ class VirtualInheritanceCheck : public ClangTidyCheck { public: VirtualInheritanceCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp index e076b39..14e11eb 100644 --- a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -89,6 +89,30 @@ static StringRef getDestTypeString(const SourceManager &SM, SM, LangOpts); } +static bool sameTypeAsWritten(QualType X, QualType Y) { + if (X.getCanonicalType() != Y.getCanonicalType()) + return false; + + auto TC = X->getTypeClass(); + if (TC != Y->getTypeClass()) + return false; + + switch (TC) { + case Type::Typedef: + return declaresSameEntity(cast<TypedefType>(X)->getDecl(), + cast<TypedefType>(Y)->getDecl()); + case Type::Pointer: + return sameTypeAsWritten(cast<PointerType>(X)->getPointeeType(), + cast<PointerType>(Y)->getPointeeType()); + case Type::RValueReference: + case Type::LValueReference: + return sameTypeAsWritten(cast<ReferenceType>(X)->getPointeeType(), + cast<ReferenceType>(Y)->getPointeeType()); + default: + return true; + } +} + void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); @@ -128,12 +152,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { // case of overloaded functions, so detection of redundant casts is trickier // in this case. Don't emit "redundant cast" warnings for function // pointer/reference types. - QualType Src = SourceTypeAsWritten, Dst = DestTypeAsWritten; - if (const auto *ElTy = dyn_cast<ElaboratedType>(Src)) - Src = ElTy->getNamedType(); - if (const auto *ElTy = dyn_cast<ElaboratedType>(Dst)) - Dst = ElTy->getNamedType(); - if (Src == Dst) { + if (sameTypeAsWritten(SourceTypeAsWritten, DestTypeAsWritten)) { diag(CastExpr->getBeginLoc(), "redundant cast to the same type") << FixItHint::CreateRemoval(ReplaceRange); return; diff --git a/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h b/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h index baec40e..b53e6c4 100644 --- a/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h +++ b/clang-tools-extra/clang-tidy/google/AvoidUnderscoreInGoogletestNameCheck.h @@ -21,6 +21,9 @@ namespace clang::tidy::google::readability { class AvoidUnderscoreInGoogletestNameCheck : public ClangTidyCheck { public: using ClangTidyCheck::ClangTidyCheck; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override; diff --git a/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h b/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h index a8f0b01..49d95a5 100644 --- a/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h +++ b/clang-tools-extra/clang-tidy/google/DefaultArgumentsCheck.h @@ -23,6 +23,9 @@ class DefaultArgumentsCheck : public ClangTidyCheck { public: DefaultArgumentsCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp b/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp index 3deea06..68233ec 100644 --- a/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp @@ -72,7 +72,7 @@ static bool isStdInitializerList(QualType Type) { } if (const auto *RT = Type->getAs<RecordType>()) { if (const auto *Specialization = - dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) + dyn_cast<ClassTemplateSpecializationDecl>(RT->getOriginalDecl())) return declIsStdInitializerList(Specialization->getSpecializedTemplate()); } return false; diff --git a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp index 805dcaf..274b8af 100644 --- a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp @@ -257,8 +257,13 @@ getAliasNameRange(const MatchFinder::MatchResult &Result) { return CharSourceRange::getTokenRange( Using->getNameInfo().getSourceRange()); } - return CharSourceRange::getTokenRange( - Result.Nodes.getNodeAs<TypeLoc>("typeloc")->getSourceRange()); + TypeLoc TL = *Result.Nodes.getNodeAs<TypeLoc>("typeloc"); + if (auto QTL = TL.getAs<QualifiedTypeLoc>()) + TL = QTL.getUnqualifiedLoc(); + + if (auto TTL = TL.getAs<TypedefTypeLoc>()) + return CharSourceRange::getTokenRange(TTL.getNameLoc()); + return CharSourceRange::getTokenRange(TL.castAs<UsingTypeLoc>().getNameLoc()); } void UpgradeGoogletestCaseCheck::check(const MatchFinder::MatchResult &Result) { diff --git a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h index 48354c3..39c45fe 100644 --- a/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h +++ b/clang-tools-extra/clang-tidy/hicpp/IgnoredRemoveResultCheck.h @@ -21,6 +21,9 @@ namespace clang::tidy::hicpp { class IgnoredRemoveResultCheck : public bugprone::UnusedReturnValueCheck { public: IgnoredRemoveResultCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; }; diff --git a/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h b/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h index 1099ab0..07e018a 100644 --- a/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/PreferRegisterOverUnsignedCheck.h @@ -23,6 +23,9 @@ class PreferRegisterOverUnsignedCheck : public ClangTidyCheck { public: PreferRegisterOverUnsignedCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h b/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h index e1f25e5..b4550ec 100644 --- a/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/TwineLocalCheck.h @@ -19,6 +19,9 @@ class TwineLocalCheck : public ClangTidyCheck { public: TwineLocalCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index fd7affd..2cfee5f 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -32,6 +32,7 @@ add_clang_library(clangTidyMiscModule STATIC NoRecursionCheck.cpp NonCopyableObjects.cpp NonPrivateMemberVariablesInClassesCheck.cpp + OverrideWithDifferentVisibilityCheck.cpp RedundantExpressionCheck.cpp StaticAssertCheck.cpp ThrowByValueCatchByReferenceCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp index 697398a..b32507d 100644 --- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp @@ -98,11 +98,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) { hasType(referenceType(pointee(hasCanonicalType(templateTypeParmType())))), hasType(referenceType(pointee(substTemplateTypeParmType())))); - const auto AllowedType = hasType(qualType(anyOf( - hasDeclaration(namedDecl(matchers::matchesAnyListedName(AllowedTypes))), - references(namedDecl(matchers::matchesAnyListedName(AllowedTypes))), - pointerType(pointee(hasDeclaration( - namedDecl(matchers::matchesAnyListedName(AllowedTypes)))))))); + auto AllowedTypeDecl = namedDecl( + anyOf(matchers::matchesAnyListedName(AllowedTypes), usingShadowDecl())); + + const auto AllowedType = hasType(qualType( + anyOf(hasDeclaration(AllowedTypeDecl), references(AllowedTypeDecl), + pointerType(pointee(hasDeclaration(AllowedTypeDecl)))))); const auto AutoTemplateType = varDecl( anyOf(hasType(autoType()), hasType(referenceType(pointee(autoType()))), diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index 6ddebcb..f675ca7 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -22,6 +22,7 @@ #include "NoRecursionCheck.h" #include "NonCopyableObjects.h" #include "NonPrivateMemberVariablesInClassesCheck.h" +#include "OverrideWithDifferentVisibilityCheck.h" #include "RedundantExpressionCheck.h" #include "StaticAssertCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" @@ -81,6 +82,8 @@ public: "misc-use-anonymous-namespace"); CheckFactories.registerCheck<UseInternalLinkageCheck>( "misc-use-internal-linkage"); + CheckFactories.registerCheck<OverrideWithDifferentVisibilityCheck>( + "misc-override-with-different-visibility"); } }; diff --git a/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp b/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp index 0cdd48c..bb64a56 100644 --- a/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp @@ -19,13 +19,13 @@ void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) { pointee(anyOf(isConstQualified(), ignoringParens(functionType())))))); Finder->addMatcher( - valueDecl(hasType(qualType( - isConstQualified(), - elaboratedType(namesType(typedefType(hasDeclaration( - anyOf(typedefDecl(NonConstAndNonFunctionPointerType) - .bind("typedef"), - typeAliasDecl(NonConstAndNonFunctionPointerType) - .bind("typeAlias"))))))))) + valueDecl( + hasType(qualType(isConstQualified(), + typedefType(hasDeclaration(anyOf( + typedefDecl(NonConstAndNonFunctionPointerType) + .bind("typedef"), + typeAliasDecl(NonConstAndNonFunctionPointerType) + .bind("typeAlias"))))))) .bind("decl"), this); } diff --git a/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp b/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp index 40808aa..2837f40 100644 --- a/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/NewDeleteOverloadsCheck.cpp @@ -59,7 +59,9 @@ AST_MATCHER(FunctionDecl, isPlacementOverload) { return true; } -OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) { +} // namespace + +static OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) { switch (FD->getOverloadedOperator()) { default: break; @@ -75,7 +77,7 @@ OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) { llvm_unreachable("Not an overloaded allocation operator"); } -const char *getOperatorName(OverloadedOperatorKind K) { +static const char *getOperatorName(OverloadedOperatorKind K) { switch (K) { default: break; @@ -91,13 +93,14 @@ const char *getOperatorName(OverloadedOperatorKind K) { llvm_unreachable("Not an overloaded allocation operator"); } -bool areCorrespondingOverloads(const FunctionDecl *LHS, - const FunctionDecl *RHS) { +static bool areCorrespondingOverloads(const FunctionDecl *LHS, + const FunctionDecl *RHS) { return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS); } -bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD, - const CXXRecordDecl *RD = nullptr) { +static bool +hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD, + const CXXRecordDecl *RD = nullptr) { if (RD) { // Check the methods in the given class and accessible to derived classes. for (const auto *BMD : RD->methods()) @@ -124,8 +127,6 @@ bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD, return false; } -} // anonymous namespace - void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) { // Match all operator new and operator delete overloads (including the array // forms). Do not match implicit operators, placement operators, or diff --git a/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp new file mode 100644 index 0000000..2fe0bcf --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp @@ -0,0 +1,150 @@ +//===--- OverrideWithDifferentVisibilityCheck.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 "OverrideWithDifferentVisibilityCheck.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; +using namespace clang; + +namespace { + +AST_MATCHER(NamedDecl, isOperatorDecl) { + const DeclarationName::NameKind NK = Node.getDeclName().getNameKind(); + return NK != DeclarationName::Identifier && + NK != DeclarationName::CXXConstructorName && + NK != DeclarationName::CXXDestructorName; +} + +} // namespace + +namespace clang::tidy { + +template <> +struct OptionEnumMapping< + misc::OverrideWithDifferentVisibilityCheck::ChangeKind> { + static llvm::ArrayRef<std::pair< + misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>> + getEnumMapping() { + static constexpr std::pair< + misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef> + Mapping[] = { + {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any, + "any"}, + {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening, + "widening"}, + {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing, + "narrowing"}, + }; + return {Mapping}; + } +}; + +namespace misc { + +OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + DetectVisibilityChange( + Options.get("DisallowedVisibilityChange", ChangeKind::Any)), + CheckDestructors(Options.get("CheckDestructors", false)), + CheckOperators(Options.get("CheckOperators", false)), + IgnoredFunctions(utils::options::parseStringList( + Options.get("IgnoredFunctions", ""))) {} + +void OverrideWithDifferentVisibilityCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange); + Options.store(Opts, "CheckDestructors", CheckDestructors); + Options.store(Opts, "CheckOperators", CheckOperators); + Options.store(Opts, "IgnoredFunctions", + utils::options::serializeStringList(IgnoredFunctions)); +} + +void OverrideWithDifferentVisibilityCheck::registerMatchers( + MatchFinder *Finder) { + const auto IgnoredDecl = + namedDecl(matchers::matchesAnyListedName(IgnoredFunctions)); + const auto FilterDestructors = + CheckDestructors ? decl() : decl(unless(cxxDestructorDecl())); + const auto FilterOperators = + CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl())); + Finder->addMatcher( + cxxMethodDecl( + isVirtual(), FilterDestructors, FilterOperators, + ofClass( + cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")), + forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")), + unless(IgnoredDecl)) + .bind("base_func"))) + .bind("func"), + this); +} + +void OverrideWithDifferentVisibilityCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *const MatchedFunction = + Result.Nodes.getNodeAs<FunctionDecl>("func"); + if (!MatchedFunction->isCanonicalDecl()) + return; + + const auto *const ParentClass = + Result.Nodes.getNodeAs<CXXRecordDecl>("class"); + const auto *const BaseClass = Result.Nodes.getNodeAs<CXXRecordDecl>("base"); + CXXBasePaths Paths; + if (!ParentClass->isDerivedFrom(BaseClass, Paths)) + return; + + const auto *const OverriddenFunction = + Result.Nodes.getNodeAs<FunctionDecl>("base_func"); + const AccessSpecifier ActualAccess = MatchedFunction->getAccess(); + AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess(); + + const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr; + for (const CXXBasePath &Path : Paths) { + for (const CXXBasePathElement &Elem : Path) { + if (Elem.Base->getAccessSpecifier() > OverriddenAccess) { + OverriddenAccess = Elem.Base->getAccessSpecifier(); + InheritanceWithStrictVisibility = Elem.Base; + } + } + } + + if (ActualAccess != OverriddenAccess) { + if (DetectVisibilityChange == ChangeKind::Widening && + ActualAccess > OverriddenAccess) + return; + if (DetectVisibilityChange == ChangeKind::Narrowing && + ActualAccess < OverriddenAccess) + return; + + if (InheritanceWithStrictVisibility) { + diag(MatchedFunction->getLocation(), + "visibility of function %0 is changed from %1 (through %1 " + "inheritance of class %2) to %3") + << MatchedFunction << OverriddenAccess + << InheritanceWithStrictVisibility->getType() << ActualAccess; + diag(InheritanceWithStrictVisibility->getBeginLoc(), + "%0 is inherited as %1 here", DiagnosticIDs::Note) + << InheritanceWithStrictVisibility->getType() << OverriddenAccess; + } else { + diag(MatchedFunction->getLocation(), + "visibility of function %0 is changed from %1 in class %2 to %3") + << MatchedFunction << OverriddenAccess << BaseClass << ActualAccess; + } + diag(OverriddenFunction->getLocation(), "function declared here as %0", + DiagnosticIDs::Note) + << OverriddenFunction->getAccess(); + } +} + +} // namespace misc + +} // namespace clang::tidy diff --git a/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h new file mode 100644 index 0000000..1f5222d --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h @@ -0,0 +1,43 @@ +//===--- OverrideWithDifferentVisibilityCheck.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_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::misc { + +/// Finds virtual function overrides with different visibility than the function +/// in the base class. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc/override-with-different-visibility.html +class OverrideWithDifferentVisibilityCheck : public ClangTidyCheck { +public: + enum class ChangeKind { Any, Widening, Narrowing }; + + OverrideWithDifferentVisibilityCheck(StringRef Name, + ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + +private: + ChangeKind DetectVisibilityChange; + bool CheckDestructors; + bool CheckOperators; + std::vector<llvm::StringRef> IgnoredFunctions; +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp index 9b2af2a..107eda2 100644 --- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp @@ -45,14 +45,6 @@ static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) { return Value < Result; } -static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left, - const NestedNameSpecifier *Right) { - llvm::FoldingSetNodeID LeftID, RightID; - Left->Profile(LeftID); - Right->Profile(RightID); - return LeftID == RightID; -} - static bool areEquivalentExpr(const Expr *Left, const Expr *Right) { if (!Left || !Right) return !Left && !Right; @@ -104,9 +96,8 @@ static bool areEquivalentExpr(const Expr *Left, const Expr *Right) { if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() != cast<DependentScopeDeclRefExpr>(Right)->getDeclName()) return false; - return areEquivalentNameSpecifier( - cast<DependentScopeDeclRefExpr>(Left)->getQualifier(), - cast<DependentScopeDeclRefExpr>(Right)->getQualifier()); + return cast<DependentScopeDeclRefExpr>(Left)->getQualifier() == + cast<DependentScopeDeclRefExpr>(Right)->getQualifier(); case Stmt::DeclRefExprClass: return cast<DeclRefExpr>(Left)->getDecl() == cast<DeclRefExpr>(Right)->getDecl(); diff --git a/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp index 3fdaf92..8200239 100644 --- a/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp @@ -29,11 +29,13 @@ void UnconventionalAssignOperatorCheck::registerMatchers( const auto HasGoodReturnType = cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee( unless(isConstQualified()), - anyOf(autoType(), hasDeclaration(equalsBoundNode("class")))))))); + anyOf(autoType(), + hasDeclaration(declaresSameEntityAsBoundNode("class")))))))); const auto IsSelf = qualType(hasCanonicalType( - anyOf(hasDeclaration(equalsBoundNode("class")), - referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))))); + anyOf(hasDeclaration(declaresSameEntityAsBoundNode("class")), + referenceType(pointee( + hasDeclaration(declaresSameEntityAsBoundNode("class"))))))); const auto IsAssign = cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class"))) diff --git a/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp index 86992cd..4fa679a 100644 --- a/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnusedAliasDeclsCheck.cpp @@ -35,12 +35,12 @@ void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) { } if (const auto *NestedName = - Result.Nodes.getNodeAs<NestedNameSpecifier>("nns")) { - if (const auto *AliasDecl = dyn_cast_if_present<NamespaceAliasDecl>( - NestedName->getAsNamespace())) { + Result.Nodes.getNodeAs<NestedNameSpecifier>("nns"); + NestedName && + NestedName->getKind() == NestedNameSpecifier::Kind::Namespace) + if (const auto *AliasDecl = dyn_cast<NamespaceAliasDecl>( + NestedName->getAsNamespaceAndPrefix().Namespace)) FoundDecls[AliasDecl] = CharSourceRange(); - } - } } void UnusedAliasDeclsCheck::onEndOfTranslationUnit() { diff --git a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp index d5c5fa3..4943207 100644 --- a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -71,11 +71,7 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) { templateArgument().bind("used")))), this); Finder->addMatcher(userDefinedLiteral().bind("used"), this); - Finder->addMatcher( - loc(elaboratedType(unless(hasQualifier(nestedNameSpecifier())), - hasUnqualifiedDesugaredType( - type(asTagDecl(tagDecl().bind("used")))))), - this); + Finder->addMatcher(loc(asTagDecl(tagDecl().bind("used"))), this); // Cases where we can identify the UsingShadowDecl directly, rather than // just its target. // FIXME: cover more cases in this way, as the AST supports it. @@ -135,8 +131,8 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { return; } if (const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) { - if (const auto *ET = ECD->getType()->getAs<EnumType>()) - removeFromFoundDecls(ET->getDecl()); + if (const auto *ET = ECD->getType()->getAsCanonical<EnumType>()) + removeFromFoundDecls(ET->getOriginalDecl()); } }; // We rely on the fact that the clang AST is walked in order, usages are only diff --git a/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp index f6404db..5d11843 100644 --- a/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp @@ -152,14 +152,14 @@ void ConcatNestedNamespacesCheck::reportDiagnostic( ConcatNameSpace.append("::"); } - for (SourceRange const &Front : Fronts) + for (const SourceRange &Front : Fronts) DB << FixItHint::CreateRemoval(Front); DB << FixItHint::CreateReplacement( Namespaces.back().getReplacedNamespaceFrontRange(), ConcatNameSpace); if (LastRBrace != Namespaces.back().getDefaultNamespaceBackRange()) DB << FixItHint::CreateReplacement(LastRBrace, ("} // " + ConcatNameSpace).str()); - for (SourceRange const &Back : llvm::reverse(Backs)) + for (const SourceRange &Back : llvm::reverse(Backs)) DB << FixItHint::CreateRemoval(Back); } diff --git a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp index f22f48d..2aca610 100644 --- a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -29,8 +29,7 @@ static std::optional<const char *> getReplacementType(StringRef Type) { void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) { auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl"); - auto IoStateType = - qualType(hasDeclaration(IoStateDecl), unless(elaboratedType())); + auto IoStateType = typedefType(hasDeclaration(IoStateDecl)); Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this); } @@ -43,12 +42,14 @@ void DeprecatedIosBaseAliasesCheck::check( StringRef TypeName = Typedef->getName(); auto Replacement = getReplacementType(TypeName); - const auto *TL = Result.Nodes.getNodeAs<TypeLoc>("TypeLoc"); - SourceLocation IoStateLoc = TL->getBeginLoc(); + TypeLoc TL = *Result.Nodes.getNodeAs<TypeLoc>("TypeLoc"); + if (auto QTL = TL.getAs<QualifiedTypeLoc>()) + TL = QTL.getUnqualifiedLoc(); + SourceLocation IoStateLoc = TL.castAs<TypedefTypeLoc>().getNameLoc(); // Do not generate fixits for matches depending on template arguments and // macro expansions. - bool Fix = Replacement && !TL->getType()->isDependentType(); + bool Fix = Replacement && !TL.getType()->isDependentType(); if (IoStateLoc.isMacroID()) { IoStateLoc = SM.getSpellingLoc(IoStateLoc); Fix = false; diff --git a/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp index c2db858..118e96a 100644 --- a/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MacroToEnumCheck.cpp @@ -395,16 +395,12 @@ void MacroToEnumCallbacks::Endif(SourceLocation Loc, SourceLocation IfLoc) { --CurrentFile->ConditionScopes; } -namespace { - template <size_t N> -bool textEquals(const char (&Needle)[N], const char *HayStack) { +static bool textEquals(const char (&Needle)[N], const char *HayStack) { return StringRef{HayStack, N - 1} == Needle; } -template <size_t N> size_t len(const char (&)[N]) { return N - 1; } - -} // namespace +template <size_t N> static size_t len(const char (&)[N]) { return N - 1; } void MacroToEnumCallbacks::PragmaDirective(SourceLocation Loc, PragmaIntroducerKind Introducer) { diff --git a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp index deef358..ac8476b 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -16,14 +16,14 @@ using namespace clang::ast_matchers; namespace clang::tidy::modernize { -namespace { - -constexpr char ConstructorCall[] = "constructorCall"; -constexpr char ResetCall[] = "resetCall"; -constexpr char NewExpression[] = "newExpression"; - -std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM, - const LangOptions &Lang) { +static constexpr char ConstructorCall[] = "constructorCall"; +static constexpr char DirectVar[] = "directVar"; +static constexpr char ResetCall[] = "resetCall"; +static constexpr char NewExpression[] = "newExpression"; + +static std::string getNewExprName(const CXXNewExpr *NewExpr, + const SourceManager &SM, + const LangOptions &Lang) { StringRef WrittenName = Lexer::getSourceText( CharSourceRange::getTokenRange( NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()), @@ -34,8 +34,6 @@ std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM, return WrittenName.str(); } -} // namespace - const char MakeSmartPtrCheck::PointerType[] = "pointerType"; MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context, @@ -81,18 +79,18 @@ void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { auto IsPlacement = hasAnyPlacementArg(anything()); Finder->addMatcher( - traverse( - TK_AsIs, - cxxBindTemporaryExpr(has(ignoringParenImpCasts( - cxxConstructExpr( - hasType(getSmartPointerTypeMatcher()), argumentCountIs(1), - hasArgument( - 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( - equalsBoundNode(PointerType))))), - CanCallCtor, unless(IsPlacement)) - .bind(NewExpression)), - unless(isInTemplateInstantiation())) - .bind(ConstructorCall))))), + traverse(TK_AsIs, + cxxConstructExpr( + anyOf(hasParent(cxxBindTemporaryExpr()), + hasParent(varDecl().bind(DirectVar))), + hasType(getSmartPointerTypeMatcher()), argumentCountIs(1), + hasArgument( + 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( + equalsBoundNode(PointerType))))), + CanCallCtor, unless(IsPlacement)) + .bind(NewExpression)), + unless(isInTemplateInstantiation())) + .bind(ConstructorCall)), this); Finder->addMatcher( @@ -119,6 +117,7 @@ void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall); + const auto *DVar = Result.Nodes.getNodeAs<VarDecl>(DirectVar); const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall); const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType); const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression); @@ -141,13 +140,14 @@ void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) { if (!Initializes && IgnoreDefaultInitialization) return; if (Construct) - checkConstruct(SM, Result.Context, Construct, Type, New); + checkConstruct(SM, Result.Context, Construct, DVar, Type, New); else if (Reset) checkReset(SM, Result.Context, Reset, New); } void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx, const CXXConstructExpr *Construct, + const VarDecl *DVar, const QualType *Type, const CXXNewExpr *New) { SourceLocation ConstructCallStart = Construct->getExprLoc(); @@ -190,9 +190,14 @@ void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx, ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle); } + std::string FinalMakeSmartPtrFunctionName = MakeSmartPtrFunctionName.str(); + if (DVar) + FinalMakeSmartPtrFunctionName = + ExprStr.str() + " = " + MakeSmartPtrFunctionName.str(); + Diag << FixItHint::CreateReplacement( CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd), - MakeSmartPtrFunctionName); + FinalMakeSmartPtrFunctionName); // If the smart_ptr is built with brace enclosed direct initialization, use // parenthesis instead. diff --git a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.h b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.h index 02374dc..e2f9abe 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.h @@ -51,8 +51,8 @@ private: const bool IgnoreDefaultInitialization; void checkConstruct(SourceManager &SM, ASTContext *Ctx, - const CXXConstructExpr *Construct, const QualType *Type, - const CXXNewExpr *New); + const CXXConstructExpr *Construct, const VarDecl *DVar, + const QualType *Type, const CXXNewExpr *New); void checkReset(SourceManager &SM, ASTContext *Ctx, const CXXMemberCallExpr *Reset, const CXXNewExpr *New); diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp index 1e271dfa..a54d072 100644 --- a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp @@ -77,8 +77,7 @@ AST_MATCHER_P(CXXRecordDecl, isMoveConstructibleInBoundCXXRecordDecl, StringRef, static TypeMatcher notTemplateSpecConstRefType() { return lValueReferenceType( - pointee(unless(elaboratedType(namesType(templateSpecializationType()))), - isConstQualified())); + pointee(unless(templateSpecializationType()), isConstQualified())); } static TypeMatcher nonConstValueType() { diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp index 24674a4..0c9e909 100644 --- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp @@ -19,9 +19,7 @@ using namespace clang::ast_matchers; namespace clang::tidy::modernize { -namespace { - -bool containsEscapes(StringRef HayStack, StringRef Escapes) { +static bool containsEscapes(StringRef HayStack, StringRef Escapes) { size_t BackSlash = HayStack.find('\\'); if (BackSlash == StringRef::npos) return false; @@ -35,16 +33,16 @@ bool containsEscapes(StringRef HayStack, StringRef Escapes) { return true; } -bool isRawStringLiteral(StringRef Text) { +static bool isRawStringLiteral(StringRef Text) { // Already a raw string literal if R comes before ". const size_t QuotePos = Text.find('"'); assert(QuotePos != StringRef::npos); return (QuotePos > 0) && (Text[QuotePos - 1] == 'R'); } -bool containsEscapedCharacters(const MatchFinder::MatchResult &Result, - const StringLiteral *Literal, - const CharsBitSet &DisallowedChars) { +static bool containsEscapedCharacters(const MatchFinder::MatchResult &Result, + const StringLiteral *Literal, + const CharsBitSet &DisallowedChars) { // FIXME: Handle L"", u8"", u"" and U"" literals. if (!Literal->isOrdinary()) return false; @@ -64,14 +62,12 @@ bool containsEscapedCharacters(const MatchFinder::MatchResult &Result, return containsEscapes(Text, R"('\"?x01)"); } -bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) { +static bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) { return Bytes.find(Delimiter.empty() ? std::string(R"lit()")lit") : (")" + Delimiter + R"(")")) != StringRef::npos; } -} // namespace - RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp index 1ad31d3..f2142b8 100644 --- a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp @@ -48,7 +48,7 @@ void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isInStdNamespace()); - auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); + auto AutoPtrType = hasCanonicalType(recordType(hasDeclaration(AutoPtrDecl))); // std::auto_ptr<int> a; // ^~~~~~~~~~~~~ @@ -58,11 +58,7 @@ void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) { // // std::auto_ptr<int> fn(std::auto_ptr<int>); // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ - Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType, - // Skip elaboratedType() as the named - // type will match soon thereafter. - unless(elaboratedType())))) - .bind(AutoPtrTokenId), + Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType))).bind(AutoPtrTokenId), this); // using std::auto_ptr; @@ -118,10 +114,13 @@ void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { } SourceLocation AutoPtrLoc; - if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { + if (const auto *PTL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { + auto TL = *PTL; + if (auto QTL = TL.getAs<QualifiedTypeLoc>()) + TL = QTL.getUnqualifiedLoc(); // std::auto_ptr<int> i; // ^ - if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>()) + if (auto Loc = TL.getAs<TemplateSpecializationTypeLoc>()) AutoPtrLoc = Loc.getTemplateNameLoc(); } else if (const auto *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { diff --git a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp index ff0b321..4721282 100644 --- a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp @@ -168,19 +168,6 @@ static DeclarationName getName(const DeclRefExpr &D) { return D.getDecl()->getDeclName(); } -static bool isNamedType(const ElaboratedTypeLoc &ETL) { - if (const auto *TFT = - ETL.getNamedTypeLoc().getTypePtr()->getAs<TypedefType>()) { - const TypedefNameDecl *Decl = TFT->getDecl(); - return Decl->getDeclName().isIdentifier() && Decl->getName() == "type"; - } - return false; -} - -static bool isNamedType(const DependentNameTypeLoc &DTL) { - return DTL.getTypePtr()->getIdentifier()->getName() == "type"; -} - namespace { AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES( DeclRefExpr, DependentScopeDeclRefExpr)) { @@ -188,25 +175,20 @@ AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES( return Ident && Ident->isStr("value"); } -AST_POLYMORPHIC_MATCHER(isType, - AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc, - DependentNameTypeLoc)) { - return Node.getBeginLoc().isValid() && isNamedType(Node); +AST_MATCHER(TypeLoc, isType) { + if (auto TL = Node.getAs<TypedefTypeLoc>()) { + const auto *TD = TL.getDecl(); + return TD->getDeclName().isIdentifier() && TD->getName() == "type"; + } + if (auto TL = Node.getAs<DependentNameTypeLoc>()) + return TL.getTypePtr()->getIdentifier()->getName() == "type"; + return false; } } // namespace static constexpr char Bind[] = ""; void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) { - const ast_matchers::internal::VariadicDynCastAllOfMatcher< - Stmt, - DependentScopeDeclRefExpr> - dependentScopeDeclRefExpr; // NOLINT(readability-identifier-naming) - const ast_matchers::internal::VariadicDynCastAllOfMatcher< - TypeLoc, - DependentNameTypeLoc> - dependentNameTypeLoc; // NOLINT(readability-identifier-naming) - // Only register matchers for trait<...>::value in c++17 mode. if (getLangOpts().CPlusPlus17) { Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr) @@ -214,10 +196,7 @@ void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) { .bind(Bind), this); } - Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc) - .with(isType()) - .bind(Bind), - this); + Finder->addMatcher(typeLoc(isType()).bind(Bind), this); } static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND, @@ -226,14 +205,11 @@ static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND, Set.contains(ND->getName()); } -static bool checkTemplatedDecl(const NestedNameSpecifier *NNS, +static bool checkTemplatedDecl(NestedNameSpecifier NNS, const llvm::StringSet<> &Set) { - if (!NNS) + if (NNS.getKind() != NestedNameSpecifier::Kind::Type) return false; - const Type *NNST = NNS->getAsType(); - if (!NNST) - return false; - const auto *TST = NNST->getAs<TemplateSpecializationType>(); + const auto *TST = NNS.getAsType()->getAs<TemplateSpecializationType>(); if (!TST) return false; if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) { @@ -250,8 +226,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) { auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc, SourceLocation EndLoc) { SourceLocation TemplateNameEndLoc; - if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); - !TSTL.isNull()) + if (auto TSTL = + QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>()) TemplateNameEndLoc = Lexer::getLocForEndOfToken( TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, Result.Context->getLangOpts()); @@ -274,8 +250,8 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) { SourceLocation EndLoc, SourceLocation TypenameLoc) { SourceLocation TemplateNameEndLoc; - if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); - !TSTL.isNull()) + if (auto TSTL = + QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>()) TemplateNameEndLoc = Lexer::getLocForEndOfToken( TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, Result.Context->getLangOpts()); @@ -301,23 +277,21 @@ void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) { if (!DRE->hasQualifier()) return; if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( - DRE->getQualifier()->getAsRecordDecl())) { + DRE->getQualifier().getAsRecordDecl())) { if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits)) EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc()); } return; } - if (const auto *ETL = Result.Nodes.getNodeAs<ElaboratedTypeLoc>(Bind)) { - const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc(); - const auto *NNS = QualLoc.getNestedNameSpecifier(); - if (!NNS) - return; + if (const auto *TL = Result.Nodes.getNodeAs<TypedefTypeLoc>(Bind)) { + const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc(); + NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier(); if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( - NNS->getAsRecordDecl())) { + NNS.getAsRecordDecl())) { if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits)) - EmitTypeWarning(ETL->getQualifierLoc(), ETL->getEndLoc(), - ETL->getElaboratedKeywordLoc()); + EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(), + TL->getElaboratedKeywordLoc()); } return; } diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp index f4b6308..aedfda8 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp @@ -186,16 +186,14 @@ TypeMatcher nestedIterator() { /// declarations and which name standard iterators for standard containers. TypeMatcher iteratorFromUsingDeclaration() { auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName())); - // Types resulting from using declarations are represented by elaboratedType. - return elaboratedType( - // Unwrap the nested name specifier to test for one of the standard - // containers. - hasQualifier(specifiesType(templateSpecializationType(hasDeclaration( - namedDecl(hasStdContainerName(), isInStdNamespace()))))), - // the named type is what comes after the final '::' in the type. It - // should name one of the standard iterator names. - namesType( - anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))); + // Unwrap the nested name specifier to test for one of the standard + // containers. + auto Qualifier = hasQualifier(specifiesType(templateSpecializationType( + hasDeclaration(namedDecl(hasStdContainerName(), isInStdNamespace()))))); + // the named type is what comes after the final '::' in the type. It should + // name one of the standard iterator names. + return anyOf(typedefType(HasIteratorDecl, Qualifier), + recordType(HasIteratorDecl, Qualifier)); } /// This matcher returns declaration statements that contain variable @@ -336,14 +334,14 @@ void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) { static void ignoreTypeLocClasses( TypeLoc &Loc, - std::initializer_list<TypeLoc::TypeLocClass> const &LocClasses) { + const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) { while (llvm::is_contained(LocClasses, Loc.getTypeLocClass())) Loc = Loc.getNextTypeLoc(); } static bool isMultiLevelPointerToTypeLocClasses( TypeLoc Loc, - std::initializer_list<TypeLoc::TypeLocClass> const &LocClasses) { + const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) { ignoreTypeLocClasses(Loc, {TypeLoc::Paren, TypeLoc::Qualified}); TypeLoc::TypeLocClass TLC = Loc.getTypeLocClass(); if (TLC != TypeLoc::Pointer && TLC != TypeLoc::MemberPointer) diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp index e9b96c4..c4a64be 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp @@ -8,6 +8,7 @@ #include "UseConstraintsCheck.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" @@ -60,9 +61,11 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) { Keyword != ElaboratedTypeKeyword::None)) { return std::nullopt; } - TheType = Dep.getQualifierLoc().getTypeLoc(); + TheType = Dep.getQualifierLoc().getAsTypeLoc(); if (TheType.isNull()) return std::nullopt; + } else { + return std::nullopt; } if (const auto SpecializationLoc = @@ -78,6 +81,13 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) { if (!TD || TD->getName() != "enable_if") return std::nullopt; + assert(!TD->getTemplateParameters()->empty() && + "found template with no template parameters?"); + const auto *FirstParam = dyn_cast<NonTypeTemplateParmDecl>( + TD->getTemplateParameters()->getParam(0)); + if (!FirstParam || !FirstParam->getType()->isBooleanType()) + return std::nullopt; + int NumArgs = SpecializationLoc.getNumArgs(); if (NumArgs != 1 && NumArgs != 2) return std::nullopt; @@ -89,9 +99,6 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) { static std::optional<TemplateSpecializationTypeLoc> matchEnableIfSpecializationImplTrait(TypeLoc TheType) { - if (const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>()) - TheType = Elaborated.getNamedTypeLoc(); - if (const auto SpecializationLoc = TheType.getAs<TemplateSpecializationTypeLoc>()) { @@ -108,6 +115,13 @@ matchEnableIfSpecializationImplTrait(TypeLoc TheType) { if (!Specialization->isTypeAlias()) return std::nullopt; + assert(!TD->getTemplateParameters()->empty() && + "found template with no template parameters?"); + const auto *FirstParam = dyn_cast<NonTypeTemplateParmDecl>( + TD->getTemplateParameters()->getParam(0)); + if (!FirstParam || !FirstParam->getType()->isBooleanType()) + return std::nullopt; + if (const auto *AliasedType = dyn_cast<DependentNameType>(Specialization->getAliasedType())) { ElaboratedTypeKeyword Keyword = AliasedType->getKeyword(); diff --git a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp index aaf24ea..ee49d8a 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp @@ -164,10 +164,10 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { auto CallEmplacy = cxxMemberCallExpr( hasDeclaration( functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))), - on(hasTypeOrPointeeType(hasCanonicalType(hasDeclaration( - has(typedefNameDecl(hasName("value_type"), - hasType(type(hasUnqualifiedDesugaredType( - recordType().bind("value_type"))))))))))); + on(hasTypeOrPointeeType( + hasCanonicalType(hasDeclaration(has(typedefNameDecl( + hasName("value_type"), + hasType(hasCanonicalType(recordType().bind("value_type")))))))))); // We can't replace push_backs of smart pointer because // if emplacement fails (f.e. bad_alloc in vector) we will have leak of @@ -241,17 +241,16 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { auto HasConstructExprWithValueTypeType = has(ignoringImplicit(cxxConstructExpr( - SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType( - type(equalsBoundNode("value_type")))))))); - - auto HasBracedInitListWithValueTypeType = - anyOf(allOf(HasConstructInitListExpr, - has(initListExpr(hasType(type(hasUnqualifiedDesugaredType( - type(equalsBoundNode("value_type")))))))), - has(cxxBindTemporaryExpr( - HasConstructInitListExpr, - has(initListExpr(hasType(type(hasUnqualifiedDesugaredType( - type(equalsBoundNode("value_type")))))))))); + SoughtConstructExpr, + hasType(hasCanonicalType(type(equalsBoundNode("value_type"))))))); + + auto HasBracedInitListWithValueTypeType = anyOf( + allOf(HasConstructInitListExpr, + has(initListExpr(hasType( + hasCanonicalType(type(equalsBoundNode("value_type"))))))), + has(cxxBindTemporaryExpr(HasConstructInitListExpr, + has(initListExpr(hasType(hasCanonicalType( + type(equalsBoundNode("value_type"))))))))); auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument( materializeTemporaryExpr( @@ -289,19 +288,17 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { this); Finder->addMatcher( - traverse( - TK_AsIs, - cxxMemberCallExpr( - CallEmplacy, - on(hasType(cxxRecordDecl(has(typedefNameDecl( - hasName("value_type"), - hasType(type( - hasUnqualifiedDesugaredType(recordType(hasDeclaration( - cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>( - TupleTypes.begin(), TupleTypes.end()))))))))))))), - has(MakeTuple), hasSameNumArgsAsDeclNumParams(), - unless(isInTemplateInstantiation())) - .bind("emplacy_call")), + traverse(TK_AsIs, + cxxMemberCallExpr( + CallEmplacy, + on(hasType(cxxRecordDecl(has(typedefNameDecl( + hasName("value_type"), + hasType(hasCanonicalType(recordType(hasDeclaration( + cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>( + TupleTypes.begin(), TupleTypes.end())))))))))))), + has(MakeTuple), hasSameNumArgsAsDeclNumParams(), + unless(isInTemplateInstantiation())) + .bind("emplacy_call")), this); } diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp index 52e9a9f..c74db0e 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp @@ -28,8 +28,8 @@ static bool isLockGuardDecl(const NamedDecl *Decl) { } static bool isLockGuard(const QualType &Type) { - if (const auto *Record = Type->getAs<RecordType>()) - if (const RecordDecl *Decl = Record->getDecl()) + if (const auto *Record = Type->getAsCanonical<RecordType>()) + if (const RecordDecl *Decl = Record->getOriginalDecl()) return isLockGuardDecl(Decl); if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>()) @@ -89,17 +89,6 @@ findLocksInCompoundStmt(const CompoundStmt *Block, return LockGuardGroups; } -static TemplateSpecializationTypeLoc -getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) { - const TypeLoc Loc = SourceInfo->getTypeLoc(); - - const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>(); - if (!ElaboratedLoc) - return {}; - - return ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>(); -} - // Find the exact source range of the 'lock_guard' token static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) { const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc(); @@ -110,7 +99,7 @@ static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) { // Find the exact source range of the 'lock_guard' name token static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) { const TemplateSpecializationTypeLoc TemplateLoc = - getTemplateLockGuardTypeLoc(SourceInfo); + SourceInfo->getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); if (!TemplateLoc) return {}; @@ -136,11 +125,11 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) { const auto LockGuardClassDecl = namedDecl(hasName("lock_guard"), isInStdNamespace()); - const auto LockGuardType = qualType(anyOf( - hasUnqualifiedDesugaredType( - recordType(hasDeclaration(LockGuardClassDecl))), - elaboratedType(namesType(hasUnqualifiedDesugaredType( - templateSpecializationType(hasDeclaration(LockGuardClassDecl))))))); + const auto LockGuardType = + qualType(anyOf(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(LockGuardClassDecl))), + hasUnqualifiedDesugaredType(templateSpecializationType( + hasDeclaration(LockGuardClassDecl))))); const auto LockVarDecl = varDecl(hasType(LockGuardType)); @@ -165,18 +154,16 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) { if (WarnOnUsingAndTypedef) { // Match 'typedef std::lock_guard<std::mutex> Lock' Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()), - hasUnderlyingType(LockGuardType)) + hasType(hasUnderlyingType(LockGuardType))) .bind("lock-guard-typedef"), this); // Match 'using Lock = std::lock_guard<std::mutex>' - Finder->addMatcher( - typeAliasDecl( - unless(isExpansionInSystemHeader()), - hasType(elaboratedType(namesType(templateSpecializationType( - hasDeclaration(LockGuardClassDecl)))))) - .bind("lock-guard-using-alias"), - this); + Finder->addMatcher(typeAliasDecl(unless(isExpansionInSystemHeader()), + hasType(templateSpecializationType( + hasDeclaration(LockGuardClassDecl)))) + .bind("lock-guard-using-alias"), + this); // Match 'using std::lock_guard' Finder->addMatcher( @@ -288,8 +275,8 @@ void UseScopedLockCheck::diagOnSourceInfo( const ast_matchers::MatchFinder::MatchResult &Result) { const TypeLoc TL = LockGuardSourceInfo->getTypeLoc(); - if (const auto ElaboratedTL = TL.getAs<ElaboratedTypeLoc>()) { - auto Diag = diag(ElaboratedTL.getBeginLoc(), UseScopedLockMessage); + if (const auto TTL = TL.getAs<TemplateSpecializationTypeLoc>()) { + auto Diag = diag(TTL.getBeginLoc(), UseScopedLockMessage); const SourceRange LockGuardRange = getLockGuardNameRange(LockGuardSourceInfo); diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp index ced4825..82f6409 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp @@ -64,66 +64,65 @@ public: return false; } - bool TraverseTypeLoc(TypeLoc TL, bool Elaborated = false) { + bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) { if (TL.isNull()) return true; - if (!Elaborated) { - switch (TL.getTypeLocClass()) { - case TypeLoc::Record: - if (visitUnqualName( - TL.getAs<RecordTypeLoc>().getTypePtr()->getDecl()->getName())) - return false; + switch (TL.getTypeLocClass()) { + case TypeLoc::InjectedClassName: + case TypeLoc::Record: + case TypeLoc::Enum: { + auto TTL = TL.getAs<TagTypeLoc>(); + const auto *T = TTL.getTypePtr(); + if (T->getKeyword() != ElaboratedTypeKeyword::None || + TTL.getQualifierLoc()) break; - case TypeLoc::Enum: - if (visitUnqualName( - TL.getAs<EnumTypeLoc>().getTypePtr()->getDecl()->getName())) - return false; - break; - case TypeLoc::TemplateSpecialization: - if (visitUnqualName(TL.getAs<TemplateSpecializationTypeLoc>() - .getTypePtr() - ->getTemplateName() - .getAsTemplateDecl() - ->getName())) - return false; - break; - case TypeLoc::Typedef: - if (visitUnqualName( - TL.getAs<TypedefTypeLoc>().getTypePtr()->getDecl()->getName())) - return false; + if (visitUnqualName(T->getOriginalDecl()->getName())) + return false; + break; + } + case TypeLoc::TemplateSpecialization: { + auto TTL = TL.getAs<TemplateSpecializationTypeLoc>(); + const auto *T = TTL.getTypePtr(); + if (T->getKeyword() != ElaboratedTypeKeyword::None || + TTL.getQualifierLoc()) break; - case TypeLoc::Using: - if (visitUnqualName(TL.getAs<UsingTypeLoc>() - .getTypePtr() - ->getFoundDecl() - ->getName())) - return false; + if (visitUnqualName(T->getTemplateName().getAsTemplateDecl()->getName())) + return false; + break; + } + case TypeLoc::Typedef: { + auto TTL = TL.getAs<TypedefTypeLoc>(); + const auto *T = TTL.getTypePtr(); + if (T->getKeyword() != ElaboratedTypeKeyword::None || + TTL.getQualifierLoc()) break; - default: + if (visitUnqualName(T->getDecl()->getName())) + return false; + break; + } + case TypeLoc::Using: { + auto TTL = TL.getAs<UsingTypeLoc>(); + const auto *T = TTL.getTypePtr(); + if (T->getKeyword() != ElaboratedTypeKeyword::None || + TTL.getQualifierLoc()) break; - } + if (visitUnqualName(T->getDecl()->getName())) + return false; + break; + } + default: + break; } - return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(TL); + return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc( + TL, TraverseQualifier); } // Replace the base method in order to call our own // TraverseTypeLoc(). - bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) { - return TraverseTypeLoc(TL.getUnqualifiedLoc()); - } - - // Replace the base version to inform TraverseTypeLoc that the type is - // elaborated. - bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) { - if (TL.getQualifierLoc() && - !TraverseNestedNameSpecifierLoc(TL.getQualifierLoc())) - return false; - const auto *T = TL.getTypePtr(); - return TraverseTypeLoc(TL.getNamedTypeLoc(), - T->getKeyword() != ElaboratedTypeKeyword::None || - T->getQualifier()); + bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL, bool TraverseQualifier) { + return TraverseTypeLoc(TL.getUnqualifiedLoc(), TraverseQualifier); } bool VisitDeclRefExpr(DeclRefExpr *S) { diff --git a/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp index a053c07..2373a26 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp @@ -37,15 +37,13 @@ void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) { // Non-transparent functor mentioned as a template parameter. FIXIT. Finder->addMatcher( - loc(qualType( - unless(elaboratedType()), - hasDeclaration(classTemplateSpecializationDecl( - unless(hasAnyTemplateArgument(templateArgument(refersToType( - qualType(pointsTo(qualType(isAnyCharacter()))))))), - hasAnyTemplateArgument( - templateArgument(refersToType(qualType(hasDeclaration( - TransparentFunctors)))) - .bind("Functor")))))) + loc(qualType(hasDeclaration(classTemplateSpecializationDecl( + unless(hasAnyTemplateArgument(templateArgument(refersToType( + qualType(pointsTo(qualType(isAnyCharacter()))))))), + hasAnyTemplateArgument( + templateArgument(refersToType(qualType( + hasDeclaration(TransparentFunctors)))) + .bind("Functor")))))) .bind("FunctorParentLoc"), this); diff --git a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp index bd9bdd1..8e4ed41 100644 --- a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp +++ b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp @@ -29,12 +29,13 @@ using namespace clang::ast_matchers; namespace clang::tidy::objc { -namespace { static constexpr StringRef WeakText = "__weak"; static constexpr StringRef StrongText = "__strong"; static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained"; +namespace { + /// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference /// Objective-C object (or block) variables or fields whose object lifetimes /// are not __unsafe_unretained. @@ -49,6 +50,8 @@ AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime, QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone; } +} // namespace + static std::optional<FixItHint> fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range, StringRef Ownership) { @@ -93,8 +96,6 @@ fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM, return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained "); } -} // namespace - void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( traverse( diff --git a/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp index ffbdb02..01ee4d5 100644 --- a/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp +++ b/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp @@ -27,11 +27,14 @@ enum NamingStyle { CategoryProperty = 2, }; +} // namespace + /// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to /// 'camelCase' or 'abc_camelCase'. For other cases the users need to /// come up with a proper name by their own. /// FIXME: provide fix for snake_case to snakeCase -FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) { +static FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, + NamingStyle Style) { auto Name = Decl->getName(); auto NewName = Decl->getName().str(); size_t Index = 0; @@ -50,7 +53,7 @@ FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) { return {}; } -std::string validPropertyNameRegex(bool UsedInMatcher) { +static std::string validPropertyNameRegex(bool UsedInMatcher) { // Allow any of these names: // foo // fooBar @@ -72,13 +75,13 @@ std::string validPropertyNameRegex(bool UsedInMatcher) { return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$"; } -bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) { +static bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) { auto RegexExp = llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$"); return RegexExp.match(PropertyName); } -bool prefixedPropertyNameValid(llvm::StringRef PropertyName) { +static bool prefixedPropertyNameValid(llvm::StringRef PropertyName) { size_t Start = PropertyName.find_first_of('_'); assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size()); auto Prefix = PropertyName.substr(0, Start); @@ -88,7 +91,6 @@ bool prefixedPropertyNameValid(llvm::StringRef PropertyName) { auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false))); return RegexExp.match(PropertyName.substr(Start + 1)); } -} // namespace void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(objcPropertyDecl( diff --git a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp index 7022e9d..1c01899 100644 --- a/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp @@ -42,11 +42,11 @@ void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) { // A matcher for a `DstT::DstT(const Src&)` where DstT also has a // `DstT::DstT(Src&&)`. const auto LValueRefCtor = cxxConstructorDecl( - hasParameter(0, - hasType(lValueReferenceType(pointee(type().bind("SrcT"))))), + hasParameter(0, hasType(hasCanonicalType( + lValueReferenceType(pointee(type().bind("SrcT")))))), ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl( - hasParameter(0, hasType(rValueReferenceType( - pointee(type(equalsBoundNode("SrcT"))))))))))); + hasParameter(0, hasType(hasCanonicalType(rValueReferenceType( + pointee(type(equalsBoundNode("SrcT")))))))))))); // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an // instantiation of `template <typename U> DstT::DstT(U&&)`. diff --git a/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp b/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp index 120f7fb..c413090 100644 --- a/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp +++ b/clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp @@ -17,7 +17,6 @@ #include <optional> namespace clang::tidy::performance { -namespace { using namespace ::clang::ast_matchers; using llvm::StringRef; @@ -30,8 +29,8 @@ static constexpr StringRef MethodDeclId = "methodDecl"; static constexpr StringRef FunctionDeclId = "functionDecl"; static constexpr StringRef OldVarDeclId = "oldVarDecl"; -void recordFixes(const VarDecl &Var, ASTContext &Context, - DiagnosticBuilder &Diagnostic) { +static void recordFixes(const VarDecl &Var, ASTContext &Context, + DiagnosticBuilder &Diagnostic) { Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context); if (!Var.getType().isLocalConstQualified()) { if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl( @@ -40,8 +39,8 @@ void recordFixes(const VarDecl &Var, ASTContext &Context, } } -std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc, - SourceManager &SM) { +static std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc, + SourceManager &SM) { bool Invalid = false; const char *TextAfter = SM.getCharacterData(Loc, &Invalid); if (Invalid) { @@ -51,8 +50,8 @@ std::optional<SourceLocation> firstLocAfterNewLine(SourceLocation Loc, return Loc.getLocWithOffset(TextAfter[Offset] == '\0' ? Offset : Offset + 1); } -void recordRemoval(const DeclStmt &Stmt, ASTContext &Context, - DiagnosticBuilder &Diagnostic) { +static void recordRemoval(const DeclStmt &Stmt, ASTContext &Context, + DiagnosticBuilder &Diagnostic) { auto &SM = Context.getSourceManager(); // Attempt to remove trailing comments as well. auto Tok = utils::lexer::findNextTokenSkippingComments(Stmt.getEndLoc(), SM, @@ -74,6 +73,8 @@ void recordRemoval(const DeclStmt &Stmt, ASTContext &Context, } } +namespace { + AST_MATCHER_FUNCTION_P(StatementMatcher, isRefReturningMethodCallWithConstOverloads, std::vector<StringRef>, ExcludedContainerTypes) { @@ -130,6 +131,8 @@ AST_MATCHER_FUNCTION_P(StatementMatcher, initializerReturnsReferenceToConst, hasUnaryOperand(OldVarDeclRef))))); } +} // namespace + // This checks that the variable itself is only used as const, and also makes // sure that it does not reference another variable that could be modified in // the BlockStmt. It does this by checking the following: @@ -180,13 +183,13 @@ static bool isInitializingVariableImmutable( return false; } -bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt, - ASTContext &Context) { +static bool isVariableUnused(const VarDecl &Var, const Stmt &BlockStmt, + ASTContext &Context) { return allDeclRefExprs(Var, BlockStmt, Context).empty(); } -const SubstTemplateTypeParmType *getSubstitutedType(const QualType &Type, - ASTContext &Context) { +static const SubstTemplateTypeParmType * +getSubstitutedType(const QualType &Type, ASTContext &Context) { auto Matches = match( qualType(anyOf(substTemplateTypeParmType().bind("subst"), hasDescendant(substTemplateTypeParmType().bind("subst")))), @@ -194,9 +197,9 @@ const SubstTemplateTypeParmType *getSubstitutedType(const QualType &Type, return selectFirst<SubstTemplateTypeParmType>("subst", Matches); } -bool differentReplacedTemplateParams(const QualType &VarType, - const QualType &InitializerType, - ASTContext &Context) { +static bool differentReplacedTemplateParams(const QualType &VarType, + const QualType &InitializerType, + ASTContext &Context) { if (const SubstTemplateTypeParmType *VarTmplType = getSubstitutedType(VarType, Context)) { if (const SubstTemplateTypeParmType *InitializerTmplType = @@ -212,8 +215,8 @@ bool differentReplacedTemplateParams(const QualType &VarType, return false; } -QualType constructorArgumentType(const VarDecl *OldVar, - const BoundNodes &Nodes) { +static QualType constructorArgumentType(const VarDecl *OldVar, + const BoundNodes &Nodes) { if (OldVar) { return OldVar->getType(); } @@ -224,8 +227,6 @@ QualType constructorArgumentType(const VarDecl *OldVar, return MethodDecl->getReturnType(); } -} // namespace - UnnecessaryCopyInitialization::UnnecessaryCopyInitialization( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index fbd2ba6..c1aa52b 100644 --- a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -21,16 +21,14 @@ using namespace clang::ast_matchers; namespace clang::tidy::performance { -namespace { - -std::string paramNameOrIndex(StringRef Name, size_t Index) { +static std::string paramNameOrIndex(StringRef Name, size_t Index) { return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1) : llvm::Twine('\'') + Name + llvm::Twine('\'')) .str(); } -bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl, - ASTContext &Context) { +static bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl, + ASTContext &Context) { auto Matches = match( traverse(TK_AsIs, decl(forEachDescendant(declRefExpr( @@ -41,8 +39,6 @@ bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl, return Matches.empty(); } -} // namespace - UnnecessaryValueParamCheck::UnnecessaryValueParamCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp index 3b4d65b..5a3c9a4 100644 --- a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp +++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.cpp @@ -15,10 +15,11 @@ namespace clang::tidy::portability { void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) { // Match std::allocator<const T>. - auto AllocatorConst = + auto AllocatorConst = qualType(hasCanonicalType( recordType(hasDeclaration(classTemplateSpecializationDecl( hasName("::std::allocator"), - hasTemplateArgument(0, refersToType(qualType(isConstQualified())))))); + hasTemplateArgument(0, + refersToType(qualType(isConstQualified())))))))); auto HasContainerName = hasAnyName("::std::vector", "::std::deque", "::std::list", @@ -31,8 +32,10 @@ void StdAllocatorConstCheck::registerMatchers(MatchFinder *Finder) { // aren't caught. Finder->addMatcher( typeLoc( - templateSpecializationTypeLoc(), - loc(hasUnqualifiedDesugaredType(anyOf( + anyOf(templateSpecializationTypeLoc(), + qualifiedTypeLoc( + hasUnqualifiedLoc(templateSpecializationTypeLoc()))), + loc(qualType(anyOf( recordType(hasDeclaration(classTemplateSpecializationDecl( HasContainerName, anyOf( diff --git a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h index d22d1aa..87702af 100644 --- a/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h +++ b/clang-tools-extra/clang-tidy/portability/StdAllocatorConstCheck.h @@ -23,6 +23,9 @@ class StdAllocatorConstCheck : public ClangTidyCheck { public: StdAllocatorConstCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp index ce736a8..c3f8106 100644 --- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -89,10 +89,6 @@ AST_MATCHER(Expr, usedInBooleanContext) { return Result; } -AST_MATCHER(CXXConstructExpr, isDefaultConstruction) { - return Node.getConstructor()->isDefaultConstructor(); -} - AST_MATCHER(QualType, isIntegralType) { return Node->isIntegralType(Finder->getASTContext()); } @@ -110,6 +106,25 @@ AST_MATCHER_P(UserDefinedLiteral, hasLiteral, return false; } +AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, + clang::ast_matchers::internal::Matcher<CXXMethodDecl>, + InnerMatcher) { + return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder); +} + +AST_POLYMORPHIC_MATCHER_P( + matchMemberName, + AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, CXXDependentScopeMemberExpr), + std::string, MemberName) { + if (const auto *E = dyn_cast<MemberExpr>(&Node)) + return E->getMemberDecl()->getName() == MemberName; + + if (const auto *E = dyn_cast<CXXDependentScopeMemberExpr>(&Node)) + return E->getMember().getAsString() == MemberName; + + return false; +} + } // namespace using utils::isBinaryOrTernary; @@ -140,9 +155,10 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { const auto ValidContainerNonTemplateType = qualType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(ValidContainerRecord)))); - const auto ValidContainerTemplateType = - qualType(hasUnqualifiedDesugaredType(templateSpecializationType( - hasDeclaration(classTemplateDecl(has(ValidContainerRecord)))))); + const auto ValidContainerTemplateType = qualType(hasUnqualifiedDesugaredType( + anyOf(templateSpecializationType( + hasDeclaration(classTemplateDecl(has(ValidContainerRecord)))), + injectedClassNameType(hasDeclaration(ValidContainerRecord))))); const auto ValidContainer = qualType( anyOf(ValidContainerNonTemplateType, ValidContainerTemplateType)); @@ -155,6 +171,9 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { .bind("SizeBinaryOp")), usedInBooleanContext()); + const auto NotInEmptyMethodOfContainer = unless( + forCallable(cxxMethodDecl(hasCanonicalDecl(equalsBoundNode("empty"))))); + Finder->addMatcher( cxxMemberCallExpr( argumentCountIs(0), @@ -164,25 +183,23 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { .bind("MemberCallObject")), callee( cxxMethodDecl(hasAnyName("size", "length")).bind("SizeMethod")), - WrongUse, - unless(hasAncestor( - cxxMethodDecl(ofClass(equalsBoundNode("container")))))) + WrongUse, NotInEmptyMethodOfContainer) .bind("SizeCallExpr"), this); Finder->addMatcher( - callExpr(argumentCountIs(0), - has(cxxDependentScopeMemberExpr( - hasObjectExpression( - expr(anyOf(hasType(ValidContainer), - hasType(pointsTo(ValidContainer)), - hasType(references(ValidContainer)))) - .bind("MemberCallObject")), - anyOf(hasMemberName("size"), hasMemberName("length"))) - .bind("DependentExpr")), - WrongUse, - unless(hasAncestor( - cxxMethodDecl(ofClass(equalsBoundNode("container")))))) + callExpr( + argumentCountIs(0), + has(mapAnyOf(memberExpr, cxxDependentScopeMemberExpr) + .with( + hasObjectExpression( + expr(anyOf(hasType(ValidContainer), + hasType(pointsTo(ValidContainer)), + hasType(references(ValidContainer)))) + .bind("MemberCallObject")), + anyOf(matchMemberName("size"), matchMemberName("length"))) + .bind("MemberExpr")), + WrongUse, NotInEmptyMethodOfContainer) .bind("SizeCallExpr"), this); @@ -190,7 +207,7 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { const auto WrongComparend = anyOf(stringLiteral(hasSize(0)), userDefinedLiteral(hasLiteral(stringLiteral(hasSize(0)))), - cxxConstructExpr(isDefaultConstruction()), + cxxConstructExpr(argumentCountIs(0)), cxxUnresolvedConstructExpr(argumentCountIs(0))); // Match the object being compared. const auto STLArg = @@ -217,8 +234,7 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { hasAnyOperatorName("==", "!="), hasOperands(WrongComparend, STLArg), unless(allOf(hasLHS(hasType(ExcludedComparisonTypesMatcher)), hasRHS(hasType(SameExcludedComparisonTypesMatcher)))), - unless(hasAncestor( - cxxMethodDecl(ofClass(equalsBoundNode("container")))))) + NotInEmptyMethodOfContainer) .bind("BinCmp"), this); } @@ -238,9 +254,12 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) { ? MemberCallObject : (Pointee ? Pointee : Result.Nodes.getNodeAs<Expr>("STLObject")); FixItHint Hint; - std::string ReplacementText = std::string( - Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()), - *Result.SourceManager, getLangOpts())); + std::string ReplacementText = + E->isImplicitCXXThis() + ? "" + : std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(E->getSourceRange()), + *Result.SourceManager, getLangOpts())); const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E); if (isBinaryOrTernary(E) || isa<UnaryOperator>(E) || (OpCallExpr && (OpCallExpr->getOperator() == OO_Star))) { @@ -251,6 +270,8 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) { // This can happen if the object is a smart pointer. Don't add anything // because a '->' is already there (PR#51776), just call the method. ReplacementText += "empty()"; + } else if (E->isImplicitCXXThis()) { + ReplacementText += "empty()"; } else if (E->getType()->isPointerType()) ReplacementText += "->empty()"; else @@ -377,9 +398,12 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) { Diag << SizeMethod; else if (const auto *DependentExpr = Result.Nodes.getNodeAs<CXXDependentScopeMemberExpr>( - "DependentExpr")) + "MemberExpr")) Diag << DependentExpr->getMember(); - else + else if (const auto *ME = + Result.Nodes.getNodeAs<MemberExpr>("MemberExpr")) { + Diag << ME->getMemberNameInfo().getName(); + } else Diag << "unknown method"; Diag << Hint; } else { diff --git a/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h index 79b0d39..1b12fec 100644 --- a/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h +++ b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.h @@ -23,6 +23,9 @@ class ConvertMemberFunctionsToStatic : public ClangTidyCheck { public: ConvertMemberFunctionsToStatic(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; diff --git a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h index e9e7c94..6e746d8 100644 --- a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h +++ b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.h @@ -22,6 +22,9 @@ class DeleteNullPointerCheck : public ClangTidyCheck { public: DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; std::optional<TraversalKind> getCheckTraversalKind() const override { diff --git a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp index b4a157c..9eef5c4 100644 --- a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp @@ -122,15 +122,15 @@ AST_MATCHER(EnumDecl, hasSequentialInitialValues) { return !AllEnumeratorsArePowersOfTwo; } -std::string getName(const EnumDecl *Decl) { +} // namespace + +static std::string getName(const EnumDecl *Decl) { if (!Decl->getDeclName()) return "<unnamed>"; return Decl->getQualifiedNameAsString(); } -} // namespace - EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp index e1fb42b..2f59aaa 100644 --- a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp @@ -144,6 +144,8 @@ struct CognitiveComplexity final { void account(SourceLocation Loc, unsigned short Nesting, Criteria C); }; +} // namespace + // All the possible messages that can be output. The choice of the message // to use is based of the combination of the CognitiveComplexity::Criteria. // It would be nice to have it in CognitiveComplexity struct, but then it is @@ -163,23 +165,27 @@ static const std::array<const StringRef, 4> Msgs = {{ }}; // Criteria is a bitset, thus a few helpers are needed. -CognitiveComplexity::Criteria operator|(CognitiveComplexity::Criteria LHS, - CognitiveComplexity::Criteria RHS) { +static CognitiveComplexity::Criteria +operator|(CognitiveComplexity::Criteria LHS, + CognitiveComplexity::Criteria RHS) { return static_cast<CognitiveComplexity::Criteria>(llvm::to_underlying(LHS) | llvm::to_underlying(RHS)); } -CognitiveComplexity::Criteria operator&(CognitiveComplexity::Criteria LHS, - CognitiveComplexity::Criteria RHS) { +static CognitiveComplexity::Criteria +operator&(CognitiveComplexity::Criteria LHS, + CognitiveComplexity::Criteria RHS) { return static_cast<CognitiveComplexity::Criteria>(llvm::to_underlying(LHS) & llvm::to_underlying(RHS)); } -CognitiveComplexity::Criteria &operator|=(CognitiveComplexity::Criteria &LHS, - CognitiveComplexity::Criteria RHS) { +static CognitiveComplexity::Criteria & +operator|=(CognitiveComplexity::Criteria &LHS, + CognitiveComplexity::Criteria RHS) { LHS = operator|(LHS, RHS); return LHS; } -CognitiveComplexity::Criteria &operator&=(CognitiveComplexity::Criteria &LHS, - CognitiveComplexity::Criteria RHS) { +static CognitiveComplexity::Criteria & +operator&=(CognitiveComplexity::Criteria &LHS, + CognitiveComplexity::Criteria RHS) { LHS = operator&(LHS, RHS); return LHS; } @@ -199,6 +205,8 @@ void CognitiveComplexity::account(SourceLocation Loc, unsigned short Nesting, Total += Increase; } +namespace { + class FunctionASTVisitor final : public RecursiveASTVisitor<FunctionASTVisitor> { using Base = RecursiveASTVisitor<FunctionASTVisitor>; diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index fab2365..3df4a03 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -953,7 +953,7 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_LowerCase: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += Word.lower(); @@ -961,7 +961,7 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_UpperCase: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += Word.upper(); @@ -969,14 +969,14 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_CamelCase: - for (auto const &Word : Words) { + for (const auto &Word : Words) { Fixup += toupper(Word.front()); Fixup += Word.substr(1).lower(); } break; case IdentifierNamingCheck::CT_CamelBack: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word == &Words.front()) { Fixup += Word.lower(); } else { @@ -987,7 +987,7 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_CamelSnakeCase: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word != &Words.front()) Fixup += "_"; Fixup += toupper(Word.front()); @@ -996,7 +996,7 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_CamelSnakeBack: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word != &Words.front()) { Fixup += "_"; Fixup += toupper(Word.front()); @@ -1008,7 +1008,7 @@ std::string IdentifierNamingCheck::fixupWithCase( break; case IdentifierNamingCheck::CT_LeadingUpperSnakeCase: - for (auto const &Word : Words) { + for (const auto &Word : Words) { if (&Word != &Words.front()) { Fixup += "_"; Fixup += Word.lower(); diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index 5f19706..6b10e6b 100644 --- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -41,9 +41,11 @@ AST_MATCHER(Stmt, isNULLMacroExpansion) { return isNULLMacroExpansion(&Node, Finder->getASTContext()); } -StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind, - QualType Type, - ASTContext &Context) { +} // namespace + +static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind, + QualType Type, + ASTContext &Context) { switch (CastExprKind) { case CK_IntegralToBoolean: return Type->isUnsignedIntegerType() ? "0u" : "0"; @@ -62,15 +64,15 @@ StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind, } } -bool isUnaryLogicalNotOperator(const Stmt *Statement) { +static bool isUnaryLogicalNotOperator(const Stmt *Statement) { const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement); return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot; } -void fixGenericExprCastToBool(DiagnosticBuilder &Diag, - const ImplicitCastExpr *Cast, const Stmt *Parent, - ASTContext &Context, - bool UseUpperCaseLiteralSuffix) { +static void fixGenericExprCastToBool(DiagnosticBuilder &Diag, + const ImplicitCastExpr *Cast, + const Stmt *Parent, ASTContext &Context, + bool UseUpperCaseLiteralSuffix) { // In case of expressions like (! integer), we should remove the redundant not // operator and use inverted comparison (integer == 0). bool InvertComparison = @@ -133,8 +135,8 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag, Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion); } -StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, - ASTContext &Context) { +static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, + ASTContext &Context) { if (isNULLMacroExpansion(Expression, Context)) { return "false"; } @@ -161,7 +163,7 @@ StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, return {}; } -bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) { +static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) { SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc); StringRef SpaceBeforeStmtStr = Lexer::getSourceText( CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(), @@ -173,9 +175,10 @@ bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) { return !AllowedCharacters.contains(SpaceBeforeStmtStr.back()); } -void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, - const ImplicitCastExpr *Cast, - ASTContext &Context, StringRef OtherType) { +static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, + const ImplicitCastExpr *Cast, + ASTContext &Context, + StringRef OtherType) { if (!Context.getLangOpts().CPlusPlus) { Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), (Twine("(") + OtherType + ")").str()); @@ -200,8 +203,9 @@ void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, } } -StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral, - QualType DestType, ASTContext &Context) { +static StringRef +getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral, + QualType DestType, ASTContext &Context) { // Prior to C++11, false literal could be implicitly converted to pointer. if (!Context.getLangOpts().CPlusPlus11 && (DestType->isPointerType() || DestType->isMemberPointerType()) && @@ -222,8 +226,8 @@ StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral, return BoolLiteral->getValue() ? "1" : "0"; } -bool isCastAllowedInCondition(const ImplicitCastExpr *Cast, - ASTContext &Context) { +static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast, + ASTContext &Context) { std::queue<const Stmt *> Q; Q.push(Cast); @@ -251,8 +255,6 @@ bool isCastAllowedInCondition(const ImplicitCastExpr *Cast, return false; } -} // anonymous namespace - ImplicitBoolConversionCheck::ImplicitBoolConversionCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp index 561f067..44a784b 100644 --- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp @@ -28,8 +28,11 @@ AST_MATCHER_P(QualType, hasUnqualifiedType, enum class Qualifier { Const, Volatile, Restrict }; -std::optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual, - const MatchFinder::MatchResult &Result) { +} // namespace + +static std::optional<Token> +findQualToken(const VarDecl *Decl, Qualifier Qual, + const MatchFinder::MatchResult &Result) { // Since either of the locs can be in a macro, use `makeFileCharRange` to be // sure that we have a consistent `CharSourceRange`, located entirely in the // source file. @@ -58,7 +61,7 @@ std::optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual, *Result.SourceManager); } -std::optional<SourceRange> +static std::optional<SourceRange> getTypeSpecifierLocation(const VarDecl *Var, const MatchFinder::MatchResult &Result) { SourceRange TypeSpecifier( @@ -73,8 +76,8 @@ getTypeSpecifierLocation(const VarDecl *Var, return TypeSpecifier; } -std::optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier, - const Token &ConstToken) { +static std::optional<SourceRange> +mergeReplacementRange(SourceRange &TypeSpecifier, const Token &ConstToken) { if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) { TypeSpecifier.setBegin(ConstToken.getLocation()); return std::nullopt; @@ -86,21 +89,19 @@ std::optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier, return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc()); } -bool isPointerConst(QualType QType) { +static bool isPointerConst(QualType QType) { QualType Pointee = QType->getPointeeType(); assert(!Pointee.isNull() && "can't have a null Pointee"); return Pointee.isConstQualified(); } -bool isAutoPointerConst(QualType QType) { +static bool isAutoPointerConst(QualType QType) { QualType Pointee = cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar(); assert(!Pointee.isNull() && "can't have a null Pointee"); return Pointee.isConstQualified(); } -} // namespace - QualifiedAutoCheck::QualifiedAutoCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), diff --git a/clang-tools-extra/clang-tidy/readability/RedundantControlFlowCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantControlFlowCheck.cpp index 70016a3..d93077c 100644 --- a/clang-tools-extra/clang-tidy/readability/RedundantControlFlowCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/RedundantControlFlowCheck.cpp @@ -14,19 +14,18 @@ using namespace clang::ast_matchers; namespace clang::tidy::readability { -namespace { +static const char *const RedundantReturnDiag = + "redundant return statement at the end " + "of a function with a void return type"; +static const char *const RedundantContinueDiag = + "redundant continue statement at the " + "end of loop statement"; -const char *const RedundantReturnDiag = "redundant return statement at the end " - "of a function with a void return type"; -const char *const RedundantContinueDiag = "redundant continue statement at the " - "end of loop statement"; - -bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) { +static bool isLocationInMacroExpansion(const SourceManager &SM, + SourceLocation Loc) { return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); } -} // namespace - void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( functionDecl(isDefinition(), returns(voidType()), diff --git a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp index fffb136..a7b3c4a 100644 --- a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp @@ -19,19 +19,25 @@ namespace { AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); } } // namespace -static unsigned getNameSpecifierNestingLevel(const QualType &QType) { - if (const auto *ElType = QType->getAs<ElaboratedType>()) { - if (const NestedNameSpecifier *NestedSpecifiers = ElType->getQualifier()) { - unsigned NameSpecifierNestingLevel = 1; - do { - NameSpecifierNestingLevel++; - NestedSpecifiers = NestedSpecifiers->getPrefix(); - } while (NestedSpecifiers); - +static unsigned getNameSpecifierNestingLevel(QualType QType) { + unsigned NameSpecifierNestingLevel = 1; + for (NestedNameSpecifier Qualifier = QType->getPrefix(); /**/; + ++NameSpecifierNestingLevel) { + switch (Qualifier.getKind()) { + case NestedNameSpecifier::Kind::Null: return NameSpecifierNestingLevel; + case NestedNameSpecifier::Kind::Global: + case NestedNameSpecifier::Kind::MicrosoftSuper: + return NameSpecifierNestingLevel + 1; + case NestedNameSpecifier::Kind::Namespace: + Qualifier = Qualifier.getAsNamespaceAndPrefix().Prefix; + continue; + case NestedNameSpecifier::Kind::Type: + Qualifier = Qualifier.getAsType()->getPrefix(); + continue; } + llvm_unreachable("unhandled nested name specifier kind"); } - return 0; } void StaticAccessedThroughInstanceCheck::storeOptions( diff --git a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h index 4044f13..9869855 100644 --- a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h +++ b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.h @@ -24,6 +24,9 @@ public: : ClangTidyCheck(Name, Context), NameSpecifierNestingThreshold( Options.get("NameSpecifierNestingThreshold", 3U)) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; diff --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp index 5a04029..a80637d 100644 --- a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp @@ -413,10 +413,11 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType, // Arithmetic types are interconvertible, except scoped enums. if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) { - if ((ParamType->isEnumeralType() && - ParamType->castAs<EnumType>()->getDecl()->isScoped()) || + if ((ParamType->isEnumeralType() && ParamType->castAsCanonical<EnumType>() + ->getOriginalDecl() + ->isScoped()) || (ArgType->isEnumeralType() && - ArgType->castAs<EnumType>()->getDecl()->isScoped())) + ArgType->castAsCanonical<EnumType>()->getOriginalDecl()->isScoped())) return false; return true; diff --git a/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h b/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h index 4dfcf36..2768955 100644 --- a/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h +++ b/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h @@ -21,6 +21,9 @@ namespace clang::tidy::readability { class UniqueptrDeleteReleaseCheck : public ClangTidyCheck { public: UniqueptrDeleteReleaseCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; diff --git a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp index 678aa8d..4ec2a63 100644 --- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp @@ -25,14 +25,12 @@ struct IntegerLiteralCheck { static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer"); // What should be skipped before looking for the Suffixes? (Nothing here.) static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral(""); - // Suffix can only consist of 'u' and 'l' chars, and can be a complex number - // ('i', 'j'). In MS compatibility mode, suffixes like i32 are supported. + // Suffix can only consist of 'u', 'l', and 'z' chars, can be a bit-precise + // integer (wb), and can be a complex number ('i', 'j'). In MS compatibility + // mode, suffixes like i32 are supported. static constexpr llvm::StringLiteral Suffixes = - llvm::StringLiteral("uUlLiIjJ"); + llvm::StringLiteral("uUlLzZwWbBiIjJ"); }; -constexpr llvm::StringLiteral IntegerLiteralCheck::Name; -constexpr llvm::StringLiteral IntegerLiteralCheck::SkipFirst; -constexpr llvm::StringLiteral IntegerLiteralCheck::Suffixes; struct FloatingLiteralCheck { using type = clang::FloatingLiteral; @@ -45,14 +43,11 @@ struct FloatingLiteralCheck { // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point // literals, we first skip everything before the exponent. static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP"); - // Suffix can only consist of 'f', 'l', "f16", 'h', 'q' chars, - // and can be a complex number ('i', 'j'). + // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl", + // 'h', 'q' chars, and can be a complex number ('i', 'j'). static constexpr llvm::StringLiteral Suffixes = - llvm::StringLiteral("fFlLhHqQiIjJ"); + llvm::StringLiteral("fFlLbBdDhHqQiIjJ"); }; -constexpr llvm::StringLiteral FloatingLiteralCheck::Name; -constexpr llvm::StringLiteral FloatingLiteralCheck::SkipFirst; -constexpr llvm::StringLiteral FloatingLiteralCheck::Suffixes; struct NewSuffix { SourceRange LiteralLocation; diff --git a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp index 6f6b8a8..718467ed 100644 --- a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp @@ -67,11 +67,7 @@ static QualType getNonTemplateAlias(QualType QT) { if (!TT->getDecl()->getDescribedTemplate() && !TT->getDecl()->getDeclContext()->isDependentContext()) return QT; - QT = TT->getDecl()->getUnderlyingType(); - } - // cast to elaborated type - else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) { - QT = ET->getNamedType(); + QT = TT->desugar(); } else { break; } diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 97dfd0f..bef3b93 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -162,14 +162,19 @@ in .clang-tidy file, if any. cl::init(false), cl::cat(ClangTidyCategory)); static cl::opt<std::string> LineFilter("line-filter", desc(R"( -List of files with line ranges to filter the -warnings. Can be used together with --header-filter. The format of the list is a -JSON array of objects: +List of files and line ranges to output diagnostics from. +The range is inclusive on both ends. Can be used together +with -header-filter. The format of the list is a JSON +array of objects. For example: + [ {"name":"file1.cpp","lines":[[1,3],[5,7]]}, {"name":"file2.h"} ] + +This will output diagnostics from 'file1.cpp' only for +the line ranges [1,3] and [5,7], as well as all from the +entire 'file2.h'. )"), cl::init(""), cl::cat(ClangTidyCategory)); @@ -712,7 +717,7 @@ int clangTidyMain(int argc, const char **argv) { EnableModuleHeadersParsing); std::vector<ClangTidyError> Errors = runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS, - FixNotes, EnableCheckProfile, ProfilePrefix); + FixNotes, EnableCheckProfile, ProfilePrefix, Quiet); bool FoundErrors = llvm::any_of(Errors, [](const ClangTidyError &E) { return E.DiagLevel == ClangTidyError::Error; }); diff --git a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py index 7cd21af..d7899e0 100755 --- a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py +++ b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py @@ -28,6 +28,7 @@ import glob import json import multiprocessing import os +import queue import re import shutil import subprocess @@ -42,13 +43,6 @@ try: except ImportError: yaml = None -is_py2 = sys.version[0] == "2" - -if is_py2: - import Queue as queue -else: - import queue as queue - def run_tidy(task_queue, lock, timeout, failed_files): watchdog = None diff --git a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py index 80f1766..670e0a2 100755 --- a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py +++ b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py @@ -483,7 +483,7 @@ async def main() -> None: parser.add_argument( "-line-filter", default=None, - help="List of files with line ranges to filter the warnings.", + help="List of files and line ranges to output diagnostics from.", ) if yaml: parser.add_argument( diff --git a/clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp b/clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp index 6914ec2..d43716e 100644 --- a/clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp +++ b/clang-tools-extra/clang-tidy/utils/DesignatedInitializers.cpp @@ -13,6 +13,7 @@ #include "DesignatedInitializers.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/ScopeExit.h" diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp index 0637d0e..4693c65 100644 --- a/clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp @@ -9,6 +9,7 @@ #include "ExceptionSpecAnalyzer.h" #include "clang/AST/Expr.h" +#include "clang/AST/Type.h" namespace clang::tidy::utils { @@ -66,9 +67,7 @@ ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier &Base, if (!RecType) return State::Unknown; - const auto *BaseClass = cast<CXXRecordDecl>(RecType->getDecl()); - - return analyzeRecord(BaseClass, Kind); + return analyzeRecord(RecType->getAsCXXRecordDecl(), Kind); } ExceptionSpecAnalyzer::State diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index e1c1bee..0d0834d 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -460,9 +460,9 @@ bool FormatStringConverter::emitIntegerArgument( // be passed as its underlying type. However, printf will have forced // the signedness based on the format string, so we need to do the // same. - if (const auto *ET = ArgType->getAs<EnumType>()) { + if (const auto *ED = ArgType->getAsEnumDecl()) { if (const std::optional<std::string> MaybeCastType = - castTypeForArgument(ArgKind, ET->getDecl()->getIntegerType())) + castTypeForArgument(ArgKind, ED->getIntegerType())) ArgFixes.emplace_back( ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str()); else diff --git a/clang-tools-extra/clang-tidy/utils/Matchers.cpp b/clang-tools-extra/clang-tidy/utils/Matchers.cpp index 4974a9c..bd7b03e 100644 --- a/clang-tools-extra/clang-tidy/utils/Matchers.cpp +++ b/clang-tools-extra/clang-tidy/utils/Matchers.cpp @@ -34,7 +34,7 @@ bool MatchesAnyListedTypeNameMatcher::matches( PrintingPolicy PrintingPolicyWithSuppressedTag( Finder->getASTContext().getLangOpts()); PrintingPolicyWithSuppressedTag.PrintAsCanonical = CanonicalTypes; - PrintingPolicyWithSuppressedTag.SuppressElaboration = true; + PrintingPolicyWithSuppressedTag.FullyQualifiedName = true; PrintingPolicyWithSuppressedTag.SuppressScope = false; PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true; PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true; diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index eaa04fe..90539ea 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -194,6 +194,8 @@ public: return; if (SM.isWrittenInCommandLineFile(MacroNameTok.getLocation())) return; + if (SM.isInSystemHeader(MacroNameTok.getLocation())) + return; Check->checkMacro(MacroNameTok, Info, SM); } @@ -281,9 +283,10 @@ public: } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) { - if (const NestedNameSpecifier *Spec = Loc.getNestedNameSpecifier()) { + if (NestedNameSpecifier Spec = Loc.getNestedNameSpecifier(); + Spec.getKind() == NestedNameSpecifier::Kind::Namespace) { if (const auto *Decl = - dyn_cast_if_present<NamespaceDecl>(Spec->getAsNamespace())) + dyn_cast<NamespaceDecl>(Spec.getAsNamespaceAndPrefix().Namespace)) Check->addUsage(Decl, Loc.getLocalSourceRange(), SM); } @@ -323,48 +326,34 @@ public: } bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) { - Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM); + Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM); return true; } bool VisitTagTypeLoc(const TagTypeLoc &Loc) { - Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); - return true; - } - - bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc &Loc) { - Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); + Check->addUsage(Loc.getOriginalDecl(), Loc.getNameLoc(), SM); return true; } bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) { - Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); + Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM); return true; } bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) { - Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); + Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM); return true; } bool VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) { const TemplateDecl *Decl = - Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(); + Loc.getTypePtr()->getTemplateName().getAsTemplateDecl( + /*IgnoreDeduced=*/true); - SourceRange Range(Loc.getTemplateNameLoc(), Loc.getTemplateNameLoc()); - if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) { + if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl()) - Check->addUsage(TemplDecl, Range, SM); - } - - return true; - } - - bool VisitDependentTemplateSpecializationTypeLoc( - const DependentTemplateSpecializationTypeLoc &Loc) { - if (const TagDecl *Decl = Loc.getTypePtr()->getAsTagDecl()) - Check->addUsage(Decl, Loc.getSourceRange(), SM); + Check->addUsage(TemplDecl, Loc.getTemplateNameLoc(), SM); return true; } diff --git a/clang-tools-extra/clang-tidy/utils/TypeTraits.cpp b/clang-tools-extra/clang-tidy/utils/TypeTraits.cpp index 44db0c2..f944306 100644 --- a/clang-tools-extra/clang-tidy/utils/TypeTraits.cpp +++ b/clang-tools-extra/clang-tidy/utils/TypeTraits.cpp @@ -13,16 +13,14 @@ namespace clang::tidy::utils::type_traits { -namespace { - -bool classHasTrivialCopyAndDestroy(QualType Type) { +static bool classHasTrivialCopyAndDestroy(QualType Type) { auto *Record = Type->getAsCXXRecordDecl(); return Record && Record->hasDefinition() && !Record->hasNonTrivialCopyConstructor() && !Record->hasNonTrivialDestructor(); } -bool hasDeletedCopyConstructor(QualType Type) { +static bool hasDeletedCopyConstructor(QualType Type) { auto *Record = Type->getAsCXXRecordDecl(); if (!Record || !Record->hasDefinition()) return false; @@ -33,8 +31,6 @@ bool hasDeletedCopyConstructor(QualType Type) { return false; } -} // namespace - std::optional<bool> isExpensiveToCopy(QualType Type, const ASTContext &Context) { if (Type->isDependentType() || Type->isIncompleteType()) @@ -123,8 +119,8 @@ bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) { if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) return true; - if (const auto *RT = CanonicalType->getAs<RecordType>()) { - return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context); + if (const auto *RD = CanonicalType->getAsRecordDecl()) { + return recordIsTriviallyDefaultConstructible(*RD, Context); } // No other types can match. diff --git a/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.h b/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.h index e1f1f06..b2d5ab6 100644 --- a/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.h +++ b/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.h @@ -24,6 +24,9 @@ public: TemporaryObjectsCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), Names(utils::options::parseStringList(Options.get("Names", ""))) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index f2631e5..2f46ecc 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -102,54 +102,78 @@ getUsingNamespaceDirectives(const DeclContext *DestContext, // ancestor is redundant, therefore we stop at lowest common ancestor. // In addition to that stops early whenever IsVisible returns true. This can be // used to implement support for "using namespace" decls. -std::string -getQualification(ASTContext &Context, const DeclContext *DestContext, - const DeclContext *SourceContext, - llvm::function_ref<bool(NestedNameSpecifier *)> IsVisible) { - std::vector<const NestedNameSpecifier *> Parents; - bool ReachedNS = false; +std::string getQualification(ASTContext &Context, + const DeclContext *DestContext, + const DeclContext *SourceContext, + llvm::function_ref<bool(const Decl *)> IsVisible) { + std::vector<const Decl *> Parents; + [[maybe_unused]] bool ReachedNS = false; for (const DeclContext *CurContext = SourceContext; CurContext; CurContext = CurContext->getLookupParent()) { // Stop once we reach a common ancestor. if (CurContext->Encloses(DestContext)) break; - NestedNameSpecifier *NNS = nullptr; + const Decl *CurD; if (auto *TD = llvm::dyn_cast<TagDecl>(CurContext)) { // There can't be any more tag parents after hitting a namespace. assert(!ReachedNS); - (void)ReachedNS; - NNS = NestedNameSpecifier::Create(Context, nullptr, TD->getTypeForDecl()); + CurD = TD; } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) { ReachedNS = true; - NNS = NestedNameSpecifier::Create(Context, nullptr, NSD); // Anonymous and inline namespace names are not spelled while qualifying // a name, so skip those. if (NSD->isAnonymousNamespace() || NSD->isInlineNamespace()) continue; + CurD = NSD; } else { // Other types of contexts cannot be spelled in code, just skip over // them. continue; } // Stop if this namespace is already visible at DestContext. - if (IsVisible(NNS)) + if (IsVisible(CurD)) break; - Parents.push_back(NNS); + Parents.push_back(CurD); + } + + // Go over the declarations in reverse order, since we stored inner-most + // parent first. + NestedNameSpecifier Qualifier = std::nullopt; + bool IsFirst = true; + for (const auto *CurD : llvm::reverse(Parents)) { + if (auto *TD = llvm::dyn_cast<TagDecl>(CurD)) { + QualType T; + if (const auto *RD = dyn_cast<CXXRecordDecl>(TD); + ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) { + ArrayRef<TemplateArgument> Args; + if (const auto *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) + Args = SD->getTemplateArgs().asArray(); + else + Args = CTD->getTemplateParameters()->getInjectedTemplateArgs(Context); + T = Context.getTemplateSpecializationType( + ElaboratedTypeKeyword::None, + Context.getQualifiedTemplateName( + Qualifier, /*TemplateKeyword=*/!IsFirst, TemplateName(CTD)), + Args, /*CanonicalArgs=*/{}, Context.getCanonicalTagType(RD)); + } else { + T = Context.getTagType(ElaboratedTypeKeyword::None, Qualifier, TD, + /*OwnsTag=*/false); + } + Qualifier = NestedNameSpecifier(T.getTypePtr()); + } else { + Qualifier = + NestedNameSpecifier(Context, cast<NamespaceDecl>(CurD), Qualifier); + } + IsFirst = false; } + if (!Qualifier) + return ""; - // Go over name-specifiers in reverse order to create necessary qualification, - // since we stored inner-most parent first. std::string Result; llvm::raw_string_ostream OS(Result); - for (const auto *Parent : llvm::reverse(Parents)) { - if (Parent != *Parents.rbegin() && Parent->isDependent() && - Parent->getAsRecordDecl() && - Parent->getAsRecordDecl()->getDescribedClassTemplate()) - OS << "template "; - Parent->print(OS, Context.getPrintingPolicy()); - } + Qualifier.print(OS, Context.getPrintingPolicy()); return OS.str(); } @@ -187,6 +211,7 @@ std::string printQualifiedName(const NamedDecl &ND) { // include them, but at query time it's hard to find all the inline // namespaces to query: the preamble doesn't have a dedicated list. Policy.SuppressUnwrittenScope = true; + Policy.SuppressScope = true; // (unnamed struct), not (unnamed struct at /path/to/foo.cc:42:1). // In clangd, context is usually available and paths are mostly noise. Policy.AnonymousTagLocations = false; @@ -213,8 +238,7 @@ std::string printUsingNamespaceName(const ASTContext &Ctx, std::string Name; llvm::raw_string_ostream Out(Name); - if (auto *Qual = D.getQualifier()) - Qual->print(Out, PP); + D.getQualifier().print(Out, PP); D.getNominatedNamespaceAsWritten()->printName(Out); return Out.str(); } @@ -229,8 +253,7 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { // Handle 'using namespace'. They all have the same name - <using-directive>. if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) { Out << "using namespace "; - if (auto *Qual = UD->getQualifier()) - Qual->print(Out, PP); + UD->getQualifier().print(Out, PP); UD->getNominatedNamespaceAsWritten()->printName(Out); return Out.str(); } @@ -250,8 +273,7 @@ std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { } // Print nested name qualifier if it was written in the source code. - if (auto *Qualifier = getQualifierLoc(ND).getNestedNameSpecifier()) - Qualifier->print(Out, PP); + getQualifierLoc(ND).getNestedNameSpecifier().print(Out, PP); // Print the name itself. ND.getDeclName().print(Out, PP); // Print template arguments. @@ -391,12 +413,13 @@ preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts, } std::string printType(const QualType QT, const DeclContext &CurContext, - const llvm::StringRef Placeholder) { + const llvm::StringRef Placeholder, bool FullyQualify) { std::string Result; llvm::raw_string_ostream OS(Result); PrintingPolicy PP(CurContext.getParentASTContext().getPrintingPolicy()); PP.SuppressTagKeyword = true; PP.SuppressUnwrittenScope = true; + PP.FullyQualifiedName = FullyQualify; class PrintCB : public PrintingCallbacks { public: @@ -439,6 +462,7 @@ QualType declaredType(const TypeDecl *D) { if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D)) if (const auto *Args = CTSD->getTemplateArgsAsWritten()) return Context.getTemplateSpecializationType( + ElaboratedTypeKeyword::None, TemplateName(CTSD->getSpecializedTemplate()), Args->arguments(), /*CanonicalArgs=*/{}); return Context.getTypeDeclType(D); @@ -664,13 +688,10 @@ std::string getQualification(ASTContext &Context, auto VisibleNamespaceDecls = getUsingNamespaceDirectives(DestContext, InsertionPoint); return getQualification( - Context, DestContext, ND->getDeclContext(), - [&](NestedNameSpecifier *NNS) { - const NamespaceDecl *NS = - dyn_cast_if_present<NamespaceDecl>(NNS->getAsNamespace()); - if (!NS) + Context, DestContext, ND->getDeclContext(), [&](const Decl *D) { + if (D->getKind() != Decl::Namespace) return false; - NS = NS->getCanonicalDecl(); + const auto *NS = cast<NamespaceDecl>(D)->getCanonicalDecl(); return llvm::any_of(VisibleNamespaceDecls, [NS](const NamespaceDecl *NSD) { return NSD->getCanonicalDecl() == NS; @@ -687,12 +708,11 @@ std::string getQualification(ASTContext &Context, (void)NS; } return getQualification( - Context, DestContext, ND->getDeclContext(), - [&](NestedNameSpecifier *NNS) { + Context, DestContext, ND->getDeclContext(), [&](const Decl *D) { return llvm::any_of(VisibleNamespaces, [&](llvm::StringRef Namespace) { std::string NS; llvm::raw_string_ostream OS(NS); - NNS->print(OS, Context.getPrintingPolicy()); + D->print(OS, Context.getPrintingPolicy()); return OS.str() == Namespace; }); }); @@ -965,7 +985,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) { // Recurse on pack parameters size_t Depth = 0; const FunctionDecl *CurrentFunction = D; - llvm::SmallSet<const FunctionTemplateDecl *, 4> SeenTemplates; + llvm::SmallPtrSet<const FunctionTemplateDecl *, 4> SeenTemplates; if (const auto *Template = D->getPrimaryTemplate()) { SeenTemplates.insert(Template); } diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h index fb0722d..1538d12 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -135,7 +135,8 @@ preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts, /// Returns a QualType as string. The result doesn't contain unwritten scopes /// like anonymous/inline namespace. std::string printType(const QualType QT, const DeclContext &CurContext, - llvm::StringRef Placeholder = ""); + llvm::StringRef Placeholder = "", + bool FullyQualify = false); /// Indicates if \p D is a template instantiation implicitly generated by the /// compiler, e.g. diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index a1e9da4..fb3f053 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -6,7 +6,7 @@ add_subdirectory(support) # Configure the Features.inc file. if (NOT DEFINED CLANGD_BUILD_XPC) - if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + if("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") set(CLANGD_BUILD_XPC_DEFAULT ON) else () set(CLANGD_BUILD_XPC_DEFAULT OFF) @@ -108,6 +108,7 @@ add_clang_library(clangDaemon STATIC SemanticHighlighting.cpp SemanticSelection.cpp SourceCode.cpp + SymbolDocumentation.cpp SystemIncludeExtractor.cpp TidyProvider.cpp TUScheduler.cpp @@ -192,7 +193,7 @@ if(CLANGD_TIDY_CHECKS) endif() add_subdirectory(refactor/tweaks) -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: Make fuzzer not use linux-specific APIs, build it everywhere. add_subdirectory(fuzzer) endif() diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 9c17b4c..c6deed3 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -97,6 +97,9 @@ toCompletionItemKind(index::SymbolKind Kind, const llvm::StringRef *Signature = nullptr) { using SK = index::SymbolKind; switch (Kind) { + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case SK::IncludeDirective: case SK::Unknown: return CompletionItemKind::Missing; case SK::Module: @@ -1466,19 +1469,15 @@ bool allowIndex(CodeCompletionContext &CC) { auto Scope = CC.getCXXScopeSpecifier(); if (!Scope) return true; - NestedNameSpecifier *NameSpec = (*Scope)->getScopeRep(); - if (!NameSpec) - return true; // We only query the index when qualifier is a namespace. // If it's a class, we rely solely on sema completions. - switch (NameSpec->getKind()) { - case NestedNameSpecifier::Global: - case NestedNameSpecifier::Namespace: + switch ((*Scope)->getScopeRep().getKind()) { + case NestedNameSpecifier::Kind::Null: + case NestedNameSpecifier::Kind::Global: + case NestedNameSpecifier::Kind::Namespace: return true; - case NestedNameSpecifier::Super: - case NestedNameSpecifier::TypeSpec: - // Unresolved inside a template. - case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Kind::MicrosoftSuper: + case NestedNameSpecifier::Kind::Type: return false; } llvm_unreachable("invalid NestedNameSpecifier kind"); @@ -2434,6 +2433,9 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CodeCompletion &C) { + OS << "Signature: " << "\"" << C.Signature << "\", " + << "SnippetSuffix: " << "\"" << C.SnippetSuffix << "\"" + << ", Rendered:"; // For now just lean on CompletionItem. return OS << C.render(CodeCompleteOptions()); } diff --git a/clang-tools-extra/clangd/CodeCompletionStrings.cpp b/clang-tools-extra/clangd/CodeCompletionStrings.cpp index 9b4442b..9c4241b 100644 --- a/clang-tools-extra/clangd/CodeCompletionStrings.cpp +++ b/clang-tools-extra/clangd/CodeCompletionStrings.cpp @@ -7,13 +7,18 @@ //===----------------------------------------------------------------------===// #include "CodeCompletionStrings.h" +#include "Config.h" +#include "SymbolDocumentation.h" #include "clang-c/Index.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Decl.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceManager.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" #include <limits> #include <utility> @@ -100,16 +105,55 @@ std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { // the comments for namespaces. return ""; } - const RawComment *RC = getCompletionComment(Ctx, &Decl); - if (!RC) - return ""; - // Sanity check that the comment does not come from the PCH. We choose to not - // write them into PCH, because they are racy and slow to load. - assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); - std::string Doc = - RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); - if (!looksLikeDocComment(Doc)) - return ""; + + const RawComment *RC = nullptr; + const Config &Cfg = Config::current(); + + std::string Doc; + + if (Cfg.Documentation.CommentFormat == Config::CommentFormatPolicy::Doxygen && + isa<ParmVarDecl, TemplateTypeParmDecl>(Decl)) { + // Parameters are documented in their declaration context (function or + // template function). + const NamedDecl *ND = dyn_cast<NamedDecl>(Decl.getDeclContext()); + if (!ND) + return ""; + + RC = getCompletionComment(Ctx, ND); + if (!RC) + return ""; + + // Sanity check that the comment does not come from the PCH. We choose to + // not write them into PCH, because they are racy and slow to load. + assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); + + comments::FullComment *FC = RC->parse(Ctx, /*PP=*/nullptr, ND); + if (!FC) + return ""; + + SymbolDocCommentVisitor V(FC, Ctx.getLangOpts().CommentOpts); + std::string RawDoc; + llvm::raw_string_ostream OS(RawDoc); + + if (auto *PVD = dyn_cast<ParmVarDecl>(&Decl)) + V.parameterDocToString(PVD->getName(), OS); + else + V.templateTypeParmDocToString( + cast<TemplateTypeParmDecl>(&Decl)->getName(), OS); + + Doc = StringRef(RawDoc).trim().str(); + } else { + RC = getCompletionComment(Ctx, &Decl); + if (!RC) + return ""; + // Sanity check that the comment does not come from the PCH. We choose to + // not write them into PCH, because they are racy and slow to load. + assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); + Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); + if (!looksLikeDocComment(Doc)) + return ""; + } + // Clang requires source to be UTF-8, but doesn't enforce this in comments. if (!llvm::json::isUTF8(Doc)) Doc = llvm::json::fixUTF8(Doc); diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index 2e3e0a4..01997ce 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -174,6 +174,9 @@ struct Config { struct { /// Whether hover show a.k.a type. bool ShowAKA = true; + /// Limit the number of characters returned when hovering a macro; + /// 0 is no limit. + uint32_t MacroContentsLimit = 2048; } Hover; struct { diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index 5dda6dd..962a48b 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -727,6 +727,12 @@ struct FragmentCompiler { C.Hover.ShowAKA = ShowAKA; }); } + if (F.MacroContentsLimit) { + Out.Apply.push_back( + [Limit(**F.MacroContentsLimit)](const Params &, Config &C) { + C.Hover.MacroContentsLimit = Limit; + }); + } } void compile(Fragment::InlayHintsBlock &&F) { diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index 0f11f37..2afeb36 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -361,6 +361,8 @@ struct Fragment { struct HoverBlock { /// Whether hover show a.k.a type. std::optional<Located<bool>> ShowAKA; + /// Limit the number of characters returned when hovering a macro. + std::optional<Located<uint32_t>> MacroContentsLimit; }; HoverBlock Hover; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index 289b7e8..392cf19 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -264,6 +264,10 @@ private: if (auto ShowAKA = boolValue(N, "ShowAKA")) F.ShowAKA = *ShowAKA; }); + Dict.handle("MacroContentsLimit", [&](Node &N) { + if (auto MacroContentsLimit = uint32Value(N, "MacroContentsLimit")) + F.MacroContentsLimit = *MacroContentsLimit; + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index c6075e7..9a8d41d 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -147,17 +147,17 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> { } llvm_unreachable("Unhandled ArgKind enum"); } - std::string getKind(const NestedNameSpecifierLoc &NNSL) { - assert(NNSL.getNestedNameSpecifier()); - switch (NNSL.getNestedNameSpecifier()->getKind()) { + std::string getKind(NestedNameSpecifierLoc NNSL) { + switch (NNSL.getNestedNameSpecifier().getKind()) { + case NestedNameSpecifier::Kind::Null: + llvm_unreachable("unexpected null nested name specifier"); #define NNS_KIND(X) \ - case NestedNameSpecifier::X: \ + case NestedNameSpecifier::Kind::X: \ return #X - NNS_KIND(Identifier); NNS_KIND(Namespace); - NNS_KIND(TypeSpec); + NNS_KIND(Type); NNS_KIND(Global); - NNS_KIND(Super); + NNS_KIND(MicrosoftSuper); #undef NNS_KIND } llvm_unreachable("Unhandled SpecifierKind enum"); @@ -261,7 +261,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> { return TL.getType().getLocalQualifiers().getAsString( Ctx.getPrintingPolicy()); if (const auto *TT = dyn_cast<TagType>(TL.getTypePtr())) - return getDetail(TT->getDecl()); + return getDetail(TT->getOriginalDecl()); if (const auto *DT = dyn_cast<DeducedType>(TL.getTypePtr())) if (DT->isDeduced()) return DT->getDeducedType().getAsString(Ctx.getPrintingPolicy()); @@ -273,16 +273,11 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> { return getDetail(TT->getDecl()); return ""; } - std::string getDetail(const NestedNameSpecifierLoc &NNSL) { - const auto &NNS = *NNSL.getNestedNameSpecifier(); - switch (NNS.getKind()) { - case NestedNameSpecifier::Identifier: - return NNS.getAsIdentifier()->getName().str() + "::"; - case NestedNameSpecifier::Namespace: - return NNS.getAsNamespace()->getNameAsString() + "::"; - default: + std::string getDetail(NestedNameSpecifierLoc NNSL) { + NestedNameSpecifier NNS = NNSL.getNestedNameSpecifier(); + if (NNS.getKind() != NestedNameSpecifier::Kind::Namespace) return ""; - } + return NNS.getAsNamespaceAndPrefix().Namespace->getNameAsString() + "::"; } std::string getDetail(const CXXCtorInitializer *CCI) { if (FieldDecl *FD = CCI->getAnyMember()) @@ -346,8 +341,10 @@ public: return !D || isInjectedClassName(D) || traverseNode("declaration", D, [&] { Base::TraverseDecl(D); }); } - bool TraverseTypeLoc(TypeLoc TL) { - return !TL || traverseNode("type", TL, [&] { Base::TraverseTypeLoc(TL); }); + bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) { + return !TL || traverseNode("type", TL, [&] { + Base::TraverseTypeLoc(TL, TraverseQualifier); + }); } bool TraverseTemplateName(const TemplateName &TN) { return traverseNode("template name", TN, @@ -389,11 +386,11 @@ public: // This means we'd never see 'int' in 'const int'! Work around that here. // (The reason for the behavior is to avoid traversing the nested Type twice, // but we ignore TraverseType anyway). - bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QTL) { + bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QTL, bool TraverseQualifier) { return TraverseTypeLoc(QTL.getUnqualifiedLoc()); } // Uninteresting parts of the AST that don't have locations within them. - bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; } + bool TraverseNestedNameSpecifier(NestedNameSpecifier) { return true; } bool TraverseType(QualType) { return true; } // OpaqueValueExpr blocks traversal, we must explicitly traverse it. @@ -420,7 +417,7 @@ ASTNode dumpAST(const DynTypedNode &N, const syntax::TokenBuffer &Tokens, V.TraverseNestedNameSpecifierLoc( *const_cast<NestedNameSpecifierLoc *>(NNSL)); else if (const auto *NNS = N.get<NestedNameSpecifier>()) - V.TraverseNestedNameSpecifier(const_cast<NestedNameSpecifier *>(NNS)); + V.TraverseNestedNameSpecifier(*NNS); else if (const auto *TL = N.get<TypeLoc>()) V.TraverseTypeLoc(*const_cast<TypeLoc *>(TL)); else if (const auto *QT = N.get<QualType>()) diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index b108957..32018d1 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -366,19 +366,11 @@ public: Visitor(TargetFinder &Outer, RelSet Flags) : Outer(Outer), Flags(Flags) {} void VisitTagType(const TagType *TT) { - Outer.add(TT->getAsTagDecl(), Flags); - } - - void VisitElaboratedType(const ElaboratedType *ET) { - Outer.add(ET->desugar(), Flags); + Outer.add(cast<TagType>(TT)->getOriginalDecl(), Flags); } void VisitUsingType(const UsingType *ET) { - Outer.add(ET->getFoundDecl(), Flags); - } - - void VisitInjectedClassNameType(const InjectedClassNameType *ICNT) { - Outer.add(ICNT->getDecl(), Flags); + Outer.add(ET->getDecl(), Flags); } void VisitDecltypeType(const DecltypeType *DTT) { @@ -483,30 +475,27 @@ public: Visitor(*this, Flags).Visit(T.getTypePtr()); } - void add(const NestedNameSpecifier *NNS, RelSet Flags) { + void add(NestedNameSpecifier NNS, RelSet Flags) { if (!NNS) return; - debug(*NNS, Flags); - switch (NNS->getKind()) { - case NestedNameSpecifier::Namespace: - add(NNS->getAsNamespace(), Flags); - return; - case NestedNameSpecifier::Identifier: - if (Resolver) { - add(Resolver->resolveNestedNameSpecifierToType(NNS), Flags); - } + debug(NNS, Flags); + switch (NNS.getKind()) { + case NestedNameSpecifier::Kind::Namespace: + add(NNS.getAsNamespaceAndPrefix().Namespace, Flags); return; - case NestedNameSpecifier::TypeSpec: - add(QualType(NNS->getAsType(), 0), Flags); + case NestedNameSpecifier::Kind::Type: + add(QualType(NNS.getAsType(), 0), Flags); return; - case NestedNameSpecifier::Global: + case NestedNameSpecifier::Kind::Global: // This should be TUDecl, but we can't get a pointer to it! return; - case NestedNameSpecifier::Super: - add(NNS->getAsRecordDecl(), Flags); + case NestedNameSpecifier::Kind::MicrosoftSuper: + add(NNS.getAsMicrosoftSuper(), Flags); return; + case NestedNameSpecifier::Kind::Null: + llvm_unreachable("unexpected null nested name specifier"); } - llvm_unreachable("unhandled NestedNameSpecifier::SpecifierKind"); + llvm_unreachable("unhandled NestedNameSpecifier::Kind"); } void add(const CXXCtorInitializer *CCI, RelSet Flags) { @@ -555,7 +544,7 @@ allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver) { else if (const NestedNameSpecifierLoc *NNSL = N.get<NestedNameSpecifierLoc>()) Finder.add(NNSL->getNestedNameSpecifier(), Flags); else if (const NestedNameSpecifier *NNS = N.get<NestedNameSpecifier>()) - Finder.add(NNS, Flags); + Finder.add(*NNS, Flags); else if (const TypeLoc *TL = N.get<TypeLoc>()) Finder.add(TL->getType(), Flags); else if (const QualType *QT = N.get<QualType>()) @@ -861,32 +850,25 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) { const HeuristicResolver *Resolver; llvm::SmallVector<ReferenceLoc> Refs; - void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) { - // We only know about qualifier, rest if filled by inner locations. - size_t InitialSize = Refs.size(); - Visit(L.getNamedTypeLoc().getUnqualifiedLoc()); - size_t NewSize = Refs.size(); - // Add qualifier for the newly-added refs. - for (unsigned I = InitialSize; I < NewSize; ++I) { - ReferenceLoc *Ref = &Refs[I]; - // Fill in the qualifier. - assert(!Ref->Qualifier.hasQualifier() && "qualifier already set"); - Ref->Qualifier = L.getQualifierLoc(); - } + void VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc L) { + Refs.push_back(ReferenceLoc{L.getQualifierLoc(), + L.getLocalSourceRange().getBegin(), + /*IsDecl=*/false, + {L.getDecl()}}); } void VisitUsingTypeLoc(UsingTypeLoc L) { - Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), + Refs.push_back(ReferenceLoc{L.getQualifierLoc(), L.getLocalSourceRange().getBegin(), /*IsDecl=*/false, - {L.getFoundDecl()}}); + {L.getDecl()}}); } void VisitTagTypeLoc(TagTypeLoc L) { - Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), + Refs.push_back(ReferenceLoc{L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false, - {L.getDecl()}}); + {L.getOriginalDecl()}}); } void VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc L) { @@ -906,25 +888,18 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) { // 2. 'vector<int>' with mask 'Underlying'. // we want to return only #1 in this case. Refs.push_back(ReferenceLoc{ - NestedNameSpecifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, + L.getQualifierLoc(), L.getTemplateNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias, Resolver)}); } void VisitDeducedTemplateSpecializationTypeLoc( DeducedTemplateSpecializationTypeLoc L) { Refs.push_back(ReferenceLoc{ - NestedNameSpecifierLoc(), L.getNameLoc(), /*IsDecl=*/false, + L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false, explicitReferenceTargets(DynTypedNode::create(L.getType()), DeclRelation::Alias, Resolver)}); } - void VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) { - Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), - TL.getNameLoc(), - /*IsDecl=*/false, - {TL.getDecl()}}); - } - void VisitDependentTemplateSpecializationTypeLoc( DependentTemplateSpecializationTypeLoc L) { Refs.push_back( @@ -943,12 +918,12 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) { } void VisitTypedefTypeLoc(TypedefTypeLoc L) { - if (shouldSkipTypedef(L.getTypedefNameDecl())) + if (shouldSkipTypedef(L.getDecl())) return; - Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), + Refs.push_back(ReferenceLoc{L.getQualifierLoc(), L.getNameLoc(), /*IsDecl=*/false, - {L.getTypedefNameDecl()}}); + {L.getDecl()}}); } void VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc L) { @@ -980,17 +955,6 @@ public: return true; } - bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc L) { - // ElaboratedTypeLoc will reports information for its inner type loc. - // Otherwise we loose information about inner types loc's qualifier. - TypeLoc Inner = L.getNamedTypeLoc().getUnqualifiedLoc(); - if (L.getBeginLoc() == Inner.getBeginLoc()) - return RecursiveASTVisitor::TraverseTypeLoc(Inner); - else - TypeLocsToSkip.insert(Inner.getBeginLoc()); - return RecursiveASTVisitor::TraverseElaboratedTypeLoc(L); - } - bool VisitStmt(Stmt *S) { visitNode(DynTypedNode::create(*S)); return true; @@ -1051,7 +1015,7 @@ public: return true; visitNode(DynTypedNode::create(L)); // Inner type is missing information about its qualifier, skip it. - if (auto TL = L.getTypeLoc()) + if (auto TL = L.getAsTypeLoc()) TypeLocsToSkip.insert(TL.getBeginLoc()); return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L); } @@ -1092,12 +1056,21 @@ private: if (auto *S = N.get<Stmt>()) return refInStmt(S, Resolver); if (auto *NNSL = N.get<NestedNameSpecifierLoc>()) { - // (!) 'DeclRelation::Alias' ensures we do not loose namespace aliases. - return {ReferenceLoc{ - NNSL->getPrefix(), NNSL->getLocalBeginLoc(), false, - explicitReferenceTargets( - DynTypedNode::create(*NNSL->getNestedNameSpecifier()), - DeclRelation::Alias, Resolver)}}; + // (!) 'DeclRelation::Alias' ensures we do not lose namespace aliases. + NestedNameSpecifierLoc Qualifier; + SourceLocation NameLoc; + if (auto TL = NNSL->getAsTypeLoc()) { + Qualifier = TL.getPrefix(); + NameLoc = TL.getNonPrefixBeginLoc(); + } else { + Qualifier = NNSL->getAsNamespaceAndPrefix().Prefix; + NameLoc = NNSL->getLocalBeginLoc(); + } + return { + ReferenceLoc{Qualifier, NameLoc, false, + explicitReferenceTargets( + DynTypedNode::create(NNSL->getNestedNameSpecifier()), + DeclRelation::Alias, Resolver)}}; } if (const TypeLoc *TL = N.get<TypeLoc>()) return refInTypeLoc(*TL, Resolver); @@ -1210,8 +1183,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R) { OS << "}"; if (R.Qualifier) { OS << ", qualifier = '"; - R.Qualifier.getNestedNameSpecifier()->print(OS, - PrintingPolicy(LangOptions())); + R.Qualifier.getNestedNameSpecifier().print(OS, + PrintingPolicy(LangOptions())); OS << "'"; } if (R.IsDecl) diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp index 7c0eb96..c6afd0b 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -833,6 +833,10 @@ bool OverlayCDB::setCompileCommand(PathRef File, std::unique_ptr<ProjectModules> OverlayCDB::getProjectModules(PathRef File) const { auto MDB = DelegatingCDB::getProjectModules(File); + if (!MDB) { + log("Failed to get compilation Database for {0}", File); + return {}; + } MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command, PathRef CommandPath) { Mangler(Command, CommandPath); diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 1e0718d..9eec322 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -18,6 +18,7 @@ #include "Protocol.h" #include "Selection.h" #include "SourceCode.h" +#include "SymbolDocumentation.h" #include "clang-include-cleaner/Analysis.h" #include "clang-include-cleaner/IncludeSpeller.h" #include "clang-include-cleaner/Types.h" @@ -41,6 +42,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" @@ -170,13 +172,14 @@ HoverInfo::PrintedType printType(QualType QT, ASTContext &ASTCtx, QT = QT->castAs<DecltypeType>()->getUnderlyingType(); HoverInfo::PrintedType Result; llvm::raw_string_ostream OS(Result.Type); - // Special case: if the outer type is a tag type without qualifiers, then - // include the tag for extra clarity. - // This isn't very idiomatic, so don't attempt it for complex cases, including - // pointers/references, template specializations, etc. + // Special case: if the outer type is a canonical tag type, then include the + // tag for extra clarity. This isn't very idiomatic, so don't attempt it for + // complex cases, including pointers/references, template specializations, + // etc. if (!QT.isNull() && !QT.hasQualifiers() && PP.SuppressTagKeyword) { - if (auto *TT = llvm::dyn_cast<TagType>(QT.getTypePtr())) - OS << TT->getDecl()->getKindName() << " "; + if (auto *TT = llvm::dyn_cast<TagType>(QT.getTypePtr()); + TT && TT->isCanonicalUnqualified()) + OS << TT->getOriginalDecl()->getKindName() << " "; } QT.print(OS, PP); @@ -451,8 +454,7 @@ std::optional<std::string> printExprValue(const Expr *E, Constant.Val.getInt().getSignificantBits() <= 64) { // Compare to int64_t to avoid bit-width match requirements. int64_t Val = Constant.Val.getInt().getExtValue(); - for (const EnumConstantDecl *ECD : - T->castAs<EnumType>()->getDecl()->enumerators()) + for (const EnumConstantDecl *ECD : T->castAsEnumDecl()->enumerators()) if (ECD->getInitVal() == Val) return llvm::formatv("{0} ({1})", ECD->getNameAsString(), printHex(Constant.Val.getInt())) @@ -627,6 +629,9 @@ HoverInfo getHoverContents(const NamedDecl *D, const PrintingPolicy &PP, HI.Name = printName(Ctx, *D); const auto *CommentD = getDeclForComment(D); HI.Documentation = getDeclComment(Ctx, *CommentD); + // save the language options to be able to create the comment::CommandTraits + // to parse the documentation + HI.CommentOpts = D->getASTContext().getLangOpts().CommentOpts; enhanceFromIndex(HI, *CommentD, Index); if (HI.Documentation.empty()) HI.Documentation = synthesizeDocumentation(D); @@ -789,7 +794,9 @@ HoverInfo getHoverContents(const DefinedMacro &Macro, const syntax::Token &Tok, for (const auto &ExpandedTok : Expansion->Expanded) { ExpansionText += ExpandedTok.text(SM); ExpansionText += " "; - if (ExpansionText.size() > 2048) { + const Config &Cfg = Config::current(); + const size_t Limit = static_cast<size_t>(Cfg.Hover.MacroContentsLimit); + if (Limit && ExpansionText.size() > Limit) { ExpansionText.clear(); break; } @@ -826,7 +833,7 @@ std::optional<HoverInfo> getThisExprHoverContents(const CXXThisExpr *CTE, ASTContext &ASTCtx, const PrintingPolicy &PP) { QualType OriginThisType = CTE->getType()->getPointeeType(); - QualType ClassType = declaredType(OriginThisType->getAsTagDecl()); + QualType ClassType = declaredType(OriginThisType->castAsTagDecl()); // For partial specialization class, origin `this` pointee type will be // parsed as `InjectedClassNameType`, which will ouput template arguments // like "type-parameter-0-0". So we retrieve user written class type in this @@ -967,10 +974,11 @@ void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) { const auto &Ctx = ND.getASTContext(); if (auto *RD = llvm::dyn_cast<RecordDecl>(&ND)) { - if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RD->getTypeForDecl())) + CanQualType RT = Ctx.getCanonicalTagType(RD); + if (auto Size = Ctx.getTypeSizeInCharsIfKnown(RT)) HI.Size = Size->getQuantity() * 8; if (!RD->isDependentType() && RD->isCompleteDefinition()) - HI.Align = Ctx.getTypeAlign(RD->getTypeForDecl()); + HI.Align = Ctx.getTypeAlign(RT); return; } @@ -1267,10 +1275,10 @@ std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos, HoverCountMetric.record(1, "include"); HoverInfo HI; HI.Name = std::string(llvm::sys::path::filename(Inc.Resolved)); - // FIXME: We don't have a fitting value for Kind. HI.Definition = URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str(); HI.DefinitionLanguage = ""; + HI.Kind = index::SymbolKind::IncludeDirective; maybeAddUsedSymbols(AST, HI, Inc); return HI; } @@ -1388,9 +1396,241 @@ static std::string formatOffset(uint64_t OffsetInBits) { return Offset; } -markup::Document HoverInfo::present() const { +void HoverInfo::calleeArgInfoToMarkupParagraph(markup::Paragraph &P) const { + assert(CallPassType); + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + OS << "Passed "; + if (CallPassType->PassBy != HoverInfo::PassType::Value) { + OS << "by "; + if (CallPassType->PassBy == HoverInfo::PassType::ConstRef) + OS << "const "; + OS << "reference "; + } + if (CalleeArgInfo->Name) + OS << "as " << CalleeArgInfo->Name; + else if (CallPassType->PassBy == HoverInfo::PassType::Value) + OS << "by value"; + if (CallPassType->Converted && CalleeArgInfo->Type) + OS << " (converted to " << CalleeArgInfo->Type->Type << ")"; + P.appendText(OS.str()); +} + +void HoverInfo::usedSymbolNamesToMarkup(markup::Document &Output) const { + markup::Paragraph &P = Output.addParagraph(); + P.appendText("provides "); + + const std::vector<std::string>::size_type SymbolNamesLimit = 5; + auto Front = llvm::ArrayRef(UsedSymbolNames).take_front(SymbolNamesLimit); + + llvm::interleave( + Front, [&](llvm::StringRef Sym) { P.appendCode(Sym); }, + [&] { P.appendText(", "); }); + if (UsedSymbolNames.size() > Front.size()) { + P.appendText(" and "); + P.appendText(std::to_string(UsedSymbolNames.size() - Front.size())); + P.appendText(" more"); + } +} + +void HoverInfo::providerToMarkupParagraph(markup::Document &Output) const { + markup::Paragraph &DI = Output.addParagraph(); + DI.appendText("provided by"); + DI.appendSpace(); + DI.appendCode(Provider); +} + +void HoverInfo::definitionScopeToMarkup(markup::Document &Output) const { + std::string Buffer; + + // Append scope comment, dropping trailing "::". + // Note that we don't print anything for global namespace, to not annoy + // non-c++ projects or projects that are not making use of namespaces. + if (!LocalScope.empty()) { + // Container name, e.g. class, method, function. + // We might want to propagate some info about container type to print + // function foo, class X, method X::bar, etc. + Buffer += "// In " + llvm::StringRef(LocalScope).rtrim(':').str() + '\n'; + } else if (NamespaceScope && !NamespaceScope->empty()) { + Buffer += "// In namespace " + + llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n'; + } + + if (!AccessSpecifier.empty()) { + Buffer += AccessSpecifier + ": "; + } + + Buffer += Definition; + + Output.addCodeBlock(Buffer, DefinitionLanguage); +} + +void HoverInfo::valueToMarkupParagraph(markup::Paragraph &P) const { + P.appendText("Value = "); + P.appendCode(*Value); +} + +void HoverInfo::offsetToMarkupParagraph(markup::Paragraph &P) const { + P.appendText("Offset: " + formatOffset(*Offset)); +} + +void HoverInfo::sizeToMarkupParagraph(markup::Paragraph &P) const { + P.appendText("Size: " + formatSize(*Size)); + if (Padding && *Padding != 0) { + P.appendText(llvm::formatv(" (+{0} padding)", formatSize(*Padding)).str()); + } + if (Align) + P.appendText(", alignment " + formatSize(*Align)); +} + +markup::Document HoverInfo::presentDoxygen() const { + markup::Document Output; + // Header contains a text of the form: + // variable `var` + // + // class `X` + // + // function `foo` + // + // expression + // + // Note that we are making use of a level-3 heading because VSCode renders + // level 1 and 2 headers in a huge font, see + // https://github.com/microsoft/vscode/issues/88417 for details. + markup::Paragraph &Header = Output.addHeading(3); + if (Kind != index::SymbolKind::Unknown && + Kind != index::SymbolKind::IncludeDirective) + Header.appendText(index::getSymbolKindString(Kind)).appendSpace(); + assert(!Name.empty() && "hover triggered on a nameless symbol"); + + if (Kind == index::SymbolKind::IncludeDirective) { + Header.appendCode(Name); + + if (!Definition.empty()) + Output.addParagraph().appendCode(Definition); + + if (!UsedSymbolNames.empty()) { + Output.addRuler(); + usedSymbolNamesToMarkup(Output); + } + + return Output; + } + + if (!Definition.empty()) { + Output.addRuler(); + definitionScopeToMarkup(Output); + } else { + Header.appendCode(Name); + } + + if (!Provider.empty()) { + providerToMarkupParagraph(Output); + } + + // Put a linebreak after header to increase readability. + Output.addRuler(); + + SymbolDocCommentVisitor SymbolDoc(Documentation, CommentOpts); + + if (SymbolDoc.hasBriefCommand()) { + SymbolDoc.briefToMarkup(Output.addParagraph()); + Output.addRuler(); + } + + // For functions we display signature in a list form, e.g.: + // Template Parameters: + // - `typename T` - description + // Parameters: + // - `bool param1` - description + // - `int param2 = 5` - description + // Returns + // `type` - description + if (TemplateParameters && !TemplateParameters->empty()) { + Output.addParagraph().appendBoldText("Template Parameters:"); + markup::BulletList &L = Output.addBulletList(); + for (const auto &Param : *TemplateParameters) { + markup::Paragraph &P = L.addItem().addParagraph(); + P.appendCode(llvm::to_string(Param)); + if (SymbolDoc.isTemplateTypeParmDocumented(llvm::to_string(Param.Name))) { + P.appendText(" - "); + SymbolDoc.templateTypeParmDocToMarkup(llvm::to_string(Param.Name), P); + } + } + Output.addRuler(); + } + + if (Parameters && !Parameters->empty()) { + Output.addParagraph().appendBoldText("Parameters:"); + markup::BulletList &L = Output.addBulletList(); + for (const auto &Param : *Parameters) { + markup::Paragraph &P = L.addItem().addParagraph(); + P.appendCode(llvm::to_string(Param)); + + if (SymbolDoc.isParameterDocumented(llvm::to_string(Param.Name))) { + P.appendText(" - "); + SymbolDoc.parameterDocToMarkup(llvm::to_string(Param.Name), P); + } + } + Output.addRuler(); + } + + // Print Types on their own lines to reduce chances of getting line-wrapped by + // editor, as they might be long. + if (ReturnType && + ((ReturnType->Type != "void" && !ReturnType->AKA.has_value()) || + (ReturnType->AKA.has_value() && ReturnType->AKA != "void"))) { + Output.addParagraph().appendBoldText("Returns:"); + markup::Paragraph &P = Output.addParagraph(); + P.appendCode(llvm::to_string(*ReturnType)); + + if (SymbolDoc.hasReturnCommand()) { + P.appendText(" - "); + SymbolDoc.returnToMarkup(P); + } + Output.addRuler(); + } + + // add specially handled doxygen commands. + SymbolDoc.warningsToMarkup(Output); + SymbolDoc.notesToMarkup(Output); + + // add any other documentation. + SymbolDoc.docToMarkup(Output); + + Output.addRuler(); + + // Don't print Type after Parameters or ReturnType as this will just duplicate + // the information + if (Type && !ReturnType && !Parameters) + Output.addParagraph().appendText("Type: ").appendCode( + llvm::to_string(*Type)); + if (Value) { + valueToMarkupParagraph(Output.addParagraph()); + } + + if (Offset) + offsetToMarkupParagraph(Output.addParagraph()); + if (Size) { + sizeToMarkupParagraph(Output.addParagraph()); + } + + if (CalleeArgInfo) { + calleeArgInfoToMarkupParagraph(Output.addParagraph()); + } + + if (!UsedSymbolNames.empty()) { + Output.addRuler(); + usedSymbolNamesToMarkup(Output); + } + + return Output; +} + +markup::Document HoverInfo::presentDefault() const { + markup::Document Output; // Header contains a text of the form: // variable `var` // @@ -1404,17 +1644,14 @@ markup::Document HoverInfo::present() const { // level 1 and 2 headers in a huge font, see // https://github.com/microsoft/vscode/issues/88417 for details. markup::Paragraph &Header = Output.addHeading(3); - if (Kind != index::SymbolKind::Unknown) + if (Kind != index::SymbolKind::Unknown && + Kind != index::SymbolKind::IncludeDirective) Header.appendText(index::getSymbolKindString(Kind)).appendSpace(); assert(!Name.empty() && "hover triggered on a nameless symbol"); Header.appendCode(Name); if (!Provider.empty()) { - markup::Paragraph &DI = Output.addParagraph(); - DI.appendText("provided by"); - DI.appendSpace(); - DI.appendCode(Provider); - Output.addRuler(); + providerToMarkupParagraph(Output); } // Put a linebreak after header to increase readability. @@ -1432,7 +1669,7 @@ markup::Document HoverInfo::present() const { } if (Parameters && !Parameters->empty()) { - Output.addParagraph().appendText("Parameters: "); + Output.addParagraph().appendText("Parameters:"); markup::BulletList &L = Output.addBulletList(); for (const auto &Param : *Parameters) L.addItem().addParagraph().appendCode(llvm::to_string(Param)); @@ -1445,41 +1682,17 @@ markup::Document HoverInfo::present() const { llvm::to_string(*Type)); if (Value) { - markup::Paragraph &P = Output.addParagraph(); - P.appendText("Value = "); - P.appendCode(*Value); + valueToMarkupParagraph(Output.addParagraph()); } if (Offset) - Output.addParagraph().appendText("Offset: " + formatOffset(*Offset)); + offsetToMarkupParagraph(Output.addParagraph()); if (Size) { - auto &P = Output.addParagraph().appendText("Size: " + formatSize(*Size)); - if (Padding && *Padding != 0) { - P.appendText( - llvm::formatv(" (+{0} padding)", formatSize(*Padding)).str()); - } - if (Align) - P.appendText(", alignment " + formatSize(*Align)); + sizeToMarkupParagraph(Output.addParagraph()); } if (CalleeArgInfo) { - assert(CallPassType); - std::string Buffer; - llvm::raw_string_ostream OS(Buffer); - OS << "Passed "; - if (CallPassType->PassBy != HoverInfo::PassType::Value) { - OS << "by "; - if (CallPassType->PassBy == HoverInfo::PassType::ConstRef) - OS << "const "; - OS << "reference "; - } - if (CalleeArgInfo->Name) - OS << "as " << CalleeArgInfo->Name; - else if (CallPassType->PassBy == HoverInfo::PassType::Value) - OS << "by value"; - if (CallPassType->Converted && CalleeArgInfo->Type) - OS << " (converted to " << CalleeArgInfo->Type->Type << ")"; - Output.addParagraph().appendText(OS.str()); + calleeArgInfoToMarkupParagraph(Output.addParagraph()); } if (!Documentation.empty()) @@ -1487,49 +1700,12 @@ markup::Document HoverInfo::present() const { if (!Definition.empty()) { Output.addRuler(); - std::string Buffer; - - if (!Definition.empty()) { - // Append scope comment, dropping trailing "::". - // Note that we don't print anything for global namespace, to not annoy - // non-c++ projects or projects that are not making use of namespaces. - if (!LocalScope.empty()) { - // Container name, e.g. class, method, function. - // We might want to propagate some info about container type to print - // function foo, class X, method X::bar, etc. - Buffer += - "// In " + llvm::StringRef(LocalScope).rtrim(':').str() + '\n'; - } else if (NamespaceScope && !NamespaceScope->empty()) { - Buffer += "// In namespace " + - llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n'; - } - - if (!AccessSpecifier.empty()) { - Buffer += AccessSpecifier + ": "; - } - - Buffer += Definition; - } - - Output.addCodeBlock(Buffer, DefinitionLanguage); + definitionScopeToMarkup(Output); } if (!UsedSymbolNames.empty()) { Output.addRuler(); - markup::Paragraph &P = Output.addParagraph(); - P.appendText("provides "); - - const std::vector<std::string>::size_type SymbolNamesLimit = 5; - auto Front = llvm::ArrayRef(UsedSymbolNames).take_front(SymbolNamesLimit); - - llvm::interleave( - Front, [&](llvm::StringRef Sym) { P.appendCode(Sym); }, - [&] { P.appendText(", "); }); - if (UsedSymbolNames.size() > Front.size()) { - P.appendText(" and "); - P.appendText(std::to_string(UsedSymbolNames.size() - Front.size())); - P.appendText(" more"); - } + usedSymbolNamesToMarkup(Output); } return Output; @@ -1538,21 +1714,19 @@ markup::Document HoverInfo::present() const { std::string HoverInfo::present(MarkupKind Kind) const { if (Kind == MarkupKind::Markdown) { const Config &Cfg = Config::current(); - if ((Cfg.Documentation.CommentFormat == - Config::CommentFormatPolicy::Markdown) || - (Cfg.Documentation.CommentFormat == - Config::CommentFormatPolicy::Doxygen)) - // If the user prefers Markdown, we use the present() method to generate - // the Markdown output. - return present().asMarkdown(); + if (Cfg.Documentation.CommentFormat == + Config::CommentFormatPolicy::Markdown) + return presentDefault().asMarkdown(); + if (Cfg.Documentation.CommentFormat == Config::CommentFormatPolicy::Doxygen) + return presentDoxygen().asMarkdown(); if (Cfg.Documentation.CommentFormat == Config::CommentFormatPolicy::PlainText) // If the user prefers plain text, we use the present() method to generate // the plain text output. - return present().asEscapedMarkdown(); + return presentDefault().asEscapedMarkdown(); } - return present().asPlainText(); + return presentDefault().asPlainText(); } // If the backtick at `Offset` starts a probable quoted range, return the range diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h index 2f65431..614180a 100644 --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -74,6 +74,8 @@ struct HoverInfo { std::optional<Range> SymRange; index::SymbolKind Kind = index::SymbolKind::Unknown; std::string Documentation; + // required to create a comments::CommandTraits object without the ASTContext + CommentOptions CommentOpts; /// Source code containing the definition of the symbol. std::string Definition; const char *DefinitionLanguage = "cpp"; @@ -118,10 +120,23 @@ struct HoverInfo { // alphabetical order. std::vector<std::string> UsedSymbolNames; - /// Produce a user-readable information. - markup::Document present() const; - + /// Produce a user-readable information based on the specified markup kind. std::string present(MarkupKind Kind) const; + +private: + void usedSymbolNamesToMarkup(markup::Document &Output) const; + void providerToMarkupParagraph(markup::Document &Output) const; + void definitionScopeToMarkup(markup::Document &Output) const; + void calleeArgInfoToMarkupParagraph(markup::Paragraph &P) const; + void valueToMarkupParagraph(markup::Paragraph &P) const; + void offsetToMarkupParagraph(markup::Paragraph &P) const; + void sizeToMarkupParagraph(markup::Paragraph &P) const; + + /// Parse and render the hover information as Doxygen documentation. + markup::Document presentDoxygen() const; + + /// Render the hover information as a default documentation. + markup::Document presentDefault() const; }; inline bool operator==(const HoverInfo::PrintedType &LHS, diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp index 50bc2bd..c27d960 100644 --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -173,7 +173,7 @@ std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel, // `enum x : int;' is not formally an incomplete type. // We may need a full definition anyway. if (auto * ET = llvm::dyn_cast<EnumType>(T)) - if (!ET->getDecl()->getDefinition()) + if (!ET->getOriginalDecl()->getDefinition()) return fixIncompleteType(*T); } } @@ -400,35 +400,35 @@ std::optional<CheapUnresolvedName> extractUnresolvedNameCheaply( CheapUnresolvedName Result; Result.Name = Unresolved.getAsString(); if (SS && SS->isNotEmpty()) { // "::" or "ns::" - if (auto *Nested = SS->getScopeRep()) { - if (Nested->getKind() == NestedNameSpecifier::Global) { - Result.ResolvedScope = ""; - } else if (const NamespaceBaseDecl *NSB = Nested->getAsNamespace()) { - if (const auto *NS = dyn_cast<NamespaceDecl>(NSB)) { - std::string SpecifiedNS = printNamespaceScope(*NS); - std::optional<std::string> Spelling = getSpelledSpecifier(*SS, SM); - - // Check the specifier spelled in the source. - // If the resolved scope doesn't end with the spelled scope, the - // resolved scope may come from a sema typo correction. For example, - // sema assumes that "clangd::" is a typo of "clang::" and uses - // "clang::" as the specified scope in: - // namespace clang { clangd::X; } - // In this case, we use the "typo" specifier as extra scope instead - // of using the scope assumed by sema. - if (!Spelling || llvm::StringRef(SpecifiedNS).ends_with(*Spelling)) { - Result.ResolvedScope = std::move(SpecifiedNS); - } else { - Result.UnresolvedScope = std::move(*Spelling); - } + NestedNameSpecifier Nested = SS->getScopeRep(); + if (Nested.getKind() == NestedNameSpecifier::Kind::Global) { + Result.ResolvedScope = ""; + } else if (Nested.getKind() == NestedNameSpecifier::Kind::Namespace) { + const NamespaceBaseDecl *NSB = Nested.getAsNamespaceAndPrefix().Namespace; + if (const auto *NS = dyn_cast<NamespaceDecl>(NSB)) { + std::string SpecifiedNS = printNamespaceScope(*NS); + std::optional<std::string> Spelling = getSpelledSpecifier(*SS, SM); + + // Check the specifier spelled in the source. + // If the resolved scope doesn't end with the spelled scope, the + // resolved scope may come from a sema typo correction. For example, + // sema assumes that "clangd::" is a typo of "clang::" and uses + // "clang::" as the specified scope in: + // namespace clang { clangd::X; } + // In this case, we use the "typo" specifier as extra scope instead + // of using the scope assumed by sema. + if (!Spelling || llvm::StringRef(SpecifiedNS).ends_with(*Spelling)) { + Result.ResolvedScope = std::move(SpecifiedNS); } else { - Result.ResolvedScope = printNamespaceScope(*cast<NamespaceAliasDecl>(NSB)->getNamespace()); + Result.UnresolvedScope = std::move(*Spelling); } } else { - // We don't fix symbols in scopes that are not top-level e.g. class - // members, as we don't collect includes for them. - return std::nullopt; + Result.ResolvedScope = printNamespaceScope(*cast<NamespaceAliasDecl>(NSB)->getNamespace()); } + } else { + // We don't fix symbols in scopes that are not top-level e.g. class + // members, as we don't collect includes for them. + return std::nullopt; } } diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp index 197c62c..cd479e1 100644 --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -55,18 +55,24 @@ void stripLeadingUnderscores(StringRef &Name) { Name = Name.ltrim('_'); } // getDeclForType() returns the decl responsible for Type's spelling. // This is the inverse of ASTContext::getTypeDeclType(). -template <typename Ty, typename = decltype(((Ty *)nullptr)->getDecl())> -const NamedDecl *getDeclForTypeImpl(const Ty *T) { - return T->getDecl(); -} -const NamedDecl *getDeclForTypeImpl(const void *T) { return nullptr; } const NamedDecl *getDeclForType(const Type *T) { switch (T->getTypeClass()) { -#define ABSTRACT_TYPE(TY, BASE) -#define TYPE(TY, BASE) \ - case Type::TY: \ - return getDeclForTypeImpl(llvm::cast<TY##Type>(T)); -#include "clang/AST/TypeNodes.inc" + case Type::Enum: + case Type::Record: + case Type::InjectedClassName: + return cast<TagType>(T)->getOriginalDecl(); + case Type::TemplateSpecialization: + return cast<TemplateSpecializationType>(T) + ->getTemplateName() + .getAsTemplateDecl(/*IgnoreDeduced=*/true); + case Type::Typedef: + return cast<TypedefType>(T)->getDecl(); + case Type::UnresolvedUsing: + return cast<UnresolvedUsingType>(T)->getDecl(); + case Type::Using: + return cast<UsingType>(T)->getDecl(); + default: + return nullptr; } llvm_unreachable("Unknown TypeClass enum"); } @@ -81,8 +87,6 @@ llvm::StringRef getSimpleName(const NamedDecl &D) { return getSimpleName(D.getDeclName()); } llvm::StringRef getSimpleName(QualType T) { - if (const auto *ET = llvm::dyn_cast<ElaboratedType>(T)) - return getSimpleName(ET->getNamedType()); if (const auto *BT = llvm::dyn_cast<BuiltinType>(T)) { PrintingPolicy PP(LangOptions{}); PP.adjustForCPlusPlus(); diff --git a/clang-tools-extra/clangd/ModulesBuilder.cpp b/clang-tools-extra/clangd/ModulesBuilder.cpp index 56d00c2..64f22b9 100644 --- a/clang-tools-extra/clangd/ModulesBuilder.cpp +++ b/clang-tools-extra/clangd/ModulesBuilder.cpp @@ -14,6 +14,8 @@ #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/ModuleCache.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/CommandLine.h" + #include <queue> namespace clang { @@ -21,6 +23,12 @@ namespace clangd { namespace { +llvm::cl::opt<bool> DebugModulesBuilder( + "debug-modules-builder", + llvm::cl::desc("Don't remove clangd's built module files for debugging. " + "Remember to remove them later after debugging."), + llvm::cl::init(false)); + // Create a path to store module files. Generally it should be: // // {TEMP_DIRS}/clangd/module_files/{hashed-file-name}-%%-%%-%%-%%-%%-%%/. @@ -122,7 +130,7 @@ struct ModuleFile { } ~ModuleFile() { - if (!ModuleFilePath.empty()) + if (!ModuleFilePath.empty() && !DebugModulesBuilder) llvm::sys::fs::remove(ModuleFilePath); } @@ -315,7 +323,7 @@ buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName, Cmds += Arg; } - clangd::vlog("Failed to compile {0} with command: {1}.", ModuleUnitFileName, + clangd::vlog("Failed to compile {0} with command: {1}", ModuleUnitFileName, Cmds); std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString(); @@ -325,7 +333,10 @@ buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName, return llvm::createStringError( llvm::formatv("Failed to compile {0}. Use '--log=verbose' to view " - "detailed failure reasons.", + "detailed failure reasons. It is helpful to use " + "'--debug-modules-builder' flag to keep the clangd's " + "built module files to reproduce the failure for " + "debugging. Remember to remove them after debugging.", ModuleUnitFileName)); } diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 2c858e2..560b8e0 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -297,6 +297,9 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { switch (Kind) { + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case index::SymbolKind::IncludeDirective: case index::SymbolKind::Unknown: return SymbolKind::Variable; case index::SymbolKind::Module: diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 3f630b0..dc4afe1 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -143,6 +143,9 @@ categorize(const index::SymbolInfo &D) { case index::SymbolKind::Parameter: case index::SymbolKind::NonTypeTemplateParm: return SymbolQualitySignals::Variable; + // FIXME: for backwards compatibility, the include directive kind is treated + // the same as Unknown + case index::SymbolKind::IncludeDirective: case index::SymbolKind::Using: case index::SymbolKind::Module: case index::SymbolKind::Unknown: diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp index e561513..672e996 100644 --- a/clang-tools-extra/clangd/ScanningProjectModules.cpp +++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp @@ -122,8 +122,17 @@ ModuleDependencyScanner::scan(PathRef FilePath, ModuleDependencyInfo Result; if (ScanningResult->Provides) { - ModuleNameToSource[ScanningResult->Provides->ModuleName] = FilePath; Result.ModuleName = ScanningResult->Provides->ModuleName; + + auto [Iter, Inserted] = ModuleNameToSource.try_emplace( + ScanningResult->Provides->ModuleName, FilePath); + + if (!Inserted && Iter->second != FilePath) { + elog("Detected multiple source files ({0}, {1}) declaring the same " + "module: '{2}'. " + "Now clangd may find the wrong source in such case.", + Iter->second, FilePath, ScanningResult->Provides->ModuleName); + } } for (auto &Required : ScanningResult->Requires) diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp index 277cb87..06165df 100644 --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -62,7 +62,8 @@ void recordMetrics(const SelectionTree &S, const LangOptions &Lang) { } // Return the range covering a node and all its children. -SourceRange getSourceRange(const DynTypedNode &N) { +SourceRange getSourceRange(const DynTypedNode &N, + bool IncludeQualifier = false) { // MemberExprs to implicitly access anonymous fields should not claim any // tokens for themselves. Given: // struct A { struct { int b; }; }; @@ -80,7 +81,7 @@ SourceRange getSourceRange(const DynTypedNode &N) { ? getSourceRange(DynTypedNode::create(*ME->getBase())) : SourceRange(); } - return N.getSourceRange(); + return N.getSourceRange(IncludeQualifier); } // An IntervalSet maintains a set of disjoint subranges of an array. @@ -643,8 +644,9 @@ public: } return traverseNode(X, [&] { return Base::TraverseDecl(X); }); } - bool TraverseTypeLoc(TypeLoc X) { - return traverseNode(&X, [&] { return Base::TraverseTypeLoc(X); }); + bool TraverseTypeLoc(TypeLoc X, bool TraverseQualifier = true) { + return traverseNode( + &X, [&] { return Base::TraverseTypeLoc(X, TraverseQualifier); }); } bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &X) { return traverseNode(&X, @@ -690,7 +692,8 @@ public: // This means we'd never see 'int' in 'const int'! Work around that here. // (The reason for the behavior is to avoid traversing the nested Type twice, // but we ignore TraverseType anyway). - bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX) { + bool TraverseQualifiedTypeLoc(QualifiedTypeLoc QX, + bool TraverseQualifier = true) { return traverseNode<TypeLoc>( &QX, [&] { return TraverseTypeLoc(QX.getUnqualifiedLoc()); }); } @@ -698,7 +701,7 @@ public: return traverseNode(&PL, [&] { return Base::TraverseObjCProtocolLoc(PL); }); } // Uninteresting parts of the AST that don't have locations within them. - bool TraverseNestedNameSpecifier(NestedNameSpecifier *) { return true; } + bool TraverseNestedNameSpecifier(NestedNameSpecifier) { return true; } bool TraverseType(QualType) { return true; } // The DeclStmt for the loop variable claims to cover the whole range @@ -798,7 +801,7 @@ private: // An optimization for a common case: nodes outside macro expansions that // don't intersect the selection may be recursively skipped. bool canSafelySkipNode(const DynTypedNode &N) { - SourceRange S = getSourceRange(N); + SourceRange S = getSourceRange(N, /*IncludeQualifier=*/true); if (auto *TL = N.get<TypeLoc>()) { // FIXME: TypeLoc::getBeginLoc()/getEndLoc() are pretty fragile // heuristics. We should consider only pruning critical TypeLoc nodes, to diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index e6d5cf7..2b151b1 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -1127,21 +1127,6 @@ public: return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L); } - // findExplicitReferences will walk nested-name-specifiers and - // find anything that can be resolved to a Decl. However, non-leaf - // components of nested-name-specifiers which are dependent names - // (kind "Identifier") cannot be resolved to a decl, so we visit - // them here. - bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) { - if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) { - if (NNS->getKind() == NestedNameSpecifier::Identifier) - H.addToken(Q.getLocalBeginLoc(), HighlightingKind::Type) - .addModifier(HighlightingModifier::DependentName) - .addModifier(HighlightingModifier::ClassScope); - } - return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q); - } - private: HighlightingsBuilder &H; }; diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp index dd7116e..3353121 100644 --- a/clang-tools-extra/clangd/SemanticSelection.cpp +++ b/clang-tools-extra/clangd/SemanticSelection.cpp @@ -175,9 +175,8 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) { return collectFoldingRanges(SyntaxTree, TM); } -// FIXME( usaxena95): Collect PP conditional regions, includes and other code -// regions (e.g. public/private/protected sections of classes, control flow -// statement bodies). +// FIXME( usaxena95): Collect includes and other code regions (e.g. +// public/private/protected sections of classes, control flow statement bodies). // Related issue: https://github.com/clangd/clangd/issues/310 llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(const std::string &Code, bool LineFoldingOnly) { @@ -186,12 +185,6 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) { auto DirectiveStructure = DirectiveTree::parse(OrigStream); chooseConditionalBranches(DirectiveStructure, OrigStream); - // FIXME: Provide ranges in the disabled-PP regions as well. - auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream); - - auto ParseableStream = cook(Preprocessed, genericLangOpts()); - pairBrackets(ParseableStream); - std::vector<FoldingRange> Result; auto AddFoldingRange = [&](Position Start, Position End, llvm::StringLiteral Kind) { @@ -220,7 +213,32 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) { auto EndPosition = [&](const Token &T) { return offsetToPosition(Code, EndOffset(T)); }; + + // Preprocessor directives + auto PPRanges = pairDirectiveRanges(DirectiveStructure, OrigStream); + for (const auto &R : PPRanges) { + auto BTok = OrigStream.tokens()[R.Begin]; + auto ETok = OrigStream.tokens()[R.End]; + if (ETok.Kind == tok::eof) + continue; + if (BTok.Line >= ETok.Line) + continue; + + Position Start = EndPosition(BTok); + Position End = StartPosition(ETok); + if (LineFoldingOnly) + End.line--; + AddFoldingRange(Start, End, FoldingRange::REGION_KIND); + } + + // FIXME: Provide ranges in the disabled-PP regions as well. + auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream); + + auto ParseableStream = cook(Preprocessed, genericLangOpts()); + pairBrackets(ParseableStream); + auto Tokens = ParseableStream.tokens(); + // Brackets. for (const auto &Tok : Tokens) { if (auto *Paired = Tok.pair()) { @@ -240,6 +258,7 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) { return OriginalToken(T).Length >= 2 && Code.substr(StartOffset(T), 2) == "/*"; }; + // Multi-line comments. for (auto *T = Tokens.begin(); T != Tokens.end();) { if (T->Kind != tok::comment) { diff --git a/clang-tools-extra/clangd/SymbolDocumentation.cpp b/clang-tools-extra/clangd/SymbolDocumentation.cpp new file mode 100644 index 0000000..9ae1ef3 --- /dev/null +++ b/clang-tools-extra/clangd/SymbolDocumentation.cpp @@ -0,0 +1,386 @@ +//===--- SymbolDocumentation.cpp ==-------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "SymbolDocumentation.h" + +#include "support/Markup.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/AST/CommentVisitor.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace clangd { +namespace { + +std::string commandMarkerAsString(comments::CommandMarkerKind CommandMarker) { + switch (CommandMarker) { + case comments::CommandMarkerKind::CMK_At: + return "@"; + case comments::CommandMarkerKind::CMK_Backslash: + return "\\"; + } + llvm_unreachable("Unknown command marker kind"); +} + +void commandToMarkup(markup::Paragraph &Out, StringRef Command, + comments::CommandMarkerKind CommandMarker, + StringRef Args) { + Out.appendBoldText(commandMarkerAsString(CommandMarker) + Command.str()); + Out.appendSpace(); + if (!Args.empty()) { + Out.appendEmphasizedText(Args.str()); + } +} +} // namespace + +class ParagraphToMarkupDocument + : public comments::ConstCommentVisitor<ParagraphToMarkupDocument> { +public: + ParagraphToMarkupDocument(markup::Paragraph &Out, + const comments::CommandTraits &Traits) + : Out(Out), Traits(Traits) {} + + void visitParagraphComment(const comments::ParagraphComment *C) { + if (!C) + return; + + for (const auto *Child = C->child_begin(); Child != C->child_end(); + ++Child) { + visit(*Child); + } + } + + void visitTextComment(const comments::TextComment *C) { + // Always trim leading space after a newline. + StringRef Text = C->getText(); + if (LastChunkEndsWithNewline && C->getText().starts_with(' ')) + Text = Text.drop_front(); + + LastChunkEndsWithNewline = C->hasTrailingNewline(); + Out.appendText(Text.str() + (LastChunkEndsWithNewline ? "\n" : "")); + } + + void visitInlineCommandComment(const comments::InlineCommandComment *C) { + + if (C->getNumArgs() > 0) { + std::string ArgText; + for (unsigned I = 0; I < C->getNumArgs(); ++I) { + if (!ArgText.empty()) + ArgText += " "; + ArgText += C->getArgText(I); + } + + switch (C->getRenderKind()) { + case comments::InlineCommandRenderKind::Monospaced: + Out.appendCode(ArgText); + break; + case comments::InlineCommandRenderKind::Bold: + Out.appendBoldText(ArgText); + break; + case comments::InlineCommandRenderKind::Emphasized: + Out.appendEmphasizedText(ArgText); + break; + default: + commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(), + ArgText); + break; + } + } else { + if (C->getCommandName(Traits) == "n") { + // \n is a special case, it is used to create a new line. + Out.appendText(" \n"); + LastChunkEndsWithNewline = true; + return; + } + + commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(), + ""); + } + } + + void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC) { + std::string TagText = "<" + STC->getTagName().str(); + + for (unsigned I = 0; I < STC->getNumAttrs(); ++I) { + const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I); + TagText += " " + Attr.Name.str() + "=\"" + Attr.Value.str() + "\""; + } + + if (STC->isSelfClosing()) + TagText += " /"; + TagText += ">"; + + LastChunkEndsWithNewline = STC->hasTrailingNewline(); + Out.appendText(TagText + (LastChunkEndsWithNewline ? "\n" : "")); + } + + void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC) { + LastChunkEndsWithNewline = ETC->hasTrailingNewline(); + Out.appendText("</" + ETC->getTagName().str() + ">" + + (LastChunkEndsWithNewline ? "\n" : "")); + } + +private: + markup::Paragraph &Out; + const comments::CommandTraits &Traits; + + /// If true, the next leading space after a new line is trimmed. + /// Initially set it to true, to always trim the first text line. + bool LastChunkEndsWithNewline = true; +}; + +class ParagraphToString + : public comments::ConstCommentVisitor<ParagraphToString> { +public: + ParagraphToString(llvm::raw_string_ostream &Out, + const comments::CommandTraits &Traits) + : Out(Out), Traits(Traits) {} + + void visitParagraphComment(const comments::ParagraphComment *C) { + if (!C) + return; + + for (const auto *Child = C->child_begin(); Child != C->child_end(); + ++Child) { + visit(*Child); + } + } + + void visitTextComment(const comments::TextComment *C) { Out << C->getText(); } + + void visitInlineCommandComment(const comments::InlineCommandComment *C) { + Out << commandMarkerAsString(C->getCommandMarker()); + Out << C->getCommandName(Traits); + if (C->getNumArgs() > 0) { + for (unsigned I = 0; I < C->getNumArgs(); ++I) + Out << " " << C->getArgText(I); + } + Out << " "; + } + + void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC) { + Out << "<" << STC->getTagName().str(); + + for (unsigned I = 0; I < STC->getNumAttrs(); ++I) { + const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I); + Out << " " << Attr.Name.str(); + if (!Attr.Value.str().empty()) + Out << "=\"" << Attr.Value.str() << "\""; + } + + if (STC->isSelfClosing()) + Out << " /"; + Out << ">"; + + Out << (STC->hasTrailingNewline() ? "\n" : ""); + } + + void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC) { + Out << "</" << ETC->getTagName().str() << ">" + << (ETC->hasTrailingNewline() ? "\n" : ""); + } + +private: + llvm::raw_string_ostream &Out; + const comments::CommandTraits &Traits; +}; + +class BlockCommentToMarkupDocument + : public comments::ConstCommentVisitor<BlockCommentToMarkupDocument> { +public: + BlockCommentToMarkupDocument(markup::Document &Out, + const comments::CommandTraits &Traits) + : Out(Out), Traits(Traits) {} + + void visitBlockCommandComment(const comments::BlockCommandComment *B) { + + switch (B->getCommandID()) { + case comments::CommandTraits::KCI_arg: + case comments::CommandTraits::KCI_li: + // \li and \arg are special cases, they are used to create a list item. + // In markdown it is a bullet list. + ParagraphToMarkupDocument(Out.addBulletList().addItem().addParagraph(), + Traits) + .visit(B->getParagraph()); + break; + default: { + // Some commands have arguments, like \throws. + // The arguments are not part of the paragraph. + // We need reconstruct them here. + std::string ArgText; + for (unsigned I = 0; I < B->getNumArgs(); ++I) { + if (!ArgText.empty()) + ArgText += " "; + ArgText += B->getArgText(I); + } + auto &P = Out.addParagraph(); + commandToMarkup(P, B->getCommandName(Traits), B->getCommandMarker(), + ArgText); + if (B->getParagraph() && !B->getParagraph()->isWhitespace()) { + // For commands with arguments, the paragraph starts after the first + // space. Therefore we need to append a space manually in this case. + if (!ArgText.empty()) + P.appendSpace(); + ParagraphToMarkupDocument(P, Traits).visit(B->getParagraph()); + } + } + } + } + + void visitVerbatimBlockComment(const comments::VerbatimBlockComment *VB) { + commandToMarkup(Out.addParagraph(), VB->getCommandName(Traits), + VB->getCommandMarker(), ""); + + std::string VerbatimText; + + for (const auto *LI = VB->child_begin(); LI != VB->child_end(); ++LI) { + if (const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) { + VerbatimText += Line->getText().str() + "\n"; + } + } + + Out.addCodeBlock(VerbatimText, ""); + + commandToMarkup(Out.addParagraph(), VB->getCloseName(), + VB->getCommandMarker(), ""); + } + + void visitVerbatimLineComment(const comments::VerbatimLineComment *VL) { + auto &P = Out.addParagraph(); + commandToMarkup(P, VL->getCommandName(Traits), VL->getCommandMarker(), ""); + P.appendSpace().appendCode(VL->getText().str(), true).appendSpace(); + } + +private: + markup::Document &Out; + const comments::CommandTraits &Traits; + StringRef CommentEscapeMarker; +}; + +void SymbolDocCommentVisitor::visitBlockCommandComment( + const comments::BlockCommandComment *B) { + switch (B->getCommandID()) { + case comments::CommandTraits::KCI_brief: { + if (!BriefParagraph) { + BriefParagraph = B->getParagraph(); + return; + } + break; + } + case comments::CommandTraits::KCI_return: + case comments::CommandTraits::KCI_returns: + if (!ReturnParagraph) { + ReturnParagraph = B->getParagraph(); + return; + } + break; + case comments::CommandTraits::KCI_retval: + RetvalParagraphs.push_back(B->getParagraph()); + return; + case comments::CommandTraits::KCI_warning: + WarningParagraphs.push_back(B->getParagraph()); + return; + case comments::CommandTraits::KCI_note: + NoteParagraphs.push_back(B->getParagraph()); + return; + default: + break; + } + + // For all other commands, we store them in the UnhandledCommands map. + // This allows us to keep the order of the comments. + UnhandledCommands[CommentPartIndex] = B; + CommentPartIndex++; +} + +void SymbolDocCommentVisitor::paragraphsToMarkup( + markup::Document &Out, + const llvm::SmallVectorImpl<const comments::ParagraphComment *> &Paragraphs) + const { + if (Paragraphs.empty()) + return; + + for (const auto *P : Paragraphs) { + ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P); + } +} + +void SymbolDocCommentVisitor::briefToMarkup(markup::Paragraph &Out) const { + if (!BriefParagraph) + return; + ParagraphToMarkupDocument(Out, Traits).visit(BriefParagraph); +} + +void SymbolDocCommentVisitor::returnToMarkup(markup::Paragraph &Out) const { + if (!ReturnParagraph) + return; + ParagraphToMarkupDocument(Out, Traits).visit(ReturnParagraph); +} + +void SymbolDocCommentVisitor::notesToMarkup(markup::Document &Out) const { + paragraphsToMarkup(Out, NoteParagraphs); +} + +void SymbolDocCommentVisitor::warningsToMarkup(markup::Document &Out) const { + paragraphsToMarkup(Out, WarningParagraphs); +} + +void SymbolDocCommentVisitor::parameterDocToMarkup( + StringRef ParamName, markup::Paragraph &Out) const { + if (ParamName.empty()) + return; + + if (const auto *P = Parameters.lookup(ParamName)) { + ParagraphToMarkupDocument(Out, Traits).visit(P->getParagraph()); + } +} + +void SymbolDocCommentVisitor::parameterDocToString( + StringRef ParamName, llvm::raw_string_ostream &Out) const { + if (ParamName.empty()) + return; + + if (const auto *P = Parameters.lookup(ParamName)) { + ParagraphToString(Out, Traits).visit(P->getParagraph()); + } +} + +void SymbolDocCommentVisitor::docToMarkup(markup::Document &Out) const { + for (unsigned I = 0; I < CommentPartIndex; ++I) { + if (const auto *BC = UnhandledCommands.lookup(I)) { + BlockCommentToMarkupDocument(Out, Traits).visit(BC); + } else if (const auto *P = FreeParagraphs.lookup(I)) { + ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P); + } + } +} + +void SymbolDocCommentVisitor::templateTypeParmDocToMarkup( + StringRef TemplateParamName, markup::Paragraph &Out) const { + if (TemplateParamName.empty()) + return; + + if (const auto *TP = TemplateParameters.lookup(TemplateParamName)) { + ParagraphToMarkupDocument(Out, Traits).visit(TP->getParagraph()); + } +} + +void SymbolDocCommentVisitor::templateTypeParmDocToString( + StringRef TemplateParamName, llvm::raw_string_ostream &Out) const { + if (TemplateParamName.empty()) + return; + + if (const auto *P = TemplateParameters.lookup(TemplateParamName)) { + ParagraphToString(Out, Traits).visit(P->getParagraph()); + } +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/SymbolDocumentation.h b/clang-tools-extra/clangd/SymbolDocumentation.h new file mode 100644 index 0000000..b0d3428 --- /dev/null +++ b/clang-tools-extra/clangd/SymbolDocumentation.h @@ -0,0 +1,211 @@ +//===--- SymbolDocumentation.h ==---------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Class to parse doxygen comments into a flat structure for consumption +// in e.g. Hover and Code Completion +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H + +#include "support/Markup.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentLexer.h" +#include "clang/AST/CommentParser.h" +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + +namespace clang { +namespace clangd { + +class SymbolDocCommentVisitor + : public comments::ConstCommentVisitor<SymbolDocCommentVisitor> { +public: + SymbolDocCommentVisitor(comments::FullComment *FC, + const CommentOptions &CommentOpts) + : Traits(Allocator, CommentOpts), Allocator() { + if (!FC) + return; + + for (auto *Block : FC->getBlocks()) { + visit(Block); + } + } + + SymbolDocCommentVisitor(llvm::StringRef Documentation, + const CommentOptions &CommentOpts) + : Traits(Allocator, CommentOpts), Allocator() { + + if (Documentation.empty()) + return; + + CommentWithMarkers.reserve(Documentation.size() + + Documentation.count('\n') * 3); + + // The comment lexer expects doxygen markers, so add them back. + // We need to use the /// style doxygen markers because the comment could + // contain the closing the closing tag "*/" of a C Style "/** */" comment + // which would break the parsing if we would just enclose the comment text + // with "/** */". + CommentWithMarkers = "///"; + bool NewLine = true; + for (char C : Documentation) { + if (C == '\n') { + CommentWithMarkers += "\n///"; + NewLine = true; + } else { + if (NewLine && (C == '<')) { + // A comment line starting with '///<' is treated as a doxygen + // comment. Therefore add a space to separate the '<' from the comment + // marker. This allows to parse html tags at the beginning of a line + // and the escape marker prevents adding the artificial space in the + // markup documentation. The extra space will not be rendered, since + // we render it as markdown. + CommentWithMarkers += ' '; + } + CommentWithMarkers += C; + NewLine = false; + } + } + SourceManagerForFile SourceMgrForFile("mock_file.cpp", CommentWithMarkers); + + SourceManager &SourceMgr = SourceMgrForFile.get(); + // The doxygen Sema requires a Diagostics consumer, since it reports + // warnings e.g. when parameters are not documented correctly. These + // warnings are not relevant for us, so we can ignore them. + SourceMgr.getDiagnostics().setClient(new IgnoringDiagConsumer); + + comments::Sema S(Allocator, SourceMgr, SourceMgr.getDiagnostics(), Traits, + /*PP=*/nullptr); + comments::Lexer L(Allocator, SourceMgr.getDiagnostics(), Traits, + SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()), + CommentWithMarkers.data(), + CommentWithMarkers.data() + CommentWithMarkers.size()); + comments::Parser P(L, S, Allocator, SourceMgr, SourceMgr.getDiagnostics(), + Traits); + comments::FullComment *FC = P.parseFullComment(); + + if (!FC) + return; + + for (auto *Block : FC->getBlocks()) { + visit(Block); + } + } + + bool isParameterDocumented(StringRef ParamName) const { + return Parameters.contains(ParamName); + } + + bool isTemplateTypeParmDocumented(StringRef ParamName) const { + return TemplateParameters.contains(ParamName); + } + + bool hasBriefCommand() const { return BriefParagraph; } + + bool hasReturnCommand() const { return ReturnParagraph; } + + bool hasRetvalCommands() const { return !RetvalParagraphs.empty(); } + + bool hasNoteCommands() const { return !NoteParagraphs.empty(); } + + bool hasWarningCommands() const { return !WarningParagraphs.empty(); } + + /// Converts all unhandled comment commands to a markup document. + void docToMarkup(markup::Document &Out) const; + /// Converts the "brief" command(s) to a markup document. + void briefToMarkup(markup::Paragraph &Out) const; + /// Converts the "return" command(s) to a markup document. + void returnToMarkup(markup::Paragraph &Out) const; + /// Converts the "note" command(s) to a markup document. + void notesToMarkup(markup::Document &Out) const; + /// Converts the "warning" command(s) to a markup document. + void warningsToMarkup(markup::Document &Out) const; + + void visitBlockCommandComment(const comments::BlockCommandComment *B); + + void templateTypeParmDocToMarkup(StringRef TemplateParamName, + markup::Paragraph &Out) const; + + void templateTypeParmDocToString(StringRef TemplateParamName, + llvm::raw_string_ostream &Out) const; + + void parameterDocToMarkup(StringRef ParamName, markup::Paragraph &Out) const; + + void parameterDocToString(StringRef ParamName, + llvm::raw_string_ostream &Out) const; + + void visitParagraphComment(const comments::ParagraphComment *P) { + FreeParagraphs[CommentPartIndex] = P; + CommentPartIndex++; + } + + void visitParamCommandComment(const comments::ParamCommandComment *P) { + Parameters[P->getParamNameAsWritten()] = P; + } + + void visitTParamCommandComment(const comments::TParamCommandComment *TP) { + TemplateParameters[TP->getParamNameAsWritten()] = std::move(TP); + } + +private: + comments::CommandTraits Traits; + llvm::BumpPtrAllocator Allocator; + std::string CommentWithMarkers; + + /// Index to keep track of the order of the comments. + /// We want to rearange some commands like \\param. + /// This index allows us to keep the order of the other comment parts. + unsigned CommentPartIndex = 0; + + /// Paragraph of the "brief" command. + const comments::ParagraphComment *BriefParagraph = nullptr; + + /// Paragraph of the "return" command. + const comments::ParagraphComment *ReturnParagraph = nullptr; + + /// Paragraph(s) of the "note" command(s) + llvm::SmallVector<const comments::ParagraphComment *> RetvalParagraphs; + + /// Paragraph(s) of the "note" command(s) + llvm::SmallVector<const comments::ParagraphComment *> NoteParagraphs; + + /// Paragraph(s) of the "warning" command(s) + llvm::SmallVector<const comments::ParagraphComment *> WarningParagraphs; + + /// All the paragraphs we don't have any special handling for, + /// e.g. "details". + llvm::SmallDenseMap<unsigned, const comments::BlockCommandComment *> + UnhandledCommands; + + /// Parsed paragaph(s) of the "param" comamnd(s) + llvm::SmallDenseMap<StringRef, const comments::ParamCommandComment *> + Parameters; + + /// Parsed paragaph(s) of the "tparam" comamnd(s) + llvm::SmallDenseMap<StringRef, const comments::TParamCommandComment *> + TemplateParameters; + + /// All "free" text paragraphs. + llvm::SmallDenseMap<unsigned, const comments::ParagraphComment *> + FreeParagraphs; + + void paragraphsToMarkup( + markup::Document &Out, + const llvm::SmallVectorImpl<const comments::ParagraphComment *> + &Paragraphs) const; +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 83a8b72..a253a63 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1876,7 +1876,7 @@ static void fillSubTypes(const SymbolID &ID, }); } -using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>; +using RecursionProtectionSet = llvm::SmallPtrSet<const CXXRecordDecl *, 4>; // Extracts parents from AST and populates the type hierarchy item. static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath, @@ -1965,7 +1965,8 @@ std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST, // Return the type most associated with an AST node. // This isn't precisely defined: we want "go to type" to do something useful. -static QualType typeForNode(const SelectionTree::Node *N) { +static QualType typeForNode(const ASTContext &Ctx, + const SelectionTree::Node *N) { // If we're looking at a namespace qualifier, walk up to what it's qualifying. // (If we're pointing at a *class* inside a NNS, N will be a TypeLoc). while (N && N->ASTNode.get<NestedNameSpecifierLoc>()) @@ -1999,10 +2000,13 @@ static QualType typeForNode(const SelectionTree::Node *N) { if (const Decl *D = N->ASTNode.get<Decl>()) { struct Visitor : ConstDeclVisitor<Visitor, QualType> { + const ASTContext &Ctx; + Visitor(const ASTContext &Ctx) : Ctx(Ctx) {} + QualType VisitValueDecl(const ValueDecl *D) { return D->getType(); } // Declaration of a type => that type. QualType VisitTypeDecl(const TypeDecl *D) { - return QualType(D->getTypeForDecl(), 0); + return Ctx.getTypeDeclType(D); } // Exception: alias declaration => the underlying type, not the alias. QualType VisitTypedefNameDecl(const TypedefNameDecl *D) { @@ -2012,7 +2016,7 @@ static QualType typeForNode(const SelectionTree::Node *N) { QualType VisitTemplateDecl(const TemplateDecl *D) { return Visit(D->getTemplatedDecl()); } - } V; + } V(Ctx); return V.Visit(D); } @@ -2156,7 +2160,8 @@ std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos, // unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you some // information about the type you may have not known before // (since unique_ptr<unique_ptr<T>> != unique_ptr<T>). - for (const QualType& Type : unwrapFindType(typeForNode(N), AST.getHeuristicResolver())) + for (const QualType &Type : unwrapFindType( + typeForNode(AST.getASTContext(), N), AST.getHeuristicResolver())) llvm::copy(locateSymbolForType(AST, Type, Index), std::back_inserter(LocatedSymbols)); diff --git a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp index 67fc451..f65c74f 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp @@ -115,13 +115,6 @@ private: const SourceManager &SM; }; -bool isFullyQualified(const NestedNameSpecifier *NNS) { - if (!NNS) - return false; - return NNS->getKind() == NestedNameSpecifier::Global || - isFullyQualified(NNS->getPrefix()); -} - struct InsertionPointData { // Location to insert the "using" statement. If invalid then the statement // should not be inserted at all (it already exists). @@ -167,18 +160,20 @@ findInsertionPoint(const Tweak::Selection &Inputs, for (auto &U : Usings) { // Only "upgrade" to fully qualified is all relevant using decls are fully // qualified. Otherwise trust what the user typed. - if (!isFullyQualified(U->getQualifier())) + if (!U->getQualifier().isFullyQualified()) AlwaysFullyQualify = false; if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc())) // "Usings" is sorted, so we're done. break; - if (const auto *Namespace = dyn_cast_if_present<NamespaceDecl>( - U->getQualifier()->getAsNamespace())) { + if (NestedNameSpecifier Qualifier = U->getQualifier(); + Qualifier.getKind() == NestedNameSpecifier::Kind::Namespace) { + const auto *Namespace = + U->getQualifier().getAsNamespaceAndPrefix().Namespace; if (Namespace->getCanonicalDecl() == QualifierToRemove.getNestedNameSpecifier() - ->getAsNamespace() - ->getCanonicalDecl() && + .getAsNamespaceAndPrefix() + .Namespace->getCanonicalDecl() && U->getName() == Name) { return InsertionPointData(); } @@ -232,8 +227,9 @@ findInsertionPoint(const Tweak::Selection &Inputs, } bool isNamespaceForbidden(const Tweak::Selection &Inputs, - const NestedNameSpecifier &Namespace) { - const auto *NS = dyn_cast<NamespaceDecl>(Namespace.getAsNamespace()); + NestedNameSpecifier Namespace) { + const auto *NS = + dyn_cast<NamespaceDecl>(Namespace.getAsNamespaceAndPrefix().Namespace); if (!NS) return true; std::string NamespaceStr = printNamespaceScope(*NS); @@ -247,11 +243,11 @@ bool isNamespaceForbidden(const Tweak::Selection &Inputs, return false; } -std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL, +std::string getNNSLAsString(NestedNameSpecifierLoc NNSL, const PrintingPolicy &Policy) { std::string Out; llvm::raw_string_ostream OutStream(Out); - NNSL.getNestedNameSpecifier()->print(OutStream, Policy); + NNSL.getNestedNameSpecifier().print(OutStream, Policy); return OutStream.str(); } @@ -276,16 +272,15 @@ bool AddUsing::prepare(const Selection &Inputs) { continue; } if (auto *T = Node->ASTNode.get<TypeLoc>()) { - if (T->getAs<ElaboratedTypeLoc>()) { + // Find the outermost TypeLoc. + if (Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) + continue; + if (isa<TagType, TemplateSpecializationType, TypedefType, UsingType, + UnresolvedUsingType>(T->getTypePtr())) break; - } - if (Node->Parent->ASTNode.get<TypeLoc>() || - Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) { - // Node is TypeLoc, but it's parent is either TypeLoc or - // NestedNameSpecifier. In both cases, we want to go up, to find - // the outermost TypeLoc. + // Find the outermost TypeLoc. + if (Node->Parent->ASTNode.get<TypeLoc>()) continue; - } } break; } @@ -307,32 +302,70 @@ bool AddUsing::prepare(const Selection &Inputs) { MustInsertAfterLoc = D->getDecl()->getBeginLoc(); } } else if (auto *T = Node->ASTNode.get<TypeLoc>()) { - if (auto E = T->getAs<ElaboratedTypeLoc>()) { - QualifierToRemove = E.getQualifierLoc(); - - SpelledNameRange = E.getSourceRange(); - if (auto T = E.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>()) { - // Remove the template arguments from the name. - SpelledNameRange.setEnd(T.getLAngleLoc().getLocWithOffset(-1)); - } - - if (const auto *ET = E.getTypePtr()) { - if (const auto *TDT = - dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) { - MustInsertAfterLoc = TDT->getDecl()->getBeginLoc(); - } else if (auto *TD = ET->getAsTagDecl()) { - MustInsertAfterLoc = TD->getBeginLoc(); - } - } + switch (T->getTypeLocClass()) { + case TypeLoc::TemplateSpecialization: { + auto TL = T->castAs<TemplateSpecializationTypeLoc>(); + QualifierToRemove = TL.getQualifierLoc(); + if (!QualifierToRemove) + break; + SpelledNameRange = TL.getTemplateNameLoc(); + if (auto *TD = TL.getTypePtr()->getTemplateName().getAsTemplateDecl( + /*IgnoreDeduced=*/true)) + MustInsertAfterLoc = TD->getBeginLoc(); + break; + } + case TypeLoc::Enum: + case TypeLoc::Record: + case TypeLoc::InjectedClassName: { + auto TL = T->castAs<TagTypeLoc>(); + QualifierToRemove = TL.getQualifierLoc(); + if (!QualifierToRemove) + break; + SpelledNameRange = TL.getNameLoc(); + MustInsertAfterLoc = TL.getOriginalDecl()->getBeginLoc(); + break; + } + case TypeLoc::Typedef: { + auto TL = T->castAs<TypedefTypeLoc>(); + QualifierToRemove = TL.getQualifierLoc(); + if (!QualifierToRemove) + break; + SpelledNameRange = TL.getNameLoc(); + MustInsertAfterLoc = TL.getDecl()->getBeginLoc(); + break; + } + case TypeLoc::UnresolvedUsing: { + auto TL = T->castAs<UnresolvedUsingTypeLoc>(); + QualifierToRemove = TL.getQualifierLoc(); + if (!QualifierToRemove) + break; + SpelledNameRange = TL.getNameLoc(); + MustInsertAfterLoc = TL.getDecl()->getBeginLoc(); + break; + } + case TypeLoc::Using: { + auto TL = T->castAs<UsingTypeLoc>(); + QualifierToRemove = TL.getQualifierLoc(); + if (!QualifierToRemove) + break; + SpelledNameRange = TL.getNameLoc(); + MustInsertAfterLoc = TL.getDecl()->getBeginLoc(); + break; + } + default: + break; } + if (QualifierToRemove) + SpelledNameRange.setBegin(QualifierToRemove.getBeginLoc()); } if (!QualifierToRemove || // FIXME: This only supports removing qualifiers that are made up of just // namespace names. If qualifier contains a type, we could take the // longest namespace prefix and remove that. - !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() || + QualifierToRemove.getNestedNameSpecifier().getKind() != + NestedNameSpecifier::Kind::Namespace || // Respect user config. - isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier())) + isNamespaceForbidden(Inputs, QualifierToRemove.getNestedNameSpecifier())) return false; // Macros are difficult. We only want to offer code action when what's spelled // under the cursor is a namespace qualifier. If it's a macro that expands to @@ -384,7 +417,7 @@ Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) { llvm::raw_string_ostream UsingTextStream(UsingText); UsingTextStream << "using "; if (InsertionPoint->AlwaysFullyQualify && - !isFullyQualified(QualifierToRemove.getNestedNameSpecifier())) + !QualifierToRemove.getNestedNameSpecifier().isFullyQualified()) UsingTextStream << "::"; UsingTextStream << QualifierToSpell << SpelledName << ";" << InsertionPoint->Suffix; diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp index cd07cbf..bc9a790 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp @@ -181,7 +181,7 @@ struct ExtractionZone { bool requiresHoisting(const SourceManager &SM, const HeuristicResolver *Resolver) const { // First find all the declarations that happened inside extraction zone. - llvm::SmallSet<const Decl *, 1> DeclsInExtZone; + llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone; for (auto *RootStmt : RootStmts) { findExplicitReferences( RootStmt, @@ -362,7 +362,7 @@ struct NewFunction { SourceLocation DefinitionPoint; std::optional<SourceLocation> ForwardDeclarationPoint; const CXXRecordDecl *EnclosingClass = nullptr; - const NestedNameSpecifier *DefinitionQualifier = nullptr; + NestedNameSpecifier DefinitionQualifier = std::nullopt; const DeclContext *SemanticDC = nullptr; const DeclContext *SyntacticDC = nullptr; const DeclContext *ForwardDeclarationSyntacticDC = nullptr; @@ -455,13 +455,12 @@ std::string NewFunction::renderQualifiers() const { } std::string NewFunction::renderDeclarationName(FunctionDeclKind K) const { - if (DefinitionQualifier == nullptr || K != OutOfLineDefinition) { + if (!DefinitionQualifier || K != OutOfLineDefinition) return Name; - } std::string QualifierName; llvm::raw_string_ostream Oss(QualifierName); - DefinitionQualifier->print(Oss, *LangOpts); + DefinitionQualifier.print(Oss, *LangOpts); return llvm::formatv("{0}{1}", QualifierName, Name); } diff --git a/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp index 43cfc76..2c98417 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp @@ -113,11 +113,11 @@ bool PopulateSwitch::prepare(const Selection &Sel) { // Ignore implicit casts, since enums implicitly cast to integer types. Cond = Cond->IgnoreParenImpCasts(); // Get the canonical type to handle typedefs. - EnumT = Cond->getType().getCanonicalType()->getAsAdjusted<EnumType>(); + EnumT = Cond->getType()->getAsCanonical<EnumType>(); if (!EnumT) return false; - EnumD = EnumT->getDecl(); - if (!EnumD || EnumD->isDependentType()) + EnumD = EnumT->getOriginalDecl()->getDefinitionOrSelf(); + if (EnumD->isDependentType()) return false; // Finally, check which cases exist and which are covered. diff --git a/clang-tools-extra/clangd/support/DirectiveTree.cpp b/clang-tools-extra/clangd/support/DirectiveTree.cpp index 7ea08ad..97b0598 100644 --- a/clang-tools-extra/clangd/support/DirectiveTree.cpp +++ b/clang-tools-extra/clangd/support/DirectiveTree.cpp @@ -356,5 +356,62 @@ TokenStream DirectiveTree::stripDirectives(const TokenStream &In) const { return Out; } +namespace { +class RangePairer { + std::vector<Token::Range> &Ranges; + +public: + RangePairer(std::vector<Token::Range> &Ranges) : Ranges(Ranges) {} + + void walk(const DirectiveTree &T) { + for (const auto &C : T.Chunks) + std::visit(*this, C); + } + + void operator()(const DirectiveTree::Code &C) {} + + void operator()(const DirectiveTree::Directive &) {} + + void operator()(const DirectiveTree::Conditional &C) { + Token::Range Range; + Token::Index Last; + auto First = true; + for (const auto &[Directive, _] : C.Branches) { + if (First) { + First = false; + } else { + Range = {Last, Directive.Tokens.Begin}; + Ranges.push_back(Range); + } + Last = Directive.Tokens.Begin; + } + + if (C.End.Kind != tok::pp_not_keyword) { + Range = {Last, C.End.Tokens.Begin}; + Ranges.push_back(Range); + } + + for (const auto &[_, SubTree] : C.Branches) + walk(SubTree); + } +}; +} // namespace + +std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree, + const TokenStream &Code) { + std::vector<Token::Range> Ranges; + RangePairer(Ranges).walk(Tree); + + // Transform paired ranges to start with last token in its logical line + for (auto &R : Ranges) { + const Token *Tok = &Code.tokens()[R.Begin + 1]; + while (Tok->Kind != tok::eof && !Tok->flag(LexFlags::StartsPPLine)) + ++Tok; + Tok = Tok - 1; + R.Begin = Tok->OriginalIndex; + } + return Ranges; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/support/DirectiveTree.h b/clang-tools-extra/clangd/support/DirectiveTree.h index 34f5a88..373af32 100644 --- a/clang-tools-extra/clangd/support/DirectiveTree.h +++ b/clang-tools-extra/clangd/support/DirectiveTree.h @@ -124,6 +124,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, /// The choices are stored in Conditional::Taken nodes. void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code); +/// Pairs preprocessor conditional directives and computes their token ranges. +std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree, + const TokenStream &Code); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/support/Markup.cpp b/clang-tools-extra/clangd/support/Markup.cpp index a130830..89bdc65 100644 --- a/clang-tools-extra/clangd/support/Markup.cpp +++ b/clang-tools-extra/clangd/support/Markup.cpp @@ -363,7 +363,12 @@ public: void renderMarkdown(llvm::raw_ostream &OS) const override { std::string Marker = getMarkerForCodeBlock(Contents); // No need to pad from previous blocks, as they should end with a new line. - OS << Marker << Language << '\n' << Contents << '\n' << Marker << '\n'; + OS << Marker << Language << '\n' << Contents; + if (!Contents.empty() && Contents.back() != '\n') + OS << '\n'; + // Always end with an empty line to separate code blocks from following + // paragraphs. + OS << Marker << "\n\n"; } void renderPlainText(llvm::raw_ostream &OS) const override { diff --git a/clang-tools-extra/clangd/test/modules_no_cdb.test b/clang-tools-extra/clangd/test/modules_no_cdb.test new file mode 100644 index 0000000..8f92be2 --- /dev/null +++ b/clang-tools-extra/clangd/test/modules_no_cdb.test @@ -0,0 +1,66 @@ +# A smoke test to check that clangd works without compilation database +# +# Windows have different escaping modes. +# FIXME: We should add one for windows. +# UNSUPPORTED: system-windows +# +# RUN: rm -fr %t +# RUN: mkdir -p %t +# RUN: split-file %s %t +# +# RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc +# +# RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \ +# RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc + +#--- A.h +void printA(); + +#--- Use.cpp +#include "A.h" +void foo() { + print +} + +#--- definition.jsonrpc.tmpl +{ + "jsonrpc": "2.0", + "id": 0, + "method": "initialize", + "params": { + "processId": 123, + "rootPath": "clangd", + "capabilities": { + "textDocument": { + "completion": { + "completionItem": { + "snippetSupport": true + } + } + } + }, + "trace": "off" + } +} +--- +{ + "jsonrpc": "2.0", + "method": "textDocument/didOpen", + "params": { + "textDocument": { + "uri": "file://DIR/Use.cpp", + "languageId": "cpp", + "version": 1, + "text": "#include \"A.h\"\nvoid foo() {\n print\n}\n" + } + } +} + +# CHECK: "message"{{.*}}printA{{.*}}(fix available) + +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}} +--- +{"jsonrpc":"2.0","id":2,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index d0bc3c4..76d46ba 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -421,7 +421,7 @@ TEST(ClangdAST, GetQualification) { { R"cpp( namespace ns1 { namespace ns2 { void Foo(); } } - void insert(); // ns2::Foo + void insert(); // ns1::ns2::Foo namespace ns1 { void insert(); // ns2::Foo namespace ns2 { @@ -429,7 +429,7 @@ TEST(ClangdAST, GetQualification) { } } )cpp", - {"ns2::", "ns2::", ""}, + {"ns1::ns2::", "ns2::", ""}, {"ns1::"}, }, { @@ -531,7 +531,8 @@ TEST(ClangdAST, PrintType) { ASSERT_EQ(InsertionPoints.size(), Case.Types.size()); for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { const auto *DC = InsertionPoints[I]; - EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC), + EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC, + /*Placeholder=*/"", /*FullyQualify=*/true), Case.Types[I]); } } diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index d425070..9656eea 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -92,6 +92,7 @@ add_unittest(ClangdUnitTests ClangdTests SourceCodeTests.cpp StdLibTests.cpp SymbolCollectorTests.cpp + SymbolDocumentationTests.cpp SymbolInfoTests.cpp SyncAPI.cpp TUSchedulerTests.cpp diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 1a1c32c..7640569 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -4473,6 +4473,198 @@ TEST(CompletionTest, SkipExplicitObjectParameter) { snippetSuffix("")))); } } + +TEST(CompletionTest, MemberAccessInExplicitObjMemfn) { + Annotations Code(R"cpp( + struct A { + int member {}; + int memberFnA(int a); + int memberFnA(this A&, float a); + + void foo(this A& self) { + // Should not offer any members here, since + // it needs to be referenced through `self`. + mem$c1^; + // should offer all results + self.mem$c2^; + + [&]() { + // should not offer any results + mem$c3^; + }(); + } + }; + )cpp"); + + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs = {"-std=c++23"}; + + auto Preamble = TU.preamble(); + ASSERT_TRUE(Preamble); + + CodeCompleteOptions Opts{}; + + MockFS FS; + auto Inputs = TU.inputs(FS); + + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"), + Preamble.get(), Inputs, Opts); + + EXPECT_THAT(Result.Completions, ElementsAre()); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"), + Preamble.get(), Inputs, Opts); + + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(named("member"), + AllOf(named("memberFnA"), signature("(int a)"), + snippetSuffix("(${1:int a})")), + AllOf(named("memberFnA"), signature("(float a)"), + snippetSuffix("(${1:float a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"), + Preamble.get(), Inputs, Opts); + + EXPECT_THAT(Result.Completions, ElementsAre()); + } +} + +TEST(CompletionTest, ListExplicitObjectOverloads) { + Annotations Code(R"cpp( + struct S { + void foo1(int a); + void foo2(int a) const; + void foo2(this const S& self, float a); + void foo3(this const S& self, int a); + void foo4(this S& self, int a); + }; + + void S::foo1(int a) { + this->$c1^; + } + + void S::foo2(int a) const { + this->$c2^; + } + + void S::foo3(this const S& self, int a) { + self.$c3^; + } + + void S::foo4(this S& self, int a) { + self.$c4^; + } + + void test1(S s) { + s.$c5^; + } + + void test2(const S s) { + s.$c6^; + } + )cpp"); + + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs = {"-std=c++23"}; + + auto Preamble = TU.preamble(); + ASSERT_TRUE(Preamble); + + CodeCompleteOptions Opts{}; + + MockFS FS; + auto Inputs = TU.inputs(FS); + + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c1"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo4"), signature("(int a)"), + snippetSuffix("(${1:int a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c2"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c3"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c4"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo4"), signature("(int a)"), + snippetSuffix("(${1:int a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c5"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo1"), signature("(int a)"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo4"), signature("(int a)"), + snippetSuffix("(${1:int a})")))); + } + { + auto Result = codeComplete(testPath(TU.Filename), Code.point("c6"), + Preamble.get(), Inputs, Opts); + EXPECT_THAT( + Result.Completions, + UnorderedElementsAre(AllOf(named("foo2"), signature("(int a) const"), + snippetSuffix("(${1:int a})")), + AllOf(named("foo2"), signature("(float a) const"), + snippetSuffix("(${1:float a})")), + AllOf(named("foo3"), signature("(int a) const"), + snippetSuffix("(${1:int a})")))); + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp index d71b8d5..d94e706 100644 --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -230,17 +230,19 @@ TEST(ParseYAML, CodePatterns) { EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None"))); } -TEST(ParseYAML, ShowAKA) { +TEST(ParseYAML, Hover) { CapturedDiags Diags; Annotations YAML(R"yaml( Hover: ShowAKA: True + MacroContentsLimit: 4096 )yaml"); auto Results = Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); ASSERT_THAT(Diags.Diagnostics, IsEmpty()); ASSERT_EQ(Results.size(), 1u); EXPECT_THAT(Results[0].Hover.ShowAKA, llvm::ValueIs(val(true))); + EXPECT_THAT(Results[0].Hover.MacroContentsLimit, llvm::ValueIs(val(4096U))); } TEST(ParseYAML, InlayHints) { diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp index cb2c17a..5c857d0 100644 --- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp @@ -72,15 +72,14 @@ declaration: Namespace - root expression: BinaryOperator - + expression: ImplicitCast - LValueToRValue expression: DeclRef - x - specifier: TypeSpec + specifier: Type type: Record - S expression: ImplicitCast - LValueToRValue expression: Member - x expression: CXXBindTemporary expression: CXXTemporaryObject - S - type: Elaborated + type: Record - S specifier: Namespace - root:: - type: Record - S )"}, {R"cpp( namespace root { @@ -104,14 +103,13 @@ declaration: Namespace - root expression: BinaryOperator - + expression: ImplicitCast - LValueToRValue expression: DeclRef - x - specifier: TypeSpec + specifier: Type type: Record - S expression: ImplicitCast - LValueToRValue expression: Member - x expression: CXXTemporaryObject - S - type: Elaborated + type: Record - S specifier: Namespace - root:: - type: Record - S )"}, {R"cpp( namespace root { @@ -138,7 +136,7 @@ declaration: Namespace - root type: Builtin - unsigned int statement: Return expression: DependentScopeDeclRef - value - specifier: TypeSpec + specifier: Type type: TemplateTypeParm - T )"}, {R"cpp( @@ -154,8 +152,7 @@ declaration: Var - root expression: DeclRef - operator+ expression: MaterializeTemporary - lvalue expression: CXXTemporaryObject - Foo - type: Elaborated - type: Record - Foo + type: Record - Foo expression: IntegerLiteral - 42 )"}, {R"cpp( diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 4d77f9d..f369e1b 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -731,6 +731,12 @@ TEST_F(TargetDeclTest, BuiltinTemplates) { using type_pack_element = [[__type_pack_element]]<N, Pack...>; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", ); + + Code = R"cpp( + template <template <class...> class Templ, class... Types> + using dedup_types = Templ<[[__builtin_dedup_pack]]<Types...>...>; + )cpp"; + EXPECT_DECLS("TemplateSpecializationTypeLoc", ); } TEST_F(TargetDeclTest, MemberOfTemplate) { @@ -992,7 +998,7 @@ TEST_F(TargetDeclTest, DependentTypes) { )cpp"; EXPECT_DECLS("DependentNameTypeLoc", "struct B"); - // Heuristic resolution of dependent type name which doesn't get a TypeLoc + // Heuristic resolution of dependent type name within a NestedNameSpecifierLoc Code = R"cpp( template <typename> struct A { struct B { struct C {}; }; }; @@ -1000,7 +1006,7 @@ TEST_F(TargetDeclTest, DependentTypes) { template <typename T> void foo(typename A<T>::[[B]]::C); )cpp"; - EXPECT_DECLS("NestedNameSpecifierLoc", "struct B"); + EXPECT_DECLS("DependentNameTypeLoc", "struct B"); // Heuristic resolution of dependent type name whose qualifier is also // dependent diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 12d260d..743c0dc 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -2894,7 +2894,7 @@ TEST(Hover, All) { )cpp", [](HoverInfo &HI) { HI.Name = "this"; - HI.Definition = "const Foo<T> *"; + HI.Definition = "const ns::Foo<T> *"; }}, { R"cpp(// this expr for specialization class @@ -2910,7 +2910,7 @@ TEST(Hover, All) { )cpp", [](HoverInfo &HI) { HI.Name = "this"; - HI.Definition = "Foo<int> *"; + HI.Definition = "ns::Foo<int> *"; }}, { R"cpp(// this expr for partial specialization struct @@ -2926,7 +2926,7 @@ TEST(Hover, All) { )cpp", [](HoverInfo &HI) { HI.Name = "this"; - HI.Definition = "const Foo<int, F> *"; + HI.Definition = "const ns::Foo<int, F> *"; }}, { R"cpp( @@ -3046,8 +3046,8 @@ TEST(Hover, All) { HI.Kind = index::SymbolKind::Function; HI.NamespaceScope = ""; HI.Definition = "MyRect foobar()"; - HI.Type = {"MyRect ()", "MyRect ()"}; - HI.ReturnType = {"MyRect", "MyRect"}; + HI.Type = {"MyRect ()", "struct MyRect ()"}; + HI.ReturnType = {"MyRect", "struct MyRect"}; HI.Parameters.emplace(); }}, {R"cpp( @@ -3409,7 +3409,8 @@ TEST(Hover, DocsFromMostSpecial) { TEST(Hover, Present) { struct { const std::function<void(HoverInfo &)> Builder; - llvm::StringRef ExpectedRender; + llvm::StringRef ExpectedMarkdownRender; + llvm::StringRef ExpectedDoxygenRender; } Cases[] = { { [](HoverInfo &HI) { @@ -3417,6 +3418,7 @@ TEST(Hover, Present) { HI.Name = "X"; }, R"(X)", + R"(### `X`)", }, { [](HoverInfo &HI) { @@ -3424,6 +3426,7 @@ TEST(Hover, Present) { HI.Name = "foo"; }, R"(namespace-alias foo)", + R"(### namespace-alias `foo`)", }, { [](HoverInfo &HI) { @@ -3446,6 +3449,24 @@ Size: 10 bytes documentation template <typename T, typename C = bool> class Foo {})", + R"(### class + +--- +```cpp +template <typename T, typename C = bool> class Foo {} +``` + +--- +**Template Parameters:** + +- `typename T` +- `typename C = bool` + +--- +documentation + +--- +Size: 10 bytes)", }, { [](HoverInfo &HI) { @@ -3476,6 +3497,26 @@ template <typename T, typename C = bool> class Foo {})", "\n" "// In namespace ns\n" "ret_type foo(params) {}", + R"(### function + +--- +```cpp +// In namespace ns +ret_type foo(params) {} +``` + +--- +**Parameters:** + +- +- `type (aka can_type)` +- `type foo (aka can_type)` +- `type foo = default (aka can_type)` + +--- +**Returns:** + +`ret_type (aka can_ret_type)`)", }, { [](HoverInfo &HI) { @@ -3502,6 +3543,22 @@ Size: 4 bytes (+4 bytes padding), alignment 4 bytes // In test::Bar def)", + R"(### field + +--- +```cpp +// In test::Bar +def +``` + +--- +Type: `type (aka can_type)` + +Value = `value` + +Offset: 12 bytes + +Size: 4 bytes (+4 bytes padding), alignment 4 bytes)", }, { [](HoverInfo &HI) { @@ -3528,6 +3585,22 @@ Size: 25 bits (+4 bits padding), alignment 8 bytes // In test::Bar def)", + R"(### field + +--- +```cpp +// In test::Bar +def +``` + +--- +Type: `type (aka can_type)` + +Value = `value` + +Offset: 4 bytes and 3 bits + +Size: 25 bits (+4 bits padding), alignment 8 bytes)", }, { [](HoverInfo &HI) { @@ -3541,6 +3614,13 @@ def)", // In test::Bar public: def)", + R"(### field + +--- +```cpp +// In test::Bar +public: def +```)", }, { [](HoverInfo &HI) { @@ -3560,6 +3640,18 @@ public: def)", // In cls<int> protected: size_t method())", + R"(### instance-method + +--- +```cpp +// In cls<int> +protected: size_t method() +``` + +--- +**Returns:** + +`size_t (aka unsigned long)`)", }, { [](HoverInfo &HI) { @@ -3587,6 +3679,19 @@ Parameters: // In cls public: cls(int a, int b = 5))", + R"(### constructor + +--- +```cpp +// In cls +public: cls(int a, int b = 5) +``` + +--- +**Parameters:** + +- `int a` +- `int b = 5`)", }, { [](HoverInfo &HI) { @@ -3600,6 +3705,13 @@ public: cls(int a, int b = 5))", // In namespace ns1 private: union foo {})", + R"(### union + +--- +```cpp +// In namespace ns1 +private: union foo {} +```)", }, { [](HoverInfo &HI) { @@ -3625,6 +3737,20 @@ Passed as arg_a // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed as arg_a)", }, { [](HoverInfo &HI) { @@ -3637,6 +3763,10 @@ int foo = 3)", R"(variable foo Passed by value)", + R"(### variable `foo` + +--- +Passed by value)", }, { [](HoverInfo &HI) { @@ -3662,6 +3792,20 @@ Passed by reference as arg_a // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed by reference as arg_a)", }, { [](HoverInfo &HI) { @@ -3687,6 +3831,20 @@ Passed as arg_a (converted to alias_int) // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed as arg_a (converted to alias_int))", }, { [](HoverInfo &HI) { @@ -3702,6 +3860,15 @@ int foo = 3)", // Expands to (1 + 1))", + R"(### macro + +--- +```cpp +#define PLUS_ONE(X) (X+1) + +// Expands to +(1 + 1) +```)", }, { [](HoverInfo &HI) { @@ -3727,38 +3894,283 @@ Passed by const reference as arg_a (converted to int) // In test::Bar int foo = 3)", + R"(### variable + +--- +```cpp +// In test::Bar +int foo = 3 +``` + +--- +Type: `int` + +Value = `3` + +Passed by const reference as arg_a (converted to int))", }, { [](HoverInfo &HI) { HI.Name = "stdio.h"; HI.Definition = "/usr/include/stdio.h"; + HI.Kind = index::SymbolKind::IncludeDirective; }, R"(stdio.h /usr/include/stdio.h)", + R"(### `stdio.h` + +`/usr/include/stdio.h`)", + }, + { + [](HoverInfo &HI) { + HI.Name = "foo.h"; + HI.UsedSymbolNames = {"Foo", "Bar", "Bar"}; + HI.Kind = index::SymbolKind::IncludeDirective; + }, + R"(foo.h + +provides Foo, Bar, Bar)", + R"(### `foo.h` + +--- +provides `Foo`, `Bar`, `Bar`)", }, {[](HoverInfo &HI) { HI.Name = "foo.h"; - HI.UsedSymbolNames = {"Foo", "Bar", "Bar"}; + HI.UsedSymbolNames = {"Foo", "Bar", "Baz", "Foobar", "Qux", "Quux"}; + HI.Kind = index::SymbolKind::IncludeDirective; }, R"(foo.h -provides Foo, Bar, Bar)"}, +provides Foo, Bar, Baz, Foobar, Qux and 1 more)", + R"(### `foo.h` + +--- +provides `Foo`, `Bar`, `Baz`, `Foobar`, `Qux` and 1 more)"}}; + + for (const auto &C : Cases) { + HoverInfo HI; + C.Builder(HI); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Markdown; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::PlainText), C.ExpectedMarkdownRender); + } + for (const auto &C : Cases) { + HoverInfo HI; + C.Builder(HI); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedDoxygenRender); + } +} + +TEST(Hover, PresentDocumentation) { + struct { + const std::function<void(HoverInfo &)> Builder; + llvm::StringRef ExpectedMarkdownRender; + llvm::StringRef ExpectedDoxygenRender; + } Cases[] = { {[](HoverInfo &HI) { - HI.Name = "foo.h"; - HI.UsedSymbolNames = {"Foo", "Bar", "Baz", "Foobar", "Qux", "Quux"}; + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "@brief brief doc\n\n" + "longer doc"; + HI.Definition = "void foo()"; + HI.Name = "foo"; }, - R"(foo.h + R"(### function `foo` + +--- +@brief brief doc + +longer doc + +--- +```cpp +void foo() +```)", + R"(### function -provides Foo, Bar, Baz, Foobar, Qux and 1 more)"}}; +--- +```cpp +void foo() +``` + +--- +brief doc + +--- +longer doc)"}, + {[](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "@brief brief doc\n\n" + "longer doc"; + HI.Definition = "int foo()"; + HI.ReturnType = "int"; + HI.Name = "foo"; + }, + R"(### function `foo` + +--- +→ `int` + +@brief brief doc + +longer doc + +--- +```cpp +int foo() +```)", + R"(### function + +--- +```cpp +int foo() +``` + +--- +brief doc + +--- +**Returns:** + +`int` + +--- +longer doc)"}, + {[](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "@brief brief doc\n\n" + "longer doc\n@param a this is a param\n@return it " + "returns something"; + HI.Definition = "int foo(int a)"; + HI.ReturnType = "int"; + HI.Name = "foo"; + HI.Parameters.emplace(); + HI.Parameters->emplace_back(); + HI.Parameters->back().Type = "int"; + HI.Parameters->back().Name = "a"; + }, + R"(### function `foo` + +--- +→ `int` + +Parameters: + +- `int a` + +@brief brief doc + +longer doc +@param a this is a param +@return it returns something + +--- +```cpp +int foo(int a) +```)", + R"(### function + +--- +```cpp +int foo(int a) +``` + +--- +brief doc + +--- +**Parameters:** + +- `int a` - this is a param + +--- +**Returns:** + +`int` - it returns something + +--- +longer doc)"}, + {[](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Function; + HI.Documentation = "@brief brief doc\n\n" + "longer doc\n@param a this is a param\n@param b " + "does not exist\n@return it returns something"; + HI.Definition = "int foo(int a)"; + HI.ReturnType = "int"; + HI.Name = "foo"; + HI.Parameters.emplace(); + HI.Parameters->emplace_back(); + HI.Parameters->back().Type = "int"; + HI.Parameters->back().Name = "a"; + }, + R"(### function `foo` + +--- +→ `int` + +Parameters: + +- `int a` + +@brief brief doc + +longer doc +@param a this is a param +@param b does not exist +@return it returns something + +--- +```cpp +int foo(int a) +```)", + R"(### function + +--- +```cpp +int foo(int a) +``` + +--- +brief doc + +--- +**Parameters:** + +- `int a` - this is a param + +--- +**Returns:** + +`int` - it returns something + +--- +longer doc)"}, + }; for (const auto &C : Cases) { HoverInfo HI; C.Builder(HI); Config Cfg; Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Markdown; WithContextValue WithCfg(Config::Key, std::move(Cfg)); - EXPECT_EQ(HI.present(MarkupKind::PlainText), C.ExpectedRender); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedMarkdownRender); + } + for (const auto &C : Cases) { + HoverInfo HI; + C.Builder(HI); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), C.ExpectedDoxygenRender); } } @@ -3936,6 +4348,21 @@ TEST(Hover, PresentRulers) { "```"; EXPECT_EQ(HI.present(MarkupKind::Markdown), ExpectedMarkdown); + llvm::StringRef ExpectedDoxygenMarkdown = // + "### variable\n" + "\n" + "---\n" + "```cpp\n" + "def\n" + "```\n\n" + "---\n" + "Value = `val`"; + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + EXPECT_EQ(HI.present(MarkupKind::Markdown), ExpectedDoxygenMarkdown); + llvm::StringRef ExpectedPlaintext = R"pt(variable foo Value = val @@ -4339,6 +4766,188 @@ constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) { EXPECT_TRUE(H->Value); EXPECT_TRUE(H->Type); } + +TEST(Hover, HoverMacroContentsLimit) { + const char *const Code = + R"cpp( + #define C(A) A##A // Concatenate + #define E(A) C(A) // Expand + #define Z0032 00000000000000000000000000000000 + #define Z0064 E(Z0032) + #define Z0128 E(Z0064) + #define Z0256 E(Z0128) + #define Z0512 E(Z0256) + #define Z1024 E(Z0512) + #define Z2048 E(Z1024) + #define Z4096 E(Z2048) // 4096 zeroes + int main() { return [[^Z4096]]; } + )cpp"; + + struct { + uint32_t MacroContentsLimit; + const std::string ExpectedDefinition; + } Cases[] = { + // With a limit of 2048, the macro expansion should get dropped. + {2048, "#define Z4096 E(Z2048)"}, + // With a limit of 8192, the macro expansion should be fully expanded. + {8192, std::string("#define Z4096 E(Z2048)\n\n") + + std::string("// Expands to\n") + std::string(4096, '0')}, + }; + for (const auto &Case : Cases) { + SCOPED_TRACE(Code); + + Annotations T(Code); + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + Config Cfg; + Cfg.Hover.MacroContentsLimit = Case.MacroContentsLimit; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + ASSERT_TRUE(H); + + EXPECT_EQ(H->Definition, Case.ExpectedDefinition); + } +} + +TEST(Hover, FunctionParameters) { + struct { + const char *const Code; + const std::function<void(HoverInfo &)> ExpectedBuilder; + std::string ExpectedRender; + } Cases[] = { + {R"cpp(/// Function doc + void foo(int [[^a]]); + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "int"; + HI.Definition = "int a"; + HI.Documentation = ""; + }, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nType: `int`"}, + {R"cpp(/// Function doc + /// @param a this is doc for a + void foo(int [[^a]]); + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "int"; + HI.Definition = "int a"; + HI.Documentation = "this is doc for a"; + }, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nthis is doc " + "for a\n\n---\nType: `int`"}, + {R"cpp(/// Function doc + /// @param b this is doc for b + void foo(int [[^a]], int b); + )cpp", + [](HoverInfo &HI) { + HI.Name = "a"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "int"; + HI.Definition = "int a"; + HI.Documentation = ""; + }, + "### param\n\n---\n```cpp\n// In foo\nint a\n```\n\n---\nType: `int`"}, + {R"cpp(/// Function doc + /// @param b this is doc for \p b + void foo(int a, int [[^b]]); + )cpp", + [](HoverInfo &HI) { + HI.Name = "b"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "int"; + HI.Definition = "int b"; + HI.Documentation = "this is doc for \\p b"; + }, + "### param\n\n---\n```cpp\n// In foo\nint b\n```\n\n---\nthis is doc " + "for `b`\n\n---\nType: `int`"}, + {R"cpp(/// Function doc + /// @param b this is doc for \p b + template <typename T> + void foo(T a, T [[^b]]); + )cpp", + [](HoverInfo &HI) { + HI.Name = "b"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "T"; + HI.Definition = "T b"; + HI.Documentation = "this is doc for \\p b"; + }, + "### param\n\n---\n```cpp\n// In foo\nT b\n```\n\n---\nthis is doc for " + "`b`\n\n---\nType: `T`"}, + {R"cpp(/// Function doc + /// @param b this is <b>doc</b> <html-tag attribute/> <another-html-tag attribute="value">for</another-html-tag> \p b + void foo(int a, int [[^b]]); + )cpp", + [](HoverInfo &HI) { + HI.Name = "b"; + HI.Kind = index::SymbolKind::Parameter; + HI.NamespaceScope = ""; + HI.LocalScope = "foo::"; + HI.Type = "int"; + HI.Definition = "int b"; + HI.Documentation = + "this is <b>doc</b> <html-tag attribute/> <another-html-tag " + "attribute=\"value\">for</another-html-tag> \\p b"; + }, + "### param\n\n---\n```cpp\n// In foo\nint b\n```\n\n---\nthis is " + "\\<b>doc\\</b> \\<html-tag attribute/> \\<another-html-tag " + "attribute=\"value\">for\\</another-html-tag> `b`\n\n---\nType: `int`"}, + }; + + // Create a tiny index, so tests above can verify documentation is fetched. + Symbol IndexSym = func("indexSymbol"); + IndexSym.Documentation = "comment from index"; + SymbolSlab::Builder Symbols; + Symbols.insert(IndexSym); + auto Index = + MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab()); + + for (const auto &Case : Cases) { + SCOPED_TRACE(Case.Code); + + Annotations T(Case.Code); + TestTU TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + Config Cfg; + Cfg.Hover.ShowAKA = true; + Cfg.Documentation.CommentFormat = Config::CommentFormatPolicy::Doxygen; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + auto H = getHover(AST, T.point(), format::getLLVMStyle(), Index.get()); + ASSERT_TRUE(H); + HoverInfo Expected; + Expected.SymRange = T.range(); + Case.ExpectedBuilder(Expected); + + EXPECT_EQ(H->present(MarkupKind::Markdown), Case.ExpectedRender); + EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope); + EXPECT_EQ(H->LocalScope, Expected.LocalScope); + EXPECT_EQ(H->Name, Expected.Name); + EXPECT_EQ(H->Kind, Expected.Kind); + EXPECT_EQ(H->Documentation, Expected.Documentation); + EXPECT_EQ(H->Definition, Expected.Definition); + EXPECT_EQ(H->Type, Expected.Type); + EXPECT_EQ(H->ReturnType, Expected.ReturnType); + EXPECT_EQ(H->Parameters, Expected.Parameters); + EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters); + EXPECT_EQ(H->SymRange, Expected.SymRange); + EXPECT_EQ(H->Value, Expected.Value); + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp index e0cd955..99e728c 100644 --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -1295,14 +1295,7 @@ TEST(TypeHints, NoQualifiers) { } } )cpp", - ExpectedHint{": S1", "x"}, - // FIXME: We want to suppress scope specifiers - // here because we are into the whole - // brevity thing, but the ElaboratedType - // printer does not honor the SuppressScope - // flag by design, so we need to extend the - // PrintingPolicy to support this use case. - ExpectedHint{": S2::Inner<int>", "y"}); + ExpectedHint{": S1", "x"}, ExpectedHint{": Inner<int>", "y"}); } TEST(TypeHints, Lambda) { diff --git a/clang-tools-extra/clangd/unittests/QualityTests.cpp b/clang-tools-extra/clangd/unittests/QualityTests.cpp index 619ea32..4954659 100644 --- a/clang-tools-extra/clangd/unittests/QualityTests.cpp +++ b/clang-tools-extra/clangd/unittests/QualityTests.cpp @@ -121,7 +121,9 @@ TEST(QualityTests, SymbolRelevanceSignalExtraction) { SymbolRelevanceSignals Relevance; Relevance.merge(CodeCompletionResult(&findDecl(AST, "deprecated"), - /*Priority=*/42, nullptr, false, + /*Priority=*/42, + /*Qualifier=*/std::nullopt, + /*QualifierIsInformative=*/false, /*Accessible=*/false)); EXPECT_EQ(Relevance.NameMatch, SymbolRelevanceSignals().NameMatch); EXPECT_TRUE(Relevance.Forbidden); @@ -487,13 +489,15 @@ TEST(QualityTests, ItemWithFixItsRankedDown) { auto AST = Header.build(); SymbolRelevanceSignals RelevanceWithFixIt; - RelevanceWithFixIt.merge(CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, - false, true, {FixItHint{}})); + RelevanceWithFixIt.merge(CodeCompletionResult( + &findDecl(AST, "x"), /*Priority=*/0, /*Qualifier=*/std::nullopt, + /*QualifierIsInformative=*/false, /*Accessible=*/true, {FixItHint{}})); EXPECT_TRUE(RelevanceWithFixIt.NeedsFixIts); SymbolRelevanceSignals RelevanceWithoutFixIt; - RelevanceWithoutFixIt.merge( - CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, false, true, {})); + RelevanceWithoutFixIt.merge(CodeCompletionResult( + &findDecl(AST, "x"), /*Priority=*/0, /*Qualifier=*/std::nullopt, + /*QualifierIsInformative=*/false, /*Accessible=*/true, {})); EXPECT_FALSE(RelevanceWithoutFixIt.NeedsFixIts); EXPECT_LT(RelevanceWithFixIt.evaluateHeuristics(), diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 2cb0722..5d2a77b 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -17,10 +17,11 @@ #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/MemoryBuffer.h" -#include <algorithm> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include <algorithm> + namespace clang { namespace clangd { namespace { @@ -861,6 +862,25 @@ TEST(RenameTest, WithinFileRename) { void func([[Fo^o]] *f) {} )cpp", + + // rename with explicit object parameter + R"cpp( + struct Foo { + int [[memb^er]] {}; + auto&& getter1(this auto&& self) { + auto local = [&] { + return self.[[memb^er]]; + }(); + return local + self.[[memb^er]]; + } + auto&& getter2(this Foo&& self) { + return self.[[memb^er]]; + } + int normal() { + return this->[[mem^ber]] + [[memb^er]]; + } + }; + )cpp", }; llvm::StringRef NewName = "NewName"; for (llvm::StringRef T : Tests) { @@ -868,6 +888,7 @@ TEST(RenameTest, WithinFileRename) { Annotations Code(T); auto TU = TestTU::withCode(Code.code()); TU.ExtraArgs.push_back("-xobjective-c++"); + TU.ExtraArgs.push_back("-std=c++23"); auto AST = TU.build(); auto Index = TU.index(); for (const auto &RenamePos : Code.points()) { diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp index aaaf758..3df19d8 100644 --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -104,9 +104,9 @@ TEST(SelectionTest, CommonAncestor) { { R"cpp( template <typename T> - int x = [[T::^U::]]ccc(); + int x = T::[[^U]]::ccc(); )cpp", - "NestedNameSpecifierLoc", + "DependentNameTypeLoc", }, { R"cpp( diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp index 7ede19c..4efae25 100644 --- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -371,6 +371,45 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) { //[[ foo /* bar */]] )cpp", + R"cpp( + //Ignore non-conditional directives + #define A 1 + + void func() {[[ + int Variable = 100; + + #ifdef FOO[[ + Variable = 1; + #if 1[[ + Variable = 4; + ]]#endif + ]]#else[[ + Variable = 2; + //handle nested directives + #if 1[[ + Variable = 3; + ]]#endif + ]]#endif + + + ]]} + )cpp", + R"cpp( + int Variable = 0; + #if defined(WALDO) + Variable = 1; + # + )cpp", + R"cpp( + int Variable = 0; + #if defined(WALDO)[[ + Variable = 1; + ]]#elif 1[[ + Variable = 2; + ]]#else + Variable = 3; + # + )cpp", }; for (const char *Test : Tests) { auto T = Annotations(Test); diff --git a/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp new file mode 100644 index 0000000..31a6a14 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp @@ -0,0 +1,215 @@ +//===-- SymbolDocumentationTests.cpp --------------------------------------===// +// +// 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 "SymbolDocumentation.h" + +#include "support/Markup.h" +#include "clang/Basic/CommentOptions.h" +#include "llvm/ADT/StringRef.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { + +TEST(SymbolDocumentation, UnhandledDocs) { + + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + { + "foo bar", + "foo bar", + "foo bar", + "foo bar", + }, + { + "foo\nbar\n", + "foo\nbar", + "foo\nbar", + "foo bar", + }, + { + "foo\n\nbar\n", + "foo\n\nbar", + "foo\n\nbar", + "foo\n\nbar", + }, + { + "foo \\p bar baz", + "foo `bar` baz", + "foo `bar` baz", + "foo bar baz", + }, + { + "foo \\e bar baz", + "foo \\*bar\\* baz", + "foo *bar* baz", + "foo *bar* baz", + }, + { + "foo \\b bar baz", + "foo \\*\\*bar\\*\\* baz", + "foo **bar** baz", + "foo **bar** baz", + }, + { + "foo \\ref bar baz", + "foo \\*\\*\\\\ref\\*\\* \\*bar\\* baz", + "foo **\\ref** *bar* baz", + "foo **\\ref** *bar* baz", + }, + { + "foo @ref bar baz", + "foo \\*\\*@ref\\*\\* \\*bar\\* baz", + "foo **@ref** *bar* baz", + "foo **@ref** *bar* baz", + }, + { + "\\brief this is a \\n\nbrief description", + "", + "", + "", + }, + { + "\\throw exception foo", + "\\*\\*\\\\throw\\*\\* \\*exception\\* foo", + "**\\throw** *exception* foo", + "**\\throw** *exception* foo", + }, + { + R"(\brief this is a brief description + +\li item 1 +\li item 2 +\arg item 3)", + R"(- item 1 + +- item 2 + +- item 3)", + R"(- item 1 + +- item 2 + +- item 3)", + R"(- item 1 + +- item 2 + +- item 3)", + }, + { + "\\defgroup mygroup this is a group\nthis is not a group description", + "\\*\\*@defgroup\\*\\* `mygroup this is a group`\n\nthis is not a " + "group " + "description", + "**@defgroup** `mygroup this is a group`\n\nthis is not a group " + "description", + "**@defgroup** `mygroup this is a group`\n\nthis is not a group " + "description", + }, + { + R"(\verbatim +this is a +verbatim block containing +some verbatim text +\endverbatim)", + R"(\*\*@verbatim\*\* + +``` +this is a +verbatim block containing +some verbatim text +``` + +\*\*@endverbatim\*\*)", + R"(**@verbatim** + +``` +this is a +verbatim block containing +some verbatim text +``` + +**@endverbatim**)", + R"(**@verbatim** + +this is a +verbatim block containing +some verbatim text + +**@endverbatim**)", + }, + { + "@param foo this is a parameter\n@param bar this is another " + "parameter", + "", + "", + "", + }, + { + R"(@brief brief docs + +@param foo this is a parameter + +\brief another brief? + +\details these are details + +More description +documentation)", + R"(\*\*\\brief\*\* another brief? + +\*\*\\details\*\* these are details + +More description +documentation)", + R"(**\brief** another brief? + +**\details** these are details + +More description +documentation)", + R"(**\brief** another brief? + +**\details** these are details + +More description documentation)", + }, + { + R"(<b>this is a bold text</b> +normal text<i>this is an italic text</i> +<code>this is a code block</code>)", + R"(\<b>this is a bold text\</b> +normal text\<i>this is an italic text\</i> +\<code>this is a code block\</code>)", + R"(\<b>this is a bold text\</b> +normal text\<i>this is an italic text\</i> +\<code>this is a code block\</code>)", + "<b>this is a bold text</b> normal text<i>this is an italic text</i> " + "<code>this is a code block</code>", + }, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.docToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/support/MarkupTests.cpp b/clang-tools-extra/clangd/unittests/support/MarkupTests.cpp index 482f230..5f91f31 100644 --- a/clang-tools-extra/clangd/unittests/support/MarkupTests.cpp +++ b/clang-tools-extra/clangd/unittests/support/MarkupTests.cpp @@ -463,6 +463,7 @@ TEST(Document, Separators) { ```cpp test ``` + bar)md"; EXPECT_EQ(D.asEscapedMarkdown(), ExpectedMarkdown); EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown); @@ -559,6 +560,7 @@ foo bar baz ``` + ```cpp foo ```)md"; @@ -571,6 +573,12 @@ foo foo)pt"; EXPECT_EQ(D.asPlainText(), ExpectedPlainText); + + Document D2; + D2.addCodeBlock(""); + EXPECT_EQ(D2.asEscapedMarkdown(), "```cpp\n```"); + EXPECT_EQ(D2.asMarkdown(), "```cpp\n```"); + EXPECT_EQ(D2.asPlainText(), ""); } TEST(BulletList, Render) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8b636018..32e4dfb 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -1,3 +1,6 @@ +.. If you want to modify sections/contents permanently, you should modify both + ReleaseNotes.rst and ReleaseNotesTemplate.txt. + ==================================================== Extra Clang Tools |release| |ReleaseNotesTitle| ==================================================== @@ -101,6 +104,14 @@ Improvements to clang-query Improvements to clang-tidy -------------------------- +- The :program:`check_clang_tidy.py` tool now recognizes the ``-std`` argument + when run over C files. If ``-std`` is not specified, it defaults to + ``c99-or-later``. + +- :program:`clang-tidy` no longer attemps to analyze code from system headers + by default, greatly improving performance. This behavior is disabled if the + `SystemHeaders` option is enabled. + - The :program:`run-clang-tidy.py` and :program:`clang-tidy-diff.py` scripts now run checks in parallel by default using all available hardware threads. Both scripts display the number of threads being used in their output. @@ -109,6 +120,12 @@ Improvements to clang-tidy `enable-check-profile` to enable per-check timing profiles and print a report based on all analyzed files. +- Improved documentation of the `-line-filter` command-line flag of + :program:`clang-tidy` and :program:`run-clang-tidy.py`. + +- Improved :program:`clang-tidy` option `-quiet` by suppressing diagnostic + count messages. + New checks ^^^^^^^^^^ @@ -118,12 +135,25 @@ New checks Detects default initialization (to 0) of variables with ``enum`` type where the enum has no enumerator with value of 0. +- New :doc:`cppcoreguidelines-pro-bounds-avoid-unchecked-container-access + <clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access>` + check. + + Finds calls to ``operator[]`` in STL containers and suggests replacing them + with safe alternatives. + - New :doc:`llvm-mlir-op-builder <clang-tidy/checks/llvm/use-new-mlir-op-builder>` check. Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form and suggests using ``T::create`` instead. +- New :doc:`misc-override-with-different-visibility + <clang-tidy/checks/misc/override-with-different-visibility>` check. + + Finds virtual function overrides with different visibility than the function + in the base class. + New check aliases ^^^^^^^^^^^^^^^^^ @@ -140,7 +170,7 @@ Changes in existing checks - Improved :doc:`bugprone-reserved-identifier <clang-tidy/checks/bugprone/reserved-identifier>` check by ignoring - declarations in system headers. + declarations and macros in system headers. - Improved :doc:`bugprone-signed-char-misuse <clang-tidy/checks/bugprone/signed-char-misuse>` check by fixing @@ -157,9 +187,23 @@ Changes in existing checks an additional matcher that generalizes the copy-and-swap idiom pattern detection. +- Improved :doc:`cppcoreguidelines-prefer-member-initializer + <clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to + avoid false positives on inherited members in class templates. + +- Improved :doc:`cppcoreguidelines-pro-bounds-pointer-arithmetic + <clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic>` check + adding an option to allow pointer arithmetic via prefix/postfix increment or + decrement operators. + - Improved :doc:`misc-header-include-cycle <clang-tidy/checks/misc/header-include-cycle>` check performance. +- Improved :doc:`modernize-use-constraints + <clang-tidy/checks/modernize/use-constraints>` check by fixing a crash on + uses of non-standard ``enable_if`` with a signature different from + ``std::enable_if`` (such as ``boost::enable_if``). + - Improved :doc:`modernize-use-designated-initializers <clang-tidy/checks/modernize/use-designated-initializers>` check to suggest using designated initializers for aliased aggregate types. @@ -186,14 +230,25 @@ Changes in existing checks <clang-tidy/checks/portability/template-virtual-member-function>` check to avoid false positives on pure virtual member functions. +- Improved :doc:`readability-container-size-empty + <clang-tidy/checks/readability/container-size-empty>` check by correctly + generating fix-it hints when size method is called from implicit ``this``, + ignoring default constructors with user provided arguments and adding + detection in container's method except ``empty``. + - Improved :doc:`readability-identifier-naming <clang-tidy/checks/readability/identifier-naming>` check by ignoring - declarations in system headers. + declarations and macros in system headers. The documentation is also improved + to differentiate the general options from the specific ones. - Improved :doc:`readability-qualified-auto <clang-tidy/checks/readability/qualified-auto>` check by adding the option `IgnoreAliasing`, that allows not looking at underlying types of type aliases. +- Improved :doc:`readability-uppercase-literal-suffix + <clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize + literal suffixes added in C++23 and C23. + Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/ReleaseNotesTemplate.txt b/clang-tools-extra/docs/ReleaseNotesTemplate.txt new file mode 100644 index 0000000..b17799b --- /dev/null +++ b/clang-tools-extra/docs/ReleaseNotesTemplate.txt @@ -0,0 +1,122 @@ +.. If you want to modify sections/contents permanently, you should modify both + ReleaseNotes.rst and ReleaseNotesTemplate.txt. + +==================================================== +Extra Clang Tools |release| |ReleaseNotesTitle| +==================================================== + +.. contents:: + :local: + :depth: 3 + +Written by the `LLVM Team <https://llvm.org/>`_ + +.. only:: PreRelease + + .. warning:: + These are in-progress notes for the upcoming Extra Clang Tools |version| release. + Release notes for previous releases can be found on + `the Download Page <https://releases.llvm.org/download.html>`_. + +Introduction +============ + +This document contains the release notes for the Extra Clang Tools, part of the +Clang release |release|. Here we describe the status of the Extra Clang Tools in +some detail, including major improvements from the previous release and new +feature work. All LLVM releases may be downloaded from the `LLVM releases web +site <https://llvm.org/releases/>`_. + +For more information about Clang or LLVM, including information about +the latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or +the `LLVM Web Site <https://llvm.org>`_. + +Note that if you are reading this file from a Git checkout or the +main Clang web page, this document applies to the *next* release, not +the current one. To see the release notes for a specific release, please +see the `releases page <https://llvm.org/releases/>`_. + +What's New in Extra Clang Tools |release|? +========================================== + +Some of the major new features and improvements to Extra Clang Tools are listed +here. Generic improvements to Extra Clang Tools as a whole or to its underlying +infrastructure are described first, followed by tool-specific sections. + +Major New Features +------------------ + +Improvements to clangd +---------------------- + +Inlay hints +^^^^^^^^^^^ + +Diagnostics +^^^^^^^^^^^ + +Semantic Highlighting +^^^^^^^^^^^^^^^^^^^^^ + +Compile flags +^^^^^^^^^^^^^ + +Hover +^^^^^ + +Code completion +^^^^^^^^^^^^^^^ + +Code actions +^^^^^^^^^^^^ + +Signature help +^^^^^^^^^^^^^^ + +Cross-references +^^^^^^^^^^^^^^^^ + +Objective-C +^^^^^^^^^^^ + +Miscellaneous +^^^^^^^^^^^^^ + +Improvements to clang-doc +------------------------- + +Improvements to clang-query +--------------------------- + +Improvements to clang-tidy +-------------------------- + +New checks +^^^^^^^^^^ + +New check aliases +^^^^^^^^^^^^^^^^^ + +Changes in existing checks +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Removed checks +^^^^^^^^^^^^^^ + +Miscellaneous +^^^^^^^^^^^^^ + +Improvements to include-fixer +----------------------------- + +Improvements to clang-include-fixer +----------------------------------- + +Improvements to modularize +-------------------------- + +Improvements to pp-trace +------------------------ + +Clang-tidy Visual Studio plugin +------------------------------- diff --git a/clang-tools-extra/docs/clang-change-namespace.rst b/clang-tools-extra/docs/clang-change-namespace.rst new file mode 100644 index 0000000..1eab83f --- /dev/null +++ b/clang-tools-extra/docs/clang-change-namespace.rst @@ -0,0 +1,314 @@ +====================== +Clang-Change-Namespace +====================== + +.. contents:: + +.. toctree:: + :maxdepth: 1 + +:program:`clang-change-namespace` can be used to change the surrounding +namespaces of class/function definitions. + +Classes/functions in the moved namespace will have new namespaces while +references to symbols (e.g. types, functions) which are not defined in the +changed namespace will be correctly qualified by prepending namespace specifiers +before them. This will try to add shortest namespace specifiers possible. + +When a symbol reference needs to be fully-qualified, this adds a `::` prefix to +the namespace specifiers unless the new namespace is the global namespace. For +classes, only classes that are declared/defined in the given namespace in +specified files will be moved: forward declarations will remain in the old +namespace. The will be demonstrated in the next example. + +Example usage +------------- + +For example, consider this `test.cc` example here with the forward declared +class `FWD` and the defined class `A`, both in the namespace `a`. + +.. code-block:: c++ + + namespace a { + class FWD; + class A { + FWD *fwd; + }; + } // namespace a + +And now let's change the namespace `a` to `x`. + +.. code-block:: console + + clang-change-namespace \ + --old_namespace "a" \ + --new_namespace "x" \ + --file_pattern "test.cc" \ + --i \ + test.cc + +Note that in the code below there's still the forward decalred class `FWD` that +stayed in the namespace `a`. It wasn't moved to the new namespace because it +wasn't defined/declared here in `a` but only forward declared. + +.. code-block:: c++ + + namespace a { + class FWD; + } // namespace a + namespace x { + + class A { + a::FWD *fwd; + }; + } // namespace x + + +Another example +--------------- + +Consider this `test.cc` file: + +.. code-block:: c++ + + namespace na { + class X {}; + namespace nb { + class Y { + X x; + }; + } // namespace nb + } // namespace na + +To move the definition of class `Y` from namespace `na::nb` to `x::y`, run: + +.. code-block:: console + + clang-change-namespace \ + --old_namespace "na::nb" \ + --new_namespace "x::y" \ + --file_pattern "test.cc" \ + --i \ + test.cc + +This will overwrite `test.cc` to look like this: + +.. code-block:: c++ + + namespace na { + class X {}; + + } // namespace na + namespace x { + namespace y { + class Y { + na::X x; + }; + } // namespace y + } // namespace x + +Note, that we've successfully moved the class `Y` from namespace `na::nb` to +namespace `x::y`. + +Caveats +======= + +Content already exists in new namespace +--------------------------------------- + +Consider this `test.cc` example that defines two `class A` one inside the +namespace `a` and one in namespace `b`: + +.. code-block:: c++ + + namespace a { + class A { + int classAFromWithinNamespace_a; + }; + } // namespace a + + namespace b { + class A { + int classAFromWithinNamespace_b; + }; + } //namespace b + +Let's move everything from namespace `a` to namespace `b`: + +.. code-block:: console + + clang-change-namespace \ + --old_namespace "a" \ + --new_namespace "b" \ + --file_pattern test.cc \ + test.cc + +As expected we now have to definitions of `class A` inside the namespace `b`: + +.. code-block:: c++ + + namespace b { + class A { + int classAFromWithinNamespace_a; + }; + } // namespace b + + namespace b { + class A { + int classAFromWithinNamespace_b; + }; + } //namespace b + +The re-factoring looks correct but the code will not compile due to the name +duplication. It is not up to the tool to ensure compilability in that sense. +But one has to be aware of that. + +Inline namespace doesn't work +----------------------------- + +Consider this usage of two versions of implementations for a `greet` function: + +.. code-block:: c++ + + #include <cstdio> + + namespace Greeter { + inline namespace Version1 { + const char* greet() { return "Hello from version 1!"; } + } // namespace Version1 + namespace Version2 { + const char* greet() { return "Hello from version 2!"; } + } // namespace Version2 + } // namespace Greeter + + int main(int argc, char* argv[]) { + printf("%s\n", Greeter::greet()); + return 0; + } + +Note, that currently `Greeter::greet()` will result in a call to +`Greeter::Version1::greet()` because that's the inlined namespace. + +Let's say you want to move one and make `Version2` the default now and remove +the `inline` from the `Version1`. First let's try to turn `namespace Version2` +into `inline namespace Version2`: + +.. code-block:: console + + clang-change-namespace \ + --old_namespace "Greeter::Version2" \ + --new_namespace "inline Version2" \ + --file_pattern main.cc main.cc + +But this will put the `inline` keyword in the wrong place resulting in: + +.. code-block:: c++ + + #include <cstdio> + + namespace Greeter { + inline namespace Version1 { + const char* greet() { return "Hello from version 1!"; } + } // namespace Version1 + + } // namespace Greeter + namespace inline Greeter { + namespace Version2 { + const char *greet() { return "Hello from version 2!"; } + } // namespace Version2 + } // namespace inline Greeter + + int main(int argc, char* argv[]) { + printf("%s\n", Greeter::greet()); + return 0; + } + +One cannot use `:program:`clang-change-namespace` to inline a namespace. + +Symbol references not updated +----------------------------- + +Consider this `test.cc` file: + +.. code-block:: c++ + + namespace old { + struct foo {}; + } // namespace old + + namespace b { + old::foo g_foo; + } // namespace b + +Notice that namespace `b` defines a global variable of type `old::foo`. If we +now change the name of the `old` namespace to `modern`, the reference will not +be updated: + +.. code-block:: console + + clang-change-namespace \ + --old_namespace "old" \ + --new_namespace "modern" \ + --file_pattern test.cc \ + test.cc + +.. code-block:: c++ + + namespace modern { + struct foo {}; + } // namespace modern + + namespace b { + old::foo g_foo; + } // namespace b + +`g_foo` is still of the no longer existing type `old::foo` while instead it +should use `modern::foo`. + +Only symbol references in the moved namespace are updated, not outside of it. + + +:program:`clang-change-namespace` Command Line Options +====================================================== + +.. option:: --allowed_file=<string> + + A file containing regexes of symbol names that are not expected to be updated + when changing namespaces around them. + +.. option:: --dump_result + + Dump new file contents in YAML, if specified. + +.. option:: --extra-arg=<string> + + Additional argument to append to the compiler command line + +.. option:: --extra-arg-before=<string> + + Additional argument to prepend to the compiler command line + +.. option:: --file_pattern=<string> + + Only rename namespaces in files that match the given regular expression + pattern. + +.. option:: -i + + Inplace edit <file>s, if specified. + +.. option:: --new_namespace=<string> + + New namespace. Use `""` when you target the global namespace. + +.. option:: --old_namespace=<string> + + Old namespace. + +.. option:: -p <string> + + Build path + +.. option:: --style=<string> + + The style name used for reformatting. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.rst new file mode 100644 index 0000000..556d902 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.rst @@ -0,0 +1,64 @@ +.. title:: clang-tidy - cppcoreguidelines-pro-bounds-avoid-unchecked-container-access + +cppcoreguidelines-pro-bounds-avoid-unchecked-container-access +============================================================= + +Finds calls to ``operator[]`` in STL containers and suggests replacing them +with safe alternatives. +Safe alternatives include STL ``at`` or GSL ``at`` functions, ``begin()`` or +``end()`` functions, ``range-for`` loops, ``std::span``, or an appropriate +function from ``<algorithms>``. + +For example, both + +.. code-block:: c++ + + std::vector<int> a; + int b = a[4]; + +and + +.. code-block:: c++ + + std::unique_ptr<vector> a; + int b = a[0]; + +will generate a warning. + +STL containers for which ``operator[]`` is well-defined for all inputs are excluded +from this check (e.g.: ``std::map::operator[]``). + +This check enforces part of the `SL.con.3 +<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#slcon3-avoid-bounds-errors>` +guideline and is part of the `Bounds Safety (Bounds 4) +<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Pro-bounds-arrayindex>` +profile from the C++ Core Guidelines. + +Options +------- + +.. option:: ExcludeClasses + + Semicolon-delimited list of class names for overwriting the default + exclusion list. The default is: + `::std::map;::std::unordered_map;::std::flat_map`. + +.. option:: FixMode + + Determines what fixes are suggested. Either `none`, `at` (use + ``a.at(index)`` if a fitting function exists) or `function` (use a + function ``f(a, index)``). The default is `none`. + +.. option:: FixFunction + + The function to use in the `function` mode. For C++23 and beyond, the + passed function must support the empty subscript operator, i.e., the case + where ``a[]`` becomes ``f(a)``. :option:`FixFunctionEmptyArgs` can be + used to override the suggested function in that case. The default is `gsl::at`. + +.. option:: FixFunctionEmptyArgs + + The function to use in the `function` mode for the empty subscript operator + case in C++23 and beyond only. If no fixes should be made for empty + subscript operators, pass an empty string. In that case, only the warnings + will be printed. The default is the value of :option:`FixFunction`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.rst index 12a8f60..a3f1371 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-pointer-arithmetic.rst @@ -13,3 +13,11 @@ arrays of data. This rule is part of the `Bounds safety (Bounds 1) <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Pro-bounds-arithmetic>`_ profile from the C++ Core Guidelines. + +Options +------- + +.. option:: AllowIncrementDecrementOperators + + When enabled, the check will allow using the prefix/postfix increment or + decrement operators on pointers. Default is ``false``. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b6444eb..5e3ffc4 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -201,6 +201,7 @@ Clang-Tidy Checks :doc:`cppcoreguidelines-owning-memory <cppcoreguidelines/owning-memory>`, :doc:`cppcoreguidelines-prefer-member-initializer <cppcoreguidelines/prefer-member-initializer>`, "Yes" :doc:`cppcoreguidelines-pro-bounds-array-to-pointer-decay <cppcoreguidelines/pro-bounds-array-to-pointer-decay>`, + :doc:`cppcoreguidelines-pro-bounds-avoid-unchecked-container-access <cppcoreguidelines/pro-bounds-avoid-unchecked-container-access>`, "Yes" :doc:`cppcoreguidelines-pro-bounds-constant-array-index <cppcoreguidelines/pro-bounds-constant-array-index>`, "Yes" :doc:`cppcoreguidelines-pro-bounds-pointer-arithmetic <cppcoreguidelines/pro-bounds-pointer-arithmetic>`, :doc:`cppcoreguidelines-pro-type-const-cast <cppcoreguidelines/pro-type-const-cast>`, @@ -271,6 +272,7 @@ Clang-Tidy Checks :doc:`misc-no-recursion <misc/no-recursion>`, :doc:`misc-non-copyable-objects <misc/non-copyable-objects>`, :doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`, + :doc:`misc-override-with-different-visibility <misc/override-with-different-visibility>`, :doc:`misc-redundant-expression <misc/redundant-expression>`, "Yes" :doc:`misc-static-assert <misc/static-assert>`, "Yes" :doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/override-with-different-visibility.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/override-with-different-visibility.rst new file mode 100644 index 0000000..310bfe2 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/override-with-different-visibility.rst @@ -0,0 +1,87 @@ +.. title:: clang-tidy - misc-override-with-different-visibility + +misc-override-with-different-visibility +======================================= + +Finds virtual function overrides with different visibility than the function +in the base class. This includes for example if a virtual function declared as +``private`` is overridden and declared as ``public`` in a subclass. The detected +change is the modification of visibility resulting from keywords ``public``, +``protected``, ``private`` at overridden virtual functions. The check applies to +any normal virtual function and optionally to destructors or operators. Use of +the ``using`` keyword is not considered as visibility change by this check. + + +.. code-block:: c++ + + class A { + public: + virtual void f_pub(); + private: + virtual void f_priv(); + }; + + class B: public A { + public: + void f_priv(); // warning: changed visibility from private to public + private: + void f_pub(); // warning: changed visibility from public to private + }; + + class C: private A { + // no warning: f_pub becomes private in this case but this is from the + // private inheritance + }; + + class D: private A { + public: + void f_pub(); // warning: changed visibility from private to public + // 'f_pub' would have private access but is forced to be + // public + }; + +If the visibility is changed in this way, it can indicate bad design or +programming error. + +If a virtual function is private in a subclass but public in the base class, it +can still be accessed from a pointer to the subclass if the pointer is converted +to the base type. Probably private inheritance can be used instead. + +A protected virtual function that is made public in a subclass may have valid +use cases but similar (not exactly same) effect can be achieved with the +``using`` keyword. + +Options +------- + +.. option:: DisallowedVisibilityChange + + Controls what kind of change to the visibility will be detected by the check. + Possible values are `any`, `widening`, `narrowing`. For example the + `widening` option will produce warning only if the visibility is changed + from more restrictive (``private``) to less restrictive (``public``). + Default value is `any`. + +.. option:: CheckDestructors + + If `true`, the check does apply to destructors too. Otherwise destructors + are ignored by the check. + Default value is `false`. + +.. option:: CheckOperators + + If `true`, the check does apply to overloaded C++ operators (as virtual + member functions) too. This includes other special member functions (like + conversions) too. This option is probably useful only in rare cases because + operators and conversions are not often virtual functions. + Default value is `false`. + +.. option:: IgnoredFunctions + + This option can be used to ignore the check at specific functions. + To configure this option, a semicolon-separated list of function names + should be provided. The list can contain regular expressions, in this way it + is possible to select all functions of a specific class (like `MyClass::.*`) + or a specific function of any class (like `my_function` or + `::.*::my_function`). The function names are matched at the base class. + Default value is empty string. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst index 8a54687..0e031a1 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst @@ -43,14 +43,21 @@ The options and their corresponding values are: - ``LowerCase`` - example: ``int i_Variable`` - ``CamelCase`` - example: ``int IVariable`` -Options -------- +Options summary +--------------- -The following options are described below: +The available options are summarized below: + +**General options** - - :option:`AbstractClassCase`, :option:`AbstractClassPrefix`, :option:`AbstractClassSuffix`, :option:`AbstractClassIgnoredRegexp`, :option:`AbstractClassHungarianPrefix` - :option:`AggressiveDependentMemberLookup` - :option:`CheckAnonFieldInParent` + - :option:`GetConfigPerFile` + - :option:`IgnoreMainLikeFunctions` + +**Specific options** + + - :option:`AbstractClassCase`, :option:`AbstractClassPrefix`, :option:`AbstractClassSuffix`, :option:`AbstractClassIgnoredRegexp`, :option:`AbstractClassHungarianPrefix` - :option:`ClassCase`, :option:`ClassPrefix`, :option:`ClassSuffix`, :option:`ClassIgnoredRegexp`, :option:`ClassHungarianPrefix` - :option:`ClassConstantCase`, :option:`ClassConstantPrefix`, :option:`ClassConstantSuffix`, :option:`ClassConstantIgnoredRegexp`, :option:`ClassConstantHungarianPrefix` - :option:`ClassMemberCase`, :option:`ClassMemberPrefix`, :option:`ClassMemberSuffix`, :option:`ClassMemberIgnoredRegexp`, :option:`ClassMemberHungarianPrefix` @@ -66,13 +73,11 @@ The following options are described below: - :option:`EnumCase`, :option:`EnumPrefix`, :option:`EnumSuffix`, :option:`EnumIgnoredRegexp` - :option:`EnumConstantCase`, :option:`EnumConstantPrefix`, :option:`EnumConstantSuffix`, :option:`EnumConstantIgnoredRegexp`, :option:`EnumConstantHungarianPrefix` - :option:`FunctionCase`, :option:`FunctionPrefix`, :option:`FunctionSuffix`, :option:`FunctionIgnoredRegexp` - - :option:`GetConfigPerFile` - :option:`GlobalConstantCase`, :option:`GlobalConstantPrefix`, :option:`GlobalConstantSuffix`, :option:`GlobalConstantIgnoredRegexp`, :option:`GlobalConstantHungarianPrefix` - :option:`GlobalConstantPointerCase`, :option:`GlobalConstantPointerPrefix`, :option:`GlobalConstantPointerSuffix`, :option:`GlobalConstantPointerIgnoredRegexp`, :option:`GlobalConstantPointerHungarianPrefix` - :option:`GlobalFunctionCase`, :option:`GlobalFunctionPrefix`, :option:`GlobalFunctionSuffix`, :option:`GlobalFunctionIgnoredRegexp` - :option:`GlobalPointerCase`, :option:`GlobalPointerPrefix`, :option:`GlobalPointerSuffix`, :option:`GlobalPointerIgnoredRegexp`, :option:`GlobalPointerHungarianPrefix` - :option:`GlobalVariableCase`, :option:`GlobalVariablePrefix`, :option:`GlobalVariableSuffix`, :option:`GlobalVariableIgnoredRegexp`, :option:`GlobalVariableHungarianPrefix` - - :option:`IgnoreMainLikeFunctions` - :option:`InlineNamespaceCase`, :option:`InlineNamespacePrefix`, :option:`InlineNamespaceSuffix`, :option:`InlineNamespaceIgnoredRegexp` - :option:`LocalConstantCase`, :option:`LocalConstantPrefix`, :option:`LocalConstantSuffix`, :option:`LocalConstantIgnoredRegexp`, :option:`LocalConstantHungarianPrefix` - :option:`LocalConstantPointerCase`, :option:`LocalConstantPointerPrefix`, :option:`LocalConstantPointerSuffix`, :option:`LocalConstantPointerIgnoredRegexp`, :option:`LocalConstantPointerHungarianPrefix` @@ -105,6 +110,12 @@ The following options are described below: - :option:`VariableCase`, :option:`VariablePrefix`, :option:`VariableSuffix`, :option:`VariableIgnoredRegexp`, :option:`VariableHungarianPrefix` - :option:`VirtualMethodCase`, :option:`VirtualMethodPrefix`, :option:`VirtualMethodSuffix`, :option:`VirtualMethodIgnoredRegexp` + +Options description +------------------- + +A detailed description of each option is presented below: + .. option:: AbstractClassCase When defined, the check will ensure abstract class names conform to the diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst index b7a366e..3abd663 100644 --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -111,6 +111,13 @@ Diagnostics which have a corresponding warning option, are named ``-Wliteral-conversion`` will be reported with check name ``clang-diagnostic-literal-conversion``. +Clang compiler errors (such as syntax errors, semantic errors, or other failures +that prevent Clang from compiling the code) are reported with the check name +``clang-diagnostic-error``. These represent fundamental compilation failures that +must be fixed before :program:`clang-tidy` can perform its analysis. Unlike other +diagnostics, ``clang-diagnostic-error`` cannot be disabled, as :program:`clang-tidy` +requires valid code to function. + The ``-fix`` flag instructs :program:`clang-tidy` to fix found errors if supported by corresponding checks. @@ -213,14 +220,19 @@ An overview of all the command-line options: Can be used together with -line-filter. This option overrides the 'HeaderFilterRegex' option in .clang-tidy file, if any. - --line-filter=<string> - List of files with line ranges to filter the - warnings. Can be used together with - -header-filter. The format of the list is a - JSON array of objects: + --line-filter=<string> - List of files and line ranges to output diagnostics from. + The range is inclusive on both ends. Can be used together + with -header-filter. The format of the list is a JSON + array of objects. For example: + [ {"name":"file1.cpp","lines":[[1,3],[5,7]]}, {"name":"file2.h"} ] + + This will output diagnostics from 'file1.cpp' only for + the line ranges [1,3] and [5,7], as well as all from the + entire 'file2.h'. --list-checks - List all enabled checks and exit. Use with -checks=* to list all available checks. --load=<pluginfilename> - Load the specified plugin @@ -331,6 +343,107 @@ An overview of all the command-line options: some-check.SomeOption: 'some value' ... +Clang-Tidy Automation +===================== + +:program:`clang-tidy` can analyze multiple source files by specifying them on +the command line. For larger projects, automation scripts provide additional +functionality like parallel execution and integration with version control +systems. + +Running Clang-Tidy in Parallel +------------------------------- + +:program:`clang-tidy` can process multiple files sequentially, but for projects +with many source files, the :program:`run-clang-tidy.py` script provides +parallel execution to significantly reduce analysis time. This script is +included with clang-tidy and runs :program:`clang-tidy` over all files in a +compilation database or a specified path concurrently. + +The script requires a compilation database (``compile_commands.json``) which +can be generated by build systems like CMake (using +``-DCMAKE_EXPORT_COMPILE_COMMANDS=ON``) or by tools like `Bear`_. + +The script supports most of the same options as :program:`clang-tidy` itself, +including ``-checks=``, ``-fix``, ``-header-filter=``, and configuration +options. Run ``run-clang-tidy.py --help`` for a complete list of available +options. + +Example invocations: + +.. code-block:: console + + # Run clang-tidy on all files in the compilation database in parallel + $ run-clang-tidy.py -p=build/ + + # Run with specific checks and apply fixes + $ run-clang-tidy.py -p=build/ -fix -checks=-*,readability-* + + # Run on specific files/directories with header filtering + $ run-clang-tidy.py -p=build/ -header-filter=src/ src/ + + # Run with parallel execution (uses all CPU cores by default) + $ run-clang-tidy.py -p=build/ -j 4 + +Running Clang-Tidy on Diff +--------------------------- + +The :program:`clang-tidy-diff.py` script allows you to run +:program:`clang-tidy` on the lines that have been modified in your working +directory or in a specific diff. Importantly, :program:`clang-tidy-diff.py` only reports +diagnostics for changed lines; :program:`clang-tidy` still analyzes the entire +file and filters out unchanged lines after analysis, so this does not improve +performance. This is particularly useful for code reviews and continuous +integration, as it focuses analysis on the changed code rather than the entire +codebase. + +The script can work with various diff sources: + +* Git working directory changes +* Output from ``git diff`` +* Output from ``svn diff`` +* Patch files + +Example invocations: + +.. code-block:: console + + # Run clang-tidy on all changes in the working directory + $ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1 + + # Run with specific checks and apply fixes + $ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1 -fix \ + -checks=-*,readability-* + + # Run on staged changes + $ git diff -U0 --no-color --cached | clang-tidy-diff.py -p1 + + # Run on changes between two commits + $ git diff -U0 --no-color HEAD~2 HEAD | clang-tidy-diff.py -p1 + + # Run on a patch file + $ clang-tidy-diff.py -p1 < changes.patch + +The ``-p1`` option tells the script to strip one level of path prefix from +the diff, which is typically needed for Git diffs. The script supports most of +the same options as :program:`clang-tidy` itself, including ``-checks=``, +``-fix``, ``-header-filter=``, and configuration options. + +While :program:`clang-tidy-diff.py` is useful for focusing on recent changes, +relying solely on it may lead to incomplete analysis. Since the script only +reports warnings from the modified lines, it may miss issues that are caused +by the changes but manifest elsewhere in the code. For example, changes that +only add lines to a function may cause it to violate size limits (e.g., +`readability-function-size <checks/readability/function-size.html>`_), but the +diagnostic will be reported at the function declaration, which may not be in +the diff and thus filtered out. Modifications to header files may also affect +many implementation files, but only warnings in the modified header lines will +be reported. + +For comprehensive analysis, especially before merging significant changes, +consider running :program:`clang-tidy` on the entire affected files or the +whole project using :program:`run-clang-tidy.py`. + .. _clang-tidy-nolint: Suppressing Undesired Diagnostics @@ -453,5 +566,6 @@ example, ``NOLINTBEGIN(check-name)`` can be paired with :program:`clang-tidy` will generate a ``clang-tidy-nolint`` error diagnostic if any ``NOLINTBEGIN``/``NOLINTEND`` comment violates these requirements. +.. _Bear: https://github.com/rizsotto/Bear .. _LibTooling: https://clang.llvm.org/docs/LibTooling.html .. _How To Setup Tooling For LLVM: https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html diff --git a/clang-tools-extra/docs/index.rst b/clang-tools-extra/docs/index.rst index 9f7324f..3f3a99d 100644 --- a/clang-tools-extra/docs/index.rst +++ b/clang-tools-extra/docs/index.rst @@ -17,6 +17,7 @@ Contents clang-tidy/index clang-include-fixer + clang-change-namespace modularize pp-trace clangd <https://clangd.llvm.org/> diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp index 49cc136..0cbf9a0 100644 --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -60,14 +60,10 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> { NamedDecl *getMemberProvider(QualType Base) { if (Base->isPointerType()) return getMemberProvider(Base->getPointeeType()); - // Unwrap the sugar ElaboratedType. - if (const auto *ElTy = dyn_cast<ElaboratedType>(Base)) - return getMemberProvider(ElTy->getNamedType()); - if (const auto *TT = dyn_cast<TypedefType>(Base)) return TT->getDecl(); if (const auto *UT = dyn_cast<UsingType>(Base)) - return UT->getFoundDecl(); + return UT->getDecl(); // A heuristic: to resolve a template type to **only** its template name. // We're only using this method for the base type of MemberExpr, in general // the template provides the member, and the critical case `unique_ptr<Foo>` @@ -135,16 +131,14 @@ public: } bool qualifierIsNamespaceOrNone(DeclRefExpr *DRE) { - const auto *Qual = DRE->getQualifier(); - if (!Qual) - return true; - switch (Qual->getKind()) { - case NestedNameSpecifier::Namespace: - case NestedNameSpecifier::Global: + NestedNameSpecifier Qual = DRE->getQualifier(); + switch (Qual.getKind()) { + case NestedNameSpecifier::Kind::Null: + case NestedNameSpecifier::Kind::Namespace: + case NestedNameSpecifier::Kind::Global: return true; - case NestedNameSpecifier::TypeSpec: - case NestedNameSpecifier::Super: - case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Kind::Type: + case NestedNameSpecifier::Kind::MicrosoftSuper: return false; } llvm_unreachable("Unknown value for NestedNameSpecifierKind"); @@ -341,17 +335,17 @@ public: } bool VisitUsingTypeLoc(UsingTypeLoc TL) { - reportType(TL.getNameLoc(), TL.getFoundDecl()); + reportType(TL.getNameLoc(), TL.getDecl()); return true; } bool VisitTagTypeLoc(TagTypeLoc TTL) { - reportType(TTL.getNameLoc(), TTL.getDecl()); + reportType(TTL.getNameLoc(), TTL.getOriginalDecl()); return true; } bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) { - reportType(TTL.getNameLoc(), TTL.getTypedefNameDecl()); + reportType(TTL.getNameLoc(), TTL.getDecl()); return true; } diff --git a/clang-tools-extra/test/clang-doc/basic-project.mustache.test b/clang-tools-extra/test/clang-doc/basic-project.mustache.test index e2d9da6..5509951 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.mustache.test +++ b/clang-tools-extra/test/clang-doc/basic-project.mustache.test @@ -2,10 +2,10 @@ // RUN: sed 's|$test_dir|%/S|g' %S/Inputs/basic-project/database_template.json > %t/build/compile_commands.json // RUN: clang-doc --format=mustache --output=%t/docs --executor=all-TUs %t/build/compile_commands.json -// RUN: FileCheck %s -input-file=%t/docs/_ZTV5Shape.html -check-prefix=HTML-SHAPE -// RUN: FileCheck %s -input-file=%t/docs/_ZTV10Calculator.html -check-prefix=HTML-CALC -// RUN: FileCheck %s -input-file=%t/docs/_ZTV9Rectangle.html -check-prefix=HTML-RECTANGLE -// RUN: FileCheck %s -input-file=%t/docs/_ZTV6Circle.html -check-prefix=HTML-CIRCLE +// RUN: FileCheck %s -input-file=%t/docs/html/_ZTV5Shape.html -check-prefix=HTML-SHAPE +// RUN: FileCheck %s -input-file=%t/docs/html/_ZTV10Calculator.html -check-prefix=HTML-CALC +// RUN: FileCheck %s -input-file=%t/docs/html/_ZTV9Rectangle.html -check-prefix=HTML-RECTANGLE +// RUN: FileCheck %s -input-file=%t/docs/html/_ZTV6Circle.html -check-prefix=HTML-CIRCLE HTML-SHAPE: <html lang="en-US"> HTML-SHAPE: <head> diff --git a/clang-tools-extra/test/clang-doc/json/class-requires.cpp b/clang-tools-extra/test/clang-doc/json/class-requires.cpp index bf6c889..5139617 100644 --- a/clang-tools-extra/test/clang-doc/json/class-requires.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-requires.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/_ZTV7MyClass.json +// RUN: FileCheck %s < %t/json/_ZTV7MyClass.json template<typename T> concept Addable = requires(T a, T b) { diff --git a/clang-tools-extra/test/clang-doc/json/class-specialization.cpp b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp index e9259ed..d3ad695 100644 --- a/clang-tools-extra/test/clang-doc/json/class-specialization.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp @@ -1,7 +1,7 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/_ZTV7MyClass.json --check-prefix=BASE -// RUN: FileCheck %s < %t/_ZTV7MyClassIiE.json --check-prefix=SPECIALIZATION +// RUN: FileCheck %s < %t/json/_ZTV7MyClass.json --check-prefix=BASE +// RUN: FileCheck %s < %t/json/_ZTV7MyClassIiE.json --check-prefix=SPECIALIZATION template<typename T> struct MyClass {}; diff --git a/clang-tools-extra/test/clang-doc/json/class-template.cpp b/clang-tools-extra/test/clang-doc/json/class-template.cpp index 149248c..5ef78f54 100644 --- a/clang-tools-extra/test/clang-doc/json/class-template.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-template.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/_ZTV7MyClass.json +// RUN: FileCheck %s < %t/json/_ZTV7MyClass.json template<typename T> struct MyClass { T MemberTemplate; diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp b/clang-tools-extra/test/clang-doc/json/class.cpp index 79b8fed..20a9f21 100644 --- a/clang-tools-extra/test/clang-doc/json/class.cpp +++ b/clang-tools-extra/test/clang-doc/json/class.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/_ZTV7MyClass.json +// RUN: FileCheck %s < %t/json/_ZTV7MyClass.json struct Foo; diff --git a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp index bb2b4ca..1a73a0d 100644 --- a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp +++ b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/index.json +// RUN: FileCheck %s < %t/json/index.json template<typename T> concept Incrementable = requires (T a) { a++; diff --git a/clang-tools-extra/test/clang-doc/json/concept.cpp b/clang-tools-extra/test/clang-doc/json/concept.cpp index 4c81024..e96ec14 100644 --- a/clang-tools-extra/test/clang-doc/json/concept.cpp +++ b/clang-tools-extra/test/clang-doc/json/concept.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/index.json +// RUN: FileCheck %s < %t/json/index.json // Requires that T suports post and pre-incrementing. template<typename T> diff --git a/clang-tools-extra/test/clang-doc/json/function-requires.cpp b/clang-tools-extra/test/clang-doc/json/function-requires.cpp index 59ed39e..9427146 100644 --- a/clang-tools-extra/test/clang-doc/json/function-requires.cpp +++ b/clang-tools-extra/test/clang-doc/json/function-requires.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/index.json +// RUN: FileCheck %s < %t/json/index.json template<typename T> concept Incrementable = requires(T x) { diff --git a/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp index b194e33..faaccb7 100644 --- a/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp +++ b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/index.json +// RUN: FileCheck %s < %t/json/index.json static void myFunction() {} diff --git a/clang-tools-extra/test/clang-doc/json/method-template.cpp b/clang-tools-extra/test/clang-doc/json/method-template.cpp index 14232d0..87977f8 100644 --- a/clang-tools-extra/test/clang-doc/json/method-template.cpp +++ b/clang-tools-extra/test/clang-doc/json/method-template.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/_ZTV7MyClass.json +// RUN: FileCheck %s < %t/json/_ZTV7MyClass.json struct MyClass { template<class T> T methodTemplate(T param) { diff --git a/clang-tools-extra/test/clang-doc/json/namespace.cpp b/clang-tools-extra/test/clang-doc/json/namespace.cpp index 4b6b388..dcf8323 100644 --- a/clang-tools-extra/test/clang-doc/json/namespace.cpp +++ b/clang-tools-extra/test/clang-doc/json/namespace.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/index.json +// RUN: FileCheck %s < %t/json/index.json class MyClass {}; diff --git a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp index 255e540..b19afc1 100644 --- a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp +++ b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp @@ -1,7 +1,7 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --output=%t --format=json --executor=standalone %s -// RUN: FileCheck %s < %t/nested.json --check-prefix=NESTED -// RUN: FileCheck %s < %t/inner.json --check-prefix=INNER +// RUN: FileCheck %s < %t/json/nested.json --check-prefix=NESTED +// RUN: FileCheck %s < %t/json/inner.json --check-prefix=INNER namespace nested { int Global; diff --git a/clang-tools-extra/test/clang-doc/long-name.cpp b/clang-tools-extra/test/clang-doc/long-name.cpp new file mode 100644 index 0000000..b333375 --- /dev/null +++ b/clang-tools-extra/test/clang-doc/long-name.cpp @@ -0,0 +1,14 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: clang-doc --output=%t --format=mustache --executor=standalone %s +// RUN: ls %t/json | FileCheck %s -check-prefix=CHECK-JSON +// RUN: ls %t/html | FileCheck %s -check-prefix=CHECK-HTML + +struct ThisStructHasANameThatResultsInAMangledNameThatIsExactly250CharactersLongThatIsSupposedToTestTheFilenameLengthLimitsWithinClangDocInOrdertoSeeifclangdocwillcrashornotdependingonthelengthofthestructIfTheLengthIsTooLongThenClangDocWillCrashAnd12 {}; + +// This name is 1 character over the limit, so it will be serialized as a USR. +struct ThisStructHasANameThatResultsInAMangledNameThatIsExactly251CharactersLongThatIsSupposedToTestTheFilenameLengthLimitsWithinClangDocInOrdertoSeeifclangdocwillcrashornotdependingonthelengthofthestructIfTheLengthIsTooLongThenClangDocWillCrashAnd123 {}; + +// CHECK-JSON: ThisStructHasANameThatResultsInAMangledNameThatIsExactly250CharactersLongThatIsSupposedToTestTheFilenameLengthLimitsWithinClangDocInOrdertoSeeifclangdocwillcrashornotdependingonthelengthofthestructIfTheLengthIsTooLongThenClangDocWillCrashAnd12.json +// CHECK-JSON: {{[0-9A-F]*}}.json +// CHECK-HTML: ThisStructHasANameThatResultsInAMangledNameThatIsExactly250CharactersLongThatIsSupposedToTestTheFilenameLengthLimitsWithinClangDocInOrdertoSeeifclangdocwillcrashornotdependingonthelengthofthestructIfTheLengthIsTooLongThenClangDocWillCrashAnd12.html +// CHECK-HTML: {{[0-9A-F]*}}.html diff --git a/clang-tools-extra/test/clang-doc/mustache-index.cpp b/clang-tools-extra/test/clang-doc/mustache-index.cpp index 910233b..f9aad19 100644 --- a/clang-tools-extra/test/clang-doc/mustache-index.cpp +++ b/clang-tools-extra/test/clang-doc/mustache-index.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --format=mustache --output=%t --executor=standalone %s -// RUN: FileCheck %s < %t/index.html +// RUN: FileCheck %s < %t/html/index.html enum Color { RED, diff --git a/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp b/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp index 7d7d108..a73a5ab 100644 --- a/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp +++ b/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: clang-doc --format=mustache --output=%t --executor=standalone %s -// RUN: FileCheck %s < %t/MyNamespace.html +// RUN: FileCheck %s < %t/html/MyNamespace.html namespace MyNamespace { class Foo; diff --git a/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.c b/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.c new file mode 100644 index 0000000..7b531d9 --- /dev/null +++ b/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.c @@ -0,0 +1,12 @@ +// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s + +struct Foo { + int x; // CHECK: {{^ int z;}} + int y; // CHECK-NEXT: {{^ int y;}} + int z; // CHECK-NEXT: {{^ int x;}} +}; + +int main() { + struct Foo foo = { 0, 1 }; // CHECK: {{^ struct Foo foo = { .y = 1, .x = 0 };}} + return 0; +} diff --git a/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.cpp b/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.cpp index 9d09c81..cda5f56 100644 --- a/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.cpp +++ b/clang-tools-extra/test/clang-reorder-fields/AggregatePartialInitialization.cpp @@ -1,4 +1,5 @@ -// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s +// RUN: clang-reorder-fields --extra-arg="-std=c++17" -record-name Foo -fields-order z,y,x %s -- 2>&1 | FileCheck --check-prefix=CHECK-MESSAGES %s +// RUN: clang-reorder-fields --extra-arg="-std=c++17" -record-name Foo -fields-order z,y,x %s -- | FileCheck %s // The order of fields should not change. class Foo { @@ -9,6 +10,7 @@ public: }; int main() { + // CHECK-MESSAGES: :[[@LINE+1]]:13: Only full initialization without implicit values is supported Foo foo = { 0, 1 }; // CHECK: {{^ Foo foo = { 0, 1 };}} return 0; } diff --git a/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c new file mode 100644 index 0000000..c05c296 --- /dev/null +++ b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c @@ -0,0 +1,31 @@ +// RUN: clang-reorder-fields -record-name Foo -fields-order z,w,y,x %s -- | FileCheck %s + +struct Foo { + const int* x; // CHECK: {{^ double z;}} + int y; // CHECK-NEXT: {{^ int w;}} + double z; // CHECK-NEXT: {{^ int y;}} + int w; // CHECK-NEXT: {{^ const int\* x}} +}; + +struct Bar { + char a; + struct Foo b; + char c; +}; + +int main() { + const int x = 13; + struct Foo foo1 = { .x=&x, .y=0, .z=1.29, .w=17 }; // CHECK: {{^ struct Foo foo1 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}} + struct Foo foo2 = { .x=&x, 0, 1.29, 17 }; // CHECK: {{^ struct Foo foo2 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}} + struct Foo foo3 = { .y=0, .z=1.29, 17, .x=&x }; // CHECK: {{^ struct Foo foo3 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}} + struct Foo foo4 = { .y=0, .z=1.29, 17 }; // CHECK: {{^ struct Foo foo4 = { .z = 1.29, .w = 17, .y = 0 };}} + + struct Foo foos1[1] = { [0] = {.x=&x, 0, 1.29, 17} }; // CHECK: {{^ struct Foo foos1\[1] = { \[0] = {.z = 1.29, .w = 17, .y = 0, .x = &x} };}} + struct Foo foos2[1] = { [0].x=&x, [0].y=0, [0].z=1.29, [0].w=17 }; // CHECK: {{^ struct Foo foos2\[1] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = &x };}} + struct Foo foos3[1] = { &x, 0, 1.29, 17 }; // CHECK: {{^ struct Foo foos3\[1] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = &x };}} + struct Foo foos4[2] = { &x, 0, 1.29, 17, &x, 0, 1.29, 17 }; // CHECK: {{^ struct Foo foos4\[2] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = &x, \[1].z = 1.29, \[1].w = 17, \[1].y = 0, \[1].x = &x };}} + + struct Bar bar1 = { .a='a', &x, 0, 1.29, 17, 'c' }; // CHECK: {{^ struct Bar bar1 = { .a = 'a', .b.z = 1.29, .b.w = 17, .b.y = 0, .b.x = &x, .c = 'c' };}} + + return 0; +} diff --git a/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.cpp b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.cpp new file mode 100644 index 0000000..15dac77 --- /dev/null +++ b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.cpp @@ -0,0 +1,24 @@ +// RUN: clang-reorder-fields --extra-arg="-std=c++20" -record-name Bar -fields-order c,a,b %s -- | FileCheck %s + +class Foo { +public: + const int* x; + int y; +}; + +class Bar { +public: + char a; // CHECK: {{^ int c;}} + Foo b; // CHECK-NEXT: {{^ char a;}} + int c; // CHECK-NEXT: {{^ Foo b;}} +}; + +int main() { + const int x = 13; + Bar bar1 = { 'a', { &x, 0 }, 123 }; // CHECK: {{^ Bar bar1 = { 123, 'a', { &x, 0 } };}} + Bar bar2 = { .a = 'a', { &x, 0 }, 123 }; // CHECK: {{^ Bar bar2 = { .c = 123, .a = 'a', .b = { &x, 0 } };}} + Bar bar3 = { 'a', .b { &x, 0 }, 123 }; // CHECK: {{^ Bar bar3 = { .c = 123, .a = 'a', .b = { &x, 0 } };}} + Bar bar4 = { .c = 123, .b { &x, 0 }, .a = 'a' }; // CHECK: {{^ Bar bar4 = { .c = 123, .a = 'a', .b = { &x, 0 } };}} + + return 0; +} diff --git a/clang-tools-extra/test/clang-reorder-fields/IdiomaticZeroInitializer.c b/clang-tools-extra/test/clang-reorder-fields/IdiomaticZeroInitializer.c new file mode 100644 index 0000000..59c12e81 --- /dev/null +++ b/clang-tools-extra/test/clang-reorder-fields/IdiomaticZeroInitializer.c @@ -0,0 +1,14 @@ +// RUN: clang-reorder-fields -record-name Foo -fields-order y,x %s -- | FileCheck %s + +struct Foo { + int x; // CHECK: {{^ int y;}} + int y; // CHECK-NEXT: {{^ int x;}} +}; + +int main() { + // The idiomatic zero initializer should remain the same. + struct Foo foo0 = { 0 }; // CHECK: {{^ struct Foo foo0 = { 0 };}} + struct Foo foo1 = { 1 }; // CHECK: {{^ struct Foo foo1 = { .x = 1 };}} + + return 0; +} diff --git a/clang-tools-extra/test/clang-reorder-fields/InitializerListExcessElements.c b/clang-tools-extra/test/clang-reorder-fields/InitializerListExcessElements.c new file mode 100644 index 0000000..b626e84 --- /dev/null +++ b/clang-tools-extra/test/clang-reorder-fields/InitializerListExcessElements.c @@ -0,0 +1,15 @@ +// RUN: clang-reorder-fields -record-name Foo -fields-order y,x %s -- 2>&1 | FileCheck --check-prefix=CHECK-MESSAGES %s +// RUN: clang-reorder-fields -record-name Foo -fields-order y,x %s -- | FileCheck %s + +// The order of fields should not change. +struct Foo { + int x; // CHECK: {{^ int x;}} + int y; // CHECK-NEXT: {{^ int y;}} +}; + +int main() { + // CHECK-MESSAGES: :[[@LINE+1]]:20: Unsupported initializer list + struct Foo foo = { .y=9, 123, .x=1 }; // CHECK: {{^ struct Foo foo = { .y=9, 123, .x=1 };}} + + return 0; +} diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 774bc97..d80a280 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -45,6 +45,7 @@ Notes import argparse import os import pathlib +import platform import re import subprocess import sys @@ -135,8 +136,7 @@ class CheckRunner: "-fblocks", ] + self.clang_extra_args - if extension in [".cpp", ".hpp", ".mm"]: - self.clang_extra_args.append("-std=" + self.std) + self.clang_extra_args.append("-std=" + self.std) # Tests should not rely on STL being available, and instead provide mock # implementations of relevant APIs. @@ -146,7 +146,12 @@ class CheckRunner: self.clang_extra_args.append("-resource-dir=%s" % self.resource_dir) def read_input(self) -> None: - with open(self.input_file_name, "r", encoding="utf-8") as input_file: + # Use a "\\?\" prefix on Windows to handle long file paths transparently: + # https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation + file_name = self.input_file_name + if platform.system() == "Windows": + file_name = "\\\\?\\" + os.path.abspath(file_name) + with open(file_name, "r", encoding="utf-8") as input_file: self.input_text = input_file.read() def get_prefixes(self) -> None: @@ -374,7 +379,7 @@ def parse_arguments() -> Tuple[argparse.Namespace, List[str]]: parser.add_argument( "-std", type=csv, - default=["c++11-or-later"], + default=None, help="Passed to clang. Special -or-later values are expanded.", ) parser.add_argument( @@ -382,7 +387,15 @@ def parse_arguments() -> Tuple[argparse.Namespace, List[str]]: action="store_true", help="allow partial line matches for fixes", ) - return parser.parse_known_args() + + args, extra_args = parser.parse_known_args() + if args.std is None: + _, extension = os.path.splitext(args.assume_filename or args.input_file_name) + args.std = [ + "c++11-or-later" if extension in [".cpp", ".hpp", ".mm"] else "c99-or-later" + ] + + return (args, extra_args) def main() -> None: diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stdfloat b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stdfloat new file mode 100644 index 0000000..c178e61 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stdfloat @@ -0,0 +1,18 @@ +//===--- stdfloat - Stub header for tests -----------------------*- 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 _STDFLOAT_ +#define _STDFLOAT_ + +namespace std { + +// TODO: define std::float16_t and friends + +} + +#endif // _STDFLOAT_ diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-other.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-other.h index 28c26b7..011c869 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-other.h +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/system-other.h @@ -11,6 +11,8 @@ // Special system calls. +#if __STDC_VERSION__ < 202311L void other_call(); +#endif #endif // _SYSTEM_OTHER_H_ diff --git a/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry-custom-macro.c b/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry-custom-macro.c index 56c382c..3f60860 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry-custom-macro.c +++ b/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry-custom-macro.c @@ -2,7 +2,7 @@ #define MY_TEMP_FAILURE_RETRY(x) \ ({ \ - typeof(x) __z; \ + __typeof__(x) __z; \ do \ __z = (x); \ while (__z == -1); \ @@ -11,7 +11,7 @@ #define MY_OTHER_TEMP_FAILURE_RETRY(x) \ ({ \ - typeof(x) __z; \ + __typeof__(x) __z; \ do \ __z = (x); \ while (__z == -1); \ diff --git a/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry.c b/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry.c index 461fb4a..1c71ebb 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry.c +++ b/clang-tools-extra/test/clang-tidy/checkers/android/comparison-in-temp-failure-retry.c @@ -2,7 +2,7 @@ #define TEMP_FAILURE_RETRY(x) \ ({ \ - typeof(x) __z; \ + __typeof__(x) __z; \ do \ __z = (x); \ while (__z == -1); \ @@ -130,7 +130,7 @@ void obscured_temp_failure_retry(void) { #undef TEMP_FAILURE_RETRY #define IMPL(x) \ ({ \ - typeof(x) __z; \ + __typeof__(x) __z; \ do \ __z = (x); \ while (__z == -1); \ diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/branch-clone-macro-crash.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/branch-clone-macro-crash.c index a4cb734..0f4389c8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/branch-clone-macro-crash.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/branch-clone-macro-crash.c @@ -1,11 +1,11 @@ // RUN: %check_clang_tidy %s bugprone-branch-clone %t int x = 0; int y = 1; -#define a(b, c) \ - typeof(b) d; \ - if (b) \ - d = b; \ - else if (c) \ +#define a(b, c) \ + __typeof__(b) d; \ + if (b) \ + d = b; \ + else if (c) \ d = b; void f(void) { diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/copy-constructor-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/copy-constructor-init.cpp index b42d3fc..35314d1 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/copy-constructor-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/copy-constructor-init.cpp @@ -165,7 +165,6 @@ FROMMACRO class X15 : public CopyableAlias2 { X15(const X15 &other) {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling a base constructor - // CHECK-FIXES: X15(const X15 &other) : Copyable5(other) {} }; class X16 : public NonCopyable { diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters-relatedness.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters-relatedness.c index 7231361..45752de 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters-relatedness.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters-relatedness.c @@ -1,4 +1,15 @@ -// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: %check_clang_tidy -std=c99,c11,c17 -check-suffixes=,BEFORE-C23 %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: 2, \ +// RUN: bugprone-easily-swappable-parameters.IgnoredParameterNames: "", \ +// RUN: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes: "", \ +// RUN: bugprone-easily-swappable-parameters.QualifiersMix: 0, \ +// RUN: bugprone-easily-swappable-parameters.ModelImplicitConversions: 0, \ +// RUN: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: 1, \ +// RUN: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 0 \ +// RUN: }}' -- -Wno-strict-prototypes -x c +// +// RUN: %check_clang_tidy -std=c23-or-later %s bugprone-easily-swappable-parameters %t \ // RUN: -config='{CheckOptions: { \ // RUN: bugprone-easily-swappable-parameters.MinimumLength: 2, \ // RUN: bugprone-easily-swappable-parameters.IgnoredParameterNames: "", \ @@ -9,7 +20,6 @@ // RUN: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 0 \ // RUN: }}' -- -Wno-strict-prototypes -x c -int myprint(); int add(int X, int Y); void notRelated(int A, int B) {} @@ -19,13 +29,18 @@ void notRelated(int A, int B) {} int addedTogether(int A, int B) { return add(A, B); } // NO-WARN: Passed to same function. +#if __STDC_VERSION__ < 202311L +int myprint(); void passedToSameKNRFunction(int A, int B) { myprint("foo", A); myprint("bar", B); } -// CHECK-MESSAGES: :[[@LINE-4]]:30: warning: 2 adjacent parameters of 'passedToSameKNRFunction' of similar type ('int') -// CHECK-MESSAGES: :[[@LINE-5]]:34: note: the first parameter in the range is 'A' -// CHECK-MESSAGES: :[[@LINE-6]]:41: note: the last parameter in the range is 'B' -// This is actually a false positive: the "passed to same function" heuristic +// CHECK-MESSAGES-BEFORE-C23: :[[@LINE-4]]:30: warning: 2 adjacent parameters of 'passedToSameKNRFunction' of similar type ('int') +// CHECK-MESSAGES-BEFORE-C23: :[[@LINE-5]]:34: note: the first parameter in the range is 'A' +// CHECK-MESSAGES-BEFORE-C23: :[[@LINE-6]]:41: note: the last parameter in the range is 'B' +// FIXME: This is actually a false positive: the "passed to same function" heuristic // can't map the parameter index 1 to A and B because myprint() has no // parameters. +// If you fix this, you should be able to combine the `%check_clang_tidy` invocations +// in this file into one and remove the `-std` and `-check-suffixes` arguments. +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters.c index b6c9bf4..be44cfc 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: %check_clang_tidy -std=c99,c11,c17 -check-suffixes=,BEFORE-23 %s bugprone-easily-swappable-parameters %t \ // RUN: -config='{CheckOptions: { \ // RUN: bugprone-easily-swappable-parameters.MinimumLength: 2, \ // RUN: bugprone-easily-swappable-parameters.IgnoredParameterNames: "", \ @@ -7,7 +7,18 @@ // RUN: bugprone-easily-swappable-parameters.ModelImplicitConversions: 0, \ // RUN: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: 0, \ // RUN: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 0 \ -// RUN: }}' -- -Wno-strict-prototypes -x c +// RUN: }}' -- -Wno-strict-prototypes +// +// RUN: %check_clang_tidy -std=c23-or-later %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: 2, \ +// RUN: bugprone-easily-swappable-parameters.IgnoredParameterNames: "", \ +// RUN: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes: "bool;MyBool;struct U;MAKE_LOGICAL_TYPE(int)", \ +// RUN: bugprone-easily-swappable-parameters.QualifiersMix: 0, \ +// RUN: bugprone-easily-swappable-parameters.ModelImplicitConversions: 0, \ +// RUN: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: 0, \ +// RUN: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 0 \ +// RUN: }}' -- -Wno-strict-prototypes #define bool _Bool #define true 1 @@ -45,8 +56,11 @@ void pointerConversion(int *IP, long *LP) {} void testVariadicsCall() { int IVal = 1; + +#if __STDC_VERSION__ < 202311L decl(IVal); // NO-WARN: Particular calls to "variadics" are like template // instantiations, and we do not model them. +#endif variadic(IVal); // NO-WARN. variadic(IVal, 2, 3, 4); // NO-WARN. @@ -64,13 +78,15 @@ void taggedTypes2(struct S SVar1, struct S SVar2) {} void wrappers(struct { int I; } I1, struct { int I; } I2) {} // NO-WARN: Distinct anonymous types. +#if __STDC_VERSION__ < 202311L void knr(I, J) int I; int J; {} -// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: 2 adjacent parameters of 'knr' of similar type ('int') -// CHECK-MESSAGES: :[[@LINE-4]]:7: note: the first parameter in the range is 'I' -// CHECK-MESSAGES: :[[@LINE-4]]:7: note: the last parameter in the range is 'J' +// CHECK-MESSAGES-BEFORE-23: :[[@LINE-3]]:3: warning: 2 adjacent parameters of 'knr' of similar type ('int') +// CHECK-MESSAGES-BEFORE-23: :[[@LINE-4]]:7: note: the first parameter in the range is 'I' +// CHECK-MESSAGES-BEFORE-23: :[[@LINE-4]]:7: note: the last parameter in the range is 'J' +#endif void boolAsWritten(bool B1, bool B2) {} // NO-WARN: The type name is ignored. // Note that "bool" is a macro that expands to "_Bool" internally, but it is @@ -145,7 +161,7 @@ void thisIsGettingRidiculous(MAKE_PRIMITIVE_WRAPPER(int) I1, void macroMagic3(MAKE_LOGICAL_TYPE(char) B1, MAKE_LOGICAL_TYPE(long) B2) {} // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 2 adjacent parameters of 'macroMagic3' of similar type ('bool') // CHECK-MESSAGES: :[[@LINE-4]]:30: note: expanded from macro 'MAKE_LOGICAL_TYPE' -// CHECK-MESSAGES: :[[@LINE-136]]:14: note: expanded from macro 'bool' +// CHECK-MESSAGES: :[[@LINE-141]]:14: note: expanded from macro 'bool' // CHECK-MESSAGES: :[[@LINE-4]]:42: note: the first parameter in the range is 'B1' // CHECK-MESSAGES: :[[@LINE-5]]:70: note: the last parameter in the range is 'B2' diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-in-initialization-strlen.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-in-initialization-strlen.c index a383958..b241d68 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-in-initialization-strlen.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-in-initialization-strlen.c @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy --match-partial-fixes %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result #include "not-null-terminated-result-c.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-before-safe.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-before-safe.c index 434cfcc..ea7e183 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-before-safe.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-before-safe.c @@ -1,7 +1,7 @@ // RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ // RUN: -config="{CheckOptions: \ // RUN: {bugprone-not-null-terminated-result.WantToUseSafeFunction: true}}" \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result #include "not-null-terminated-result-c.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-safe.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-safe.c index 5a5e35e..ae43087 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-safe.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-memcpy-safe.c @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result #include "not-null-terminated-result-c.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-stdc-want-lib-ext1-not-a-literal.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-stdc-want-lib-ext1-not-a-literal.c index 0628d0ca..9480671 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-stdc-want-lib-ext1-not-a-literal.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-stdc-want-lib-ext1-not-a-literal.c @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result #include "not-null-terminated-result-c.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-strlen.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-strlen.c index 4970af8..366c169 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-strlen.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-strlen.c @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy --match-partial-fixes %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result // FIXME: Something wrong with the APInt un/signed conversion on Windows: // in 'strncmp(str6, "string", 7);' it tries to inject '4294967302' as length. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-undef-stdc-want-lib-ext1.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-undef-stdc-want-lib-ext1.c index 2704dc1..bb5961f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-undef-stdc-want-lib-ext1.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/not-null-terminated-result-undef-stdc-want-lib-ext1.c @@ -1,5 +1,5 @@ // RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 -I %S/Inputs/not-null-terminated-result +// RUN: -- -I %S/Inputs/not-null-terminated-result #include "not-null-terminated-result-c.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/signal-handler.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/signal-handler.c index c7daec0..a8f6f3b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/signal-handler.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/signal-handler.c @@ -1,4 +1,5 @@ -// RUN: %check_clang_tidy %s bugprone-signal-handler %t -- -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy -std=c99,c11,c17 -check-suffixes=,BEFORE-23 %s bugprone-signal-handler %t -- -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy -std=c23-or-later %s bugprone-signal-handler %t -- -- -isystem %clang_tidy_headers #include "signal.h" #include "stdlib.h" @@ -174,8 +175,10 @@ void test_other(void) { signal(SIGINT, handler_signal); signal(SIGINT, _Exit); +#if __STDC_VERSION__ < 202311L signal(SIGINT, other_call); - // CHECK-NOTES: :[[@LINE-1]]:18: warning: standard function 'other_call' may not be asynchronous-safe; using it as a signal handler may be dangerous [bugprone-signal-handler] + // CHECK-NOTES-BEFORE-23: :[[@LINE-1]]:18: warning: standard function 'other_call' may not be asynchronous-safe; using it as a signal handler may be dangerous [bugprone-signal-handler] +#endif signal(SIGINT, f_extern_handler); // CHECK-NOTES: :[[@LINE-1]]:18: warning: cannot verify that external function 'f_extern_handler' is asynchronous-safe; using it as a signal handler may be dangerous [bugprone-signal-handler] diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/signed-char-misuse-c23.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/signed-char-misuse-c23.c index ad6e31d..cd66511 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/signed-char-misuse-c23.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/signed-char-misuse-c23.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-signed-char-misuse %t -- -- -std=c23 +// RUN: %check_clang_tidy -std=c23-or-later %s bugprone-signed-char-misuse %t /////////////////////////////////////////////////////////////////// /// Test cases correctly caught by the check. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.c index b898071..871715b2 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.c @@ -1,5 +1,5 @@ -// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- -- -// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t -- -- -x c++ +// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t +// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-sizeof-expression %t -- -- -x c++ #ifdef __cplusplus #define STRKWD diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp index 3167b85..4911157 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp @@ -141,6 +141,17 @@ void nullable_value_after_swap(BloombergLP::bdlb::NullableValue<int> &opt1, Bloo } } +void assertion_handler() __attribute__((analyzer_noreturn)); + +void function_calling_analyzer_noreturn(const bsl::optional<int>& opt) +{ + if (!opt) { + assertion_handler(); + } + + *opt; // no-warning: The previous condition guards this dereference. +} + template <typename T> void function_template_without_user(const absl::optional<T> &opt) { opt.value(); // no-warning diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c index 0409dd6..452e229 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c @@ -5,14 +5,14 @@ // parsing and preprocessor state will not have that case. // UNSUPPORTED: target={{.*-(ps4|ps5)}} // -// RUN: %check_clang_tidy -check-suffix=WITH-ANNEX-K %s bugprone-unsafe-functions %t -- -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1 -// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -U__STDC_LIB_EXT1__ -U__STDC_WANT_LIB_EXT1__ -// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -D__STDC_LIB_EXT1__=1 -U__STDC_WANT_LIB_EXT1__ -// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -U__STDC_LIB_EXT1__ -D__STDC_WANT_LIB_EXT1__=1 -// RUN: %check_clang_tidy -check-suffix=WITH-ANNEX-K-CERT-ONLY %s bugprone-unsafe-functions %t -- \ +// RUN: %check_clang_tidy -std=c11-or-later -check-suffix=WITH-ANNEX-K %s bugprone-unsafe-functions %t -- -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1 +// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -U__STDC_LIB_EXT1__ -U__STDC_WANT_LIB_EXT1__ +// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -D__STDC_LIB_EXT1__=1 -U__STDC_WANT_LIB_EXT1__ +// RUN: %check_clang_tidy -check-suffix=WITHOUT-ANNEX-K %s bugprone-unsafe-functions %t -- -- -U__STDC_LIB_EXT1__ -D__STDC_WANT_LIB_EXT1__=1 +// RUN: %check_clang_tidy -std=c11-or-later -check-suffix=WITH-ANNEX-K-CERT-ONLY %s bugprone-unsafe-functions %t -- \ // RUN: -config="{CheckOptions: {bugprone-unsafe-functions.ReportMoreUnsafeFunctions: false}}" \ // RUN: -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1 -// RUN: %check_clang_tidy -check-suffix=WITH-NONE-ENABLED %s bugprone-unsafe-functions %t --\ +// RUN: %check_clang_tidy -check-suffix=WITH-NONE-ENABLED %s bugprone-unsafe-functions %t --\ // RUN: -config="{CheckOptions: {bugprone-unsafe-functions.ReportDefaultFunctions: false}}" \ // RUN: -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1 diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp index 721c55b..5dd2387 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp @@ -69,14 +69,14 @@ template<typename T> T qux(T Generic) { async::Future<Units> PendingA = acquireUnits(); auto PendingB = acquireUnits(); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'async::Future<Units>') [bugprone-unused-local-non-trivial-variable] async::Future<Units> MustBeUsed; // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unused local variable 'MustBeUsed' of type 'async::Future<Units>' [bugprone-unused-local-non-trivial-variable] PendingA.get(); async::Future<T> TemplateType; // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'TemplateType' of type 'async::Future<T>' [bugprone-unused-local-non-trivial-variable] a::Future<T> AliasTemplateType; - // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: unused local variable 'AliasTemplateType' of type 'a::Future<T>' (aka 'Future<T>') [bugprone-unused-local-non-trivial-variable] + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: unused local variable 'AliasTemplateType' of type 'a::Future<T>' (aka 'async::Future<T>') [bugprone-unused-local-non-trivial-variable] [[maybe_unused]] async::Future<Units> MaybeUnused; return Generic; } @@ -86,7 +86,7 @@ async::Future<int> Global; int bar(int Num) { a::Future<Units> PendingA = acquireUnits(); a::Future<Units> PendingB = acquireUnits(); // not used at all, unused variable not fired because of destructor side effect - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'async::Future<Units>') [bugprone-unused-local-non-trivial-variable] auto Num2 = PendingA.get(); auto Num3 = qux(Num); async::Ptr<a::Future<Units>> Shared = async::Ptr<a::Future<Units>>(acquireUnits()); diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert/uppercase-literal-suffix-integer.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert/uppercase-literal-suffix-integer.cpp index 6fa700b..e7c0dd6 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cert/uppercase-literal-suffix-integer.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cert/uppercase-literal-suffix-integer.cpp @@ -1,14 +1,9 @@ // RUN: %check_clang_tidy %s cert-dcl16-c %t -- -- -I %clang_tidy_headers -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,cert-dcl16-c' -fix -- -I %clang_tidy_headers -// RUN: clang-tidy %t.cpp -checks='-*,cert-dcl16-c' -warnings-as-errors='-*,cert-dcl16-c' -- -I %clang_tidy_headers #include "integral_constant.h" void integer_suffix() { static constexpr auto v0 = __LINE__; // synthetic - static_assert(v0 == 9 || v0 == 5, ""); - static constexpr auto v1 = __cplusplus; // synthetic, long static constexpr auto v2 = 1; // no literal @@ -29,9 +24,6 @@ void integer_suffix() { static constexpr auto v5 = 1l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1l; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}L{{$}} // CHECK-FIXES: static constexpr auto v5 = 1L; static_assert(is_same<decltype(v5), const long>::value, ""); static_assert(v5 == 1, ""); @@ -44,9 +36,6 @@ void integer_suffix() { static constexpr auto v7 = 1ll; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'll', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1ll; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LL{{$}} // CHECK-FIXES: static constexpr auto v7 = 1LL; static_assert(is_same<decltype(v7), const long long>::value, ""); static_assert(v7 == 1, ""); @@ -77,27 +66,18 @@ void integer_suffix() { static constexpr auto v13 = 1lu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v13 = 1lu; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LU{{$}} // CHECK-FIXES: static constexpr auto v13 = 1LU; static_assert(is_same<decltype(v13), const unsigned long>::value, ""); static_assert(v13 == 1, ""); static constexpr auto v14 = 1Lu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Lu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1Lu; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LU{{$}} // CHECK-FIXES: static constexpr auto v14 = 1LU; static_assert(is_same<decltype(v14), const unsigned long>::value, ""); static_assert(v14 == 1, ""); static constexpr auto v15 = 1lU; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lU', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v15 = 1lU; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LU{{$}} // CHECK-FIXES: static constexpr auto v15 = 1LU; static_assert(is_same<decltype(v15), const unsigned long>::value, ""); static_assert(v15 == 1, ""); @@ -128,27 +108,18 @@ void integer_suffix() { static constexpr auto v21 = 1llu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v21 = 1llu; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LLU{{$}} // CHECK-FIXES: static constexpr auto v21 = 1LLU; static_assert(is_same<decltype(v21), const unsigned long long>::value, ""); static_assert(v21 == 1, ""); static constexpr auto v22 = 1LLu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'LLu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v22 = 1LLu; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LLU{{$}} // CHECK-FIXES: static constexpr auto v22 = 1LLU; static_assert(is_same<decltype(v22), const unsigned long long>::value, ""); static_assert(v22 == 1, ""); static constexpr auto v23 = 1llU; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llU', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v23 = 1llU; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: {{^ *| *}}LLU{{$}} // CHECK-FIXES: static constexpr auto v23 = 1LLU; static_assert(is_same<decltype(v23), const unsigned long long>::value, ""); static_assert(v23 == 1, ""); diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer.cpp index 7d61649..e8d7db1 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer.cpp @@ -650,3 +650,16 @@ struct InitFromBindingDecl { } }; } // namespace GH82970 + +struct A { + int m; +}; + +struct B : A { + B() { m = 0; } +}; + +template <class T> +struct C : A { + C() { m = 0; } +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.cpp new file mode 100644 index 0000000..30d03bd --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-avoid-unchecked-container-access.cpp @@ -0,0 +1,341 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: split-file %s %t + + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=DEFAULT %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=AT %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: at}}' -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=FUNC %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunction: "f"}}' -- -I%t + + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=DEFAULT-NO-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-no-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: ""}}' -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=AT-NO-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-no-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: "", \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: at}}' -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=FUNC-NO-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-no-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: "", \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunction: "f"}}' -- -I%t + + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=DEFAULT-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: "ExcludedClass1;ExcludedClass2"}}' -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=AT-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: "ExcludedClass1;ExcludedClass2", \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: at}}' -- -I%t + +// RUN: %check_clang_tidy -std=c++11,c++14,c++17,c++20 -check-suffix=FUNC-EXCL %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-excl.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.ExcludeClasses: "ExcludedClass1;ExcludedClass2", \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunction: "f"}}' -- -I%t + + +// RUN: %check_clang_tidy -std=c++23 -check-suffixes=DEFAULT-CXX-23 %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- -- -I%t -DCXX_23=1 + +// RUN: %check_clang_tidy -std=c++23 -check-suffixes=AT-CXX-23 %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: at}}' -- -I%t -DCXX_23=1 + +// RUN: %check_clang_tidy -std=c++23 -check-suffixes=FUNC-CXX-23 %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunction: "f"}}' -- -I%t -DCXX_23=1 + +// RUN: %check_clang_tidy -std=c++23 -check-suffixes=FUNC-EMPTY-ARGS-CXX-23 %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunction: "f", cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunctionEmptyArgs: "g", }}' -- -I%t -DCXX_23=1 + +// RUN: %check_clang_tidy -std=c++23 -check-suffix=FUNC-EMPTY-ARGS-EMPTY-CXX-23 %t/cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access %t -- \ +// RUN: -config='{CheckOptions: {cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixMode: function, \ +// RUN: cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.FixFunctionEmptyArgs: "", }}' -- -I%t -DCXX_23=1 + +//--- cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.h + +namespace std { + template<typename T, unsigned size> + struct array { + T operator[](unsigned i) { + return T{1}; + } + T at(unsigned i) { + return T{1}; + } + T at() { + return T{1}; + } + }; + + template<typename T, typename V> + struct map { + T operator[](unsigned i) { + return T{1}; + } + T at(unsigned i) { + return T{1}; + } + }; + + template<typename T> + struct unique_ptr { + T operator[](unsigned i) { + return T{1}; + } + }; + + template<typename T> + struct span { + T operator[](unsigned i) { + return T{1}; + } + }; +} // namespace std + +namespace json { + template<typename T> + struct node{ + T operator[](unsigned i) { + return T{1}; + } + }; +} // namespace json + +//--- cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.cpp + +#include "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.h" + +struct SubClass : std::array<int, 3> {}; + +template<class T> int f(T, unsigned){ return 0;} +template<class T> int f(T){ return 0;} + +std::array<int, 3> a; + +auto b = a[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto b = a.at(0); +// CHECK-FIXES-FUNC: auto b = f(a, 0); + +auto c = a[1+1]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto c = a.at(1+1); +// CHECK-FIXES-FUNC: auto c = f(a, 1+1); + +constexpr int Index = 1; + +auto d = a[Index]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto d = a.at(Index); +// CHECK-FIXES-FUNC: auto d = f(a, Index); + +int e(int Ind) { + return a[Ind]; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] + // CHECK-FIXES-AT: return a.at(Ind); + // CHECK-FIXES-FUNC: return f(a, Ind); +} + +auto fa = (&a)->operator[](1); +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto fa = (&a)->at(1); +// CHECK-FIXES-FUNC: auto fa = f(*(&a), 1); + +auto fd = a.operator[](1); +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto fd = a.at(1); +// CHECK-FIXES-FUNC: auto fd = f(a, 1); + + + +auto g = a.at(0); + +std::unique_ptr<int> p; +auto q = p[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto q = p[0]; +// CHECK-FIXES-FUNC: auto q = f(p, 0); + +std::span<int> s; +auto t = s[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto t = s[0]; +// CHECK-FIXES-FUNC: auto t = f(s, 0); + +json::node<int> n; +auto m = n[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto m = n[0]; +// CHECK-FIXES-FUNC: auto m = f(n, 0); + +SubClass Sub; +auto r = Sub[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto r = Sub.at(0); +// CHECK-FIXES-FUNC: auto r = f(Sub, 0); + +typedef std::array<int, 3> ar; +ar BehindDef; +auto u = BehindDef[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:19: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto u = BehindDef.at(0); +// CHECK-FIXES-FUNC: auto u = f(BehindDef, 0); + +template<typename T> int TestTemplate(T t){ + return t[0]; + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:10: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] + +} + + +auto v = TestTemplate<>(a); +auto w = TestTemplate<>(p); + +#define SUBSCRIPT_BEHIND_MACRO(x) a[x] +#define ARG_BEHIND_MACRO 0 +#define OBJECT_BEHIND_MACRO a + +auto m1 = SUBSCRIPT_BEHIND_MACRO(0); +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] + +auto m2 = a[ARG_BEHIND_MACRO]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto m2 = a.at(ARG_BEHIND_MACRO); +// CHECK-FIXES-FUNC: auto m2 = f(a, ARG_BEHIND_MACRO); + +auto m3 = OBJECT_BEHIND_MACRO[0]; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:30: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto m3 = OBJECT_BEHIND_MACRO.at(0); +// CHECK-FIXES-FUNC: auto m3 = f(OBJECT_BEHIND_MACRO, 0); + +// Check that spacing does not invalidate the fixes +std::array<int , 3> longname; + +auto z1 = longname [ 0 ] ; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:22: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto z1 = longname .at( 0 ) ; +// CHECK-FIXES-FUNC: auto z1 = f(longname , 0 ) ; +auto z2 = longname . operator[] ( 0 ); +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto z2 = longname . at ( 0 ); +// CHECK-FIXES-FUNC: auto z2 = f(longname , 0 ); +auto z3 = (&longname) -> operator[] ( 0 ); +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:11: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT: auto z3 = (&longname) -> at ( 0 ); +// CHECK-FIXES-FUNC: auto z3 = f(*(&longname) , 0 ); + + +//--- cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-no-excl.cpp + +#include "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.h" + +class ExcludedClass1 { + public: + int operator[](unsigned i) { + return 1; + } + int at(unsigned i) { + return 1; + } +}; + +class ExcludedClass2 { + public: + int operator[](unsigned i) { + return 1; + } + int at(unsigned i) { + return 1; + } +}; + +ExcludedClass1 E1; +auto x1 = E1[0]; +// CHECK-MESSAGES-DEFAULT-NO-EXCL: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-NO-EXCL: auto x1 = E1.at(0); +// CHECK-FIXES-FUNC-NO-EXCL: auto x1 = f(E1, 0); + +ExcludedClass2 E2; +auto x2 = E2[0]; +// CHECK-MESSAGES-DEFAULT-NO-EXCL: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-NO-EXCL: auto x2 = E2.at(0); +// CHECK-FIXES-FUNC-NO-EXCL: auto x2 = f(E2, 0); + +std::map<int,int> TestMapNoExcl; +auto y = TestMapNoExcl[0]; +// CHECK-MESSAGES-DEFAULT-NO-EXCL: :[[@LINE-1]]:23: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-NO-EXCL: auto y = TestMapNoExcl.at(0); +// CHECK-FIXES-FUNC-NO-EXCL: auto y = f(TestMapNoExcl, 0); + + +//--- cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-excl.cpp + +#include "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.h" + +std::map<int,int> TestMapExcl; +auto y = TestMapExcl[0]; +// CHECK-MESSAGES-DEFAULT-EXCL: :[[@LINE-1]]:21: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-EXCL: auto y = TestMapExcl.at(0); +// CHECK-FIXES-FUNC-EXCL: auto y = f(TestMapExcl, 0); + + +//--- cppcoreguidelines-pro-bounds-avoid-unchecked-container-access-cxx-23.cpp +#ifdef CXX_23 +#include "cppcoreguidelines-pro-bounds-avoid-unchecked-container-access.h" + +namespace std { + template<typename T, unsigned size> + struct array_cxx_23 { + T operator[]() { + return T{1}; + } + T at() { + return T{1}; + } + }; +}; + +std::array_cxx_23<int, 3> a; + +auto b23 = a[]; +// CHECK-MESSAGES-DEFAULT-CXX-23: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-CXX-23: auto b23 = a.at(); +// CHECK-FIXES-FUNC-CXX-23: auto b23 = f(a); +// CHECK-FIXES-FUNC-EMPTY-ARGS-CXX-23: auto b23 = g(a); +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23: :[[@LINE-5]]:13: warning: possibly unsafe 'operator[]' [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23-NOT: :[[@LINE-6]]:{{.*}}: note: FIX-IT applied suggested code changes + +auto fa23 = (&a)->operator[](); +// CHECK-MESSAGES-DEFAULT-CXX-23: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-CXX-23: auto fa23 = (&a)->at(); +// CHECK-FIXES-FUNC-CXX-23: auto fa23 = f(*(&a)); +// CHECK-FIXES-FUNC-EMPTY-ARGS-CXX-23: auto fa23 = g(*(&a)); +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23: :[[@LINE-5]]:13: warning: possibly unsafe 'operator[]' [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23-NOT: :[[@LINE-6]]:{{.*}}: note: FIX-IT applied suggested code changes + +auto fd23 = a.operator[](); +// CHECK-MESSAGES-DEFAULT-CXX-23: :[[@LINE-1]]:13: warning: possibly unsafe 'operator[]', consider bounds-safe alternatives [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-FIXES-AT-CXX-23: auto fd23 = a.at(); +// CHECK-FIXES-FUNC-CXX-23: auto fd23 = f(a); +// CHECK-FIXES-FUNC-EMPTY-ARGS-CXX-23: auto fd23 = g(a); +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23: :[[@LINE-5]]:13: warning: possibly unsafe 'operator[]' [cppcoreguidelines-pro-bounds-avoid-unchecked-container-access] +// CHECK-MESSAGES-FUNC-EMPTY-ARGS-EMPTY-CXX-23-NOT: :[[@LINE-6]]:{{.*}}: note: FIX-IT applied suggested code changes +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-pointer-arithmetic.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-pointer-arithmetic.cpp index aed6080..fa81c13 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-pointer-arithmetic.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-bounds-pointer-arithmetic.cpp @@ -1,4 +1,6 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-pointer-arithmetic %t +// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-pointer-arithmetic -check-suffixes=,DEFAULT %t +// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-pointer-arithmetic %t -- \ +// RUN: -config="{CheckOptions: {cppcoreguidelines-pro-bounds-pointer-arithmetic.AllowIncrementDecrementOperators: true}}" -- enum E { ENUM_LITERAL = 1 @@ -42,14 +44,14 @@ void fail() { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic p++; - // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:4: warning: do not use pointer arithmetic ++p; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:3: warning: do not use pointer arithmetic p--; - // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:4: warning: do not use pointer arithmetic --p; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:3: warning: do not use pointer arithmetic i = p[1]; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use pointer arithmetic @@ -57,7 +59,7 @@ void fail() { p = ip + 1; // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: do not use pointer arithmetic ip++; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:5: warning: do not use pointer arithmetic i = ip[1]; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use pointer arithmetic } @@ -72,7 +74,7 @@ void template_fail() { q = p - 1; // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic p++; - // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic + // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:4: warning: do not use pointer arithmetic i = p[1]; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use pointer arithmetic } diff --git a/clang-tools-extra/test/clang-tidy/checkers/google/objc-function-naming.m b/clang-tools-extra/test/clang-tidy/checkers/google/objc-function-naming.m index 4c204c7..13d00a8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/google/objc-function-naming.m +++ b/clang-tools-extra/test/clang-tidy/checkers/google/objc-function-naming.m @@ -8,7 +8,7 @@ static void TestImplicitFunctionDeclaration(int a) { printf("%d", a); } -typedef _Bool bool; +#define bool _Bool static bool ispositive(int a) { return a > 0; } // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: static function named 'ispositive' diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/override-with-different-visibility/test-system-header.h b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/override-with-different-visibility/test-system-header.h new file mode 100644 index 0000000..e64e192 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/override-with-different-visibility/test-system-header.h @@ -0,0 +1,14 @@ +#pragma clang system_header + +namespace sys { + +struct Base { + virtual void publicF(); +}; + +struct Derived: public Base { +private: + void publicF() override; +}; + +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-ignore.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-ignore.cpp new file mode 100644 index 0000000..934cfb7 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-ignore.cpp @@ -0,0 +1,60 @@ +// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- \ +// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.IgnoredFunctions: 'IgnoreAlways::.*;::a::IgnoreSelected::.*;IgnoreFunctions::f1;ignored_f'}}" + +class IgnoreAlways { + virtual void f(); +}; + +class IgnoreSelected { + virtual void f(); +}; + +namespace a { +class IgnoreAlways { + virtual void f(); +}; +class IgnoreSelected { + virtual void f(); +}; +} + +namespace ignore_always { +class Test1: public IgnoreAlways { +public: + void f(); + void ignored_f(int); +}; +class Test2: public a::IgnoreAlways { +public: + void f(); +}; +} + +namespace ignore_selected { +class Test1: public IgnoreSelected { +public: + void f(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f' + // CHECK-MESSAGES: :9:16: note: function declared here + void ignored_f(int); +}; +class Test2: public a::IgnoreSelected { +public: + void f(); +}; +} + +class IgnoreFunctions { + virtual void f1(); + virtual void f2(); + virtual void ignored_f(); +}; + +class IgnoreFunctionsTest: public IgnoreFunctions { +public: + void f1(); + void f2(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f2' + // CHECK-MESSAGES: :[[@LINE-9]]:16: note: function declared here + void ignored_f(); +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-options.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-options.cpp new file mode 100644 index 0000000..0a363dd --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility-options.cpp @@ -0,0 +1,75 @@ +// RUN: %check_clang_tidy -check-suffixes=DTORS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \ +// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true}}" + +// RUN: %check_clang_tidy -check-suffixes=OPS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \ +// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckOperators: true}}" + +// RUN: %check_clang_tidy -check-suffixes=WIDENING %s misc-override-with-different-visibility %t -- \ +// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'widening'}}" + +// RUN: %check_clang_tidy -check-suffixes=NARROWING %s misc-override-with-different-visibility %t -- \ +// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'narrowing'}}" + +namespace test_change { + +class A { +protected: + virtual void f1(); + virtual void f2(); +}; + +class B: public A { +public: + void f1(); + // CHECK-MESSAGES-WIDENING: :[[@LINE-1]]:8: warning: visibility of function 'f1' + // CHECK-MESSAGES-WIDENING: :[[@LINE-8]]:16: note: function declared here +private: + void f2(); + // CHECK-MESSAGES-NARROWING: :[[@LINE-1]]:8: warning: visibility of function 'f2' + // CHECK-MESSAGES-NARROWING: :[[@LINE-11]]:16: note: function declared here +}; + +} + +namespace test_destructor { + +class A { +public: + virtual ~A(); +}; + +class B: public A { +protected: + ~B(); + // CHECK-MESSAGES-DTORS: :[[@LINE-1]]:3: warning: visibility of function '~B' + // CHECK-MESSAGES-DTORS: :[[@LINE-7]]:11: note: function declared here +}; + +} + +namespace test_operator { + +class A { + virtual A& operator=(const A&); + virtual A& operator++(); + virtual int operator()(int); + virtual operator double() const; +}; + +class B: public A { +protected: + A& operator=(const A&); + // CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator=' + // CHECK-MESSAGES-OPS: :[[@LINE-10]]:14: note: function declared here + A& operator++(); + // CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator++' + // CHECK-MESSAGES-OPS: :[[@LINE-12]]:14: note: function declared here + int operator()(int); + // CHECK-MESSAGES-OPS: :[[@LINE-1]]:7: warning: visibility of function 'operator()' + // CHECK-MESSAGES-OPS: :[[@LINE-14]]:15: note: function declared here + operator double() const; + // CHECK-MESSAGES-OPS: :[[@LINE-1]]:3: warning: visibility of function 'operator double' + // CHECK-MESSAGES-OPS: :[[@LINE-16]]:11: note: function declared here +}; + +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility.cpp new file mode 100644 index 0000000..fd541a4 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/override-with-different-visibility.cpp @@ -0,0 +1,289 @@ +// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true,misc-override-with-different-visibility.CheckOperators: true}}" -- -I %S/Inputs/override-with-different-visibility +#include <test-system-header.h> +class A { +public: + virtual void pub_foo1() {} + virtual void pub_foo2() {} + virtual void pub_foo3() {} +protected: + virtual void prot_foo1(); + virtual void prot_foo2(); + virtual void prot_foo3(); +private: + virtual void priv_foo1() {} + virtual void priv_foo2() {} + virtual void priv_foo3() {} +}; + +void A::prot_foo1() {} +void A::prot_foo2() {} +void A::prot_foo3() {} + +namespace test1 { + +class B: public A { +public: + void pub_foo1() override {} + void prot_foo1() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'A' to public [misc-override-with-different-visibility] + // CHECK-MESSAGES: :9:16: note: function declared here as protected + void priv_foo1() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'A' to public [misc-override-with-different-visibility] + // CHECK-MESSAGES: :13:16: note: function declared here as private +protected: + void pub_foo2() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo2' is changed from public in class 'A' to protected [misc-override-with-different-visibility] + // CHECK-MESSAGES: :6:16: note: function declared here as public + void prot_foo2() override {} + void priv_foo2() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to protected [misc-override-with-different-visibility] + // CHECK-MESSAGES: :14:16: note: function declared here as private +private: + void pub_foo3() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo3' is changed from public in class 'A' to private [misc-override-with-different-visibility] + // CHECK-MESSAGES: :7:16: note: function declared here as public + void prot_foo3() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo3' is changed from protected in class 'A' to private [misc-override-with-different-visibility] + // CHECK-MESSAGES: :11:16: note: function declared here as protected + void priv_foo3() override {} +}; + +class C: public B { +public: + void pub_foo1() override; +protected: + void prot_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from public in class 'B' to protected [misc-override-with-different-visibility] + // CHECK-MESSAGES: :27:8: note: function declared here as public +private: + void priv_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from public in class 'B' to private [misc-override-with-different-visibility] + // CHECK-MESSAGES: :30:8: note: function declared here as public +}; + +void C::prot_foo1() {} +void C::priv_foo1() {} + +} + +namespace test2 { + +class B: public A { +public: + void pub_foo1() override; +protected: + void prot_foo1() override; +private: + void priv_foo1() override; +}; + +class C: public B { +public: + void pub_foo1() override; + void prot_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'B' to public + // CHECK-MESSAGES: :75:8: note: function declared here as protected + void priv_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'B' to public + // CHECK-MESSAGES: :77:8: note: function declared here as private + + void pub_foo2() override; + void prot_foo2() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from protected in class 'A' to public + // CHECK-MESSAGES: :10:16: note: function declared here as protected + void priv_foo2() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public + // CHECK-MESSAGES: :14:16: note: function declared here as private +}; + +} + +namespace test3 { + +class B: private A { +public: + void pub_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public + // CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here + // CHECK-MESSAGES: :5:16: note: function declared here as public +protected: + void prot_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from private (through private inheritance of class 'A') to protected + // CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here + // CHECK-MESSAGES: :9:16: note: function declared here as protected +private: + void priv_foo1() override; + +public: + void prot_foo2() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from private (through private inheritance of class 'A') to public + // CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here + // CHECK-MESSAGES: :10:16: note: function declared here as protected + void priv_foo2() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public + // CHECK-MESSAGES: :14:16: note: function declared here as private + +private: + void pub_foo3() override; + void prot_foo3() override; +}; + +class C: private A { +}; + +class D: public C { +public: + void pub_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public + // CHECK-MESSAGES: :131:10: note: 'A' is inherited as private here + // CHECK-MESSAGES: :5:16: note: function declared here as public +}; + + +} + +namespace test4 { + +struct Base1 { +public: + virtual void foo1(); +private: + virtual void foo2(); +}; + +struct Base2 { +public: + virtual void foo2(); +private: + virtual void foo1(); +}; + +struct A : public Base1, public Base2 { +protected: + void foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo1' is changed from private in class 'Base2' to protected + // CHECK-MESSAGES: :158:16: note: function declared here as private + // CHECK-MESSAGES: :[[@LINE-3]]:8: warning: visibility of function 'foo1' is changed from public in class 'Base1' to protected + // CHECK-MESSAGES: :149:16: note: function declared here as public +private: + void foo2() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo2' is changed from public in class 'Base2' to private + // CHECK-MESSAGES: :156:16: note: function declared here as public +}; + +} + +namespace test5 { + +struct B1: virtual public A {}; +struct B2: virtual private A {}; +struct B: public B1, public B2 { +public: + void pub_foo1() override; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public + // CHECK-MESSAGES: :179:12: note: 'A' is inherited as private here + // CHECK-MESSAGES: :5:16: note: function declared here as public +}; + +} + +namespace test_using { + +class A { +private: + A(int); +protected: + virtual void f(); +}; + +class B: public A { +public: + using A::A; + using A::f; +}; + +} + +namespace test_template { + +template <typename T> +class A { +protected: + virtual T foo(); +}; + +template <typename T> +class B: public A<T> { +private: + T foo() override; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from protected in class 'A<int>' to private + // CHECK-MESSAGES: :[[@LINE-8]]:13: note: function declared here as protected +}; + +template <typename T> +class C: private A<T> { +public: + T foo() override; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from private (through private inheritance of class 'A<int>') to public + // CHECK-MESSAGES: :[[@LINE-4]]:10: note: 'A<int>' is inherited as private here + // CHECK-MESSAGES: :[[@LINE-17]]:13: note: function declared here as protected +}; + +B<int> fB() { + return B<int>{}; +} + +C<int> fC() { + return C<int>{}; +} + +} + +namespace test_system_header { + +struct SysDerived: public sys::Base { +private: + void publicF(); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'publicF' is changed from public in class 'Base' to private +}; + +} + +namespace test_destructor { + +class A { +public: + virtual ~A(); +}; + +class B: public A { +protected: + ~B(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function '~B' + // CHECK-MESSAGES: :[[@LINE-7]]:11: note: function declared here +}; + +} + +namespace test_operator { + +class A { + virtual int operator()(int); + virtual A& operator++(); + virtual operator double() const; +}; + +class B: public A { +protected: + int operator()(int); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: visibility of function 'operator()' + // CHECK-MESSAGES: :[[@LINE-9]]:15: note: function declared here + A& operator++(); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: visibility of function 'operator++' + // CHECK-MESSAGES: :[[@LINE-11]]:14: note: function declared here + operator double() const; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function 'operator double' + // CHECK-MESSAGES: :[[@LINE-13]]:11: note: function declared here +}; + +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/static-assert.c b/clang-tools-extra/test/clang-tidy/checkers/misc/static-assert.c index 77262c2..b83a093 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/static-assert.c +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/static-assert.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s misc-static-assert %t -- -- -std=c11 +// RUN: %check_clang_tidy -std=c11-or-later %s misc-static-assert %t // RUN: clang-tidy %s -checks=-*,misc-static-assert -- -std=c99 | count 0 void abort(void) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/unconventional-assign-operator.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/unconventional-assign-operator.cpp index 28b53ae..d7a5797 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/unconventional-assign-operator.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/unconventional-assign-operator.cpp @@ -176,3 +176,11 @@ struct TemplateAssignment { } }; } + +namespace GH153770 { + struct A; + struct A { + A() = default; + A& operator=(const A&) = default; + }; +} // namespace GH153770 diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-parameters.c b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-parameters.c index bde93ea..e2f501c7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-parameters.c +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-parameters.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -- -Wno-strict-prototypes -xc +// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -- -Wno-strict-prototypes // Basic removal // ============= @@ -6,14 +6,18 @@ void a(int i) {;} // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters] // CHECK-FIXES: {{^}}void a(int i) {;}{{$}} -static void b(); // In C, forward declarations can leave out parameters. +#if __STDC_VERSION__ < 202311L +static void b(); // In C before C23, forward declarations can leave out parameters. +#endif static void b(int i) {;} // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i' is unused [misc-unused-parameters] // CHECK-FIXES: {{^}}static void b() {;}{{$}} // Unchanged cases // =============== +#if __STDC_VERSION__ < 202311L void h(i, c, d) int i; char *c, *d; {} // Don't mess with K&R style +#endif // Do not warn on naked functions. __attribute__((naked)) void nakedFunction(int a, int b) { ; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-shared.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-shared.cpp index e57f45c..65ece77 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-shared.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-shared.cpp @@ -109,6 +109,8 @@ void basic() { } std::shared_ptr<int> R(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead + // CHECK-FIXES: std::shared_ptr<int> R = std::make_shared<int>(); std::shared_ptr<int> S(new int); // Create the shared_ptr as a parameter to a function. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp index e665ca0..13103c7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp @@ -154,6 +154,8 @@ void basic() { } std::unique_ptr<int> R(new int()); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead + // CHECK-FIXES: std::unique_ptr<int> R = std::make_unique<int>(); std::unique_ptr<int> S(new int); // Create the unique_ptr as a parameter to a function. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits-GH153649.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits-GH153649.cpp new file mode 100644 index 0000000..142eb58 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits-GH153649.cpp @@ -0,0 +1,15 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-type-traits %t + +namespace std { +template <class> struct tuple_size { + static const int value = 1; +}; +template <int, class> struct tuple_element { + using type = int; +}; +} + +struct A {}; +template <int> int get(const A&); + +auto [a] = A(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp index 3bcd5cd..90131c3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy -std=c++20 %s modernize-use-constraints %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-constraints %t -- -- -fno-delayed-template-parsing // NOLINTBEGIN namespace std { @@ -756,3 +756,69 @@ abs(const number<T, ExpressionTemplates> &v) { } } + +template <typename T> +struct some_type_trait { + static constexpr bool value = true; +}; + +// Fix-its are offered even for a non-standard enable_if. +namespace nonstd { + +template <bool Condition, typename T = void> +struct enable_if : std::enable_if<Condition, T> {}; + +template <bool Condition, typename T = void> +using enable_if_t = typename enable_if<Condition, T>::type; + +} + +template <typename T> +typename nonstd::enable_if<some_type_trait<T>::value, void>::type nonstd_enable_if() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] +// CHECK-FIXES: {{^}}void nonstd_enable_if() requires some_type_trait<T>::value {}{{$}} + +template <typename T> +nonstd::enable_if_t<some_type_trait<T>::value, void> nonstd_enable_if_t() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] +// CHECK-FIXES: {{^}}void nonstd_enable_if_t() requires some_type_trait<T>::value {}{{$}} + +template <> +nonstd::enable_if_t<some_type_trait<int>::value, void> nonstd_enable_if_t<int>() {} +// FIXME - Support non-dependent enable_ifs. + +template <typename T> +typename nonstd::enable_if<some_type_trait<T>::value>::type nonstd_enable_if_one_param() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] +// CHECK-FIXES: {{^}}void nonstd_enable_if_one_param() requires some_type_trait<T>::value {}{{$}} + +template <typename T> +nonstd::enable_if_t<some_type_trait<T>::value> nonstd_enable_if_t_one_param() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] +// CHECK-FIXES: {{^}}void nonstd_enable_if_t_one_param() requires some_type_trait<T>::value {}{{$}} + +// No fix-its are offered for an enable_if with a different signature from the standard one. +namespace boost { + +template <typename Condition, typename T = void> +struct enable_if : std::enable_if<Condition::value, T> {}; + +template <typename Condition, typename T = void> +using enable_if_t = typename enable_if<Condition, T>::type; + +} + +template <typename T> +typename boost::enable_if<some_type_trait<T>, void>::type boost_enable_if() {} + +template <typename T> +boost::enable_if_t<some_type_trait<T>, void> boost_enable_if_t() {} + +template <> +boost::enable_if_t<some_type_trait<int>, void> boost_enable_if_t<int>() {} + +template <typename T> +typename boost::enable_if<some_type_trait<T>>::type boost_enable_if_one_param() {} + +template <typename T> +boost::enable_if_t<some_type_trait<T>> boost_enable_if_t_one_param() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-c23.c b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-c23.c index 6fb879b..d9b7ec2 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-c23.c +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-c23.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- -- -std=c23 +// RUN: %check_clang_tidy -std=c23-or-later %s modernize-use-nullptr %t #define NULL 0 diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/std-allocator-const.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/std-allocator-const.cpp index 732cf5d..a38594a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/portability/std-allocator-const.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/std-allocator-const.cpp @@ -43,25 +43,25 @@ template <class T> class allocator {}; void simple(const std::vector<const char> &v, std::deque<const short> *d) { - // CHECK-MESSAGES: [[#@LINE-1]]:24: warning: container using std::allocator<const T> is a deprecated libc++ extension; remove const for compatibility with other standard libraries - // CHECK-MESSAGES: [[#@LINE-2]]:52: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:19: warning: container using std::allocator<const T> is a deprecated libc++ extension; remove const for compatibility with other standard libraries + // CHECK-MESSAGES: [[#@LINE-2]]:47: warning: container std::list<const long> l; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::multiset<int *const> ms; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::set<const std::hash<int>> s; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::unordered_multiset<int *const> ums; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::unordered_set<const int> us; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container absl::flat_hash_set<const int> fhs; - // CHECK-MESSAGES: [[#@LINE-1]]:9: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container using my_vector = std::vector<const int>; - // CHECK-MESSAGES: [[#@LINE-1]]:26: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:21: warning: container my_vector v1; using my_vector2 = my_vector; @@ -76,7 +76,7 @@ void simple(const std::vector<const char> &v, std::deque<const short> *d) { template <class T> void temp1() { std::vector<const T> v; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::vector<T> neg1; std::forward_list<const T> neg2; @@ -87,7 +87,7 @@ template <class T> void temp2() { // Match std::vector<const dependent> for the uninstantiated temp2. std::vector<const T> v; - // CHECK-MESSAGES: [[#@LINE-1]]:8: warning: container + // CHECK-MESSAGES: [[#@LINE-1]]:3: warning: container std::vector<T> neg1; std::forward_list<const T> neg2; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/bitint-no-crash.c b/clang-tools-extra/test/clang-tidy/checkers/readability/bitint-no-crash.c index f866092..01e458e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/bitint-no-crash.c +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/bitint-no-crash.c @@ -1,6 +1,5 @@ -// RUN: %check_clang_tidy %s readability-magic-numbers %t -- +// RUN: %check_clang_tidy -std=c23-or-later %s readability-magic-numbers %t // Don't crash _BitInt(128) A = 4533629751480627964421wb; -// CHECK-MESSAGES: warning diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp index 2fd0b222..6a67310 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp @@ -895,3 +895,93 @@ namespace PR94454 { int operator""_ci() { return 0; } auto eq = 0_ci == 0; } + +namespace GH152387 { + +class foo : public std::string{ + void doit() { + if (!size()) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: the 'empty' method should be used to check for emptiness instead of 'size' + // CHECK-FIXES: if (empty()) { + } + } +}; + +} + +namespace GH154762 { +class TypeRange { + std::vector<int> b; + +public: + TypeRange(std::vector<int> b = {}); + TypeRange(int); + bool operator==(const TypeRange& other) const; + + size_t size() const { + return b.size(); + } + + bool empty() const { + return size() == 0; + } +}; + +void foo(std::vector<int> v) { + if (TypeRange(1) == TypeRange(v)) { // no warning + } + + if (TypeRange(1) == TypeRange()) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object + // CHECK-FIXES: if (TypeRange(1).empty()) { + } + + if (TypeRange(v) == TypeRange()) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object + // CHECK-FIXES: if (TypeRange(v).empty()) { + } +} +} + +class ReportInContainerNonEmptyMethod { +public: + int size() const; + bool empty() const; + + void doit() { + if (!size()) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: the 'empty' method should be used to check for emptiness instead of 'size' + // CHECK-FIXES: if (empty()) + } + } +}; + +template <typename T> +class ReportInTemplateContainerNonEmptyMethod { +public: + int size() const; + bool empty() const; + + void doit() { + if (!size()) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: the 'empty' method should be used to check for emptiness instead of 'size' + // CHECK-FIXES: if (empty()) { + } + } +}; + + + +class ReportInContainerNonEmptyMethodCompare { +public: + bool operator==(const ReportInContainerNonEmptyMethodCompare& other) const; + int size() const; + bool empty() const; + + void doit() { + if (*this == ReportInContainerNonEmptyMethodCompare()) { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object + // CHECK-FIXES: if (this->empty()) { + } + } +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-standard-types.h b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-standard-types.h index e1036483..51fa9c3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-standard-types.h +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-standard-types.h @@ -50,8 +50,8 @@ typedef void* FILE; // NOLINT #define NULL (0) // NOLINT #ifndef __cplusplus -typedef _Bool bool; // NOLINT typedef __WCHAR_TYPE__ wchar_t; // NOLINT +#define bool _Bool // NOLINT #define true 1 // NOLINT #define false 0 // NOLINT #endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.c b/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.c index 11ff7dd..5a4627a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.c +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.c @@ -1,8 +1,8 @@ -// RUN: %check_clang_tidy --match-partial-fixes %s readability-implicit-bool-conversion %t -- -- -std=c23 -// RUN: %check_clang_tidy -check-suffix=UPPER-CASE %s readability-implicit-bool-conversion %t -- \ +// RUN: %check_clang_tidy -std=c23-or-later --match-partial-fixes %s readability-implicit-bool-conversion %t +// RUN: %check_clang_tidy -std=c23-or-later -check-suffix=UPPER-CASE %s readability-implicit-bool-conversion %t -- \ // RUN: -config='{CheckOptions: { \ // RUN: readability-implicit-bool-conversion.UseUpperCaseLiteralSuffix: true \ -// RUN: }}' -- -std=c23 +// RUN: }}' #undef NULL #define NULL 0L diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c index db50467..e254215 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s readability-non-const-parameter %t +// RUN: %check_clang_tidy -std=c99,c11,c17 %s readability-non-const-parameter %t static int f(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c new file mode 100644 index 0000000..d8b104f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c @@ -0,0 +1,120 @@ +// TODO: When Clang adds support for decimal floating point types, enable these tests by: +// 1. Removing all the #if 0 + #endif guards. +// 2. Removing all occurrences of the string "DISABLED-" in this file. +// 3. Deleting this message. + +// RUN: %check_clang_tidy -std=c23-or-later %s readability-uppercase-literal-suffix %t + +void bit_precise_literal_suffix() { + // _BitInt() + + static constexpr auto v1 = 1wb; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wb', which is not uppercase + // CHECK-FIXES: static constexpr auto v1 = 1WB; + static_assert(v1 == 1WB); + + static constexpr auto v2 = 1WB; // OK. + static_assert(v2 == 1WB); + + // _BitInt() Unsigned + + static constexpr auto v3 = 1wbu; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbu', which is not uppercase + // CHECK-FIXES: static constexpr auto v3 = 1WBU; + static_assert(v3 == 1WBU); + + static constexpr auto v4 = 1WBu; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'WBu', which is not uppercase + // CHECK-FIXES: static constexpr auto v4 = 1WBU; + static_assert(v4 == 1WBU); + + static constexpr auto v5 = 1wbU; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbU', which is not uppercase + // CHECK-FIXES: static constexpr auto v5 = 1WBU; + static_assert(v5 == 1WBU); + + static constexpr auto v6 = 1WBU; // OK. + static_assert(v6 == 1WBU); + + // Unsigned _BitInt() + + static constexpr auto v7 = 1uwb; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uwb', which is not uppercase + // CHECK-FIXES: static constexpr auto v7 = 1UWB; + static_assert(v7 == 1UWB); + + static constexpr auto v8 = 1uWB; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uWB', which is not uppercase + // CHECK-FIXES: static constexpr auto v8 = 1UWB; + static_assert(v8 == 1UWB); + + static constexpr auto v9 = 1Uwb; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'Uwb', which is not uppercase + // CHECK-FIXES: static constexpr auto v9 = 1UWB; + static_assert(v9 == 1UWB); + + static constexpr auto v10 = 1UWB; // OK. + static_assert(v10 == 1UWB); +} + +void decimal_floating_point_suffix() { + // _Decimal32 + +#if 0 + static constexpr auto v1 = 1.df; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.DF; + static_assert(v1 == 1.DF); + + static constexpr auto v2 = 1.e0df; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0DF; + static_assert(v2 == 1.DF); + + static constexpr auto v3 = 1.DF; // OK. + static_assert(v3 == 1.DF); + + static constexpr auto v4 = 1.e0DF; // OK. + static_assert(v4 == 1.DF); +#endif + + // _Decimal64 + +#if 0 + static constexpr auto v5 = 1.dd; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v5 = 1.DD; + static_assert(v5 == 1.DD); + + static constexpr auto v6 = 1.e0dd; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v6 = 1.e0DD; + static_assert(v6 == 1.DD); + + static constexpr auto v7 = 1.DD; // OK. + static_assert(v7 == 1.DD); + + static constexpr auto v8 = 1.e0DD; // OK. + static_assert(v8 == 1.DD); +#endif + + // _Decimal128 + +#if 0 + static constexpr auto v9 = 1.dl; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dl', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.DL; + static_assert(v9 == 1.DL); + + static constexpr auto v10 = 1.e0dl; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'dl', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0DL; + static_assert(v10 == 1.DL); + + static constexpr auto v11 = 1.DL; // OK. + static_assert(v11 == 1.DL); + + static constexpr auto v12 = 1.e0DL; // OK. + static_assert(v12 == 1.DL); +#endif +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-cxx23.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-cxx23.cpp new file mode 100644 index 0000000..6602fd9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-cxx23.cpp @@ -0,0 +1,264 @@ +// TODO: When Clang adds support for C++23 floating-point types, enable these tests by: +// 1. Removing all the #if 0 + #endif guards. +// 2. Removing all occurrences of the string "DISABLED-" in this file. +// 3. Deleting this message. +// These suffixes may be relevant to C too: https://github.com/llvm/llvm-project/issues/97335 + +// RUN: %check_clang_tidy -std=c++23-or-later %s readability-uppercase-literal-suffix %t -- -- -target aarch64-linux-gnu -I %clang_tidy_headers + +#include "integral_constant.h" +#include <cstddef> +#if 0 +#include <stdfloat> +#endif + +void normal_literals() { + // std::bfloat16_t + +#if 0 + static constexpr auto v1 = 1.bf16; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.BF16; + static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, ""); + static_assert(v1 == 1.BF16, ""); + + static constexpr auto v2 = 1.e0bf16; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0BF16; + static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, ""); + static_assert(v2 == 1.BF16, ""); + + static constexpr auto v3 = 1.BF16; // OK. + static_assert(is_same<decltype(v3), const std::bfloat16_t>::value, ""); + static_assert(v3 == 1.BF16, ""); + + static constexpr auto v4 = 1.e0BF16; // OK. + static_assert(is_same<decltype(v4), const std::bfloat16_t>::value, ""); + static_assert(v4 == 1.BF16, ""); +#endif + + // _Float16/std::float16_t + + static constexpr auto v5 = 1.f16; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase + // CHECK-FIXES: static constexpr auto v5 = 1.F16; + static_assert(is_same<decltype(v5), const _Float16>::value, ""); + static_assert(v5 == 1.F16, ""); + + static constexpr auto v6 = 1.e0f16; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase + // CHECK-FIXES: static constexpr auto v6 = 1.e0F16; + static_assert(is_same<decltype(v6), const _Float16>::value, ""); + static_assert(v6 == 1.F16, ""); + + static constexpr auto v7 = 1.F16; // OK. + static_assert(is_same<decltype(v7), const _Float16>::value, ""); + static_assert(v7 == 1.F16, ""); + + static constexpr auto v8 = 1.e0F16; // OK. + static_assert(is_same<decltype(v8), const _Float16>::value, ""); + static_assert(v8 == 1.F16, ""); + + // std::float32_t + +#if 0 + static constexpr auto v9 = 1.f32; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.F32; + static_assert(is_same<decltype(v9), const std::float32_t>::value, ""); + static_assert(v9 == 1.F32, ""); + + static constexpr auto v10 = 1.e0f32; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f32', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0F32; + static_assert(is_same<decltype(v10), const std::float32_t>::value, ""); + static_assert(v10 == 1.F32, ""); + + static constexpr auto v11 = 1.F32; // OK. + static_assert(is_same<decltype(v11), const std::float32_t>::value, ""); + static_assert(v11 == 1.F32, ""); + + static constexpr auto v12 = 1.e0F32; // OK. + static_assert(is_same<decltype(v12), const std::float32_t>::value, ""); + static_assert(v12 == 1.F32, ""); +#endif + + // std::float64_t + +#if 0 + static constexpr auto v13 = 1.f64; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v13 = 1.F64; + static_assert(is_same<decltype(v13), const std::float64_t>::value, ""); + static_assert(v13 == 1.F64, ""); + + static constexpr auto v14 = 1.e0f64; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v14 = 1.e0F64; + static_assert(is_same<decltype(v14), const std::float64_t>::value, ""); + static_assert(v14 == 1.F64, ""); + + static constexpr auto v15 = 1.F64; // OK. + static_assert(is_same<decltype(v15), const std::float64_t>::value, ""); + static_assert(v15 == 1.F64, ""); + + static constexpr auto v16 = 1.e0F64; // OK. + static_assert(is_same<decltype(v16), const std::float64_t>::value, ""); + static_assert(v16 == 1.F64, ""); +#endif + + // std::float128_t + +#if 0 + static constexpr auto v17 = 1.f128; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v17 = 1.F128; + static_assert(is_same<decltype(v17), const std::float128_t>::value, ""); + static_assert(v17 == 1.F128, ""); + + static constexpr auto v18 = 1.e0f128; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v18 = 1.e0F128; + static_assert(is_same<decltype(v18), const std::float128_t>::value, ""); + static_assert(v18 == 1.F128, ""); + + static constexpr auto v19 = 1.F128; // OK. + static_assert(is_same<decltype(v19), const std::float128_t>::value, ""); + static_assert(v19 == 1.F128, ""); + + static constexpr auto v20 = 1.e0F128; // OK. + static_assert(is_same<decltype(v20), const std::float128_t>::value, ""); + static_assert(v20 == 1.F128, ""); +#endif +} + +void hexadecimal_literals() { + // std::bfloat16_t + +#if 0 + static constexpr auto v1 = 0xfp0bf16; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v1 = 0xfp0BF16; + static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, ""); + static_assert(v1 == 0xfp0BF16, ""); + + static constexpr auto v2 = 0xfp0BF16; // OK. + static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, ""); + static_assert(v2 == 0xfp0BF16, ""); +#endif + + // _Float16/std::float16_t + + static constexpr auto v3 = 0xfp0f16; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase + // CHECK-FIXES: static constexpr auto v3 = 0xfp0F16; + static_assert(is_same<decltype(v3), const _Float16>::value, ""); + static_assert(v3 == 0xfp0F16, ""); + + static constexpr auto v4 = 0xfp0F16; // OK. + static_assert(is_same<decltype(v4), const _Float16>::value, ""); + static_assert(v4 == 0xfp0F16, ""); + + // std::float32_t + +#if 0 + static constexpr auto v5 = 0xfp0f32; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v5 = 0xfp0F32; + static_assert(is_same<decltype(v5), const std::float32_t>::value, ""); + static_assert(v5 == 0xfp0F32, ""); + + static constexpr auto v6 = 0xfp0F32; // OK. + static_assert(is_same<decltype(v6), const std::float32_t>::value, ""); + static_assert(v6 == 0xfp0F32, ""); +#endif + + // std::float64_t + +#if 0 + static constexpr auto v7 = 0xfp0f64; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f64', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v7 = 0xfp0F64; + static_assert(is_same<decltype(v7), const std::float64_t>::value, ""); + static_assert(v7 == 0xfp0F64, ""); + + static constexpr auto v8 = 0xfp0F64; // OK. + static_assert(is_same<decltype(v8), const std::float64_t>::value, ""); + static_assert(v8 == 0xfp0F64, ""); +#endif + + // std::float128_t + +#if 0 + static constexpr auto v9 = 0xfp0f128; + // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f128', which is not uppercase + // DISABLED-CHECK-FIXES: static constexpr auto v9 = 0xfp0F128; + static_assert(is_same<decltype(v9), const std::float128_t>::value, ""); + static_assert(v9 == 0xfp0F128, ""); + + static constexpr auto v10 = 0xfp0F128; // OK. + static_assert(is_same<decltype(v10), const std::float128_t>::value, ""); + static_assert(v10 == 0xfp0F128, ""); +#endif + +} + +void size_t_suffix() { + // Signed + + static constexpr auto v29 = 1z; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'z', which is not uppercase + // CHECK-FIXES: static constexpr auto v29 = 1Z; + static_assert(v29 == 1Z, ""); + + static constexpr auto v30 = 1Z; // OK. + static_assert(v30 == 1Z, ""); + + // size_t Unsigned + + static constexpr auto v31 = 1zu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'zu', which is not uppercase + // CHECK-FIXES: static constexpr auto v31 = 1ZU; + static_assert(is_same<decltype(v31), const size_t>::value, ""); + static_assert(v31 == 1ZU, ""); + + static constexpr auto v32 = 1Zu; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Zu', which is not uppercase + // CHECK-FIXES: static constexpr auto v32 = 1ZU; + static_assert(is_same<decltype(v32), const size_t>::value, ""); + static_assert(v32 == 1ZU, ""); + + static constexpr auto v33 = 1zU; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'zU', which is not uppercase + // CHECK-FIXES: static constexpr auto v33 = 1ZU; + static_assert(is_same<decltype(v33), const size_t>::value, ""); + static_assert(v33 == 1ZU, ""); + + static constexpr auto v34 = 1ZU; // OK. + static_assert(is_same<decltype(v34), const size_t>::value, ""); + static_assert(v34 == 1ZU, ""); + + // Unsigned size_t + + static constexpr auto v35 = 1uz; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uz', which is not uppercase + // CHECK-FIXES: static constexpr auto v35 = 1UZ; + static_assert(is_same<decltype(v35), const size_t>::value, ""); + static_assert(v35 == 1UZ); + + static constexpr auto v36 = 1uZ; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uZ', which is not uppercase + // CHECK-FIXES: static constexpr auto v36 = 1UZ; + static_assert(is_same<decltype(v36), const size_t>::value, ""); + static_assert(v36 == 1UZ); + + static constexpr auto v37 = 1Uz; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Uz', which is not uppercase + // CHECK-FIXES: static constexpr auto v37 = 1UZ; + static_assert(is_same<decltype(v37), const size_t>::value, ""); + static_assert(v37 == 1UZ); + + static constexpr auto v38 = 1UZ; // OK. + static_assert(is_same<decltype(v38), const size_t>::value, ""); + static_assert(v38 == 1UZ); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-float16.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-float16.cpp deleted file mode 100644 index 46d7bc1..0000000 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-float16.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -target aarch64-linux-gnu -I %clang_tidy_headers - -#include "integral_constant.h" - -void float16_normal_literals() { - // _Float16 - - static constexpr auto v14 = 1.f16; - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f16', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1.f16; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F16{{$}} - // CHECK-FIXES: static constexpr auto v14 = 1.F16; - static_assert(is_same<decltype(v14), const _Float16>::value, ""); - static_assert(v14 == 1.F16, ""); - - static constexpr auto v15 = 1.e0f16; - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f16', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v15 = 1.e0f16; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F16{{$}} - // CHECK-FIXES: static constexpr auto v15 = 1.e0F16; - static_assert(is_same<decltype(v15), const _Float16>::value, ""); - static_assert(v15 == 1.F16, ""); - - static constexpr auto v16 = 1.F16; // OK. - static_assert(is_same<decltype(v16), const _Float16>::value, ""); - static_assert(v16 == 1.F16, ""); - - static constexpr auto v17 = 1.e0F16; // OK. - static_assert(is_same<decltype(v17), const _Float16>::value, ""); - static_assert(v17 == 1.F16, ""); -} - -void float16_hexadecimal_literals() { -// _Float16 - - static constexpr auto v13 = 0xfp0f16; - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f16', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v13 = 0xfp0f16; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F16{{$}} - // CHECK-FIXES: static constexpr auto v13 = 0xfp0F16; - static_assert(is_same<decltype(v13), const _Float16>::value, ""); - static_assert(v13 == 0xfp0F16, ""); - - static constexpr auto v14 = 0xfp0F16; // OK. - static_assert(is_same<decltype(v14), const _Float16>::value, ""); - static_assert(v14 == 0xfp0F16, ""); - -} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point-opencl-half.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point-opencl-half.cpp index ef905da..f644235 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point-opencl-half.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point-opencl-half.cpp @@ -1,7 +1,4 @@ // RUN: %check_clang_tidy -std=cl2.0 %s readability-uppercase-literal-suffix %t -- -- -target x86_64-pc-linux-gnu -I %S -x cl -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -target x86_64-pc-linux-gnu -I %S -std=cl2.0 -x cl -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -target x86_64-pc-linux-gnu -I %S -std=cl2.0 -x cl #pragma OPENCL EXTENSION cl_khr_fp16 : enable @@ -12,16 +9,10 @@ void floating_point_half_suffix() { static half v2 = 1.h; // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: floating point literal has suffix 'h', which is not uppercase - // CHECK-MESSAGES-NEXT: static half v2 = 1.h; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: H{{$}} // CHECK-HIXES: static half v2 = 1.H; static half v3 = 1.e0h; // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: floating point literal has suffix 'h', which is not uppercase - // CHECK-MESSAGES-NEXT: static half v3 = 1.e0h; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: H{{$}} // CHECK-HIXES: static half v3 = 1.e0H; static half v4 = 1.H; // OK. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp index d9f5bfb..fc1976b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-floating-point.cpp @@ -1,7 +1,4 @@ // RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers #include "integral_constant.h" @@ -18,18 +15,12 @@ void floating_point_suffix() { static constexpr auto v2 = 1.f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v2 = 1.F; static_assert(is_same<decltype(v2), const float>::value, ""); static_assert(v2 == 1.0F, ""); static constexpr auto v3 = 1.e0f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1.e0f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v3 = 1.e0F; static_assert(is_same<decltype(v3), const float>::value, ""); static_assert(v3 == 1.0F, ""); @@ -46,18 +37,12 @@ void floating_point_suffix() { static constexpr auto v6 = 1.l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.l; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: L{{$}} // CHECK-FIXES: static constexpr auto v6 = 1.L; static_assert(is_same<decltype(v6), const long double>::value, ""); static_assert(v6 == 1., ""); static constexpr auto v7 = 1.e0l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1.e0l; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: L{{$}} // CHECK-FIXES: static constexpr auto v7 = 1.e0L; static_assert(is_same<decltype(v7), const long double>::value, ""); static_assert(v7 == 1., ""); @@ -74,18 +59,12 @@ void floating_point_suffix() { static constexpr auto v10 = 1.q; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'q', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1.q; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: Q{{$}} // CHECK-FIXES: static constexpr auto v10 = 1.Q; static_assert(is_same<decltype(v10), const __float128>::value, ""); static_assert(v10 == 1., ""); static constexpr auto v11 = 1.e0q; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'q', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v11 = 1.e0q; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: Q{{$}} // CHECK-FIXES: static constexpr auto v11 = 1.e0Q; static_assert(is_same<decltype(v11), const __float128>::value, ""); static_assert(v11 == 1., ""); @@ -104,18 +83,12 @@ void floating_point_complex_suffix() { static constexpr auto v14 = 1.i; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'i', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1.i; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: I{{$}} // CHECK-FIXES: static constexpr auto v14 = 1.I; static_assert(is_same<decltype(v14), const _Complex double>::value, ""); static_assert(v14 == 1.I, ""); static constexpr auto v15 = 1.e0i; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'i', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v15 = 1.e0i; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: I{{$}} // CHECK-FIXES: static constexpr auto v15 = 1.e0I; static_assert(is_same<decltype(v15), const _Complex double>::value, ""); static_assert(v15 == 1.I, ""); @@ -132,18 +105,12 @@ void floating_point_complex_suffix() { static constexpr auto v18 = 1.j; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'j', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v18 = 1.j; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: J{{$}} // CHECK-FIXES: static constexpr auto v18 = 1.J; static_assert(is_same<decltype(v18), const _Complex double>::value, ""); static_assert(v18 == 1.J, ""); static constexpr auto v19 = 1.e0j; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'j', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v19 = 1.e0j; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: J{{$}} // CHECK-FIXES: static constexpr auto v19 = 1.e0J; static_assert(is_same<decltype(v19), const _Complex double>::value, ""); static_assert(v19 == 1.J, ""); @@ -161,9 +128,6 @@ void macros() { #define PASSTHROUGH(X) X static constexpr auto m0 = PASSTHROUGH(1.f); // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(1.f); - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(1.F); static_assert(is_same<decltype(m0), const float>::value, ""); static_assert(m0 == 1.0F, ""); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-hexadecimal-floating-point.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-hexadecimal-floating-point.cpp index 7207715..9bb1cee 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-hexadecimal-floating-point.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-hexadecimal-floating-point.cpp @@ -1,7 +1,4 @@ // RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers #include "integral_constant.h" @@ -14,9 +11,6 @@ void floating_point_suffix() { static constexpr auto v1 = 0xfp0f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v1 = 0xfp0f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v1 = 0xfp0F; static_assert(is_same<decltype(v1), const float>::value, ""); static_assert(v1 == 15, ""); @@ -27,9 +21,6 @@ void floating_point_suffix() { static constexpr auto v3 = 0xfP0f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 0xfP0f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v3 = 0xfP0F; static_assert(is_same<decltype(v3), const float>::value, ""); static_assert(v3 == 15, ""); @@ -40,9 +31,6 @@ void floating_point_suffix() { static constexpr auto v5 = 0xFP0f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 0xFP0f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v5 = 0xFP0F; static_assert(is_same<decltype(v5), const float>::value, ""); static_assert(v5 == 15, ""); @@ -53,9 +41,6 @@ void floating_point_suffix() { static constexpr auto v7 = 0xFp0f; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 0xFp0f; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto v7 = 0xFp0F; static_assert(is_same<decltype(v7), const float>::value, ""); static_assert(v7 == 15, ""); @@ -68,9 +53,6 @@ void floating_point_suffix() { static constexpr auto v9 = 0xfp0l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 0xfp0l; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: L{{$}} // CHECK-FIXES: static constexpr auto v9 = 0xfp0L; static_assert(is_same<decltype(v9), const long double>::value, ""); static_assert(v9 == 0xfp0, ""); @@ -83,9 +65,6 @@ void floating_point_suffix() { static constexpr auto v11 = 0xfp0q; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'q', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v11 = 0xfp0q; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: Q{{$}} // CHECK-FIXES: static constexpr auto v11 = 0xfp0Q; static_assert(is_same<decltype(v11), const __float128>::value, ""); static_assert(v11 == 0xfp0, ""); @@ -100,9 +79,6 @@ void floating_point_complex_suffix() { static constexpr auto v14 = 0xfp0i; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'i', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 0xfp0i; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: I{{$}} // CHECK-FIXES: static constexpr auto v14 = 0xfp0I; static_assert(is_same<decltype(v14), const _Complex double>::value, ""); static_assert(v14 == 0xfp0I, ""); @@ -115,9 +91,6 @@ void floating_point_complex_suffix() { static constexpr auto v18 = 0xfp0j; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'j', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v18 = 0xfp0j; - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: J{{$}} // CHECK-FIXES: static constexpr auto v18 = 0xfp0J; static_assert(is_same<decltype(v18), const _Complex double>::value, ""); static_assert(v18 == 0xfp0J, ""); @@ -131,9 +104,6 @@ void macros() { #define PASSTHROUGH(X) X static constexpr auto m0 = PASSTHROUGH(0x0p0f); // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: floating point literal has suffix 'f', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(0x0p0f); - // CHECK-MESSAGES-NEXT: ^ ~ - // CHECK-MESSAGES-NEXT: F{{$}} // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(0x0p0F); static_assert(is_same<decltype(m0), const float>::value, ""); static_assert(m0 == 0x0p0F, ""); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-custom-list.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-custom-list.cpp index 3215075..a4fcf3a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-custom-list.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-custom-list.cpp @@ -1,7 +1,4 @@ // RUN: %check_clang_tidy --match-partial-fixes %s readability-uppercase-literal-suffix %t -- -config="{CheckOptions: {readability-uppercase-literal-suffix.NewSuffixes: 'L;uL'}}" -- -I %clang_tidy_headers -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -config="{CheckOptions: {readability-uppercase-literal-suffix.NewSuffixes: 'L;uL'}}" -- -I %clang_tidy_headers -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -config="{CheckOptions: {readability-uppercase-literal-suffix.NewSuffixes: 'L;uL'}}" -- -I %clang_tidy_headers #include "integral_constant.h" @@ -20,9 +17,6 @@ void integer_suffix() { static constexpr auto v5 = 1l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1l; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: L{{$}} // CHECK-FIXES: static constexpr auto v5 = 1L; static_assert(is_same<decltype(v5), const long>::value, ""); static_assert(v5 == 1, ""); @@ -45,9 +39,6 @@ void integer_suffix() { static constexpr auto v9 = 1ul; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'ul', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1ul; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: uL{{$}} // CHECK-FIXES: static constexpr auto v9 = 1uL; static_assert(is_same<decltype(v9), const unsigned long>::value, ""); static_assert(v9 == 1, ""); @@ -58,18 +49,12 @@ void integer_suffix() { static constexpr auto v11 = 1Ul; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Ul', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v11 = 1Ul; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: uL{{$}} // CHECK-FIXES: static constexpr auto v11 = 1uL; static_assert(is_same<decltype(v11), const unsigned long>::value, ""); static_assert(v11 == 1, ""); static constexpr auto v12 = 1UL; // OK. // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'UL', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v12 = 1UL; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: uL{{$}} // CHECK-FIXES: static constexpr auto v12 = 1uL; static_assert(is_same<decltype(v12), const unsigned long>::value, ""); static_assert(v12 == 1, ""); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-ms.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-ms.cpp index 7ac4a75..7462818 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-ms.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer-ms.cpp @@ -1,14 +1,9 @@ // RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -fms-extensions -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -fms-extensions -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -target x86_64-pc-linux-gnu -I %clang_tidy_headers -fms-extensions #include "integral_constant.h" void integer_suffix() { static constexpr auto v0 = __LINE__; // synthetic - static_assert(v0 == 9 || v0 == 5, ""); - static constexpr auto v1 = __cplusplus; // synthetic, long static constexpr auto v2 = 1; // no literal @@ -19,9 +14,6 @@ void integer_suffix() { static constexpr auto v3 = 1i32; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'i32', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1i32; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: I32{{$}} // CHECK-FIXES: static constexpr auto v3 = 1I32; static_assert(is_same<decltype(v3), const int>::value, ""); static_assert(v3 == 1I32, ""); @@ -34,9 +26,6 @@ void integer_suffix() { static constexpr auto v5 = 1i64; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'i64', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1i64; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: I64{{$}} // CHECK-FIXES: static constexpr auto v5 = 1I64; static_assert(is_same<decltype(v5), const long int>::value, ""); static_assert(v5 == 1I64, ""); @@ -49,9 +38,6 @@ void integer_suffix() { static constexpr auto v7 = 1i16; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'i16', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1i16; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: I16{{$}} // CHECK-FIXES: static constexpr auto v7 = 1I16; static_assert(is_same<decltype(v7), const short>::value, ""); static_assert(v7 == 1I16, ""); @@ -64,9 +50,6 @@ void integer_suffix() { static constexpr auto v9 = 1i8; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'i8', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1i8; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: I8{{$}} // CHECK-FIXES: static constexpr auto v9 = 1I8; static_assert(is_same<decltype(v9), const char>::value, ""); static_assert(v9 == 1I8, ""); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp index 084d9f6..e4dd968 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp @@ -1,14 +1,10 @@ // RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -I %clang_tidy_headers -// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -fix -- -I %clang_tidy_headers -// RUN: clang-tidy %t.cpp -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -I %clang_tidy_headers #include "integral_constant.h" +#include <cstddef> void integer_suffix() { static constexpr auto v0 = __LINE__; // synthetic - static_assert(v0 == 9 || v0 == 5, ""); - static constexpr auto v1 = __cplusplus; // synthetic, long static constexpr auto v2 = 1; // no literal @@ -19,9 +15,6 @@ void integer_suffix() { static constexpr auto v3 = 1u; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'u', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1u; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: U{{$}} // CHECK-FIXES: static constexpr auto v3 = 1U; static_assert(is_same<decltype(v3), const unsigned int>::value, ""); static_assert(v3 == 1, ""); @@ -34,9 +27,6 @@ void integer_suffix() { static constexpr auto v5 = 1l; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'l', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1l; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: L{{$}} // CHECK-FIXES: static constexpr auto v5 = 1L; static_assert(is_same<decltype(v5), const long>::value, ""); static_assert(v5 == 1, ""); @@ -49,9 +39,6 @@ void integer_suffix() { static constexpr auto v7 = 1ll; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'll', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1ll; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: LL{{$}} // CHECK-FIXES: static constexpr auto v7 = 1LL; static_assert(is_same<decltype(v7), const long long>::value, ""); static_assert(v7 == 1, ""); @@ -64,27 +51,18 @@ void integer_suffix() { static constexpr auto v9 = 1ul; // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'ul', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1ul; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: UL{{$}} // CHECK-FIXES: static constexpr auto v9 = 1UL; static_assert(is_same<decltype(v9), const unsigned long>::value, ""); static_assert(v9 == 1, ""); static constexpr auto v10 = 1uL; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uL', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1uL; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: UL{{$}} // CHECK-FIXES: static constexpr auto v10 = 1UL; static_assert(is_same<decltype(v10), const unsigned long>::value, ""); static_assert(v10 == 1, ""); static constexpr auto v11 = 1Ul; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Ul', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v11 = 1Ul; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: UL{{$}} // CHECK-FIXES: static constexpr auto v11 = 1UL; static_assert(is_same<decltype(v11), const unsigned long>::value, ""); static_assert(v11 == 1, ""); @@ -97,27 +75,18 @@ void integer_suffix() { static constexpr auto v13 = 1lu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v13 = 1lu; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: LU{{$}} // CHECK-FIXES: static constexpr auto v13 = 1LU; static_assert(is_same<decltype(v13), const unsigned long>::value, ""); static_assert(v13 == 1, ""); static constexpr auto v14 = 1Lu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Lu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1Lu; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: LU{{$}} // CHECK-FIXES: static constexpr auto v14 = 1LU; static_assert(is_same<decltype(v14), const unsigned long>::value, ""); static_assert(v14 == 1, ""); static constexpr auto v15 = 1lU; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'lU', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v15 = 1lU; - // CHECK-MESSAGES-NEXT: ^~~ - // CHECK-MESSAGES-NEXT: LU{{$}} // CHECK-FIXES: static constexpr auto v15 = 1LU; static_assert(is_same<decltype(v15), const unsigned long>::value, ""); static_assert(v15 == 1, ""); @@ -130,27 +99,18 @@ void integer_suffix() { static constexpr auto v17 = 1ull; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'ull', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v17 = 1ull; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: ULL{{$}} // CHECK-FIXES: static constexpr auto v17 = 1ULL; static_assert(is_same<decltype(v17), const unsigned long long>::value, ""); static_assert(v17 == 1, ""); static constexpr auto v18 = 1uLL; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'uLL', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v18 = 1uLL; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: ULL{{$}} // CHECK-FIXES: static constexpr auto v18 = 1ULL; static_assert(is_same<decltype(v18), const unsigned long long>::value, ""); static_assert(v18 == 1, ""); static constexpr auto v19 = 1Ull; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'Ull', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v19 = 1Ull; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: ULL{{$}} // CHECK-FIXES: static constexpr auto v19 = 1ULL; static_assert(is_same<decltype(v19), const unsigned long long>::value, ""); static_assert(v19 == 1, ""); @@ -163,27 +123,18 @@ void integer_suffix() { static constexpr auto v21 = 1llu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v21 = 1llu; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: LLU{{$}} // CHECK-FIXES: static constexpr auto v21 = 1LLU; static_assert(is_same<decltype(v21), const unsigned long long>::value, ""); static_assert(v21 == 1, ""); static constexpr auto v22 = 1LLu; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'LLu', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v22 = 1LLu; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: LLU{{$}} // CHECK-FIXES: static constexpr auto v22 = 1LLU; static_assert(is_same<decltype(v22), const unsigned long long>::value, ""); static_assert(v22 == 1, ""); static constexpr auto v23 = 1llU; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'llU', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v23 = 1llU; - // CHECK-MESSAGES-NEXT: ^~~~ - // CHECK-MESSAGES-NEXT: LLU{{$}} // CHECK-FIXES: static constexpr auto v23 = 1LLU; static_assert(is_same<decltype(v23), const unsigned long long>::value, ""); static_assert(v23 == 1, ""); @@ -198,9 +149,6 @@ void integer_complex_suffix() { static constexpr auto v25 = 1i; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'i', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v25 = 1i; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: I{{$}} // CHECK-FIXES: static constexpr auto v25 = 1I; static_assert(is_same<decltype(v25), const _Complex int>::value, ""); static_assert(v25 == 1I, ""); @@ -213,9 +161,6 @@ void integer_complex_suffix() { static constexpr auto v27 = 1j; // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: integer literal has suffix 'j', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto v27 = 1j; - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: J{{$}} // CHECK-FIXES: static constexpr auto v27 = 1J; static_assert(is_same<decltype(v27), const _Complex int>::value, ""); static_assert(v27 == 1J, ""); @@ -229,9 +174,6 @@ void macros() { #define PASSTHROUGH(X) X static constexpr auto m0 = PASSTHROUGH(1u); // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: integer literal has suffix 'u', which is not uppercase - // CHECK-MESSAGES-NEXT: static constexpr auto m0 = PASSTHROUGH(1u); - // CHECK-MESSAGES-NEXT: ^~ - // CHECK-MESSAGES-NEXT: U{{$}} // CHECK-FIXES: static constexpr auto m0 = PASSTHROUGH(1U); static_assert(is_same<decltype(m0), const unsigned int>::value, ""); static_assert(m0 == 1, ""); diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp index 7efa7d0..58f3b23 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter-symlinks.cpp @@ -10,7 +10,7 @@ // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s -// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER --allow-empty %s // Check that `-header-filter` operates on the same file paths as paths in // diagnostics printed by ClangTidy. diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp index 448ef9d..d9ec104 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp @@ -66,19 +66,14 @@ class A { A(int); }; // CHECK4-NOT: warning: // CHECK4-QUIET-NOT: warning: -// CHECK: Suppressed 3 warnings (3 in non-user code) // CHECK: Use -header-filter=.* to display errors from all non-system headers. // CHECK-QUIET-NOT: Suppressed -// CHECK2: Suppressed 1 warnings (1 in non-user code) -// CHECK2: Use -header-filter=.* {{.*}} // CHECK2-QUIET-NOT: Suppressed -// CHECK3: Suppressed 2 warnings (2 in non-user code) // CHECK3: Use -header-filter=.* {{.*}} // CHECK3-QUIET-NOT: Suppressed // CHECK4-NOT: Suppressed {{.*}} warnings // CHECK4-NOT: Use -header-filter=.* {{.*}} // CHECK4-QUIET-NOT: Suppressed -// CHECK6: Suppressed 2 warnings (2 in non-user code) // CHECK6: Use -header-filter=.* {{.*}} int x = 123; diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/quiet-flag.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/quiet-flag.cpp new file mode 100644 index 0000000..0ed6d01 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/quiet-flag.cpp @@ -0,0 +1,26 @@ +// This test ensures that the --quiet flag only suppresses the "X warnings generated" +// message while keeping all diagnostic information including caret indicators (^). + +// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare %s -- \ +// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL +// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare -quiet %s -- \ +// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-QUIET + +// CHECK-NORMAL: 2 warnings generated +// CHECK-NORMAL-DAG: warning: 42 is a magic number +// CHECK-NORMAL-DAG: {{[ ]*\^}} +// CHECK-NORMAL-DAG: warning: comparison of integers of different signs +// CHECK-NORMAL-DAG: {{[ ]*~ \^ ~}} + +// CHECK-QUIET-NOT: {{[0-9]+}} warning{{s?}} generated +// CHECK-QUIET-DAG: warning: 42 is a magic number +// CHECK-QUIET-DAG: {{[ ]*\^}} +// CHECK-QUIET-DAG: warning: comparison of integers of different signs +// CHECK-QUIET-DAG: {{[ ]*~ \^ ~}} + +int main() { + const int CONST_VAL = 10; + int x = 42; // trigger 'readability-magic-numbers' with caret: ^ + unsigned int y = CONST_VAL; + return x < y; // trigger 'clang-diagnostic-sign-compare' with caret: ^ +} diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp index 9fa990b..a25480e 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/system-headers.cpp @@ -11,9 +11,9 @@ // RUN: clang-tidy -help | FileCheck -check-prefix=CHECK-OPT-PRESENT %s // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=true %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s -// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: true' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s -// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s #include <system_header.h> // CHECK-SYSTEM-HEADERS: system_header.h:1:13: warning: single-argument constructors must be marked explicit |