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 @@ -17,6 +17,9 @@ #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "commentsema" namespace clang { namespace comments { @@ -729,93 +732,202 @@ } } -void Sema::resolveParamCommandIndexes(const FullComment *FC) { - if (!isFunctionDecl()) { - // We already warned that \\param commands are not attached to a function - // decl. - return; +/// Finds ParmVarDecl with matching Name +/// @param IsFunctionOrMethodVariadic If true this will match Name "..." to +/// VarArgParamIndex +/// @return index of match within ParamVars, InvalidParamIndex if no match +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; +} - SmallVector UnresolvedParamCommands; +// Extracts all ParamCommandComments while setting IsVarArgParam and ParamIndex +static SmallVector +extractParamCommandComments(const FullComment *const FC, + ArrayRef ParamVars, + const bool IsFunctionOrMethodVariadic) { + SmallVector PCCs; + + for (BlockContentComment *const Block : FC->getBlocks()) { + ParamCommandComment *const 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); + } + } - // 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; + return PCCs; +} - ArrayRef ParamVars = getParamVars(); - ParamVarDocs.resize(ParamVars.size(), nullptr); +/// 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 (const ParamCommandComment *const 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 there is a previous comment for the same ParmVarDecl, this is a + // duplicate. + if (const ParamCommandComment *const 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(); + } + // Store mapping of ParmVarDecl[index] to ParamCommandComment to detect + // duplicates (above) and to detect unmapped parameters (below). + // Overwrite in case of duplicate, it does not matter. + ParmVarDeclToParamCommandComment[PCC->getParamIndex()] = PCC; + } else + Diags.Report(PCC->getLocation(), diag::warn_doc_param_not_found) + << PCC->getParamNameAsWritten() << PCC->getParamNameRange(); + } - // 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(); + SmallVector ParmVarsWithoutComment; - // 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; + for (unsigned I = 0, E = ParmVars.size(); I != E; ++I) + if (!ParmVarDeclToParamCommandComment[I]) + ParmVarsWithoutComment.push_back(ParmVars[I]); + + return ParmVarsWithoutComment; +} + +static SmallVector +extractUnresolvedParamComments( + const SmallVector &ParamComments) { + SmallVector UnresolvedParamComments; + + for (const ParamCommandComment *const 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. This is for detecting + // the case where different comments are corrected to the same parameter. + // 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) { + // Correction suggestion exists (close match by name). + + if (CommentToParmVar[CorrectedParmVarIndex] != + ParamCommandComment::InvalidParamIndex) { + // There was already a suggestion to match a different comment to this + // parameter! Remove old and new correction. + LLVM_DEBUG(llvm::dbgs() << "Duplicate suggestion to correct @" + << ParamName << " to " + << OrphanedParmVars[CorrectedParmVarIndex] + ->getIdentifier() + ->getName() + << "\n"); + ParmVarForComment[CommentToParmVar[CorrectedParmVarIndex]] = nullptr; + ParmVarForComment[I] = nullptr; + } else { + // Store suggestion into vector for duplicate detection and into + // vector of suggested corrections. + LLVM_DEBUG(llvm::dbgs() + << "Suggestion to correct @" << ParamName << " to " + << OrphanedParmVars[CorrectedParmVarIndex] + ->getIdentifier() + ->getName() + << "\n"); + 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() { @@ -939,8 +1051,8 @@ return false; } static bool isClassOrStructDeclImpl(const Decl *D) { - if (auto *record = dyn_cast_or_null(D)) - return !record->isUnion(); + if (const auto *Record = dyn_cast_or_null(D)) + return !Record->isUnion(); return false; } @@ -969,12 +1081,15 @@ if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl)) return true; - if (auto *ThisTypedefDecl = dyn_cast(ThisDeclInfo->CurrentDecl)) { + if (const auto *ThisTypedefDecl = + dyn_cast(ThisDeclInfo->CurrentDecl)) { auto UnderlyingType = ThisTypedefDecl->getUnderlyingType(); - if (auto ThisElaboratedType = dyn_cast(UnderlyingType)) { + if (const auto *ThisElaboratedType = + dyn_cast(UnderlyingType)) { auto DesugaredType = ThisElaboratedType->desugar(); - if (auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) { - if (auto *ThisRecordType = dyn_cast(DesugaredTypePtr)) { + if (const auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) { + if (const auto *ThisRecordType = + dyn_cast(DesugaredTypePtr)) { return isClassOrStructDeclImpl(ThisRecordType->getAsRecordDecl()); } } @@ -1032,14 +1147,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 { @@ -1074,19 +1183,14 @@ }; void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { - unsigned CurrIndex = NextIndex++; + const unsigned CurrIndex = NextIndex++; - const IdentifierInfo *II = ND->getIdentifier(); + const IdentifierInfo *const II = ND->getIdentifier(); if (!II) return; StringRef Name = II->getName(); - unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); - if (MinPossibleEditDistance > 0 && - Typo.size() / MinPossibleEditDistance < 3) - return; - - unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); + const unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); if (EditDistance < BestEditDistance) { BestEditDistance = EditDistance; BestDecl = ND; @@ -1099,8 +1203,8 @@ StringRef Typo, ArrayRef ParamVars) { SimpleTypoCorrector Corrector(Typo); - for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) - Corrector.addDecl(ParamVars[i]); + for (unsigned I = 0, E = ParamVars.size(); I != E; ++I) + Corrector.addDecl(ParamVars[I]); if (Corrector.getBestDecl()) return Corrector.getBestDeclIndex(); else @@ -1108,22 +1212,21 @@ } namespace { -bool ResolveTParamReferenceHelper( - StringRef Name, - const TemplateParameterList *TemplateParameters, - SmallVectorImpl *Position) { - for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { - const NamedDecl *Param = TemplateParameters->getParam(i); +bool resolveTParamReferenceHelper( + StringRef Name, const TemplateParameterList *TemplateParameters, + SmallVectorImpl *Position) { + for (unsigned I = 0, E = TemplateParameters->size(); I != E; ++I) { + const NamedDecl *Param = TemplateParameters->getParam(I); const IdentifierInfo *II = Param->getIdentifier(); if (II && II->getName() == Name) { - Position->push_back(i); + Position->push_back(I); return true; } if (const TemplateTemplateParmDecl *TTP = dyn_cast(Param)) { - Position->push_back(i); - if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), + Position->push_back(I); + if (resolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), Position)) return true; Position->pop_back(); @@ -1141,20 +1244,20 @@ if (!TemplateParameters) return false; - return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); + return resolveTParamReferenceHelper(Name, TemplateParameters, Position); } namespace { -void CorrectTypoInTParamReferenceHelper( - const TemplateParameterList *TemplateParameters, - SimpleTypoCorrector &Corrector) { - for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { - const NamedDecl *Param = TemplateParameters->getParam(i); +void correctTypoInTParamReferenceHelper( + const TemplateParameterList *TemplateParameters, + SimpleTypoCorrector &Corrector) { + for (unsigned I = 0, E = TemplateParameters->size(); I != E; ++I) { + const NamedDecl *Param = TemplateParameters->getParam(I); Corrector.addDecl(Param); if (const TemplateTemplateParmDecl *TTP = dyn_cast(Param)) - CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), + correctTypoInTParamReferenceHelper(TTP->getTemplateParameters(), Corrector); } } @@ -1164,7 +1267,7 @@ StringRef Typo, const TemplateParameterList *TemplateParameters) { SimpleTypoCorrector Corrector(Typo); - CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); + correctTypoInTParamReferenceHelper(TemplateParameters, Corrector); if (const NamedDecl *ND = Corrector.getBestDecl()) { const IdentifierInfo *II = ND->getIdentifier(); assert(II && "SimpleTypoCorrector should not return this decl"); 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,198 @@ // 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); +int fixParamCommentToBestMatch(int aaa, int bbb); -// expected-warning@+1 {{template parameter 'ZZZZZZZZZZ' not found in the template declaration}} expected-note@+1 {{did you mean 'T'?}} +// 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); + +// Test data and expectations from https://bugs.llvm.org/show_bug.cgi?id=43755. +// Note: there is a an intentional typo in sligtlyMisspelled. +// expected-warning@+4 {{parameter 'sligtlyMisspelled' not found in the function declaration}} expected-note@+4 {{did you mean 'slightlyMisspelled'?}} +// expected-warning@+4 {{parameter 'someParameterWhichWasRemovedLongAgo' 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 sligtlyMisspelled hello +/// @param someParameterWhichWasRemovedLongAgo world +void doNotDuplicateCommentsViaFixIt_43755(int slightlyMisspelled); + +// 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); + +// expected-warning@+6 {{parameter 'aaab' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'aabb' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'abbb' 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 aaab Almost there! +/// \param aabb Me too! +/// \param abbb Me three! +int onlyFixWhenExactlyOneBestParamComment_WithUnrelatedParam(int aaa, int NotMe); + +// expected-warning@+6 {{parameter 'aaab' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'aabb' not found in the function declaration}} expected-note-NOT@+6 {{did you mean}} +// expected-warning@+6 {{parameter 'abbb' 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 aaab Almost there! +/// \param aabb Me too! +/// \param abbb Me three! +/// \param NotMe Match! +int onlyFixWhenExactlyOneBestParamComment_WithUnrelatedParam2(int aaa, int NotMe); + +// 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 Almost matches. +/// \param ArgV Almost matches. +int test2to2_bothAlmost(int argc, int argv); + +// Note: first parameter is the same as above, bug gets auto fixed here since +// it's obviously the better choice. +// expected-warning@+4 {{parameter 'ArgC' not found in the function declaration}} expected-note@+4 {{did you mean}} +// expected-warning@+4 {{parameter 'ZZZZZZ' 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 ArgC Almost matches. +/// \param ZZZZZZ Does not match at all. +int test2to2_oneAlmost(int argc, int argv); + +// Note: first parameter is the same as above, bug gets auto fixed here since +// the other is already ok. +// expected-warning@+4 {{parameter 'ArgC' not found in the function declaration}} expected-note@+4 {{did you mean}} +// expected-warning-NOT@+4 {{parameter 'ZZZZZZ' 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 ArgC Almost matches. +/// \param argv Already matches. +int test2to2_oneAlmost2(int argc, int argv); + +// expected-warning@+4 {{parameter 'YYYYYY' not found in the function declaration}} expected-note-NOT@+4 {{did you mean}} +// expected-warning@+4 {{parameter 'ZZZZZZ' 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 YYYYYY Does not match at all. +/// \param ZZZZZZ Does not match at all. +int test2to2_noMatch(int argc, int argv); + +// expected-warning@+7 {{parameter 'aaab' not found in the function declaration}} expected-note@+7 {{did you mean 'aab'?}} +// expected-warning@+7 {{parameter 'aaac' not found in the function declaration}} expected-note@+7 {{did you mean 'aac'?}} +// expected-warning@+7 {{parameter 'aaad' not found in the function declaration}} expected-note@+7 {{did you mean 'aad'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +/** + * \param aaab Should match aab + * \param aaac Should match aac + * \param aaad Should match aad + */ +void test3to3_allAlmost(int aab, int aac, int aad); + +// expected-warning@+7 {{parameter 'aaab' not found in the function declaration}} expected-note@+7 {{did you mean 'aab'?}} +// expected-warning@+7 {{parameter 'aaac' not found in the function declaration}} expected-note@+7 {{did you mean 'aac'?}} +// expected-warning@+7 {{parameter 'aaad' not found in the function declaration}} expected-note@+7 {{did you mean 'aad'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +// CHECK: fix-it:"{{.*}}":{[[@LINE+4]]: +/** + * \param aaab Should match aab + * \param aaac Should match aac + * \param aaad Should match aad + */ +void test3to3_allAlmost_rev(int aad, int aac, int aab); + + + + +// Test data and expectations from https://bugs.llvm.org/show_bug.cgi?id=43808 +// expected-warning@+5 {{parameter 'arg[]' not found in the function declaration}} expected-note@+5 {{did you mean 'arg'?}} +// expected-warning@+5 {{parameter 'cnt' not found in the function declaration}} expected-note-NOT@+5 {{did you mean 'argc'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+3]]:{{.*}}:"arg" +// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+3]]: +/** + * @param[in] arg[] Docu_1 + * @param[in] cnt Docu_2 + */ +void run(const char *const arg[], int argc); + +// 2nd run after the above has been fixed. Now 1 parameter needs fixing and +// there is only one option, so it's matched regardless of how close they are. +// (see also fixSinceThereIsOnlyOneOption test). +// This could be handled in a single pass in the future. +// expected-warning@+4 {{parameter 'cnt' not found in the function declaration}} expected-note@+4 {{did you mean 'argc'?}} +// CHECK: fix-it:"{{.*}}":{[[@LINE+3]]:{{.*}}:"argc" +/** + * @param[in] arg Docu_1 + * @param[in] cnt Docu_2 + */ +void run(const char *const arg[], int argc); + + + + +// 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 +201,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 +244,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 +256,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 +286,9 @@ /// \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: clang/test/Sema/warn-documentation.cpp =================================================================== --- clang/test/Sema/warn-documentation.cpp +++ clang/test/Sema/warn-documentation.cpp @@ -1041,8 +1041,9 @@ template class test_attach35 { - // expected-warning@+2 {{empty paragraph passed to '\brief' command}} - // expected-warning@+2 {{template parameter 'T' not found in the template declaration}} + // expected-warning@+3 {{empty paragraph passed to '\brief' command}} + // expected-warning@+3 {{template parameter 'T' not found in the template declaration}} + // expected-note@+2 {{did you mean 'TT'?}} /// \brief\author Aaa /// \tparam T Aaa template 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");