Index: clang/include/clang/AST/CommentSema.h =================================================================== --- clang/include/clang/AST/CommentSema.h +++ clang/include/clang/AST/CommentSema.h @@ -238,8 +238,9 @@ /// Returns index of a function parameter with the name closest to a given /// typo. - unsigned correctTypoInParmVarReference(StringRef Typo, - ArrayRef ParamVars); + static unsigned + correctTypoInParmVarReference(StringRef Typo, + ArrayRef ParamVars); bool resolveTParamReference(StringRef Name, const TemplateParameterList *TemplateParameters, Index: clang/lib/AST/CommentSema.cpp =================================================================== --- clang/lib/AST/CommentSema.cpp +++ clang/lib/AST/CommentSema.cpp @@ -729,93 +729,174 @@ } } -void Sema::resolveParamCommandIndexes(const FullComment *FC) { - if (!isFunctionDecl()) { - // We already warned that \\param commands are not attached to a function - // decl. - return; +static unsigned resolveParmVarReference(StringRef Name, + ArrayRef ParamVars, + bool IsFunctionOrMethodVariadic) { + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { + const IdentifierInfo *II = ParamVars[i]->getIdentifier(); + if (II && II->getName() == Name) + return i; + } + if (Name == "..." && IsFunctionOrMethodVariadic) + return ParamCommandComment::VarArgParamIndex; + return ParamCommandComment::InvalidParamIndex; +} + +// Extracts all ParamCommandComments while setting IsVarArgParam and ParamIndex +static SmallVector +extractParamCommandComments(const FullComment *const FC, + ArrayRef ParamVars, + const bool IsFunctionOrMethodVariadic) { + SmallVector PCCs; + + for (auto Block : FC->getBlocks()) { + ParamCommandComment *PCC = dyn_cast(Block); + if (PCC && PCC->hasParamName()) { + StringRef ParamName = PCC->getParamNameAsWritten(); + const unsigned Index = resolveParmVarReference( + ParamName, ParamVars, IsFunctionOrMethodVariadic); + if (Index == ParamCommandComment::VarArgParamIndex) + PCC->setIsVarArgParam(); + else if (Index != ParamCommandComment::InvalidParamIndex) + PCC->setParamIndex(Index); + + PCCs.push_back(PCC); + } } - SmallVector UnresolvedParamCommands; + return PCCs; +} - // Comment AST nodes that correspond to \c ParamVars for which we have - // found a \\param command or NULL if no documentation was found so far. - SmallVector ParamVarDocs; +/// Returns all ParmVarDecl without a comment. Diagnosis is emitted in case of +/// mapping issues (no matching ParmVar or duplicate Comment). +static SmallVector mapParamCommentsToParmVars( + const SmallVector &PCCs, + const ArrayRef &ParmVars, DiagnosticsEngine &Diags) { + // Matching ParamCommandComment by index of ParmVars. + // It's basically a map in terms of + // readability, but vector is faster, as it usually contains only very few + // elements and the index is known on access. Downside is no support for + // VarArg. + SmallVector ParmVarDeclToParamCommandComment( + ParmVars.size(), nullptr); + + for (auto PCC : PCCs) { + if (PCC->isVarArgParam()) { + // No additional diagnostic supported yet. + } else if (PCC->isParamIndexValid()) { + assert(PCC->getParamIndex() < ParmVars.size() && + "PCCs and ParmVars vectors do not match"); + if (auto PrevPCC = + ParmVarDeclToParamCommandComment[PCC->getParamIndex()]) { + Diags.Report(PCC->getLocation(), diag::warn_doc_param_duplicate) + << PCC->getParamNameAsWritten() << PCC->getParamNameRange(); + Diags.Report(PrevPCC->getLocation(), diag::note_doc_param_previous) + << PrevPCC->getParamNameRange(); + } + ParmVarDeclToParamCommandComment[PCC->getParamIndex()] = PCC; + } else + Diags.Report(PCC->getLocation(), diag::warn_doc_param_not_found) + << PCC->getParamNameAsWritten() << PCC->getParamNameRange(); + } - ArrayRef ParamVars = getParamVars(); - ParamVarDocs.resize(ParamVars.size(), nullptr); + SmallVector ParmVarsWithoutComment; - // First pass over all \\param commands: resolve all parameter names. - for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end(); - I != E; ++I) { - ParamCommandComment *PCC = dyn_cast(*I); - if (!PCC || !PCC->hasParamName()) - continue; - StringRef ParamName = PCC->getParamNameAsWritten(); + for (unsigned i = 0, e = ParmVars.size(); i != e; ++i) + if (!ParmVarDeclToParamCommandComment[i]) + ParmVarsWithoutComment.push_back(ParmVars[i]); - // Check that referenced parameter name is in the function decl. - const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName, - ParamVars); - if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) { - PCC->setIsVarArgParam(); - continue; - } - if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) { - UnresolvedParamCommands.push_back(PCC); - continue; + return ParmVarsWithoutComment; +} + +static SmallVector +extractUnresolvedParamComments( + const SmallVector &ParamComments) { + SmallVector UnresolvedParamComments; + + for (auto PCC : ParamComments) + if (!PCC->isParamIndexValid()) + UnresolvedParamComments.push_back(PCC); + + return UnresolvedParamComments; +} + +void attemptToMatchUnresolvedComments( + const SmallVector &UnresolvedParamComments, + const SmallVector &OrphanedParmVars, + DiagnosticsEngine &Diags) { + // Quick exit for the normal case where UnresolvedParamComments is empty. + if (UnresolvedParamComments.empty() || OrphanedParmVars.empty()) + return; + + // Corrected OrphanedParmVar by index of UnresolvedParamComments. + SmallVector ParmVarForComment( + UnresolvedParamComments.size(), nullptr); + + // ParmVarForComment index by index of OrphanedParmVars. + // Note: Entries are never deleted and are therefore partially part of + // duplications. + SmallVector CommentToParmVar( + OrphanedParmVars.size(), ParamCommandComment::InvalidParamIndex); + + if (UnresolvedParamComments.size() == 1 && OrphanedParmVars.size() == 1) + // If one parameter is not documented then that parameter is the only + // possible suggestion. + ParmVarForComment[0] = OrphanedParmVars.front(); + else { + for (unsigned i = 0, e = UnresolvedParamComments.size(); i != e; ++i) { + + StringRef ParamName = UnresolvedParamComments[i]->getParamNameAsWritten(); + + unsigned CorrectedParmVarIndex = + Sema::correctTypoInParmVarReference(ParamName, OrphanedParmVars); + + if (CorrectedParmVarIndex != ParamCommandComment::InvalidParamIndex) { + if (CommentToParmVar[CorrectedParmVarIndex] != + ParamCommandComment::InvalidParamIndex) { + ParmVarForComment[CommentToParmVar[CorrectedParmVarIndex]] = nullptr; + ParmVarForComment[i] = nullptr; + } else { + CommentToParmVar[CorrectedParmVarIndex] = i; + ParmVarForComment[i] = OrphanedParmVars[CorrectedParmVarIndex]; + } + } } - PCC->setParamIndex(ResolvedParamIndex); - if (ParamVarDocs[ResolvedParamIndex]) { - SourceRange ArgRange = PCC->getParamNameRange(); - Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate) - << ParamName << ArgRange; - ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex]; - Diag(PrevCommand->getLocation(), diag::note_doc_param_previous) - << PrevCommand->getParamNameRange(); + } + + for (unsigned i = 0, e = UnresolvedParamComments.size(); i != e; ++i) { + if (ParmVarForComment[i]) { + if (const IdentifierInfo *CorrectedII = + ParmVarForComment[i]->getIdentifier()) { + const ParamCommandComment *PCC = UnresolvedParamComments[i]; + + Diags.Report(PCC->getLocation(), diag::note_doc_param_name_suggestion) + << CorrectedII->getName() + << FixItHint::CreateReplacement(PCC->getParamNameRange(), + CorrectedII->getName()); + } } - ParamVarDocs[ResolvedParamIndex] = PCC; } +} - // Find parameter declarations that have no corresponding \\param. - SmallVector OrphanedParamDecls; - for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) { - if (!ParamVarDocs[i]) - OrphanedParamDecls.push_back(ParamVars[i]); +void Sema::resolveParamCommandIndexes(const FullComment *FC) { + if (!isFunctionDecl()) { + // We already warned that \\param commands are not attached to a function + // decl. + return; } - // Second pass over unresolved \\param commands: do typo correction. - // Suggest corrections from a set of parameter declarations that have no - // corresponding \\param. - for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) { - const ParamCommandComment *PCC = UnresolvedParamCommands[i]; + const ArrayRef ParmVars = getParamVars(); + const SmallVector ParamComments = + extractParamCommandComments(FC, ParmVars, isFunctionOrMethodVariadic()); - SourceRange ArgRange = PCC->getParamNameRange(); - StringRef ParamName = PCC->getParamNameAsWritten(); - Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found) - << ParamName << ArgRange; + const SmallVector ParmVarsWithoutComment = + mapParamCommentsToParmVars(ParamComments, ParmVars, Diags); - // All parameters documented -- can't suggest a correction. - if (OrphanedParamDecls.size() == 0) - continue; + const SmallVector UnresolvedParamComments = + extractUnresolvedParamComments(ParamComments); - unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex; - if (OrphanedParamDecls.size() == 1) { - // If one parameter is not documented then that parameter is the only - // possible suggestion. - CorrectedParamIndex = 0; - } else { - // Do typo correction. - CorrectedParamIndex = correctTypoInParmVarReference(ParamName, - OrphanedParamDecls); - } - if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) { - const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex]; - if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier()) - Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion) - << CorrectedII->getName() - << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName()); - } - } + attemptToMatchUnresolvedComments(UnresolvedParamComments, + ParmVarsWithoutComment, Diags); } bool Sema::isFunctionDecl() { @@ -1032,14 +1113,8 @@ unsigned Sema::resolveParmVarReference(StringRef Name, ArrayRef ParamVars) { - for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { - const IdentifierInfo *II = ParamVars[i]->getIdentifier(); - if (II && II->getName() == Name) - return i; - } - if (Name == "..." && isFunctionOrMethodVariadic()) - return ParamCommandComment::VarArgParamIndex; - return ParamCommandComment::InvalidParamIndex; + return ::clang::comments::resolveParmVarReference( + Name, ParamVars, isFunctionOrMethodVariadic()); } namespace { Index: clang/test/Sema/warn-documentation-fixits.cpp =================================================================== --- clang/test/Sema/warn-documentation-fixits.cpp +++ clang/test/Sema/warn-documentation-fixits.cpp @@ -2,39 +2,82 @@ // RUN %clang_cc1 -std=c++11 -fsyntax-only -Wdocumentation -Wdocumentation-pedantic -fcomment-block-commands=foobar -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck -DATTRIBUTE="__attribute__((deprecated))" %s // RUN: %clang_cc1 -std=c++14 -fsyntax-only -Wdocumentation -Wdocumentation-pedantic -fcomment-block-commands=foobar -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK14 -DATTRIBUTE="[[deprecated]]" %s -// expected-warning@+1 {{parameter 'ZZZZZZZZZZ' not found in the function declaration}} expected-note@+1 {{did you mean 'a'?}} +// expected-warning@+2 {{parameter 'ZZZZZZZZZZ' not found in the function declaration}} expected-note@+2 {{did you mean 'a'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:22}:"a" /// \param ZZZZZZZZZZ Blah blah. -int test1(int a); +int fixSinceThereIsOnlyOneOption(int a); -// expected-warning@+1 {{parameter 'aab' not found in the function declaration}} expected-note@+1 {{did you mean 'aaa'?}} +// expected-warning@+2 {{parameter 'aab' not found in the function declaration}} expected-note@+2 {{did you mean 'aaa'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:12-[[@LINE+1]]:15}:"aaa" /// \param aab Blah blah. -int test2(int aaa, int bbb); - -// expected-warning@+1 {{template parameter 'ZZZZZZZZZZ' not found in the template declaration}} expected-note@+1 {{did you mean 'T'?}} +int fixParamCommentToBestMatch(int aaa, int bbb); + +// expected-warning@+3 {{parameter 'aab' not found in the function declaration}} expected-note-NOT@+3 {{did you mean}} +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+2]]: +/// \param aaa Documented +/// \param aab Do not duplicate documentation of aaa. +int doNotDuplicateExistingComments(int aaa); + +// expected-warning@+4 {{parameter 'aaaa' not found in the function declaration}} expected-note@+4 {{did you mean 'aaa'?}} +// expected-warning@+4 {{parameter 'ThisOneHasBeenRemovedButIsStillDocumented' not found in the function declaration}} expected-note-NOT@+4 {{did you mean}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]: +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+2]]: +/// \param aaaa Almost there. +/// \param ThisOneHasBeenRemovedButIsStillDocumented Widely different name! +int doNotDuplicateCommentsViaFixIt(int aaa); + +// expected-warning@+6 {{parameter 'aaaa' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'aaab' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'aaac' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+3]]: +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+3]]: +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+3]]: +/// \param aaaa Almost there! +/// \param aaab Me too! +/// \param aaac Me three! +int onlyFixWhenExactlyOneBestParamComment(int aaa); + +// Auto-fixing those two parameters would potentially worsen the situation as information is lost. +// At the moment they at least have different names and a human can resonably pick the correct one by name. +// expected-warning@+4 {{parameter 'ArgC' not found in the function declaration}} expected-note-NOT@+4 {{did you mean}} +// expected-warning@+4 {{parameter 'ArgV' not found in the function declaration}} expected-note-NOT@+4 {{did you mean}} +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+2]]: +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+2]]: +/// \param ArgC Number of elements. +/// \param ArgV Array of elements (int for simplicity here). +int test2to2(int argc, int argv); + +// expected-warning@+2 {{template parameter 'ZZZZZZZZZZ' not found in the template declaration}} expected-note@+2 {{did you mean 'T'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:23}:"T" /// \tparam ZZZZZZZZZZ Aaa template void test3(T aaa); -// expected-warning@+1 {{template parameter 'SomTy' not found in the template declaration}} expected-note@+1 {{did you mean 'SomeTy'?}} +// expected-warning@+2 {{template parameter 'SomTy' not found in the template declaration}} expected-note@+2 {{did you mean 'SomeTy'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:13-[[@LINE+1]]:18}:"SomeTy" /// \tparam SomTy Aaa /// \tparam OtherTy Bbb template void test4(SomeTy aaa, OtherTy bbb); -// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_1(); -// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_2(int a); struct test_deprecated_3 { - // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_4(); - // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_5() { } @@ -42,24 +85,28 @@ template struct test_deprecated_6 { - // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_7(); - // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated void test_deprecated_8() { } }; class PR43753 { - // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} - // expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+3 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} + // expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated static void test_deprecated_static(); - // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} - // expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+3 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} + // expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated static auto test_deprecated_static_trailing_return() -> int; @@ -81,8 +128,9 @@ auto test_deprecated_trailing_return() -> int; #if __cplusplus >= 201402L - // expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} - // expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + // expected-warning@+3 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} + // expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} + // CHECK14: fix-it:"{{.*}}":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"[[ATTRIBUTE]] " /// \deprecated decltype(auto) test_deprecated_decltype_auto() const { return a; } @@ -92,25 +140,29 @@ }; #define MY_ATTR_DEPRECATED __attribute__((deprecated)) -// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"MY_ATTR_DEPRECATED " /// \deprecated void test_deprecated_9(int a); #if __cplusplus >= 201402L #define ATTRIBUTE_DEPRECATED [[deprecated]] -// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +// CHECK14: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"ATTRIBUTE_DEPRECATED " /// \deprecated void test_deprecated_10(int a); #endif // rdar://12381408 -// expected-warning@+2 {{unknown command tag name 'retur'; did you mean 'return'?}} +// expected-warning@+3 {{unknown command tag name 'retur'; did you mean 'return'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:6-[[@LINE+2]]:11}:"return" /// \brief testing fixit /// \retur int in FooBar int FooBar(); -// expected-warning@+1 {{unknown command tag name 'fooba'; did you mean 'foobar'?}} +// expected-warning@+2 {{unknown command tag name 'fooba'; did you mean 'foobar'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:6-[[@LINE+1]]:11}:"foobar" /// \fooba bbb IS_DOXYGEN_END int gorf(); @@ -118,26 +170,8 @@ /// \t bbb IS_DOXYGEN_END int Bar(); -// expected-warning@+2 {{unknown command tag name 'encode'; did you mean 'endcode'?}} -// expected-warning@+1 {{'\endcode' command does not terminate a verbatim text block}} +// expected-warning@+3 {{unknown command tag name 'encode'; did you mean 'endcode'?}} +// expected-warning@+2 {{'\endcode' command does not terminate a verbatim text block}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:6-[[@LINE+1]]:12}:"endcode" /// \encode PR18051 int PR18051(); - -// CHECK: fix-it:"{{.*}}":{6:12-6:22}:"a" -// CHECK: fix-it:"{{.*}}":{10:12-10:15}:"aaa" -// CHECK: fix-it:"{{.*}}":{14:13-14:23}:"T" -// CHECK: fix-it:"{{.*}}":{19:13-19:18}:"SomeTy" -// CHECK: fix-it:"{{.*}}":{26:1-26:1}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{30:1-30:1}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{35:3-35:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{39:3-39:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{47:3-47:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{51:3-51:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{76:3-76:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{81:3-81:3}:"[[ATTRIBUTE]] " -// CHECK14: fix-it:"{{.*}}":{87:3-87:3}:"[[ATTRIBUTE]] " -// CHECK: fix-it:"{{.*}}":{97:1-97:1}:"MY_ATTR_DEPRECATED " -// CHECK14: fix-it:"{{.*}}":{104:1-104:1}:"ATTRIBUTE_DEPRECATED " -// CHECK: fix-it:"{{.*}}":{110:6-110:11}:"return" -// CHECK: fix-it:"{{.*}}":{114:6-114:11}:"foobar" -// CHECK: fix-it:"{{.*}}":{123:6-123:12}:"endcode" Index: llvm/unittests/ADT/StringRefTest.cpp =================================================================== --- llvm/unittests/ADT/StringRefTest.cpp +++ llvm/unittests/ADT/StringRefTest.cpp @@ -531,6 +531,9 @@ TEST(StringRefTest, EditDistance) { StringRef Hello("hello"); + EXPECT_EQ(2U, Hello.edit_distance("hellooo")); + EXPECT_EQ(2U, Hello.edit_distance("hel")); + EXPECT_EQ(2U, Hello.edit_distance("Hallo")); EXPECT_EQ(2U, Hello.edit_distance("hill")); StringRef Industry("industry");