diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -602,6 +602,13 @@ MostRecentLocation = *StartLoc; } + // If either location is invalid, set it to std::nullopt to avoid + // letting users of RegionStack think that region has a valid start/end + // location. + if (StartLoc && StartLoc->isInvalid()) + StartLoc = std::nullopt; + if (EndLoc && EndLoc->isInvalid()) + EndLoc = std::nullopt; RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc); return RegionStack.size() - 1; @@ -624,7 +631,8 @@ assert(RegionStack.size() >= ParentIndex && "parent not in stack"); while (RegionStack.size() > ParentIndex) { SourceMappingRegion &Region = RegionStack.back(); - if (Region.hasStartLoc()) { + if (Region.hasStartLoc() && + (Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) { SourceLocation StartLoc = Region.getBeginLoc(); SourceLocation EndLoc = Region.hasEndLoc() ? Region.getEndLoc() @@ -691,7 +699,7 @@ assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc)); assert(SpellingRegion(SM, Region).isInSourceOrder()); SourceRegions.push_back(Region); - } + } RegionStack.pop_back(); } } @@ -891,6 +899,8 @@ /// Find a valid gap range between \p AfterLoc and \p BeforeLoc. std::optional findGapAreaBetween(SourceLocation AfterLoc, SourceLocation BeforeLoc) { + if (AfterLoc.isInvalid() || BeforeLoc.isInvalid()) + return std::nullopt; // If AfterLoc is in function-like macro, use the right parenthesis // location. if (AfterLoc.isMacroID()) { diff --git a/clang/test/CoverageMapping/invalid_location.cpp b/clang/test/CoverageMapping/invalid_location.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/invalid_location.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++20 -stdlib=libc++ -triple %itanium_abi_triple %s | FileCheck %s + +namespace std { template class initializer_list {}; } + +template struct T { + T(std::initializer_list, int = int()); + bool b; +}; + +template struct S1 { + static void foo() { + class C; + 0 ? T{} : T{}; + 0 ? 1 : 2; + } +}; + +void bar() { + S1::foo(); +} + + +// CHECK: _ZN2S1IiE3fooEv: +// CHECK-NEXT: File 0, 11:21 -> 15:4 = #0 +// CHECK-NEXT: File 0, 13:5 -> 13:6 = #0 +// CHECK-NEXT: Branch,File 0, 13:5 -> 13:6 = 0, 0 +// CHECK-NEXT: Gap,File 0, 13:8 -> 13:9 = #1 +// FIXME: It's supposed to have two different counters for true and false +// branches at line 13 as it has for line 14, shown below. It's missing +// now because 'T{}' doesn't have a valid end location for now. +// Once it's fixed, correct the following two "CHECK-NETX" and #3 and #2 +// may need to swap positions. +// CHECK-NETX: File 0, 13:9 -> 13:14 = #3 +// CHECK-NETX: File 0, 13:18 -> 13:23 = (#0 - #3) + +// CHECK-NEXT: File 0, 14:5 -> 14:6 = #0 +// CHECK-NEXT: Branch,File 0, 14:5 -> 14:6 = 0, 0 +// CHECK-NEXT: Gap,File 0, 14:8 -> 14:9 = #2 +// CHECK-NEXT: File 0, 14:9 -> 14:10 = #2 +// CHECK-NEXT: File 0, 14:13 -> 14:14 = (#0 - #2)