blob: b592636c2f98eb656b68209a120cfe858165732c [file] [log] [blame]
/*
* Copyright 2017 The Bazel Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.idea.blaze.cpp.includes;
import static com.google.common.truth.Truth.assertThat;
import com.google.idea.blaze.cpp.BlazeCppIntegrationTestCase;
import com.google.idea.blaze.cpp.includes.IwyuPragmas.ExportPragma;
import com.google.idea.blaze.cpp.includes.IwyuPragmas.KeepPragma;
import com.google.idea.blaze.cpp.includes.IwyuPragmas.PrivatePragma;
import com.jetbrains.cidr.lang.psi.OCFile;
import com.jetbrains.cidr.lang.psi.OCIncludeDirective.Delimiters;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for parsing {@link IwyuPragmas}. */
@RunWith(JUnit4.class)
public class IwyuPragmasTest extends BlazeCppIntegrationTestCase {
@Test
public void noPragmas() {
OCFile file =
createFile(
"bar.cc",
"#include \"bar.h\"",
"",
"#include <memory>",
"#include <vector>",
"",
"#include \"f/foo1.h\"",
"#include \"f/foo2.h\"",
"#include \"f/foo3.h\" // blah",
"",
"void bar() {}");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isFalse();
assertThat(pragmas.keeps).isEmpty();
assertThat(pragmas.exports).isEmpty();
assertThat(pragmas.associatedHeader.isPresent()).isFalse();
}
@Test
public void incompletePath_parse() {
OCFile file = createFile("bar.cc", "#include <memory>", "#include <vecto");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps).isEmpty();
}
@Test
public void noPathYet_parse() {
OCFile file = createFile("bar.cc", "#include <memory>", "#include ");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps).isEmpty();
}
@Test
public void lineComment_parseKeep() {
OCFile file =
createFile(
"bar.cc",
"#include \"bar.h\"",
"",
"#include <memory>",
"#include <vector> // IWYU pragma: keep",
"",
"#include \"f/foo1.h\" // IWYU pragma: keep",
"#include \"f/foo2.h\"",
"#include \"f/foo3.h\" // IWYU pragma: keep ",
"",
"void bar() {}");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(
KeepPragma.create(IncludePath.create("vector", Delimiters.ANGLE_BRACKETS)),
KeepPragma.create(IncludePath.create("f/foo1.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("f/foo3.h", Delimiters.QUOTES)));
}
@Test
public void blockComment_parseKeep() {
OCFile file =
createFile(
"bar.cc",
"#include \"bar.h\"",
"",
"#include <memory>",
"#include <vector> /* IWYU pragma: keep */",
"",
"#include \"f/foo1.h\" /* IWYU pragma: keep */",
"#include \"f/foo2.h\"",
"#include \"f/foo3.h\" /* IWYU pragma: keep */",
"",
"void bar() {}");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(
KeepPragma.create(IncludePath.create("vector", Delimiters.ANGLE_BRACKETS)),
KeepPragma.create(IncludePath.create("f/foo1.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("f/foo3.h", Delimiters.QUOTES)));
}
@Test
public void lineCommentIncludeGuardPrefixes_parseKeep() {
OCFile file =
createFile(
"bar.h",
"#ifndef BAR_H_",
"#define BAR_H_",
"#include <memory>",
"#include <vector> // IWYU pragma: keep",
"",
"#include \"f/foo1.h\" // IWYU pragma: keep",
"#include \"f/foo2.h\"",
"#include \"f/foo3.h\" // IWYU pragma: keep ",
"",
"void bar();",
"#endif");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(
KeepPragma.create(IncludePath.create("vector", Delimiters.ANGLE_BRACKETS)),
KeepPragma.create(IncludePath.create("f/foo1.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("f/foo3.h", Delimiters.QUOTES)));
}
@Test
public void incompletePragma_parseKeep() {
OCFile file =
createFile(
"bar.cc",
"#include \"f/foo1.h\" // IWYU pragma: kee",
"#include \"f/foo2.h\" // IWYU pragma: keep");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(KeepPragma.create(IncludePath.create("f/foo2.h", Delimiters.QUOTES)));
}
@Test
public void hasSuffixSoNoMatch_parseKeep() {
OCFile file =
createFile(
"bar.cc",
"#include \"f/foo1.h\" // IWYU pragma: keepaway",
"#include \"f/foo2.h\" // IWYU pragma: keep");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(KeepPragma.create(IncludePath.create("f/foo2.h", Delimiters.QUOTES)));
}
@Test
public void insideNamespace_parseKeep() {
OCFile file =
createFile(
"bar.cc",
"#include \"f/foo1.h\" // IWYU pragma: keep",
"#include \"f/foo2.h\"",
"namespace change_namespace {",
"#include \"f/foo3.h\" // IWYU pragma: keep",
"}",
"class C {",
"#include \"f/foo4.h\" // IWYU pragma: keep",
"};");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(
KeepPragma.create(IncludePath.create("f/foo1.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("f/foo3.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("f/foo4.h", Delimiters.QUOTES)));
}
@Test
public void duplicate_parseKeep() {
// Probably need to disambiguate the PSI nodes somewhere (last one shouldn't be kept?)
OCFile file =
createFile(
"bar.cc",
"#include \"f/foo1.h\" // IWYU pragma: keep",
"#include \"f/foo1.h\" // IWYU pragma: keep",
"#include \"f/foo1.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(KeepPragma.create(IncludePath.create("f/foo1.h", Delimiters.QUOTES)));
}
@Test
public void parseExport() {
OCFile file =
createFile(
"public/foo.h",
"/** Stuff */",
"#include <private/memory> // IWYU pragma: export",
"#include <vector>",
"",
"#include \"private/foo1.h\" // IWYU pragma: export",
"#include \"private/foo2.h\"",
"#include \"private/foo3.h\" // IWYU pragma: export",
"",
"void doFoo(Foo* f);");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.exports)
.containsExactly(
ExportPragma.create(IncludePath.create("private/memory", Delimiters.ANGLE_BRACKETS)),
ExportPragma.create(IncludePath.create("private/foo1.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo3.h", Delimiters.QUOTES)));
}
@Test
public void singeRange_parseExportBeginEnd() {
OCFile file =
createFile(
"public/foo.h",
"/** Stuff */",
"// IWYU pragma: begin_exports",
"#include <private/memory>",
"#include \"private/foo1.h\"",
"#include \"private/foo2.h\"",
"// IWYU pragma: end_exports",
"#include \"private/foo3.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.exports)
.containsExactly(
ExportPragma.create(IncludePath.create("private/memory", Delimiters.ANGLE_BRACKETS)),
ExportPragma.create(IncludePath.create("private/foo1.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo2.h", Delimiters.QUOTES)));
}
@Test
public void multipleRanges_parseExportBeginEnd() {
OCFile file =
createFile(
"public/foo.h",
"/** Stuff */",
"// IWYU pragma: begin_exports",
"#include <private/memory>",
"#include \"private/foo1.h\"",
"#include \"private/foo2.h\"",
"// IWYU pragma: end_exports",
"#include \"private/foo3.h\"",
"#include \"private/foo4.h\"",
" // IWYU pragma: begin_exports",
"#include \"private/foo5.h\"",
" // IWYU pragma: end_exports",
"#include \"private/foo6.h\"",
"",
"void doFoo(Foo& f);");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.exports)
.containsExactly(
ExportPragma.create(IncludePath.create("private/memory", Delimiters.ANGLE_BRACKETS)),
ExportPragma.create(IncludePath.create("private/foo1.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo2.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo5.h", Delimiters.QUOTES)));
}
@Test
public void exportRangePlusInlineKeep() {
// Probably need to disambiguate the PSI nodes somewhere (last one shouldn't be kept?)
OCFile file =
createFile(
"public/foo.h",
"// IWYU pragma: begin_exports",
"#include <memory>",
"#include \"private/foo1.h\" // IWYU pragma: keep",
"#include \"private/foo2.h\" // IWYU pragma: keep",
"#include \"private/foo3.h\"",
"// IWYU pragma: end_exports",
"#include \"private/foo4.h\"",
"",
"void doFoo(Foo& f);");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.keeps)
.containsExactly(
KeepPragma.create(IncludePath.create("private/foo1.h", Delimiters.QUOTES)),
KeepPragma.create(IncludePath.create("private/foo2.h", Delimiters.QUOTES)));
assertThat(pragmas.exports)
.containsExactly(
ExportPragma.create(IncludePath.create("memory", Delimiters.ANGLE_BRACKETS)),
ExportPragma.create(IncludePath.create("private/foo1.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo2.h", Delimiters.QUOTES)),
ExportPragma.create(IncludePath.create("private/foo3.h", Delimiters.QUOTES)));
}
@Test
public void parsePrivate() {
OCFile file =
createFile(
"private.h", "// Stuff", "// IWYU pragma: private", "#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isFalse();
}
@Test
public void incomplete_parsePrivate() {
OCFile file =
createFile(
"private.h", "// Stuff", "// IWYU pragma: privat", "#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isFalse();
}
@Test
public void parsePrivateIncludeOther() {
OCFile file =
createFile(
"private.h",
"// IWYU pragma: private, include \"f/public.h\"",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isTrue();
assertThat(privatePragma.includeOther.get())
.isEqualTo(IncludePath.create("f/public.h", Delimiters.QUOTES));
}
@Test
public void withWhiteSpace_parsePrivateIncludeOther() {
OCFile file =
createFile(
"private.h",
"// IWYU pragma:private,include\"f/public.h\" ",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isTrue();
assertThat(privatePragma.includeOther.get())
.isEqualTo(IncludePath.create("f/public.h", Delimiters.QUOTES));
}
@Test
public void parsePrivateIncludeOtherAngle() {
OCFile file =
createFile(
"private.h",
"// IWYU pragma: private, include <f/public.h>",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isTrue();
assertThat(privatePragma.includeOther.get())
.isEqualTo(IncludePath.create("f/public.h", Delimiters.ANGLE_BRACKETS));
}
@Test
public void parsePrivateIncludeOtherNoQuotes() {
OCFile file =
createFile(
"private.h",
"// IWYU pragma: private, include f/public.h",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isTrue();
assertThat(privatePragma.includeOther.get())
.isEqualTo(IncludePath.create("f/public.h", Delimiters.NONE));
}
@Test
public void hasMultiple_parsePrivate() {
// We shouldn't have multiple private pragmas, but we just take the last one.
// An alternative might be to take the most descriptive one (when there is a unique
// most descriptive one).
OCFile file =
createFile(
"private.h",
"// Stuff",
"// IWYU pragma: private",
"// IWYU pragma: private, include \"public.h\"",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isTrue();
assertThat(privatePragma.includeOther.get())
.isEqualTo(IncludePath.create("public.h", Delimiters.QUOTES));
}
@Test
public void hasMultipleOtherOrder_parsePrivate() {
// Like hasMultiple_parsePrivate, but with order swapped
OCFile file =
createFile(
"private.h",
"// Stuff",
"// IWYU pragma: private, include \"public.h\"",
"// IWYU pragma: private",
"#include \"private_details.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isFalse();
}
@Test
public void sortOfCommentViaIfdef_parsePrivate() {
OCFile file =
createFile("bar.cc", "#include <memory>", "#if 0", "IWYU pragma: private", "#endif");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
// We don't really support this, but #ifdef'ed out things may be parsed as PsiComment,
// so at least make sure we don't assert. Answer could go either way.
assertThat(pragmas.privatePragma.isPresent()).isTrue();
PrivatePragma privatePragma = pragmas.privatePragma.get();
assertThat(privatePragma.includeOther.isPresent()).isFalse();
}
@Test
public void parseAssociated() {
OCFile file =
createFile(
"implementation.cc",
"#include \"some/interface.h\" // IWYU pragma: associated",
"",
"#include \"other_stuff.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.associatedHeader.isPresent()).isTrue();
assertThat(pragmas.associatedHeader.get())
.isEqualTo(IncludePath.create("some/interface.h", Delimiters.QUOTES));
}
@Test
public void parseAssociatedAngle() {
OCFile file =
createFile(
"implementation.cc",
"#include <some/interface.h> // IWYU pragma: associated",
"",
"#include \"other_stuff.h\"");
IwyuPragmas pragmas = IwyuPragmas.parse(file);
assertThat(pragmas.associatedHeader.isPresent()).isTrue();
assertThat(pragmas.associatedHeader.get())
.isEqualTo(IncludePath.create("some/interface.h", Delimiters.ANGLE_BRACKETS));
}
}